diff options
21 files changed, 175 insertions, 265 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj index ebbeb375..68b172fa 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj +++ b/NorthstarDLL/NorthstarDLL.vcxproj @@ -447,7 +447,7 @@ <ClInclude Include="squirrel\squirreldatatypes.h" />
<ClInclude Include="util\utils.h" />
<ClInclude Include="util\version.h" />
- <ClInclude Include="util\wininfo.h" />
+ <ClInclude Include="util\wininfo.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\include\spdlog\fmt\bundled\LICENSE.rst" />
@@ -496,7 +496,6 @@ <ClCompile Include="masterserver\masterserver.cpp" />
<ClCompile Include="mods\compiled\kb_act.cpp" />
<ClCompile Include="mods\compiled\modkeyvalues.cpp" />
- <ClCompile Include="mods\compiled\modpdef.cpp" />
<ClCompile Include="mods\compiled\modscriptsrson.cpp" />
<ClCompile Include="mods\modmanager.cpp" />
<ClCompile Include="pch.cpp">
@@ -538,7 +537,7 @@ <ClCompile Include="util\printmaps.cpp" />
<ClCompile Include="util\utils.cpp" />
<ClCompile Include="util\version.cpp" />
- <ClCompile Include="util\wininfo.cpp" />
+ <ClCompile Include="util\wininfo.cpp" />
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm" />
@@ -547,4 +546,4 @@ <ImportGroup Label="ExtensionTargets">
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets" />
</ImportGroup>
-</Project>
+</Project>
\ No newline at end of file diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters index d8437ba5..e149396f 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj.filters +++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters @@ -1173,8 +1173,8 @@ <ClInclude Include="core\macros.h">
<Filter>Header Files\core</Filter>
</ClInclude>
- <ClInclude Include="util\wininfo.h">
- <Filter>Header Files</Filter>
+ <ClInclude Include="util\wininfo.h">
+ <Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util\utils.h">
<Filter>Header Files\util</Filter>
@@ -1240,9 +1240,6 @@ <ClCompile Include="mods\compiled\modkeyvalues.cpp">
<Filter>Source Files\mods\compiled</Filter>
</ClCompile>
- <ClCompile Include="mods\compiled\modpdef.cpp">
- <Filter>Source Files\mods\compiled</Filter>
- </ClCompile>
<ClCompile Include="mods\compiled\modscriptsrson.cpp">
<Filter>Source Files\mods\compiled</Filter>
</ClCompile>
@@ -1435,4 +1432,4 @@ <Filter>Source Files</Filter>
</MASM>
</ItemGroup>
-</Project>
+</Project>
\ No newline at end of file diff --git a/NorthstarDLL/client/audio.h b/NorthstarDLL/client/audio.h index 26cda205..915d52ce 100644 --- a/NorthstarDLL/client/audio.h +++ b/NorthstarDLL/client/audio.h @@ -1,7 +1,5 @@ #pragma once -#include <vector> -#include <filesystem> #include <regex> #include <shared_mutex> diff --git a/NorthstarDLL/client/clientvideooverrides.cpp b/NorthstarDLL/client/clientvideooverrides.cpp index 1a5924c7..ab78b943 100644 --- a/NorthstarDLL/client/clientvideooverrides.cpp +++ b/NorthstarDLL/client/clientvideooverrides.cpp @@ -12,20 +12,20 @@ void*, __fastcall, (const char* path, uint32_t flags)) spdlog::info("BinkOpen {}", filename); // figure out which mod is handling the bink - Mod* fileOwner = nullptr; - for (Mod& mod : g_pModManager->m_LoadedMods) + Mod* pFileOwner = nullptr; + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.m_bEnabled) continue; if (std::find(mod.BinkVideos.begin(), mod.BinkVideos.end(), filename) != mod.BinkVideos.end()) - fileOwner = &mod; + pFileOwner = &mod; } - if (fileOwner) + if (pFileOwner) { // create new path - fs::path binkPath(fileOwner->m_ModDirectory / "media" / filename); + fs::path binkPath(pFileOwner->m_ModDirectory / "media" / filename); return BinkOpen(binkPath.string().c_str(), flags); } else diff --git a/NorthstarDLL/client/modlocalisation.cpp b/NorthstarDLL/client/modlocalisation.cpp index e5afc793..db138315 100644 --- a/NorthstarDLL/client/modlocalisation.cpp +++ b/NorthstarDLL/client/modlocalisation.cpp @@ -26,7 +26,7 @@ void, __fastcall, (void* pVguiLocalize)) // clang-format on { // load all mod localization manually, so we keep track of all files, not just previously loaded ones - for (Mod mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) if (mod.m_bEnabled) for (std::string& localisationFile : mod.LocalisationFiles) CLocalize__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false); @@ -44,7 +44,7 @@ void, __fastcall, (void* self)) // previously we did this in CLocalize::AddFile, but for some reason it won't properly overwrite localization from // files loaded previously if done there, very weird but this works so whatever - for (Mod mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) if (mod.m_bEnabled) for (std::string& localisationFile : mod.LocalisationFiles) CLocalize__AddFile(g_pVguiLocalize, localisationFile.c_str(), nullptr, false); diff --git a/NorthstarDLL/core/convar/concommand.h b/NorthstarDLL/core/convar/concommand.h index 89363bc7..c170c6a5 100644 --- a/NorthstarDLL/core/convar/concommand.h +++ b/NorthstarDLL/core/convar/concommand.h @@ -97,7 +97,6 @@ class ConCommandBase bool IsCommand(void) const; bool IsRegistered(void) const; bool IsFlagSet(int nFlags) const; - static bool IsFlagSet(ConCommandBase* pCommandBase, int nFlags); // For hooking to engine's implementation. int GetFlags(void) const; ConCommandBase* GetNext(void) const; diff --git a/NorthstarDLL/core/convar/convar.cpp b/NorthstarDLL/core/convar/convar.cpp index 11411c0a..81648f53 100644 --- a/NorthstarDLL/core/convar/convar.cpp +++ b/NorthstarDLL/core/convar/convar.cpp @@ -527,4 +527,6 @@ int ParseConVarFlagsString(std::string modName, std::string sFlags) sCurrentFlag += sFlags[i]; } } + + return FCVAR_NONE; } diff --git a/NorthstarDLL/core/filesystem/filesystem.cpp b/NorthstarDLL/core/filesystem/filesystem.cpp index ac42d00f..bfdfdb19 100644 --- a/NorthstarDLL/core/filesystem/filesystem.cpp +++ b/NorthstarDLL/core/filesystem/filesystem.cpp @@ -95,8 +95,8 @@ bool TryReplaceFile(const char* pPath, bool shouldCompile) // idk how efficient the lexically normal check is // can't just set all /s in path to \, since some paths aren't in writeable memory - auto file = g_pModManager->m_ModFiles.find(g_pModManager->NormaliseModFilePath(fs::path(pPath))); - if (file != g_pModManager->m_ModFiles.end()) + auto file = g_pModManager->GetModFiles().find(g_pModManager->NormaliseModFilePath(fs::path(pPath))); + if (file != g_pModManager->GetModFiles().end()) { SetNewModSearchPaths(file->second.m_pOwningMod); return true; @@ -147,7 +147,7 @@ HOOK(MountVPKHook, MountVPK, VPKData*, , (IFileSystem * fileSystem, const char* NS::log::fs->info("MountVPK {}", pVpkPath); VPKData* ret = MountVPK(fileSystem, pVpkPath); - for (Mod mod : g_pModManager->m_LoadedMods) + for (Mod mod : g_pModManager->GetMods()) { if (!mod.m_bEnabled) continue; diff --git a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp b/NorthstarDLL/core/filesystem/rpakfilesystem.cpp index c3463781..2bddd8fe 100644 --- a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp +++ b/NorthstarDLL/core/filesystem/rpakfilesystem.cpp @@ -90,15 +90,14 @@ void* PakLoadManager::LoadFile(const char* path) void HandlePakAliases(char** map) { // convert the pak being loaded to it's aliased one, e.g. aliasing mp_hub_timeshift => sp_hub_timeshift - for (int64_t i = g_pModManager->m_LoadedMods.size() - 1; i > -1; i--) + for (Mod& mod : g_pModManager->GetMods() | std::views::reverse) { - Mod* mod = &g_pModManager->m_LoadedMods[i]; - if (!mod->m_bEnabled) + if (!mod.m_bEnabled) continue; - if (mod->RpakAliases.find(*map) != mod->RpakAliases.end()) + if (mod.RpakAliases.find(*map) != mod.RpakAliases.end()) { - *map = &mod->RpakAliases[*map][0]; + *map = &mod.RpakAliases[*map][0]; return; } } @@ -107,7 +106,7 @@ void HandlePakAliases(char** map) void LoadPreloadPaks() { // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.m_bEnabled) continue; @@ -124,7 +123,7 @@ void LoadPreloadPaks() void LoadPostloadPaks(const char* pPath) { // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.m_bEnabled) continue; @@ -144,7 +143,7 @@ void LoadCustomMapPaks(char** pakName, bool* bNeedToFreePakName) bool bHasOriginalPak = fs::exists(fs::path("r2/paks/Win64/") / *pakName); // note, loading from ./ is necessary otherwise paks will load from gamedir/r2/paks - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.m_bEnabled) continue; @@ -273,8 +272,8 @@ void*, __fastcall, (const char* pPath, void* pCallback)) NS::log::rpak->info("LoadStreamBsp: {}", filename.string()); // resolve modded stbsp path so we can load mod stbsps - auto modFile = g_pModManager->m_ModFiles.find(g_pModManager->NormaliseModFilePath(fs::path("maps" / filename))); - if (modFile != g_pModManager->m_ModFiles.end()) + auto modFile = g_pModManager->GetModFiles().find(g_pModManager->NormaliseModFilePath(fs::path("maps" / filename))); + if (modFile != g_pModManager->GetModFiles().end()) { newPath = (modFile->second.m_pOwningMod->m_ModDirectory / "mod" / modFile->second.m_Path).string(); pPath = newPath.c_str(); @@ -305,7 +304,7 @@ void*, __fastcall, (const char* pPath, void* pCallback)) size_t hashed = STR_HASH(starpakPath); // loop through all loaded mods - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { // ignore non-loaded mods if (!mod.m_bEnabled) diff --git a/NorthstarDLL/logging/crashhandler.cpp b/NorthstarDLL/logging/crashhandler.cpp index 1e9bf4b3..3427e10a 100644 --- a/NorthstarDLL/logging/crashhandler.cpp +++ b/NorthstarDLL/logging/crashhandler.cpp @@ -34,12 +34,10 @@ void PrintExceptionLog(ExceptionLog& exc) if (g_pModManager) { spdlog::error("Loaded mods: "); - for (const auto& mod : g_pModManager->m_LoadedMods) + for (const Mod& mod : g_pModManager->GetMods()) { if (mod.m_bEnabled) - { spdlog::error("{} {}", mod.Name, mod.Version); - } } } spdlog::error(exc.cause); diff --git a/NorthstarDLL/mods/compiled/kb_act.cpp b/NorthstarDLL/mods/compiled/kb_act.cpp index 4a011dc7..f3ad460f 100644 --- a/NorthstarDLL/mods/compiled/kb_act.cpp +++ b/NorthstarDLL/mods/compiled/kb_act.cpp @@ -17,7 +17,7 @@ void ModManager::BuildKBActionsList() // write vanilla file's content to compiled file soCompiledKeys << R2::ReadVPKOriginalFile(KB_ACT_PATH); - for (Mod& mod : m_LoadedMods) + for (Mod& mod : GetMods()) { if (!mod.m_bEnabled) continue; @@ -38,8 +38,8 @@ void ModManager::BuildKBActionsList() overrideFile.m_pOwningMod = nullptr; overrideFile.m_Path = KB_ACT_PATH; - if (m_ModFiles.find(KB_ACT_PATH) == m_ModFiles.end()) - m_ModFiles.insert(std::make_pair(KB_ACT_PATH, overrideFile)); + if (GetModFiles().find(KB_ACT_PATH) == GetModFiles().end()) + GetModFiles().insert(std::make_pair(KB_ACT_PATH, overrideFile)); else - m_ModFiles[KB_ACT_PATH] = overrideFile; + GetModFiles()[KB_ACT_PATH] = overrideFile; } diff --git a/NorthstarDLL/mods/compiled/modkeyvalues.cpp b/NorthstarDLL/mods/compiled/modkeyvalues.cpp index 774be0eb..051b193e 100644 --- a/NorthstarDLL/mods/compiled/modkeyvalues.cpp +++ b/NorthstarDLL/mods/compiled/modkeyvalues.cpp @@ -25,14 +25,14 @@ void ModManager::TryBuildKeyValues(const char* filename) // copy over patch kv files, and add #bases to new file, last mods' patches should be applied first // note: #include should be identical but it's actually just broken, thanks respawn - for (int64_t i = m_LoadedMods.size() - 1; i > -1; i--) + for (Mod& mod : GetMods() | std::views::reverse) { - if (!m_LoadedMods[i].m_bEnabled) + if (mod.m_bEnabled) continue; size_t fileHash = STR_HASH(normalisedPath); - auto modKv = m_LoadedMods[i].KeyValues.find(fileHash); - if (modKv != m_LoadedMods[i].KeyValues.end()) + auto modKv = mod.KeyValues.find(fileHash); + if (modKv != mod.KeyValues.end()) { // should result in smth along the lines of #include "mod_patch_5_mp_weapon_car.txt" @@ -47,7 +47,7 @@ void ModManager::TryBuildKeyValues(const char* filename) fs::remove(compiledDir / patchFilePath); - fs::copy_file(m_LoadedMods[i].m_ModDirectory / "keyvalues" / filename, compiledDir / patchFilePath); + fs::copy_file(mod.m_ModDirectory / "keyvalues" / filename, compiledDir / patchFilePath); } } @@ -100,8 +100,8 @@ void ModManager::TryBuildKeyValues(const char* filename) overrideFile.m_pOwningMod = nullptr; overrideFile.m_Path = normalisedPath; - if (m_ModFiles.find(normalisedPath) == m_ModFiles.end()) - m_ModFiles.insert(std::make_pair(normalisedPath, overrideFile)); + if (GetModFiles().find(normalisedPath) == GetModFiles().end()) + GetModFiles().insert(std::make_pair(normalisedPath, overrideFile)); else - m_ModFiles[normalisedPath] = overrideFile; + GetModFiles()[normalisedPath] = overrideFile; } diff --git a/NorthstarDLL/mods/compiled/modpdef.cpp b/NorthstarDLL/mods/compiled/modpdef.cpp deleted file mode 100644 index 219c744b..00000000 --- a/NorthstarDLL/mods/compiled/modpdef.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "pch.h" -#include "mods/modmanager.h" -#include "core/filesystem/filesystem.h" - -#include <map> -#include <sstream> -#include <fstream> - -const fs::path MOD_PDEF_SUFFIX = "cfg/server/persistent_player_data_version_231.pdef"; -const char* VPK_PDEF_PATH = "cfg/server/persistent_player_data_version_231.pdef"; - -void ModManager::BuildPdef() -{ - spdlog::info("Building persistent_player_data_version_231.pdef..."); - - fs::path MOD_PDEF_PATH = fs::path(GetCompiledAssetsPath() / MOD_PDEF_SUFFIX); - - fs::remove(MOD_PDEF_PATH); - std::string pdef = R2::ReadVPKOriginalFile(VPK_PDEF_PATH); - - for (Mod& mod : m_LoadedMods) - { - if (!mod.m_bEnabled || !mod.Pdiff.size()) - continue; - - // this code probably isn't going to be pretty lol - // refer to shared/pjson.js for an actual okish parser of the pdiff format - // but pretty much, $ENUM_ADD blocks define members added to preexisting enums - // $PROP_START ends the custom stuff, and from there it's just normal props we append to the pdef - - std::map<std::string, std::vector<std::string>> enumAdds; - - // read pdiff - bool inEnum = false; - bool inProp = false; - std::string currentEnum; - std::string currentLine; - std::istringstream pdiffStream(mod.Pdiff); - - while (std::getline(pdiffStream, currentLine)) - { - if (inProp) - { - // just append to pdef here - pdef += currentLine; - pdef += '\n'; - continue; - } - - // trim leading whitespace - size_t start = currentLine.find_first_not_of(" \n\r\t\f\v"); - size_t end = currentLine.find("//"); - if (end == std::string::npos) - end = currentLine.size() - 1; // last char - - if (!currentLine.size() || !currentLine.compare(start, 2, "//")) - continue; - - if (inEnum) - { - if (!currentLine.compare(start, 9, "$ENUM_END")) - inEnum = false; - else - enumAdds[currentEnum].push_back(currentLine); // only need to push_back current line, if there's syntax errors then game - // pdef parser will handle them - } - else if (!currentLine.compare(start, 9, "$ENUM_ADD")) - { - inEnum = true; - currentEnum = currentLine.substr(start + 10 /*$ENUM_ADD + 1*/, currentLine.size() - end - (start + 10)); - enumAdds.insert(std::make_pair(currentEnum, std::vector<std::string>())); - } - else if (!currentLine.compare(start, 11, "$PROP_START")) - { - inProp = true; - pdef += "\n// $PROP_START "; - pdef += mod.Name; - pdef += "\n"; - } - } - - // add new members to preexisting enums - // note: this code could 100% be messed up if people put //$ENUM_START comments and the like - // could make it protect against this, but honestly not worth atm - for (auto enumAdd : enumAdds) - { - std::string addStr; - for (std::string enumMember : enumAdd.second) - { - addStr += enumMember; - addStr += '\n'; - } - - // start of enum we're adding to - std::string startStr = "$ENUM_START "; - startStr += enumAdd.first; - - // insert enum values into enum - size_t insertIdx = pdef.find("$ENUM_END", pdef.find(startStr)); - pdef.reserve(addStr.size()); - pdef.insert(insertIdx, addStr); - } - } - - fs::create_directories(MOD_PDEF_PATH.parent_path()); - - std::ofstream writeStream(MOD_PDEF_PATH, std::ios::binary); - writeStream << pdef; - writeStream.close(); - - ModOverrideFile overrideFile; - overrideFile.m_pOwningMod = nullptr; - overrideFile.m_Path = VPK_PDEF_PATH; - - if (m_ModFiles.find(VPK_PDEF_PATH) == m_ModFiles.end()) - m_ModFiles.insert(std::make_pair(VPK_PDEF_PATH, overrideFile)); - else - m_ModFiles[VPK_PDEF_PATH] = overrideFile; -} diff --git a/NorthstarDLL/mods/compiled/modscriptsrson.cpp b/NorthstarDLL/mods/compiled/modscriptsrson.cpp index 15fcdd13..b24aabe9 100644 --- a/NorthstarDLL/mods/compiled/modscriptsrson.cpp +++ b/NorthstarDLL/mods/compiled/modscriptsrson.cpp @@ -17,7 +17,7 @@ void ModManager::BuildScriptsRson() std::string scriptsRson = R2::ReadVPKOriginalFile(VPK_SCRIPTS_RSON_PATH); scriptsRson += "\n\n// START MODDED SCRIPT CONTENT\n\n"; // newline before we start custom stuff - for (Mod& mod : m_LoadedMods) + for (Mod& mod : GetMods()) { if (!mod.m_bEnabled) continue; @@ -56,10 +56,10 @@ void ModManager::BuildScriptsRson() overrideFile.m_pOwningMod = nullptr; overrideFile.m_Path = VPK_SCRIPTS_RSON_PATH; - if (m_ModFiles.find(VPK_SCRIPTS_RSON_PATH) == m_ModFiles.end()) - m_ModFiles.insert(std::make_pair(VPK_SCRIPTS_RSON_PATH, overrideFile)); + if (GetModFiles().find(VPK_SCRIPTS_RSON_PATH) == GetModFiles().end()) + GetModFiles().insert(std::make_pair(VPK_SCRIPTS_RSON_PATH, overrideFile)); else - m_ModFiles[VPK_SCRIPTS_RSON_PATH] = overrideFile; + GetModFiles()[VPK_SCRIPTS_RSON_PATH] = overrideFile; // todo: for preventing dupe scripts in scripts.rson, we could actually parse when conditions with the squirrel vm, just need a way to // get a result out of squirrelmanager.ExecuteCode this would probably be the best way to do this, imo diff --git a/NorthstarDLL/mods/modmanager.cpp b/NorthstarDLL/mods/modmanager.cpp index e37df6e5..5011f0fb 100644 --- a/NorthstarDLL/mods/modmanager.cpp +++ b/NorthstarDLL/mods/modmanager.cpp @@ -98,27 +98,27 @@ Mod::Mod(fs::path modDir, char* jsonBuf) // have to allocate this manually, otherwise convar registration will break // unfortunately this causes us to leak memory on reload, unsure of a way around this rn - ModConVar* convar = new ModConVar; - convar->Name = convarObj["Name"].GetString(); - convar->DefaultValue = convarObj["DefaultValue"].GetString(); + ModConVar convar; + convar.Name = convarObj["Name"].GetString(); + convar.DefaultValue = convarObj["DefaultValue"].GetString(); if (convarObj.HasMember("HelpString")) - convar->HelpString = convarObj["HelpString"].GetString(); + convar.HelpString = convarObj["HelpString"].GetString(); else - convar->HelpString = ""; + convar.HelpString = ""; - convar->Flags = FCVAR_NONE; + convar.Flags = FCVAR_NONE; if (convarObj.HasMember("Flags")) { // read raw integer flags if (convarObj["Flags"].IsInt()) - convar->Flags = convarObj["Flags"].GetInt(); + convar.Flags = convarObj["Flags"].GetInt(); else if (convarObj["Flags"].IsString()) { // parse cvar flags from string // example string: ARCHIVE_PLAYERPROFILE | GAMEDLL - convar->Flags |= ParseConVarFlagsString(convar->Name, convarObj["Flags"].GetString()); + convar.Flags |= ParseConVarFlagsString(convar.Name, convarObj["Flags"].GetString()); } } @@ -126,6 +126,7 @@ Mod::Mod(fs::path modDir, char* jsonBuf) } } + // mod commands if (modJson.HasMember("ConCommands") && modJson["ConCommands"].IsArray()) { for (auto& concommandObj : modJson["ConCommands"].GetArray()) @@ -138,35 +139,35 @@ Mod::Mod(fs::path modDir, char* jsonBuf) // have to allocate this manually, otherwise concommand registration will break // unfortunately this causes us to leak memory on reload, unsure of a way around this rn - ModConCommand* concommand = new ModConCommand; - concommand->Name = concommandObj["Name"].GetString(); - concommand->Function = concommandObj["Function"].GetString(); - concommand->Context = ScriptContextFromString(concommandObj["Context"].GetString()); - if (concommand->Context == ScriptContext::INVALID) + ModConCommand concommand; + concommand.Name = concommandObj["Name"].GetString(); + concommand.Function = concommandObj["Function"].GetString(); + concommand.Context = ScriptContextFromString(concommandObj["Context"].GetString()); + if (concommand.Context == ScriptContext::INVALID) { - spdlog::warn("Mod ConCommand {} has invalid context {}", concommand->Name, concommandObj["Context"].GetString()); + spdlog::warn("Mod ConCommand {} has invalid context {}", concommand.Name, concommandObj["Context"].GetString()); continue; } if (concommandObj.HasMember("HelpString")) - concommand->HelpString = concommandObj["HelpString"].GetString(); + concommand.HelpString = concommandObj["HelpString"].GetString(); else - concommand->HelpString = ""; + concommand.HelpString = ""; - concommand->Flags = FCVAR_NONE; + concommand.Flags = FCVAR_NONE; if (concommandObj.HasMember("Flags")) { // read raw integer flags if (concommandObj["Flags"].IsInt()) { - concommand->Flags = concommandObj["Flags"].GetInt(); + concommand.Flags = concommandObj["Flags"].GetInt(); } else if (concommandObj["Flags"].IsString()) { // parse cvar flags from string // example string: ARCHIVE_PLAYERPROFILE | GAMEDLL - concommand->Flags |= ParseConVarFlagsString(concommand->Name, concommandObj["Flags"].GetString()); + concommand.Flags |= ParseConVarFlagsString(concommand.Name, concommandObj["Flags"].GetString()); } } @@ -289,66 +290,54 @@ ModManager::ModManager() LoadMods(); } -struct Test -{ - std::string funcName; - ScriptContext context; -}; - template <ScriptContext context> auto ModConCommandCallback_Internal(std::string name, const CCommand& command) { - if (g_pSquirrel<context>->m_pSQVM && g_pSquirrel<context>->m_pSQVM) + if (g_pSquirrel<context>->m_pSQVM && g_pSquirrel<context>->m_pSQVM->sqvm) { - std::vector<std::string> args; - args.reserve(command.ArgC()); + std::vector<std::string> vArgs; + vArgs.reserve(command.ArgC()); for (int i = 1; i < command.ArgC(); i++) - args.push_back(command.Arg(i)); - g_pSquirrel<context>->AsyncCall(name, args); + vArgs.push_back(command.Arg(i)); + + g_pSquirrel<context>->AsyncCall(name, vArgs); } else - { - spdlog::warn("ConCommand `{}` was called while the associated Squirrel VM `{}` was unloaded", name, GetContextName(context)); - } + spdlog::warn("ConCommand \"{}\" was called while the associated Squirrel VM \"{}\" was unloaded", name, GetContextName(context)); } auto ModConCommandCallback(const CCommand& command) { - ModConCommand* found = nullptr; - auto commandString = std::string(command.GetCommandString()); - - // Finding the first space to remove the command's name - auto firstSpace = commandString.find(' '); - if (firstSpace) - { - commandString = commandString.substr(0, firstSpace); - } + ModConCommand* pFoundCommand = nullptr; + std::string sCommandName = command.Arg(0); // Find the mod this command belongs to - for (auto& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { auto res = std::find_if( mod.ConCommands.begin(), mod.ConCommands.end(), - [&commandString](const ModConCommand* other) { return other->Name == commandString; }); + [&sCommandName](const ModConCommand* other) { return other->Name == sCommandName; }); + if (res != mod.ConCommands.end()) { - found = *res; + pFoundCommand = &*res; break; } } - if (!found) + + if (!pFoundCommand) return; - switch (found->Context) + switch (pFoundCommand->Context) { case ScriptContext::CLIENT: - ModConCommandCallback_Internal<ScriptContext::CLIENT>(found->Function, command); + ModConCommandCallback_Internal<ScriptContext::CLIENT>(pFoundCommand->Function, command); break; case ScriptContext::SERVER: - ModConCommandCallback_Internal<ScriptContext::SERVER>(found->Function, command); + ModConCommandCallback_Internal<ScriptContext::SERVER>(pFoundCommand->Function, command); break; case ScriptContext::UI: - ModConCommandCallback_Internal<ScriptContext::UI>(found->Function, command); + ModConCommandCallback_Internal<ScriptContext::UI>(pFoundCommand->Function, command); break; }; } @@ -444,7 +433,10 @@ void ModManager::LoadMods() } // sort by load prio, lowest-highest - std::sort(m_LoadedMods.begin(), m_LoadedMods.end(), [](Mod& a, Mod& b) { return a.LoadPriority < b.LoadPriority; }); + std::sort( + m_ModLoadState.m_LoadedMods.begin(), + m_ModLoadState.m_LoadedMods.end(), + [](Mod& a, Mod& b) { return a.LoadPriority < b.LoadPriority; }); for (Mod& mod : m_LoadedMods) { @@ -452,27 +444,47 @@ void ModManager::LoadMods() continue; // register convars - // for reloads, this is sorta barebones, when we have a good findconvar method, we could probably reset flags and stuff on - // preexisting convars note: we don't delete convars if they already exist because they're used for script stuff, unfortunately this - // causes us to leak memory on reload, but not much, potentially find a way to not do this at some point - for (ModConVar* convar : mod.ConVars) + for (ModConVar convar : mod.ConVars) { - // make sure convar isn't registered yet, unsure if necessary but idk what - // behaviour is for defining same convar multiple times - if (!R2::g_pCVar->FindVar(convar->Name.c_str())) + ConVar* pVar = R2::g_pCVar->FindVar(convar.Name.c_str()); + + // make sure convar isn't registered yet, if it is then modify its flags, helpstring etc + if (!pVar) + new ConVar(convar.Name.c_str(), convar.DefaultValue.c_str(), convar.Flags, convar.HelpString.c_str()); + else { - new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str()); + // TODO: should probably make sure this is actually a mod convar we're messing with + + pVar->m_ConCommandBase.m_nFlags = convar.Flags; + + // unfortunately this leaks memory and we can't really not leak memory because we don't know who allocated this + // so we can't delete it without risking a crash + if (convar.HelpString.compare(pVar->GetHelpText())) + { + int nHelpSize = convar.HelpString.size(); + char* pNewHelpString = new char[nHelpSize + 1]; + strncpy_s(pNewHelpString, nHelpSize + 1, convar.HelpString.c_str(), convar.HelpString.size()); + pVar->m_ConCommandBase.m_pszHelpString = pNewHelpString; + } + + if (convar.DefaultValue.compare(pVar->m_pszDefaultValue)) + { + int nDefaultValueSize = convar.DefaultValue.size(); + char* pNewDefaultValueString = new char[nDefaultValueSize + 1]; + strncpy_s(pNewDefaultValueString, nDefaultValueSize + 1, convar.DefaultValue.c_str(), convar.DefaultValue.size()); + pVar->m_pszDefaultValue = pNewDefaultValueString; + pVar->SetValue(pNewDefaultValueString); + } } } - for (ModConCommand* command : mod.ConCommands) + for (ModConCommand command : mod.ConCommands) { // make sure command isnt't registered multiple times. - if (!R2::g_pCVar->FindCommand(command->Name.c_str())) + if (!R2::g_pCVar->FindCommand(command.Name.c_str())) { - ConCommand* newCommand = new ConCommand(); - std::string funcName = command->Function; - RegisterConCommand(command->Name.c_str(), ModConCommandCallback, command->HelpString.c_str(), command->Flags); + std::string funcName = command.Function; + RegisterConCommand(command.Name.c_str(), ModConCommandCallback, command.HelpString.c_str(), command.Flags); } } @@ -735,6 +747,8 @@ void ModManager::LoadMods() void ModManager::UnloadMods() { + m_LastModLoadState = m_ModLoadState; + // clean up stuff from mods before we unload m_ModFiles.clear(); fs::remove_all(GetCompiledAssetsPath()); @@ -750,8 +764,6 @@ void ModManager::UnloadMods() for (std::pair<size_t, std::string> kvPaths : mod.KeyValues) fs::remove(GetCompiledAssetsPath() / fs::path(kvPaths.second).lexically_relative(mod.m_ModDirectory)); - mod.KeyValues.clear(); - // write to m_enabledModsCfg // should we be doing this here or should scripts be doing this manually? // main issue with doing this here is when we reload mods for connecting to a server, we write enabled mods, which isn't necessarily @@ -790,13 +802,15 @@ void ModManager::CompileAssetsForFile(const char* filename) if (fileHash == m_hScriptsRsonHash) BuildScriptsRson(); else if (fileHash == m_hPdefHash) - BuildPdef(); + { + // BuildPdef(); todo + } else if (fileHash == m_hKBActHash) BuildKBActionsList(); else { // check if we should build keyvalues, depending on whether any of our mods have patch kvs for this file - for (Mod& mod : m_LoadedMods) + for (Mod& mod : GetMods()) { if (!mod.m_bEnabled) continue; diff --git a/NorthstarDLL/mods/modmanager.h b/NorthstarDLL/mods/modmanager.h index 369eb07b..d44a202f 100644 --- a/NorthstarDLL/mods/modmanager.h +++ b/NorthstarDLL/mods/modmanager.h @@ -98,9 +98,9 @@ class Mod // custom scripts used by the mod std::vector<ModScript> Scripts; // convars created by the mod - std::vector<ModConVar*> ConVars; + std::vector<ModConVar> ConVars; // concommands created by the mod - std::vector<ModConCommand*> ConCommands; + std::vector<ModConCommand> ConCommands; // custom localisation files created by the mod std::vector<std::string> LocalisationFiles; @@ -127,10 +127,18 @@ class Mod struct ModOverrideFile { public: - Mod* m_pOwningMod; + Mod* m_pOwningMod; // don't need to explicitly clean this up fs::path m_Path; }; +class ModLoadState +{ + public: + std::vector<Mod> m_LoadedMods; + std::unordered_map<std::string, ModOverrideFile> m_ModFiles; + std::unordered_map<std::string, std::string> m_DependencyConstants; +}; + class ModManager { private: @@ -144,9 +152,8 @@ class ModManager size_t m_hKBActHash; public: - std::vector<Mod> m_LoadedMods; - std::unordered_map<std::string, ModOverrideFile> m_ModFiles; - std::unordered_map<std::string, std::string> m_DependencyConstants; + ModLoadState m_LastModLoadState; + ModLoadState m_ModLoadState; public: ModManager(); @@ -155,6 +162,22 @@ class ModManager std::string NormaliseModFilePath(const fs::path path); void CompileAssetsForFile(const char* filename); + // getters + inline std::vector<Mod>& GetMods() + { + return m_ModLoadState.m_LoadedMods; + }; + + inline std::unordered_map<std::string, ModOverrideFile>& GetModFiles() + { + return m_ModLoadState.m_ModFiles; + }; + + inline std::unordered_map<std::string, std::string>& GetDependencyConstants() + { + return m_ModLoadState.m_DependencyConstants; + }; + // compile asset type stuff, these are done in files under runtime/compiled/ void BuildScriptsRson(); void TryBuildKeyValues(const char* filename); diff --git a/NorthstarDLL/pch.h b/NorthstarDLL/pch.h index 55ebba8b..433030b6 100644 --- a/NorthstarDLL/pch.h +++ b/NorthstarDLL/pch.h @@ -17,6 +17,8 @@ #include <map> #include <filesystem> #include <sstream> +#include <ranges> +#include <vector> namespace fs = std::filesystem; diff --git a/NorthstarDLL/scripts/client/scriptmodmenu.cpp b/NorthstarDLL/scripts/client/scriptmodmenu.cpp index 75d05acc..0beea4f2 100644 --- a/NorthstarDLL/scripts/client/scriptmodmenu.cpp +++ b/NorthstarDLL/scripts/client/scriptmodmenu.cpp @@ -6,7 +6,7 @@ ADD_SQFUNC("array<string>", NSGetModNames, "", "", ScriptContext::SERVER | Scrip { g_pSquirrel<context>->newarray(sqvm, 0); - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { g_pSquirrel<context>->pushstring(sqvm, mod.Name.c_str()); g_pSquirrel<context>->arrayappend(sqvm, -2); @@ -20,7 +20,7 @@ ADD_SQFUNC("bool", NSIsModEnabled, "string modName", "", ScriptContext::SERVER | const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { @@ -38,7 +38,7 @@ ADD_SQFUNC("void", NSSetModEnabled, "string modName, bool enabled", "", ScriptCo const SQBool enabled = g_pSquirrel<context>->getbool(sqvm, 2); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { @@ -55,7 +55,7 @@ ADD_SQFUNC("string", NSGetModDescriptionByModName, "string modName", "", ScriptC const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { @@ -72,7 +72,7 @@ ADD_SQFUNC("string", NSGetModVersionByModName, "string modName", "", ScriptConte const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { @@ -89,7 +89,7 @@ ADD_SQFUNC("string", NSGetModDownloadLinkByModName, "string modName", "", Script const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { @@ -106,7 +106,7 @@ ADD_SQFUNC("int", NSGetModLoadPriority, "string modName", "", ScriptContext::SER const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { @@ -123,7 +123,7 @@ ADD_SQFUNC("bool", NSIsModRequiredOnClient, "string modName", "", ScriptContext: const SQChar* modName = g_pSquirrel<context>->getstring(sqvm, 1); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { @@ -142,7 +142,7 @@ ADD_SQFUNC( g_pSquirrel<context>->newarray(sqvm, 0); // manual lookup, not super performant but eh not a big deal - for (Mod& mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.Name.compare(modName)) { diff --git a/NorthstarDLL/squirrel/squirrel.cpp b/NorthstarDLL/squirrel/squirrel.cpp index 25fb90d5..0bbc496f 100644 --- a/NorthstarDLL/squirrel/squirrel.cpp +++ b/NorthstarDLL/squirrel/squirrel.cpp @@ -190,11 +190,11 @@ template <ScriptContext context> void SquirrelManager<context>::VMCreated(CSquir RegisterSquirrelFunc(m_pSQVM, funcReg, 1); } - for (auto& pair : g_pModManager->m_DependencyConstants) + for (auto& pair : g_pModManager->GetDependencyConstants()) { bool bWasFound = false; - for (Mod& dependency : g_pModManager->m_LoadedMods) + for (Mod& dependency : g_pModManager->GetMods()) { if (!dependency.m_bEnabled) continue; @@ -472,7 +472,7 @@ template <ScriptContext context> bool __fastcall CallScriptInitCallbackHook(void if (bShouldCallCustomCallbacks) { - for (Mod mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.m_bEnabled) continue; @@ -499,7 +499,7 @@ template <ScriptContext context> bool __fastcall CallScriptInitCallbackHook(void // run after callbacks if (bShouldCallCustomCallbacks) { - for (Mod mod : g_pModManager->m_LoadedMods) + for (Mod& mod : g_pModManager->GetMods()) { if (!mod.m_bEnabled) continue; diff --git a/NorthstarDLL/squirrel/squirrel.h b/NorthstarDLL/squirrel/squirrel.h index ce758d7c..65bda7e3 100644 --- a/NorthstarDLL/squirrel/squirrel.h +++ b/NorthstarDLL/squirrel/squirrel.h @@ -239,17 +239,15 @@ class SquirrelManagerBase { SQStackInfos stackInfo {}; if (1 + depth >= sqvm->_callstacksize) - { return nullptr; - } + sq_stackinfos(sqvm, 1 + depth, stackInfo); std::string sourceName = stackInfo._sourceName; std::replace(sourceName.begin(), sourceName.end(), '/', '\\'); std::string filename = "scripts\\vscripts\\" + sourceName; - if (auto res = g_pModManager->m_ModFiles.find(filename); res != g_pModManager->m_ModFiles.end()) - { + if (auto res = g_pModManager->GetModFiles().find(filename); res != g_pModManager->GetModFiles().end()) return res->second.m_pOwningMod; - } + return nullptr; } template <typename T> inline SQRESULT getuserdata(HSquirrelVM* sqvm, const SQInteger stackpos, T* data, uint64_t* typeId) diff --git a/NorthstarDLL/util/printmaps.cpp b/NorthstarDLL/util/printmaps.cpp index e0192d69..0b4593de 100644 --- a/NorthstarDLL/util/printmaps.cpp +++ b/NorthstarDLL/util/printmaps.cpp @@ -37,7 +37,7 @@ void RefreshMapList() // get modded maps // TODO: could probably check mod vpks to get mapnames from there too? - for (auto& modFilePair : g_pModManager->m_ModFiles) + for (auto& modFilePair : g_pModManager->GetModFiles()) { ModOverrideFile file = modFilePair.second; if (file.m_Path.extension() == ".bsp" && file.m_Path.parent_path().string() == "maps") // only allow mod maps actually in /maps atm |