diff options
-rw-r--r-- | NorthstarDLL/NorthstarDLL.vcxproj | 1 | ||||
-rw-r--r-- | NorthstarDLL/NorthstarDLL.vcxproj.filters | 6 | ||||
-rw-r--r-- | NorthstarDLL/core/convar/concommand.cpp | 2 | ||||
-rw-r--r-- | NorthstarDLL/core/hooks.cpp | 39 | ||||
-rw-r--r-- | NorthstarDLL/core/hooks.h | 86 | ||||
-rw-r--r-- | NorthstarDLL/engine/host.cpp | 3 | ||||
-rw-r--r-- | NorthstarDLL/engine/hoststate.cpp | 7 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.cpp | 9 | ||||
-rw-r--r-- | NorthstarDLL/mods/modmanager.h | 8 | ||||
-rw-r--r-- | NorthstarDLL/mods/reload/reloadmodweapons.cpp | 112 | ||||
-rw-r--r-- | NorthstarDLL/shared/playlist.cpp | 13 |
11 files changed, 228 insertions, 58 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/core/convar/concommand.cpp b/NorthstarDLL/core/convar/concommand.cpp index d6874c5c..fb5e137b 100644 --- a/NorthstarDLL/core/convar/concommand.cpp +++ b/NorthstarDLL/core/convar/concommand.cpp @@ -4,8 +4,6 @@ #include <iostream> -bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource); - //----------------------------------------------------------------------------- // Purpose: Returns true if this is a command // Output : bool diff --git a/NorthstarDLL/core/hooks.cpp b/NorthstarDLL/core/hooks.cpp index 34b48d1d..7c70b0a5 100644 --- a/NorthstarDLL/core/hooks.cpp +++ b/NorthstarDLL/core/hooks.cpp @@ -65,6 +65,9 @@ __dllLoadCallback::__dllLoadCallback( void __fileAutohook::Dispatch() { + for (__autovar* var : vars) + var->Dispatch(); + for (__autohook* hook : hooks) hook->Dispatch(); } @@ -114,6 +117,42 @@ bool ManualHook::Dispatch(LPVOID addr, LPVOID* orig) return false; } +uintptr_t ParseDLLOffsetString(const char* pAddrString) +{ + // in the format server.dll + 0xDEADBEEF + int iDllNameEnd = 0; + for (; !isspace(pAddrString[iDllNameEnd]) && pAddrString[iDllNameEnd] != '+'; iDllNameEnd++) + ; + + char* pModuleName = new char[iDllNameEnd + 1]; + memcpy(pModuleName, pAddrString, iDllNameEnd); + pModuleName[iDllNameEnd] = '\0'; + + // get the module address + const HMODULE pModuleAddr = GetModuleHandleA(pModuleName); + + if (!pModuleAddr) + return 0; + + // get the offset string + uintptr_t iOffset = 0; + + int iOffsetBegin = iDllNameEnd; + int iOffsetEnd = strlen(pAddrString); + + // seek until we hit the start of the number offset + for (; !(pAddrString[iOffsetBegin] >= '0' && pAddrString[iOffsetBegin] <= '9') && pAddrString[iOffsetBegin]; iOffsetBegin++) + ; + + bool bIsHex = pAddrString[iOffsetBegin] == '0' && (pAddrString[iOffsetBegin + 1] == 'X' || pAddrString[iOffsetBegin + 1] == 'x'); + if (bIsHex) + iOffset = std::stoi(pAddrString + iOffsetBegin + 2, 0, 16); + else + iOffset = std::stoi(pAddrString + iOffsetBegin); + + return ((uintptr_t)pModuleAddr + iOffset); +} + // dll load callback stuff // this allows for code to register callbacks to be run as soon as a dll is loaded, mainly to allow for patches to be made on dll load struct DllLoadCallback diff --git a/NorthstarDLL/core/hooks.h b/NorthstarDLL/core/hooks.h index f47791fb..205b6079 100644 --- a/NorthstarDLL/core/hooks.h +++ b/NorthstarDLL/core/hooks.h @@ -63,16 +63,20 @@ class __dllLoadCallback // new macro hook stuff class __autohook; +class __autovar; class __fileAutohook { public: std::vector<__autohook*> hooks; + std::vector<__autovar*> vars; void Dispatch(); void DispatchForModule(const char* pModuleName); }; +uintptr_t ParseDLLOffsetString(const char* pAddrString); + // initialise autohooks for this file #define AUTOHOOK_INIT() \ namespace \ @@ -187,39 +191,7 @@ class __autohook case OFFSET_STRING: { - // in the format server.dll + 0xDEADBEEF - int iDllNameEnd = 0; - for (; !isspace(pAddrString[iDllNameEnd]) && pAddrString[iDllNameEnd] != '+'; iDllNameEnd++) - ; - - char* pModuleName = new char[iDllNameEnd + 1]; - memcpy(pModuleName, pAddrString, iDllNameEnd); - pModuleName[iDllNameEnd] = '\0'; - - // get the module address - const HMODULE pModuleAddr = GetModuleHandleA(pModuleName); - - if (!pModuleAddr) - break; - - // get the offset string - uintptr_t iOffset = 0; - - int iOffsetBegin = iDllNameEnd; - int iOffsetEnd = strlen(pAddrString); - - // seek until we hit the start of the number offset - for (; !(pAddrString[iOffsetBegin] >= '0' && pAddrString[iOffsetBegin] <= '9') && pAddrString[iOffsetBegin]; iOffsetBegin++) - ; - - bool bIsHex = - pAddrString[iOffsetBegin] == '0' && (pAddrString[iOffsetBegin + 1] == 'X' || pAddrString[iOffsetBegin + 1] == 'x'); - if (bIsHex) - iOffset = std::stoi(pAddrString + iOffsetBegin + 2, 0, 16); - else - iOffset = std::stoi(pAddrString + iOffsetBegin); - - targetAddr = (LPVOID)((uintptr_t)pModuleAddr + iOffset); + targetAddr = (LPVOID)ParseDLLOffsetString(pAddrString); break; } @@ -309,3 +281,51 @@ class ManualHook void MakeHook(LPVOID pTarget, LPVOID pDetour, void* ppOriginal, const char* pFuncName = ""); #define MAKEHOOK(pTarget, pDetour, ppOriginal) MakeHook(pTarget, pDetour, ppOriginal, __STR(pDetour)) + +class __autovar +{ + public: + char* m_pAddrString; + void** m_pTarget; + + public: + __autovar(__fileAutohook* pAutohook, const char* pAddrString, void** pTarget) + { + m_pTarget = pTarget; + + const int iAddrStrlen = strlen(pAddrString) + 1; + m_pAddrString = new char[iAddrStrlen]; + memcpy(m_pAddrString, pAddrString, iAddrStrlen); + + pAutohook->vars.push_back(this); + } + + void Dispatch() + { + *m_pTarget = (void*)ParseDLLOffsetString(m_pAddrString); + } +}; + +// VAR_AT(engine.dll+0x404, ConVar*, Cvar_host_timescale) +#define VAR_AT(addrString, type, name) \ + type name; \ + namespace \ + { \ + __autovar CONCAT2(__autovar, __LINE__)(&__FILEAUTOHOOK, __STR(addrString), (void**)&name); \ + } + +// FUNCTION_AT(engine.dll + 0xDEADBEEF, void, __fastcall, SomeFunc, (void* a1)) +#define FUNCTION_AT(addrString, type, callingConvention, name, args) \ + type(*callingConvention name) args; \ + namespace \ + { \ + __autovar CONCAT2(__autovar, __LINE__)(&__FILEAUTOHOOK, __STR(addrString), (void**)&name); \ + } + +// int* g_pSomeInt; +// DEFINED_VAR_AT(engine.dll + 0x5005, g_pSomeInt) +#define DEFINED_VAR_AT(addrString, name) \ + namespace \ + { \ + __autovar CONCAT2(__autovar, __LINE__)(&__FILEAUTOHOOK, __STR(addrString), (void**)&name); \ + } diff --git a/NorthstarDLL/engine/host.cpp b/NorthstarDLL/engine/host.cpp index 436a169d..49cc3663 100644 --- a/NorthstarDLL/engine/host.cpp +++ b/NorthstarDLL/engine/host.cpp @@ -5,7 +5,9 @@ #include "shared/misccommands.h" #include "r2engine.h" #include "core/tier0.h" + AUTOHOOK_INIT() + // clang-format off AUTOHOOK(Host_Init, engine.dll + 0x155EA0, void, __fastcall, (bool bDedicated)) @@ -24,6 +26,7 @@ void, __fastcall, (bool bDedicated)) else R2::Cbuf_AddText(R2::Cbuf_GetCurrentPlayer(), "exec autoexec_ns_client", R2::cmd_source_t::kCommandSrcCode); } + ON_DLL_LOAD("engine.dll", Host_Init, (CModule module)) { AUTOHOOK_DISPATCH() diff --git a/NorthstarDLL/engine/hoststate.cpp b/NorthstarDLL/engine/hoststate.cpp index e9c44803..ec4733d6 100644 --- a/NorthstarDLL/engine/hoststate.cpp +++ b/NorthstarDLL/engine/hoststate.cpp @@ -18,10 +18,10 @@ namespace R2 CHostState* g_pHostState; } // namespace R2 -ConVar* Cvar_hostport; std::string sLastMode; -void (*_fastcall _Cmd_Exec_f)(const CCommand& arg, bool bOnlyIfExists, bool bUseWhitelists); +VAR_AT(engine.dll + 0x13FA6070, ConVar*, Cvar_hostport); +FUNCTION_AT(engine.dll + 0x1232C0, void, __fastcall, _Cmd_Exec_f, (const CCommand& arg, bool bOnlyIfExists, bool bUseWhitelists)); void ServerStartingOrChangingMap() { @@ -198,7 +198,4 @@ ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module)) AUTOHOOK_DISPATCH() g_pHostState = module.Offset(0x7CF180).As<CHostState*>(); - Cvar_hostport = module.Offset(0x13FA6070).As<ConVar*>(); - - _Cmd_Exec_f = module.Offset(0x1232C0).As<void (*__fastcall)(const CCommand&, bool, bool)>(); } diff --git a/NorthstarDLL/mods/modmanager.cpp b/NorthstarDLL/mods/modmanager.cpp index 06e32e29..6fc64f4f 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/")) { @@ -914,13 +915,6 @@ void ModManager::CheckModFilesForChanges() // 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")) - // we also check these on change of mod keyvalues - if (!m_AssetTypesToReload.bWeaponSettings && !pChangedFile->m_Path.parent_path().compare("scripts/weapons/")) - { - m_AssetTypesToReload.bWeaponSettings = true; - continue; - } - if (!m_AssetTypesToReload.bPlayerSettings && !pChangedFile->m_Path.parent_path().compare("scripts/players/")) { m_AssetTypesToReload.bPlayerSettings = true; @@ -1080,7 +1074,6 @@ void ModManager::UnloadMods() m_AssetTypesToReload.bAimAssistSettings = false; m_AssetTypesToReload.bMaterials = false; m_AssetTypesToReload.bRPaks = false; - m_AssetTypesToReload.bWeaponSettings = false; m_AssetTypesToReload.bPlayerSettings = false; m_AssetTypesToReload.bAiSettings = false; m_AssetTypesToReload.bDamageDefs = false; 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..ce661493 --- /dev/null +++ b/NorthstarDLL/mods/reload/reloadmodweapons.cpp @@ -0,0 +1,112 @@ +#include "mods/modmanager.h" + +AUTOHOOK_INIT() + +OFFSET_STRUCT(WeaponDefinition) +{ + FIELD(5, bool bReloadScriptFuncs); + 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[]); +}; + +VAR_AT(client.dll + 0xB33A02, uint16_t*, g_pnClientWeaponsLoaded); +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 +{ + // data pointers + uint16_t* m_pnWeaponsLoaded; + GlobalWeaponDefs** m_ppWeaponDefs; + + // funcs + void (*m_fnReparseWeapon)(WeaponDefinition* pWeapon); + void (*m_fnReloadWeaponCallbacks)(int nWeaponIndex); + + SidedWeaponReloadPointers( + uint16_t* pnWeaponsLoaded, + GlobalWeaponDefs** ppWeaponDefs, + void (*fnReparseWeapon)(WeaponDefinition*), + void (*fnReloadWeaponCallbacks)(int)) + { + m_pnWeaponsLoaded = pnWeaponsLoaded; + m_ppWeaponDefs = ppWeaponDefs; + m_fnReparseWeapon = fnReparseWeapon; + m_fnReloadWeaponCallbacks = fnReloadWeaponCallbacks; + } +}; + +int WeaponIndexByName(const char* pWeaponName, const SidedWeaponReloadPointers* pReloadPointers) +{ + for (int i = 0; i < *pReloadPointers->m_pnWeaponsLoaded; i++) + { + const WeaponDefinition* pWeapon = (*pReloadPointers->m_ppWeaponDefs)->m_Weapons[i].pWeaponDef; + if (!strcmp(pWeapon->pWeaponName, pWeaponName)) + return i; + } + + return -1; +} + +bool ModManager::TryReloadWeapon(const char* pWeaponName, const SidedWeaponReloadPointers* pReloadPointers) +{ + if (!m_AssetTypesToReload.setsWeaponSettings.contains(pWeaponName)) + return false; // don't reload + + int nWeaponIndex = WeaponIndexByName(pWeaponName, pReloadPointers); + 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[nWeaponIndex].pWeaponDef; + bool bReloadScriptFuncs = pWeapon->bReloadScriptFuncs; // this is reset after reparse + pReloadPointers->m_fnReparseWeapon(pWeapon); + if (bReloadScriptFuncs) // always false in testing? + pReloadPointers->m_fnReloadWeaponCallbacks(nWeaponIndex); + + m_AssetTypesToReload.setsWeaponSettings.erase(pWeaponName); + return true; +} + +// TODO: server implementation for this? +// clang-format off +AUTOHOOK(ClientPrecacheWeaponFromStringtable, client.dll + 0x195A60, +bool, __fastcall, (void* a1, void* a2, void* a3, const char* pWeaponName)) +// clang-format on +{ + static SidedWeaponReloadPointers clientReloadPointers( + g_pnClientWeaponsLoaded, g_ppClientWeaponDefs, ClientReparseWeapon, ClientReloadWeaponCallbacks); + + 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_MODULE(server.dll) +} diff --git a/NorthstarDLL/shared/playlist.cpp b/NorthstarDLL/shared/playlist.cpp index 018b2a9b..f19f32bb 100644 --- a/NorthstarDLL/shared/playlist.cpp +++ b/NorthstarDLL/shared/playlist.cpp @@ -11,10 +11,10 @@ AUTOHOOK_INIT() // use the R2 namespace for game funcs namespace R2 { - const char* (*GetCurrentPlaylistName)(); - void (*SetCurrentPlaylist)(const char* pPlaylistName); - void (*SetPlaylistVarOverride)(const char* pVarName, const char* pValue); - const char* (*GetCurrentPlaylistVar)(const char* pVarName, bool bUseOverrides); + FUNCTION_AT(engine.dll + 0x18C640, const char*, , GetCurrentPlaylistName, ()); + FUNCTION_AT(engine.dll + 0x18EB20, void, , SetCurrentPlaylist, (const char* pPlaylistName)); + FUNCTION_AT(engine.dll + 0x18ED00, void, , SetPlaylistVarOverride, (const char* pVarName, const char* pValue)); + FUNCTION_AT(engine.dll + 0x18C680, const char*, , GetCurrentPlaylistVar, (const char* pVarName, bool bUseOverrides)); } // namespace R2 ConVar* Cvar_ns_use_clc_SetPlaylistVarOverride; @@ -104,11 +104,6 @@ ON_DLL_LOAD_RELIESON("engine.dll", PlaylistHooks, (ConCommand, ConVar), (CModule { AUTOHOOK_DISPATCH() - R2::GetCurrentPlaylistName = module.Offset(0x18C640).As<const char* (*)()>(); - R2::SetCurrentPlaylist = module.Offset(0x18EB20).As<void (*)(const char*)>(); - R2::SetPlaylistVarOverride = module.Offset(0x18ED00).As<void (*)(const char*, const char*)>(); - R2::GetCurrentPlaylistVar = module.Offset(0x18C680).As<const char* (*)(const char*, bool)>(); - // playlist is the name of the command on respawn servers, but we already use setplaylist so can't get rid of it RegisterConCommand("playlist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); RegisterConCommand("setplaylist", ConCommand_playlist, "Sets the current playlist", FCVAR_NONE); |