aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj1
-rw-r--r--NorthstarDLL/NorthstarDLL.vcxproj.filters6
-rw-r--r--NorthstarDLL/core/convar/concommand.cpp2
-rw-r--r--NorthstarDLL/core/hooks.cpp39
-rw-r--r--NorthstarDLL/core/hooks.h86
-rw-r--r--NorthstarDLL/engine/host.cpp3
-rw-r--r--NorthstarDLL/engine/hoststate.cpp7
-rw-r--r--NorthstarDLL/mods/modmanager.cpp9
-rw-r--r--NorthstarDLL/mods/modmanager.h8
-rw-r--r--NorthstarDLL/mods/reload/reloadmodweapons.cpp112
-rw-r--r--NorthstarDLL/shared/playlist.cpp13
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);