diff options
author | BobTheBob <for.oliver.kirkham@gmail.com> | 2023-03-04 23:07:05 +0000 |
---|---|---|
committer | BobTheBob <for.oliver.kirkham@gmail.com> | 2023-03-04 23:07:05 +0000 |
commit | 546de11c5524cb7b7bf67388c77a1508942d8fca (patch) | |
tree | 7fc3a6a37dfb136626532b029de2f8a8916e653e | |
parent | e95aa783fa04aa6fb9f930e5b4d74ad447e7f087 (diff) | |
download | NorthstarLauncher-546de11c5524cb7b7bf67388c77a1508942d8fca.tar.gz NorthstarLauncher-546de11c5524cb7b7bf67388c77a1508942d8fca.zip |
add basic deferred reloading and reloading for keyvalues
-rw-r--r-- | NorthstarDLL/NorthstarDLL.vcxproj | 1 | ||||
-rw-r--r-- | NorthstarDLL/NorthstarDLL.vcxproj.filters | 3 | ||||
-rw-r--r-- | NorthstarDLL/core/filesystem/rpakfilesystem.cpp | 11 | ||||
-rw-r--r-- | NorthstarDLL/mods/compiled/modkeyvalues.cpp | 22 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.cpp | 255 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.h | 17 | ||||
-rw-r--r-- | NorthstarDLL/mods/reload/reloadmodweapons.cpp | 8 | ||||
-rw-r--r-- | NorthstarDLL/mods/reload/reloadmodweapons_misc.cpp | 45 | ||||
-rw-r--r-- | NorthstarDLL/scripts/client/scriptmodmenu.cpp | 6 |
9 files changed, 245 insertions, 123 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj index 5e07191d..3826c232 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj +++ b/NorthstarDLL/NorthstarDLL.vcxproj @@ -501,6 +501,7 @@ <ClCompile Include="mods\compiled\modscriptsrson.cpp" />
<ClCompile Include="mods\modmanager.cpp" />
<ClCompile Include="mods\reload\reloadmodweapons.cpp" />
+ <ClCompile Include="mods\reload\reloadmodweapons_misc.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters index 125bdf04..8baef399 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj.filters +++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters @@ -1432,6 +1432,9 @@ <ClCompile Include="mods\reload\reloadmodweapons.cpp">
<Filter>Source Files\mods\reload</Filter>
</ClCompile>
+ <ClCompile Include="mods\reload\reloadmodweapons_misc.cpp">
+ <Filter>Source Files\mods\reload</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm">
diff --git a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp b/NorthstarDLL/core/filesystem/rpakfilesystem.cpp index e8c4232d..26afda06 100644 --- a/NorthstarDLL/core/filesystem/rpakfilesystem.cpp +++ b/NorthstarDLL/core/filesystem/rpakfilesystem.cpp @@ -133,11 +133,8 @@ 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->GetMods()) + for (Mod& mod : g_pModManager->GetMods() | ModManager::FilterEnabled) { - if (!mod.m_bEnabled) - continue; - // need to get a relative path of mod to mod folder fs::path modPakPath("./" / mod.m_ModDirectory / "paks"); @@ -294,12 +291,8 @@ void*, __fastcall, (const char* pPath, void* pCallback)) size_t hashed = STR_HASH(starpakPath); // loop through all loaded mods - for (Mod& mod : g_pModManager->GetMods()) + for (Mod& mod : g_pModManager->GetMods() | ModManager::FilterEnabled) { - // ignore non-loaded mods - if (!mod.m_bEnabled) - continue; - // loop through the stored starpak paths for (size_t hash : mod.StarpakPaths) { diff --git a/NorthstarDLL/mods/compiled/modkeyvalues.cpp b/NorthstarDLL/mods/compiled/modkeyvalues.cpp index 4672d195..37ee0ab2 100644 --- a/NorthstarDLL/mods/compiled/modkeyvalues.cpp +++ b/NorthstarDLL/mods/compiled/modkeyvalues.cpp @@ -24,26 +24,18 @@ 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 (Mod& mod : GetMods() | ModManager::FilterEnabled | std::views::reverse) + auto findKv = m_ModLoadState->m_KeyValues.find(filename); + if (findKv != m_ModLoadState->m_KeyValues.end()) { - size_t fileHash = STR_HASH(normalisedPath); - auto modKv = mod.KeyValues.find(fileHash); - if (modKv != mod.KeyValues.end()) + for (ModOverrideFile& modKv : findKv->second) { - // should result in smth along the lines of #include "mod_patch_5_mp_weapon_car.txt" - - std::string patchFilePath = "mod_patch_"; - patchFilePath += std::to_string(patchNum++); - patchFilePath += "_"; - patchFilePath += kvPath.filename().string(); - - newKvs += "#base \""; - newKvs += patchFilePath; - newKvs += "\"\n"; + // should result in smth along the lines of #include "_mod_patch_5_mp_weapon_car.txt" + std::string patchFilePath = fmt::format("_mod_patch_{}_{}", patchNum++, kvPath.filename().string()); fs::remove(compiledDir / patchFilePath); + fs::copy_file(modKv.m_pOwningMod->m_ModDirectory / "keyvalues" / filename, compiledDir / patchFilePath); - fs::copy_file(mod.m_ModDirectory / "keyvalues" / filename, compiledDir / patchFilePath); + newKvs += fmt::format("#base \"{}\"\n", patchFilePath); } } diff --git a/NorthstarDLL/mods/modmanager.cpp b/NorthstarDLL/mods/modmanager.cpp index 6fc64f4f..0203a66e 100644 --- a/NorthstarDLL/mods/modmanager.cpp +++ b/NorthstarDLL/mods/modmanager.cpp @@ -294,7 +294,7 @@ ModManager::ModManager() m_LastModLoadState = nullptr; m_ModLoadState = new ModLoadState; - LoadMods(); + LoadMods(false); } template <ScriptContext context> auto ModConCommandCallback_Internal(std::string name, const CCommand& command) @@ -352,7 +352,7 @@ auto ModConCommandCallback(const CCommand& command) -void ModManager::LoadMods() +void ModManager::LoadMods(bool bDeferredAssetReload) { // reset state of all currently loaded mods, if we've loaded once already if (m_bHasLoadedMods) @@ -366,36 +366,13 @@ void ModManager::LoadMods() LoadModDefinitions(); // install mods (load all files) - InstallMods(); + InstallMods(bDeferredAssetReload); // write json storing currently enabled mods SaveEnabledMods(); - // build modinfo obj for masterserver - rapidjson_document modinfoDoc; - auto& alloc = modinfoDoc.GetAllocator(); - modinfoDoc.SetObject(); - modinfoDoc.AddMember("Mods", rapidjson::kArrayType, alloc); - - int currentModIndex = 0; - for (Mod& mod : GetMods()) - { - if (!mod.m_bEnabled || !mod.RequiredOnClient) // (!mod.RequiredOnClient && !mod.Pdiff.size()) - continue; - - modinfoDoc["Mods"].PushBack(rapidjson::kObjectType, modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Name", rapidjson::StringRef(&mod.Name[0]), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("Version", rapidjson::StringRef(&mod.Version[0]), modinfoDoc.GetAllocator()); - modinfoDoc["Mods"][currentModIndex].AddMember("RequiredOnClient", mod.RequiredOnClient, modinfoDoc.GetAllocator()); - - currentModIndex++; - } - - rapidjson::StringBuffer buffer; - buffer.Clear(); - rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); - modinfoDoc.Accept(writer); - g_pMasterServerManager->m_sOwnModInfoJson = std::string(buffer.GetString()); + // build public-facing mod list for masterserver + BuildPublicModList(); // don't need this anymore delete m_LastModLoadState; @@ -789,8 +766,12 @@ void ModManager::InstallModKeyValues(Mod& mod) { if (fs::is_regular_file(file)) { - std::string kvStr = g_pModManager->NormaliseModFilePath(file.path().lexically_relative(mod.m_ModDirectory / "keyvalues")); - mod.KeyValues.emplace(STR_HASH(kvStr), kvStr); + ModOverrideFile modKv; + modKv.m_pOwningMod = &mod; + modKv.m_Path = g_pModManager->NormaliseModFilePath(file.path().lexically_relative(mod.m_ModDirectory / "keyvalues")); + modKv.m_tLastWriteTime = fs::last_write_time(file); + + m_ModLoadState->m_KeyValues[modKv.m_Path.string()].push_back(modKv); } } } @@ -839,7 +820,7 @@ void ModManager::InstallModFileOverrides(Mod& mod) ModOverrideFile modFile; modFile.m_pOwningMod = &mod; modFile.m_Path = path; - modFile.m_tLastWriteTime = fs::last_write_time(file.path()); // need real path for this + modFile.m_tLastWriteTime = fs::last_write_time(file); // need real path for this m_ModLoadState->m_ModFiles.insert(std::make_pair(path, modFile)); } } @@ -856,6 +837,7 @@ void ModManager::CheckModFilesForChanges() // a) the asset was overriden previously but has changed owner // b) the asset no longer has any overrides (use vanilla file) // c) the asset was using vanilla file but isn't anymore + // d) the asset has been edited std::vector<ModOverrideFile*> vpChangedFiles; @@ -875,18 +857,18 @@ void ModManager::CheckModFilesForChanges() for (ModOverrideFile* pChangedFile : vpChangedFiles) { - if (IsDedicatedServer()) + if (!IsDedicatedServer()) { // could check localisation here? but what's the point, localisation shouldn't be in mod fs // if (m_AssetTypesToReload.bLocalisation) - if (!m_AssetTypesToReload.bAimAssistSettings && !pChangedFile->m_Path.parent_path().compare("cfg/aimassist/")) + if (!m_AssetTypesToReload.bAimAssistSettings && pChangedFile->m_Path.parent_path().string().starts_with("cfg/aimassist/")) { m_AssetTypesToReload.bAimAssistSettings = true; continue; } - if (!m_AssetTypesToReload.bMaterials && !pChangedFile->m_Path.parent_path().compare("materials/")) + if (!m_AssetTypesToReload.bMaterials && pChangedFile->m_Path.parent_path().string().starts_with("materials/")) { m_AssetTypesToReload.bMaterials = true; continue; @@ -898,7 +880,7 @@ void ModManager::CheckModFilesForChanges() // TODO: need to check whether any ui scripts have changed // need to do this by calling LoadScriptsRson (client.dll+3177D0) and getting the list of scripts loaded from that maybe - if (!pChangedFile->m_Path.parent_path().compare("resource/ui/")) + if (pChangedFile->m_Path.parent_path().string().starts_with("resource/ui/")) { m_AssetTypesToReload.bUiScript = true; continue; @@ -906,35 +888,34 @@ void ModManager::CheckModFilesForChanges() } } - if (!m_AssetTypesToReload.bModels && !pChangedFile->m_Path.parent_path().compare("models/")) + if (!m_AssetTypesToReload.bModels && pChangedFile->m_Path.parent_path().string().starts_with("models/")) { m_AssetTypesToReload.bModels = true; continue; } // could also check this but no point as it should only be changed from mod keyvalues - // if (!m_AssetTypesToReload.bPlaylists && !pChangedFile->m_Path.compare("playlists_v2.txt")) - if (!m_AssetTypesToReload.bPlayerSettings && !pChangedFile->m_Path.parent_path().compare("scripts/players/")) + if (!m_AssetTypesToReload.bPlayerSettings && pChangedFile->m_Path.parent_path().string().starts_with("scripts/players/")) { m_AssetTypesToReload.bPlayerSettings = true; continue; } // maybe also aibehaviour? - if (!m_AssetTypesToReload.bAiSettings && !pChangedFile->m_Path.parent_path().compare("scripts/aisettings/")) + if (!m_AssetTypesToReload.bAiSettings && pChangedFile->m_Path.parent_path().string().starts_with("scripts/aisettings/")) { m_AssetTypesToReload.bAiSettings = true; continue; } - if (!m_AssetTypesToReload.bDamageDefs && !pChangedFile->m_Path.parent_path().compare("scripts/damage/")) + if (!m_AssetTypesToReload.bDamageDefs && pChangedFile->m_Path.parent_path().string().starts_with("scripts/damage/")) { m_AssetTypesToReload.bDamageDefs = true; continue; } - if (m_AssetTypesToReload.bDatatables && !pChangedFile->m_Path.parent_path().compare("scripts/datatable/")) + if (m_AssetTypesToReload.bDatatables && pChangedFile->m_Path.parent_path().string().starts_with("scripts/datatable/")) { m_AssetTypesToReload.bDatatables = true; continue; @@ -943,40 +924,87 @@ void ModManager::CheckModFilesForChanges() } // keyvalues + { + // check which file overrides have changed + // we need to trigger a reload of a given asset if + // a) the asset is being overriden by different mods than previously + // b) the asset has been edited - //if (!m_AssetTypesToReload.bWeaponSettings && kvStr.compare("scripts/weapons/")) - //{ - // m_AssetTypesToReload.bWeaponSettings = true; - // continue; - //} - // - //if (!m_AssetTypesToReload.bPlayerSettings && kvStr.compare("scripts/players/")) - //{ - // m_AssetTypesToReload.bPlayerSettings = true; - // continue; - //} - // - //// maybe also aibehaviour? - //if (!m_AssetTypesToReload.bAiSettings && kvStr.compare("scripts/aisettings/")) - //{ - // m_AssetTypesToReload.bAiSettings = true; - // continue; - //} - // - //if (!m_AssetTypesToReload.bDamageDefs && kvStr.compare("scripts/damage/")) - //{ - // m_AssetTypesToReload.bDamageDefs = true; - // continue; - //} + std::vector<std::string> vsChangedFiles; + + // check currently loaded mods for any removed or updated files vs last load + for (auto& filePair : m_ModLoadState->m_KeyValues) + { + auto findFile = m_LastModLoadState->m_KeyValues.find(filePair.first); + if (findFile == m_LastModLoadState->m_KeyValues.end() || findFile->second.size() != filePair.second.size()) + vsChangedFiles.push_back(filePair.first); + else + { + // check the actual override list to ensure it's the same files + // even if just file order has changed, we should still reload + for (int i = 0; i < filePair.second.size(); i++) + { + if (filePair.second[i].m_pOwningMod->m_ModDirectory != findFile->second[i].m_pOwningMod->m_ModDirectory) + { + vsChangedFiles.push_back(filePair.first); + break; + } + } + } + } + + // check last load for any files removed + for (auto& filePair : m_LastModLoadState->m_KeyValues) + if (m_ModLoadState->m_KeyValues.find(filePair.first) == m_ModLoadState->m_KeyValues.end()) + vsChangedFiles.push_back(filePair.first); + + for (std::string& sChangedPath : vsChangedFiles) + { + fs::path fChangedPath(sChangedPath); + + if (!m_AssetTypesToReload.bPlaylists && fChangedPath == "playlists_v2.txt") + { + m_AssetTypesToReload.bPlaylists = true; + continue; + } + + if (!m_AssetTypesToReload.bPlayerSettings && fChangedPath.parent_path().string().starts_with("scripts/players/")) + { + m_AssetTypesToReload.bPlayerSettings = true; + continue; + } + + if (!m_AssetTypesToReload.bAiSettings && fChangedPath.parent_path().string().starts_with("scripts/aisettings/")) + { + m_AssetTypesToReload.bAiSettings = true; + continue; + } + + if (!m_AssetTypesToReload.bDamageDefs && fChangedPath.parent_path().string().starts_with("scripts/damage/")) + { + m_AssetTypesToReload.bDamageDefs = true; + continue; + } + + if (!fChangedPath.parent_path().string().starts_with("scripts/weapons/")) + { + if (fChangedPath.filename() == "ammo_suck_behaviours.txt") + m_AssetTypesToReload.bAmmoSuckBehaviours = true; + else if (fChangedPath.filename() == "springs.txt") + m_AssetTypesToReload.bWeaponSprings = true; + else + m_AssetTypesToReload.setsWeaponSettings.insert(fChangedPath.replace_extension().string()); + + continue; + } + } + } } -void ModManager::ReloadNecessaryModAssets() +void ModManager::ReloadNecessaryModAssets(bool bDeferred) { std::vector<std::string> vReloadCommands; - if (m_AssetTypesToReload.bUiScript) - vReloadCommands.push_back("uiscript_reset"); - if (m_AssetTypesToReload.bLocalisation) vReloadCommands.push_back("reload_localization"); @@ -984,26 +1012,44 @@ void ModManager::ReloadNecessaryModAssets() if (m_AssetTypesToReload.bPlaylists || m_AssetTypesToReload.bLocalisation) vReloadCommands.push_back("loadPlaylists"); + if (m_AssetTypesToReload.bUiScript) + vReloadCommands.push_back("uiscript_reset"); + if (m_AssetTypesToReload.bAimAssistSettings) vReloadCommands.push_back("ReloadAimAssistSettings"); + if (m_AssetTypesToReload.bModels) + spdlog::warn("Need to reload models but can't without a restart!"); + if (m_AssetTypesToReload.bDatatables) { // TODO: clear disk datatable cache in scriptdatatables.cpp } + // deferred - load files using engine functions where possible, on level load + if (bDeferred) + { + if (m_AssetTypesToReload.bAimAssistSettings) + DeferredReloadADSPulls(); + + if (m_AssetTypesToReload.bAmmoSuckBehaviours) + DeferredReloadAmmoSuckBehaviours(); + + if (m_AssetTypesToReload.bDamageDefs) + DeferredReloadDamageFlags(); + + if (m_AssetTypesToReload.bWeaponSprings) + DeferredReloadWeaponSprings(); + } + else + { + + } + // need to reimplement mat_reloadmaterials for this //if (m_AssetTypesToReload.bMaterials) // R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "mat_reloadmaterials", R2::cmd_source_t::kCommandSrcCode); - //if (m_AssetTypesToReload.bWeaponSettings) - //if (m_AssetTypesToReload.bPlayerSettings) - //if (m_AssetTypesToReload.bAiSettings) - //if (m_AssetTypesToReload.bDamageDefs) - - if (m_AssetTypesToReload.bModels) - spdlog::warn("Need to reload models but can't without a restart!"); - for (std::string& sReloadCommand : vReloadCommands) { spdlog::info("Executing command {} for asset reload", sReloadCommand); @@ -1019,28 +1065,33 @@ void ModManager::ReloadNecessaryModAssets() m_AssetTypesToReload.bAimAssistSettings = false; m_AssetTypesToReload.bDatatables = false; m_AssetTypesToReload.bModels = false; + m_AssetTypesToReload.bAmmoSuckBehaviours = false; + m_AssetTypesToReload.bDamageDefs = false; + m_AssetTypesToReload.bWeaponSprings = false; } -void ModManager::InstallMods() +void ModManager::InstallMods(bool bDeferredAssetReload) { for (Mod& mod : GetMods() | FilterEnabled) { InstallModCvars(mod); InstallModVpks(mod); InstallModRpaks(mod); - InstallModKeyValues(mod); InstallModBinks(mod); InstallModAudioOverrides(mod); } // in a seperate loop because we register mod files in reverse order, since mods loaded later should have their files prioritised for (Mod& mod : GetMods() | FilterEnabled | std::views::reverse) + { + InstallModKeyValues(mod); InstallModFileOverrides(mod); + } if (m_bHasLoadedMods) // only reload assets after initial load { CheckModFilesForChanges(); - ReloadNecessaryModAssets(); + ReloadNecessaryModAssets(bDeferredAssetReload); } } @@ -1061,6 +1112,35 @@ void ModManager::SaveEnabledMods() enabledModsCfg.Accept(writer); } +void ModManager::BuildPublicModList() +{ + // build modinfo obj for masterserver + rapidjson_document modinfoDoc; + auto& alloc = modinfoDoc.GetAllocator(); + modinfoDoc.SetObject(); + modinfoDoc.AddMember("Mods", rapidjson::kArrayType, alloc); + + int currentModIndex = 0; + for (Mod& mod : GetMods() | FilterEnabled) + { + if (!mod.RequiredOnClient) // (!mod.RequiredOnClient && !mod.Pdiff.size()) + continue; + + modinfoDoc["Mods"].PushBack(rapidjson::kObjectType, modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Name", rapidjson::StringRef(&mod.Name[0]), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("Version", rapidjson::StringRef(&mod.Version[0]), modinfoDoc.GetAllocator()); + modinfoDoc["Mods"][currentModIndex].AddMember("RequiredOnClient", mod.RequiredOnClient, modinfoDoc.GetAllocator()); + + currentModIndex++; + } + + rapidjson::StringBuffer buffer; + buffer.Clear(); + rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); + modinfoDoc.Accept(writer); + g_pMasterServerManager->m_sOwnModInfoJson = std::string(buffer.GetString()); +} + void ModManager::UnloadMods() { // save last state so we know what we need to reload @@ -1113,12 +1193,9 @@ void ModManager::CompileAssetsForFile(const char* filename) else { // check if we should build keyvalues, depending on whether any of our mods have patch kvs for this file - for (Mod& mod : GetMods()) + for (Mod& mod : GetMods() | FilterEnabled) { - if (!mod.m_bEnabled) - continue; - - if (mod.KeyValues.find(fileHash) != mod.KeyValues.end()) + if (m_ModLoadState->m_KeyValues.find(filename) != m_ModLoadState->m_KeyValues.end()) { TryBuildKeyValues(filename); return; @@ -1129,7 +1206,12 @@ void ModManager::CompileAssetsForFile(const char* filename) void ConCommand_mods_reload(const CCommand& args) { - g_pModManager->LoadMods(); + g_pModManager->LoadMods(false); +} + +void ConCommand_mods_reload_deferred(const CCommand& args) +{ + g_pModManager->LoadMods(true); } void ConCommand_mods_getfileowner(const CCommand& args) @@ -1172,5 +1254,6 @@ ON_DLL_LOAD_RELIESON("engine.dll", ModManager, (ConCommand, MasterServer), (CMod RegisterConCommand("reload_mods", ConCommand_mods_reload, "reloads mods", FCVAR_NONE); RegisterConCommand("mods_reload", ConCommand_mods_reload, "reloads mods", FCVAR_NONE); + RegisterConCommand("mods_reload_deferred", ConCommand_mods_reload_deferred, "reloads mods, prefers reloading assets on level load rather than now", FCVAR_NONE); RegisterConCommand("mods_getfileowner", ConCommand_mods_getfileowner, "find the mod that owns a given file", FCVAR_NONE); } diff --git a/NorthstarDLL/mods/modmanager.h b/NorthstarDLL/mods/modmanager.h index 940f1967..73d6cb3c 100644 --- a/NorthstarDLL/mods/modmanager.h +++ b/NorthstarDLL/mods/modmanager.h @@ -101,7 +101,6 @@ struct Mod // other files: std::vector<ModVPKEntry> Vpks; - std::unordered_map<size_t, std::string> KeyValues; std::vector<std::string> BinkVideos; // todo audio override struct @@ -142,7 +141,8 @@ class ModManager void LoadModDefinitions(); void SaveEnabledMods(); - void InstallMods(); + void BuildPublicModList(); + void InstallMods(bool bDeferredAssetReload); // mod installation funcs void InstallModCvars(Mod& mod); @@ -171,6 +171,8 @@ class ModManager bool bPlayerSettings = false; bool bAiSettings = false; bool bDamageDefs = false; // damagedefs + bool bWeaponSprings = false; + bool bAmmoSuckBehaviours = false; bool bDatatables = false; // can't actually reload this atm, just print a warning (todo, could maybe restart client to ensure loaded?) @@ -184,7 +186,7 @@ class ModManager } m_AssetTypesToReload; void CheckModFilesForChanges(); - void ReloadNecessaryModAssets(); + void ReloadNecessaryModAssets(bool bDeferred); struct ModLoadState @@ -192,6 +194,7 @@ class ModManager std::vector<Mod> m_LoadedMods; std::unordered_map<std::string, ModOverrideFile> m_ModFiles; std::unordered_map<std::string, std::string> m_DependencyConstants; + std::unordered_map<std::string, std::vector<ModOverrideFile>> m_KeyValues; }; // unfortunately need to be ptrs, so we can copy m_ModLoadState => m_LastModLoadState @@ -202,7 +205,7 @@ class ModManager public: ModManager(); - void LoadMods(); + void LoadMods(bool bDeferredAssetReload); std::string NormaliseModFilePath(const fs::path path); void CompileAssetsForFile(const char* filename); @@ -227,6 +230,12 @@ class ModManager void TryBuildKeyValues(const char* filename); void BuildKBActionsList(); + // deferred asset reloading funcs (i.e. set engine flags to reload later) + void DeferredReloadDamageFlags(); + void DeferredReloadWeaponSprings(); + void DeferredReloadAmmoSuckBehaviours(); + void DeferredReloadADSPulls(); + // asset reloading funcs bool TryReloadWeapon(const char* pWeaponName, const SidedWeaponReloadPointers* pReloadPointers); diff --git a/NorthstarDLL/mods/reload/reloadmodweapons.cpp b/NorthstarDLL/mods/reload/reloadmodweapons.cpp index ce661493..1384621a 100644 --- a/NorthstarDLL/mods/reload/reloadmodweapons.cpp +++ b/NorthstarDLL/mods/reload/reloadmodweapons.cpp @@ -27,12 +27,6 @@ VAR_AT(client.dll + 0xB339E8, GlobalWeaponDefs**, g_ppClientWeaponDefs); FUNCTION_AT(client.dll + 0x3D2FB0, void,, ClientReparseWeapon, (WeaponDefinition* pWeapon)); FUNCTION_AT(client.dll + 0x3CE270, void,, ClientReloadWeaponCallbacks, (int nWeaponIndex)); -/* uint16_t* g_pnServerWeaponsLoaded; -GlobalWeaponDefs** g_ppServerWeaponDefs; - -void (*ServerReparseWeapon)(WeaponDefinition* pWeapon); -void (*ServerReloadWeaponCallbacks)(int nWeaponIndex);*/ - // used for passing client/server funcs/data/pointers to TryReloadWeapon struct SidedWeaponReloadPointers { @@ -83,7 +77,7 @@ bool ModManager::TryReloadWeapon(const char* pWeaponName, const SidedWeaponReloa WeaponDefinition* pWeapon = (*pReloadPointers->m_ppWeaponDefs)->m_Weapons[nWeaponIndex].pWeaponDef; bool bReloadScriptFuncs = pWeapon->bReloadScriptFuncs; // this is reset after reparse pReloadPointers->m_fnReparseWeapon(pWeapon); - if (bReloadScriptFuncs) // always false in testing? + if (bReloadScriptFuncs) pReloadPointers->m_fnReloadWeaponCallbacks(nWeaponIndex); m_AssetTypesToReload.setsWeaponSettings.erase(pWeaponName); diff --git a/NorthstarDLL/mods/reload/reloadmodweapons_misc.cpp b/NorthstarDLL/mods/reload/reloadmodweapons_misc.cpp new file mode 100644 index 00000000..5f43b11f --- /dev/null +++ b/NorthstarDLL/mods/reload/reloadmodweapons_misc.cpp @@ -0,0 +1,45 @@ +#include "mods/modmanager.h" + +AUTOHOOK_INIT() + +// these are all used in their respective precacheweapon functions to determine whether to load these files +// just set these to make game reload assets during load! +VAR_AT(client.dll + 0x23EF0C5, bool*, g_pbClientHasLoadedDamageFlags); +VAR_AT(client.dll + 0x23EF0C6, bool*, g_pbClientHasLoadedWeaponSprings); +VAR_AT(client.dll + 0x23EF0C7, bool*, g_pbClientHasLoadedWeaponAmmoSuckBehaviours); +VAR_AT(client.dll + 0x23EF0C4, bool*, g_pbClientHasLoadedWeaponADSPulls); + +VAR_AT(server.dll + 0x160B474, bool*, g_pbServerHasLoadedDamageFlags); +VAR_AT(server.dll + 0x160B475, bool*, g_pbServerHasLoadedWeaponSprings); +VAR_AT(server.dll + 0x160B476, bool*, g_pbServerHasLoadedWeaponAmmoSuckBehaviours); +VAR_AT(server.dll + 0x160B477, bool*, g_pbServerHasLoadedWeaponADSPulls); + +void ModManager::DeferredReloadDamageFlags() +{ + *g_pbClientHasLoadedDamageFlags = false; + *g_pbServerHasLoadedDamageFlags = false; +} + +void ModManager::DeferredReloadWeaponSprings() +{ + *g_pbClientHasLoadedWeaponSprings = false; + *g_pbServerHasLoadedWeaponSprings = false; +} + +void ModManager::DeferredReloadAmmoSuckBehaviours() +{ + *g_pbClientHasLoadedWeaponAmmoSuckBehaviours = false; + *g_pbServerHasLoadedWeaponAmmoSuckBehaviours = false; +} + +void ModManager::DeferredReloadADSPulls() +{ + *g_pbClientHasLoadedWeaponADSPulls = false; + *g_pbServerHasLoadedWeaponADSPulls = false; +} + + +ON_DLL_LOAD_CLIENT("client.dll", ClientModReloadWeaponsMisc, (CModule module)) +{ + AUTOHOOK_DISPATCH_MODULE(client.dll) +} diff --git a/NorthstarDLL/scripts/client/scriptmodmenu.cpp b/NorthstarDLL/scripts/client/scriptmodmenu.cpp index 9d024936..e16216fd 100644 --- a/NorthstarDLL/scripts/client/scriptmodmenu.cpp +++ b/NorthstarDLL/scripts/client/scriptmodmenu.cpp @@ -158,8 +158,10 @@ ADD_SQFUNC( return SQRESULT_NOTNULL; // return empty array } -ADD_SQFUNC("void", NSReloadMods, "", "", ScriptContext::UI) +ADD_SQFUNC("void", NSReloadMods, "bool deferredReload = false", "", ScriptContext::UI) { - g_pModManager->LoadMods(); + const SQBool bDeferredReload = g_pSquirrel<context>->getbool(sqvm, 1); + g_pModManager->LoadMods(bDeferredReload); + return SQRESULT_NULL; } |