From 063260ca0b1f633cf120d79796fe7aa3329c1e26 Mon Sep 17 00:00:00 2001 From: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> Date: Fri, 13 Jan 2023 14:28:09 +0000 Subject: Execute cfgs before server startup for gamemode being run (#398) * run cfg to initialise gamemode on startup * remove ; characters from mp_gamemode * exec using _Cmd_Exec_f to avoid issues with semicolons in gamemode names allowing additional commands to be run * run from cfg/server/ rather than cfg/ * fixup formatting --- NorthstarDLL/core/convar/concommand.cpp | 3 + NorthstarDLL/engine/hoststate.cpp | 44 ++++++++++++ NorthstarDLL/engine/r2engine.cpp | 4 ++ NorthstarDLL/engine/r2engine.h | 2 + NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp | 6 +- NorthstarDLL/shared/misccommands.cpp | 78 ++++++++++++++++++++++ 6 files changed, 132 insertions(+), 5 deletions(-) (limited to 'NorthstarDLL') diff --git a/NorthstarDLL/core/convar/concommand.cpp b/NorthstarDLL/core/convar/concommand.cpp index 02c9e50f..094fa813 100644 --- a/NorthstarDLL/core/convar/concommand.cpp +++ b/NorthstarDLL/core/convar/concommand.cpp @@ -1,9 +1,12 @@ #include "pch.h" #include "concommand.h" #include "shared/misccommands.h" +#include "engine/r2engine.h" #include +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/engine/hoststate.cpp b/NorthstarDLL/engine/hoststate.cpp index d474b908..4900539b 100644 --- a/NorthstarDLL/engine/hoststate.cpp +++ b/NorthstarDLL/engine/hoststate.cpp @@ -20,9 +20,35 @@ namespace R2 } // namespace R2 ConVar* Cvar_hostport; +std::string sLastMode; + +void (*_fastcall _Cmd_Exec_f)(const CCommand& arg, bool bOnlyIfExists, bool bUseWhitelists); void ServerStartingOrChangingMap() { + ConVar* Cvar_mp_gamemode = g_pCVar->FindVar("mp_gamemode"); + + // directly call _Cmd_Exec_f to avoid weirdness with ; being in mp_gamemode potentially + // if we ran exec {mp_gamemode} and mp_gamemode contained semicolons, this could be used to execute more commands + char* commandBuf[1040]; // assumedly this is the size of CCommand since we don't have an actual constructor + memset(commandBuf, 0, sizeof(commandBuf)); + CCommand tempCommand = *(CCommand*)&commandBuf; + if (sLastMode.length() && + CCommand__Tokenize( + tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), R2::cmd_source_t::kCommandSrcCode)) + _Cmd_Exec_f(tempCommand, false, false); + + memset(commandBuf, 0, sizeof(commandBuf)); + if (CCommand__Tokenize( + tempCommand, + fmt::format("exec server/setup_gamemode_{}", sLastMode = Cvar_mp_gamemode->GetString()).c_str(), + R2::cmd_source_t::kCommandSrcCode)) + { + _Cmd_Exec_f(tempCommand, false, false); + } + + Cbuf_Execute(); // exec everything right now + // net_data_block_enabled is required for sp, force it if we're on an sp map // sucks for security but just how it be if (!strncmp(g_pHostState->m_levelName, "sp_", 3)) @@ -119,6 +145,22 @@ void, __fastcall, (CHostState* self)) g_pServerAuthentication->StopPlayerAuthServer(); CHostState__State_GameShutdown(self); + + // run gamemode cleanup cfg now instead of when we start next map + if (sLastMode.length()) + { + char* commandBuf[1040]; // assumedly this is the size of CCommand since we don't have an actual constructor + memset(commandBuf, 0, sizeof(commandBuf)); + CCommand tempCommand = *(CCommand*)&commandBuf; + if (CCommand__Tokenize( + tempCommand, fmt::format("exec server/cleanup_gamemode_{}", sLastMode).c_str(), R2::cmd_source_t::kCommandSrcCode)) + { + _Cmd_Exec_f(tempCommand, false, false); + Cbuf_Execute(); + } + + sLastMode.clear(); + } } // clang-format off @@ -154,4 +196,6 @@ ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module)) g_pHostState = module.Offset(0x7CF180).As(); Cvar_hostport = module.Offset(0x13FA6070).As(); + + _Cmd_Exec_f = module.Offset(0x1232C0).As(); } diff --git a/NorthstarDLL/engine/r2engine.cpp b/NorthstarDLL/engine/r2engine.cpp index 11233a2d..22884a73 100644 --- a/NorthstarDLL/engine/r2engine.cpp +++ b/NorthstarDLL/engine/r2engine.cpp @@ -10,6 +10,8 @@ namespace R2 Cbuf_AddTextType Cbuf_AddText; Cbuf_ExecuteType Cbuf_Execute; + bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource); + CEngine* g_pEngine; void (*CBaseClient__Disconnect)(void* self, uint32_t unknownButAlways1, const char* reason, ...); @@ -27,6 +29,8 @@ ON_DLL_LOAD("engine.dll", R2Engine, (CModule module)) Cbuf_AddText = module.Offset(0x1203B0).As(); Cbuf_Execute = module.Offset(0x1204B0).As(); + CCommand__Tokenize = module.Offset(0x418380).As(); + g_pEngine = module.Offset(0x7D70C8).Deref().As(); CBaseClient__Disconnect = module.Offset(0x1012C0).As(); diff --git a/NorthstarDLL/engine/r2engine.h b/NorthstarDLL/engine/r2engine.h index a7ed62a3..bc242201 100644 --- a/NorthstarDLL/engine/r2engine.h +++ b/NorthstarDLL/engine/r2engine.h @@ -59,6 +59,8 @@ namespace R2 typedef void (*Cbuf_ExecuteType)(); extern Cbuf_ExecuteType Cbuf_Execute; + extern bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource); + // CEngine enum EngineQuitState diff --git a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp b/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp index e4430fd4..8cb956e6 100644 --- a/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp +++ b/NorthstarDLL/shared/exploit_fixes/exploitfixes.cpp @@ -266,8 +266,6 @@ bool, __fastcall, (const char* pModName)) // 48 83 EC 28 48 8B 0D ? ? ? ? 48 8D } // ratelimit stringcmds, and prevent remote clients from calling commands that they shouldn't -bool (*CCommand__Tokenize)(CCommand& self, const char* pCommandString, R2::cmd_source_t commandSource); - // clang-format off AUTOHOOK(CGameClient__ExecuteStringCommand, engine.dll + 0x1022E0, bool, __fastcall, (R2::CBaseClient* self, uint32_t unknown, const char* pCommandString)) @@ -287,7 +285,7 @@ bool, __fastcall, (R2::CBaseClient* self, uint32_t unknown, const char* pCommand memset(commandBuf, 0, sizeof(commandBuf)); CCommand tempCommand = *(CCommand*)&commandBuf; - if (!CCommand__Tokenize(tempCommand, pCommandString, R2::cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC()) + if (!R2::CCommand__Tokenize(tempCommand, pCommandString, R2::cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC()) return false; ConCommand* command = R2::g_pCVar->FindCommand(tempCommand.Arg(0)); @@ -402,8 +400,6 @@ ON_DLL_LOAD("engine.dll", EngineExploitFixes, (CModule module)) { AUTOHOOK_DISPATCH_MODULE(engine.dll) - CCommand__Tokenize = module.Offset(0x418380).As(); - // allow client/ui to run clientcommands despite restricting servercommands module.Offset(0x4FB65).Patch("EB 11"); module.Offset(0x4FBAC).Patch("EB 16"); diff --git a/NorthstarDLL/shared/misccommands.cpp b/NorthstarDLL/shared/misccommands.cpp index ad8b2a32..f3817867 100644 --- a/NorthstarDLL/shared/misccommands.cpp +++ b/NorthstarDLL/shared/misccommands.cpp @@ -45,6 +45,72 @@ void ConCommand_ns_end_reauth_and_leave_to_lobby(const CCommand& arg) } } +void ConCommand_cvar_setdefaultvalue(const CCommand& arg) +{ + if (arg.ArgC() < 3) + { + spdlog::info("usage: cvar_setdefaultvalue mp_gamemode tdm"); + return; + } + + ConVar* pCvar = R2::g_pCVar->FindVar(arg.Arg(1)); + if (!pCvar) + { + spdlog::info("usage: cvar_setdefaultvalue mp_gamemode tdm"); + return; + } + + // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default + int nLen = strlen(arg.Arg(2)); + char* pBuf = new char[nLen + 1]; + strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen); + + pCvar->m_pszDefaultValue = pBuf; +} + +void ConCommand_cvar_setvalueanddefaultvalue(const CCommand& arg) +{ + if (arg.ArgC() < 3) + { + spdlog::info("usage: cvar_setvalueanddefaultvalue mp_gamemode tdm"); + return; + } + + ConVar* pCvar = R2::g_pCVar->FindVar(arg.Arg(1)); + if (!pCvar) + { + spdlog::info("usage: cvar_setvalueanddefaultvalue mp_gamemode tdm"); + return; + } + + // unfortunately no way for us to not leak memory here, as default value might not be in writeable memory by default + int nLen = strlen(arg.Arg(2)); + char* pBuf = new char[nLen + 1]; + strncpy_s(pBuf, nLen + 1, arg.Arg(2), nLen); + + pCvar->m_pszDefaultValue = pBuf; + pCvar->SetValue(pCvar->m_pszDefaultValue); +} + +void ConCommand_cvar_reset(const CCommand& arg) +{ + if (arg.ArgC() < 2) + { + spdlog::info("usage: cvar_reset mp_gamemode"); + return; + } + + ConVar* pCvar = R2::g_pCVar->FindVar(arg.Arg(1)); + if (!pCvar) + { + spdlog::info("usage: cvar_reset mp_gamemode"); + return; + } + + // reset cvar + pCvar->SetValue(pCvar->m_pszDefaultValue); +} + void AddMiscConCommands() { RegisterConCommand( @@ -61,6 +127,18 @@ void AddMiscConCommands() // this is a concommand because we make a deferred call to it from another thread RegisterConCommand("ns_end_reauth_and_leave_to_lobby", ConCommand_ns_end_reauth_and_leave_to_lobby, "", FCVAR_NONE); + + RegisterConCommand( + "cvar_setdefaultvalue", + ConCommand_cvar_setdefaultvalue, + "overwrites the default value of a cvar, for use with script and cvar_reset", + FCVAR_NONE); + RegisterConCommand( + "cvar_setvalueanddefaultvalue", + ConCommand_cvar_setvalueanddefaultvalue, + "overwrites the current value and default value of a cvar, for use with script and cvar_reset", + FCVAR_NONE); + RegisterConCommand("cvar_reset", ConCommand_cvar_reset, "resets a cvar's value to its default value", FCVAR_NONE); } // fixes up various cvar flags to have more sane values -- cgit v1.2.3