diff options
-rw-r--r-- | NorthstarDLL/NorthstarDLL.vcxproj | 1 | ||||
-rw-r--r-- | NorthstarDLL/NorthstarDLL.vcxproj.filters | 6 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.cpp | 1 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.h | 8 | ||||
-rw-r--r-- | NorthstarDLL/mods/reload/reloadmodweapons.cpp | 108 |
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)>(); +} |