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 /NorthstarDLL/mods/modmanager.cpp | |
parent | e95aa783fa04aa6fb9f930e5b4d74ad447e7f087 (diff) | |
download | NorthstarLauncher-546de11c5524cb7b7bf67388c77a1508942d8fca.tar.gz NorthstarLauncher-546de11c5524cb7b7bf67388c77a1508942d8fca.zip |
add basic deferred reloading and reloading for keyvalues
Diffstat (limited to 'NorthstarDLL/mods/modmanager.cpp')
-rw-r--r-- | NorthstarDLL/mods/modmanager.cpp | 255 |
1 files changed, 169 insertions, 86 deletions
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); } |