diff options
Diffstat (limited to 'primedev/engine/hoststate.cpp')
-rw-r--r-- | primedev/engine/hoststate.cpp | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/primedev/engine/hoststate.cpp b/primedev/engine/hoststate.cpp new file mode 100644 index 00000000..ec728afb --- /dev/null +++ b/primedev/engine/hoststate.cpp @@ -0,0 +1,191 @@ +#include "engine/hoststate.h" +#include "masterserver/masterserver.h" +#include "server/auth/serverauthentication.h" +#include "server/serverpresence.h" +#include "shared/playlist.h" +#include "core/tier0.h" +#include "engine/r2engine.h" +#include "shared/exploit_fixes/ns_limits.h" +#include "squirrel/squirrel.h" +#include "plugins/plugins.h" +#include "plugins/pluginbackend.h" + +AUTOHOOK_INIT() + +CHostState* g_pHostState; + +std::string sLastMode; + +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() +{ + 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(), 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(), + 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)) + { + g_pCVar->FindVar("net_data_block_enabled")->SetValue(true); + g_pServerAuthentication->m_bStartingLocalSPGame = true; + } + else + g_pServerAuthentication->m_bStartingLocalSPGame = false; +} + +// clang-format off +AUTOHOOK(CHostState__State_NewGame, engine.dll + 0x16E7D0, +void, __fastcall, (CHostState* self)) +// clang-format on +{ + spdlog::info("HostState: NewGame"); + + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); + Cbuf_Execute(); + + // need to do this to ensure we don't go to private match + if (g_pServerAuthentication->m_bNeedLocalAuthForNewgame) + R2::SetCurrentPlaylist("tdm"); + + ServerStartingOrChangingMap(); + + double dStartTime = Plat_FloatTime(); + CHostState__State_NewGame(self); + spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime); + + // setup server presence + g_pServerPresence->CreatePresence(); + g_pServerPresence->SetMap(g_pHostState->m_levelName, true); + g_pServerPresence->SetPlaylist(R2::GetCurrentPlaylistName()); + g_pServerPresence->SetPort(Cvar_hostport->GetInt()); + + g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false; +} + +// clang-format off +AUTOHOOK(CHostState__State_LoadGame, engine.dll + 0x16E730, +void, __fastcall, (CHostState* self)) +// clang-format on +{ + // singleplayer server starting + // useless in 99% of cases but without it things could potentially break very much + + spdlog::info("HostState: LoadGame"); + + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode); + Cbuf_Execute(); + + // this is normally done in ServerStartingOrChangingMap(), but seemingly the map name isn't set at this point + g_pCVar->FindVar("net_data_block_enabled")->SetValue(true); + g_pServerAuthentication->m_bStartingLocalSPGame = true; + + double dStartTime = Plat_FloatTime(); + CHostState__State_LoadGame(self); + spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime); + + // no server presence, can't do it because no map name in hoststate + // and also not super important for sp saves really + + g_pServerAuthentication->m_bNeedLocalAuthForNewgame = false; +} + +// clang-format off +AUTOHOOK(CHostState__State_ChangeLevelMP, engine.dll + 0x16E520, +void, __fastcall, (CHostState* self)) +// clang-format on +{ + spdlog::info("HostState: ChangeLevelMP"); + + ServerStartingOrChangingMap(); + + double dStartTime = Plat_FloatTime(); + CHostState__State_ChangeLevelMP(self); + spdlog::info("loading took {}s", Plat_FloatTime() - dStartTime); + + g_pServerPresence->SetMap(g_pHostState->m_levelName); +} + +// clang-format off +AUTOHOOK(CHostState__State_GameShutdown, engine.dll + 0x16E640, +void, __fastcall, (CHostState* self)) +// clang-format on +{ + spdlog::info("HostState: GameShutdown"); + + g_pServerPresence->DestroyPresence(); + + 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(), cmd_source_t::kCommandSrcCode)) + { + _Cmd_Exec_f(tempCommand, false, false); + Cbuf_Execute(); + } + + sLastMode.clear(); + } +} + +// clang-format off +AUTOHOOK(CHostState__FrameUpdate, engine.dll + 0x16DB00, +void, __fastcall, (CHostState* self, double flCurrentTime, float flFrameTime)) +// clang-format on +{ + CHostState__FrameUpdate(self, flCurrentTime, flFrameTime); + + if (*g_pServerState == server_state_t::ss_active) + { + // update server presence + g_pServerPresence->RunFrame(flCurrentTime); + + // update limits for frame + g_pServerLimits->RunFrame(flCurrentTime, flFrameTime); + } + + // Run Squirrel message buffer + if (g_pSquirrel<ScriptContext::UI>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::UI>->m_pSQVM->sqvm != nullptr) + g_pSquirrel<ScriptContext::UI>->ProcessMessageBuffer(); + + if (g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::CLIENT>->m_pSQVM->sqvm != nullptr) + g_pSquirrel<ScriptContext::CLIENT>->ProcessMessageBuffer(); + + if (g_pSquirrel<ScriptContext::SERVER>->m_pSQVM != nullptr && g_pSquirrel<ScriptContext::SERVER>->m_pSQVM->sqvm != nullptr) + g_pSquirrel<ScriptContext::SERVER>->ProcessMessageBuffer(); + + g_pPluginManager->RunFrame(); +} + +ON_DLL_LOAD_RELIESON("engine.dll", HostState, ConVar, (CModule module)) +{ + AUTOHOOK_DISPATCH() + + g_pHostState = module.Offset(0x7CF180).RCast<CHostState*>(); +} |