aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj1
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj.filters6
-rw-r--r--NorthstarDLL/mods/modmanager.cpp1
-rw-r--r--NorthstarDLL/mods/modmanager.h8
-rw-r--r--NorthstarDLL/mods/reload/reloadmodweapons.cpp108
5 files changed, 123 insertions, 1 deletions
diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj
index 9bd3d6fa..5e07191d 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj
+++ b/NorthstarDLL/NorthstarDLL.vcxproj
@@ -500,6 +500,7 @@
<ClCompile Include="mods\compiled\modkeyvalues.cpp" />
<ClCompile Include="mods\compiled\modscriptsrson.cpp" />
<ClCompile Include="mods\modmanager.cpp" />
+ <ClCompile Include="mods\reload\reloadmodweapons.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 e149396f..125bdf04 100644
--- a/NorthstarDLL/NorthstarDLL.vcxproj.filters
+++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters
@@ -181,6 +181,9 @@
<Filter Include="Header Files\shared\exploit_fixes">
<UniqueIdentifier>{1a377c09-bd3d-4757-b3bc-9cd0a1e6ac0d}</UniqueIdentifier>
</Filter>
+ <Filter Include="Source Files\mods\reload">
+ <UniqueIdentifier>{f7aff0ef-a51c-4053-866a-4e5e3db71fce}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\httplib.h">
@@ -1426,6 +1429,9 @@
<ClCompile Include="util\utils.cpp">
<Filter>Source Files\util</Filter>
</ClCompile>
+ <ClCompile Include="mods\reload\reloadmodweapons.cpp">
+ <Filter>Source Files\mods\reload</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<MASM Include="audio_asm.asm">
diff --git a/NorthstarDLL/mods/modmanager.cpp b/NorthstarDLL/mods/modmanager.cpp
index 06e32e29..10c351ba 100644
--- a/NorthstarDLL/mods/modmanager.cpp
+++ b/NorthstarDLL/mods/modmanager.cpp
@@ -896,6 +896,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/"))
{
diff --git a/NorthstarDLL/mods/modmanager.h b/NorthstarDLL/mods/modmanager.h
index 253b7812..940f1967 100644
--- a/NorthstarDLL/mods/modmanager.h
+++ b/NorthstarDLL/mods/modmanager.h
@@ -126,6 +126,9 @@ struct ModOverrideFile
fs::file_time_type m_tLastWriteTime;
};
+// defined in reloadmodweapons.cpp
+extern struct SidedWeaponReloadPointers;
+
class ModManager
{
private:
@@ -165,7 +168,6 @@ class ModManager
bool bPlaylists = false;
bool bAimAssistSettings = false;
bool bMaterials = false; // vmts
- bool bWeaponSettings = false;
bool bPlayerSettings = false;
bool bAiSettings = false;
bool bDamageDefs = false; // damagedefs
@@ -177,6 +179,7 @@ class ModManager
bool bRPaks = false;
// assets that we can reload individually
+ std::unordered_set<std::string> setsWeaponSettings;
//std::vector<ModAudioOverride> vAudioOverrides
} m_AssetTypesToReload;
@@ -224,6 +227,9 @@ class ModManager
void TryBuildKeyValues(const char* filename);
void BuildKBActionsList();
+ // asset reloading funcs
+ bool TryReloadWeapon(const char* pWeaponName, const SidedWeaponReloadPointers* pReloadPointers);
+
// for std::views::filter, e.g. for (Mod& mod : g_pModManager::GetMods() | ModManager::FilterEnabled)
static inline constexpr auto FilterEnabled = std::views::filter([](Mod& mod) { return mod.m_bEnabled; });
static inline constexpr auto FilterRemote = std::views::filter([](Mod& mod) { return mod.m_bRemote; });
diff --git a/NorthstarDLL/mods/reload/reloadmodweapons.cpp b/NorthstarDLL/mods/reload/reloadmodweapons.cpp
new file mode 100644
index 00000000..297c3d48
--- /dev/null
+++ b/NorthstarDLL/mods/reload/reloadmodweapons.cpp
@@ -0,0 +1,108 @@
+#include "mods/modmanager.h"
+
+AUTOHOOK_INIT()
+
+OFFSET_STRUCT(WeaponDefinition)
+{
+ FIELD(5, bool bUnk); // this controls whether we reload script funcs
+ FIELD(6, char pWeaponName[]); // this probably has a max length but i do not know what it is
+};
+
+OFFSET_STRUCT(GlobalWeaponDefs)
+{
+ // each entry is 24 bytes, but no clue what the bytes after the def are, so just ignore atm
+ // need the full struct so we iterate properly
+ OFFSET_STRUCT(WeaponDefContainer)
+ {
+ STRUCT_SIZE(24);
+ WeaponDefinition* pWeaponDef;
+ };
+
+ FIELD(16, WeaponDefContainer m_Weapons[]);
+};
+
+uint16_t* g_pnClientWeaponsLoaded;
+GlobalWeaponDefs** g_ppClientWeaponDefs;
+
+void (*ClientReparseWeapon)(WeaponDefinition* pWeapon);
+void (*ClientReloadWeaponCallbacks)(int nWeaponIndex);
+int (*ClientWeaponIndexByName)(const char* pWeaponName);
+
+uint16_t* g_pnServerWeaponsLoaded;
+GlobalWeaponDefs** g_ppServerWeaponDefs;
+
+void (*ServerReparseWeapon)(WeaponDefinition* pWeapon);
+void (*ServerReloadWeaponCallbacks)(int nWeaponIndex);
+int (*ServerWeaponIndexByName)(const char* pWeaponName);
+
+// used for passing client/server funcs/data/pointers to TryReloadWeapon
+struct SidedWeaponReloadPointers
+{
+ // data pointers
+ uint16_t* m_pnWeaponsLoaded;
+ GlobalWeaponDefs** m_ppWeaponDefs;
+
+ // funcs
+ void (*m_fnReparseWeapon)(WeaponDefinition* pWeapon);
+ void (*m_fnReloadWeaponCallbacks)(int nWeaponIndex);
+ int (*m_fnWeaponIndexByName)(const char* pWeaponName);
+
+ SidedWeaponReloadPointers(
+ uint16_t* pnWeaponsLoaded,
+ GlobalWeaponDefs** ppWeaponDefs,
+ void (*fnReparseWeapon)(WeaponDefinition*),
+ void (*fnReloadWeaponCallbacks)(int),
+ int (*fnWeaponIndexByName)(const char*))
+ {
+ m_pnWeaponsLoaded = pnWeaponsLoaded;
+ m_ppWeaponDefs = ppWeaponDefs;
+ m_fnReparseWeapon = fnReparseWeapon;
+ m_fnReloadWeaponCallbacks = fnReloadWeaponCallbacks;
+ m_fnWeaponIndexByName = fnWeaponIndexByName;
+ }
+};
+
+bool ModManager::TryReloadWeapon(const char* pWeaponName, const SidedWeaponReloadPointers* pReloadPointers)
+{
+ if (!m_AssetTypesToReload.setsWeaponSettings.contains(pWeaponName))
+ return false; // don't reload
+
+ int nWeaponIndex = pReloadPointers->m_fnWeaponIndexByName(pWeaponName);
+ if (nWeaponIndex == -1) // weapon isn't loaded at all, no need to reload!
+ return false;
+
+ spdlog::info("ModManager::TryReloadWeapon reloading weapon {}", pWeaponName);
+
+ WeaponDefinition* pWeapon = (*pReloadPointers->m_ppWeaponDefs)->m_Weapons->pWeaponDef;
+ pReloadPointers->m_fnReparseWeapon(pWeapon);
+ if (pWeapon->bUnk)
+ pReloadPointers->m_fnReloadWeaponCallbacks(nWeaponIndex);
+
+ m_AssetTypesToReload.setsWeaponSettings.erase(pWeaponName);
+ return true;
+}
+
+// TODO: server implementation for this?
+AUTOHOOK(ClientPrecacheWeaponFromStringtable, client.dll + 0x195A60,
+bool, __fastcall, (void* a1, void* a2, void* a3, const char* pWeaponName))
+{
+ static SidedWeaponReloadPointers clientReloadPointers(
+ g_pnClientWeaponsLoaded, g_ppClientWeaponDefs, ClientReparseWeapon, ClientReloadWeaponCallbacks, ClientWeaponIndexByName);
+
+ if (g_pModManager->TryReloadWeapon(pWeaponName, &clientReloadPointers))
+ return true;
+
+ spdlog::info("PrecacheWeaponFromStringtable: {}", pWeaponName);
+ return ClientPrecacheWeaponFromStringtable(a1, a2, a3, pWeaponName);
+}
+
+ON_DLL_LOAD_CLIENT("client.dll", ModReloadWeaponsClient, (CModule module))
+{
+ AUTOHOOK_DISPATCH()
+
+ g_pnClientWeaponsLoaded = module.Offset(0xB33A02).As<uint16_t*>();
+ g_ppClientWeaponDefs = module.Offset(0xB339E8).As<GlobalWeaponDefs**>();
+
+ ClientReparseWeapon = module.Offset(0x3D2FB0).As<void(*)(WeaponDefinition*)>();
+ ClientReloadWeaponCallbacks = module.Offset(0x3CE270).As<void(*)(int)>();
+}