aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobTheBob <for.oliver.kirkham@gmail.com>2023-03-04 23:07:05 +0000
committerBobTheBob <for.oliver.kirkham@gmail.com>2023-03-04 23:07:05 +0000
commit546de11c5524cb7b7bf67388c77a1508942d8fca (patch)
tree7fc3a6a37dfb136626532b029de2f8a8916e653e
parente95aa783fa04aa6fb9f930e5b4d74ad447e7f087 (diff)
downloadNorthstarLauncher-546de11c5524cb7b7bf67388c77a1508942d8fca.tar.gz
NorthstarLauncher-546de11c5524cb7b7bf67388c77a1508942d8fca.zip
add basic deferred reloading and reloading for keyvalues
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj1
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj.filters3
-rw-r--r--NorthstarDLL/core/filesystem/rpakfilesystem.cpp11
-rw-r--r--NorthstarDLL/mods/compiled/modkeyvalues.cpp22
-rw-r--r--NorthstarDLL/mods/modmanager.cpp255
-rw-r--r--NorthstarDLL/mods/modmanager.h17
-rw-r--r--NorthstarDLL/mods/reload/reloadmodweapons.cpp8
-rw-r--r--NorthstarDLL/mods/reload/reloadmodweapons_misc.cpp45
-rw-r--r--NorthstarDLL/scripts/client/scriptmodmenu.cpp6
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;
}