From 5a2dfaf03ad5aa71cbb715103ccab5ca3ca3107b Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 12 Apr 2024 22:06:31 +0200 Subject: [PATCH] Server: add option to respect game state when performing auto reload Can be toggled with the new cvar 'host_autoReloadRespectGameState', and used in combination with the new server script func 'SetAutoReloadState( bool state )'. This makes sure that even when the timer reaches 'host_autoReloadRate', it would wait with the reload until the game itself is finished (which is when SetAutoReloadState( true ) is being called from scripts). --- r5dev/engine/host_state.cpp | 40 ++++++++++++++++++++-------- r5dev/engine/host_state.h | 3 +++ r5dev/engine/server/server.cpp | 6 +++-- r5dev/engine/server/server.h | 9 ++++--- r5dev/game/server/vscript_server.cpp | 18 +++++++++++++ r5dev/game/server/vscript_server.h | 2 ++ 6 files changed, 61 insertions(+), 17 deletions(-) diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp index 8b1cc15a..9871e075 100644 --- a/r5dev/engine/host_state.cpp +++ b/r5dev/engine/host_state.cpp @@ -57,10 +57,10 @@ #include "game/shared/vscript_shared.h" #ifndef CLIENT_DLL -static ConVar sv_pylonVisibility("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", "0 = Offline, 1 = Hidden, 2 = Public."); -static ConVar sv_pylonRefreshRate("sv_pylonRefreshRate", "5.0", FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds)."); +static ConVar host_statusRefreshRate("host_statusRefreshRate", "0.5", FCVAR_RELEASE, "Host status refresh rate (seconds).", true, 0.f, false, 0.f); -static ConVar sv_autoReloadRate("sv_autoReloadRate", "0", FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null)."); +static ConVar host_autoReloadRate("host_autoReloadRate", "0", FCVAR_RELEASE, "Time in seconds between each auto-reload (disabled if null)."); +static ConVar host_autoReloadRespectGameState("host_autoReloadRespectGameState", "0", FCVAR_RELEASE, "Check the game state before proceeding to auto-reload (don't reload in the middle of a match)."); #endif // !CLIENT_DLL #ifdef DEDICATED @@ -136,6 +136,24 @@ static void HostState_KeepAlive() } #endif // DEDICATED +#ifndef CLIENT_DLL +void HostState_HandleAutoReload() +{ + if (host_autoReloadRate.GetBool()) + { + if (g_ServerGlobalVariables->m_flCurTime > host_autoReloadRate.GetFloat()) + { + // We should respect the game state, and the game isn't finished yet so + // don't reload the server now. + if (host_autoReloadRespectGameState.GetBool() && !g_hostReloadState) + return; + + Cbuf_AddText(Cbuf_GetCurrentPlayer(), "reload\n", cmd_source_t::kCommandSrcCode); + } + } +} +#endif // !CLIENT_DLL + //----------------------------------------------------------------------------- // Purpose: state machine's main processing loop //----------------------------------------------------------------------------- @@ -343,14 +361,10 @@ void CHostState::Think(void) const #endif // DEDICATED bInitialized = true; } - if (sv_autoReloadRate.GetBool()) - { - if (g_ServerGlobalVariables->m_flCurTime > sv_autoReloadRate.GetFloat()) - { - Cbuf_AddText(Cbuf_GetCurrentPlayer(), "reload\n", cmd_source_t::kCommandSrcCode); - } - } - if (statsTimer.GetDurationInProgress().GetSeconds() > sv_statusRefreshRate.GetFloat()) + + HostState_HandleAutoReload(); + + if (statsTimer.GetDurationInProgress().GetSeconds() > host_statusRefreshRate.GetFloat()) { SetConsoleTitleA(Format("%s - %d/%d Players (%s on %s) - %d%% Server CPU (%.3f msec on frame %d)", hostname->GetString(), g_pServer->GetNumClients(), @@ -547,3 +561,7 @@ void VHostState::Detour(const bool bAttach) const /////////////////////////////////////////////////////////////////////////////// CHostState* g_pHostState = nullptr; + +#ifndef CLIENT_DLL +bool g_hostReloadState = false; +#endif // !CLIENT_DLL diff --git a/r5dev/engine/host_state.h b/r5dev/engine/host_state.h index be1dbc0f..77ced4c7 100644 --- a/r5dev/engine/host_state.h +++ b/r5dev/engine/host_state.h @@ -58,6 +58,9 @@ inline void(*v_HostState_ChangeLevelMP)(char const* pNewLevel, char const* pLand /////////////////////////////////////////////////////////////////////////////// extern CHostState* g_pHostState; +#ifndef CLIENT_DLL +extern bool g_hostReloadState; +#endif // !CLIENT_DLL /////////////////////////////////////////////////////////////////////////////// class VHostState : public IDetour diff --git a/r5dev/engine/server/server.cpp b/r5dev/engine/server/server.cpp index b5efc531..2645da8d 100644 --- a/r5dev/engine/server/server.cpp +++ b/r5dev/engine/server/server.cpp @@ -25,10 +25,12 @@ // Console variables //--------------------------------------------------------------------------------- ConVar sv_showconnecting("sv_showconnecting", "1", FCVAR_RELEASE, "Logs information about the connecting client to the console"); -ConVar sv_globalBanlist("sv_globalBanlist", "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, "0 = Disable, 1 = Enable."); +ConVar sv_pylonVisibility("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", "0 = Offline, 1 = Hidden, 2 = Public."); +ConVar sv_pylonRefreshRate("sv_pylonRefreshRate", "5.0", FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds)."); + +ConVar sv_globalBanlist("sv_globalBanlist", "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, "0 = Disable, 1 = Enable."); ConVar sv_banlistRefreshRate("sv_banlistRefreshRate", "30.0", FCVAR_DEVELOPMENTONLY, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f); -ConVar sv_statusRefreshRate("sv_statusRefreshRate", "0.5", FCVAR_RELEASE, "Server status refresh rate (seconds).", true, 0.f, false, 0.f); static ConVar sv_validatePersonaName("sv_validatePersonaName", "1", FCVAR_RELEASE, "Validate the client's textual persona name on connect."); static ConVar sv_minPersonaNameLength("sv_minPersonaNameLength", "4", FCVAR_RELEASE, "The minimum length of the client's textual persona name.", true, 0.f, false, 0.f); diff --git a/r5dev/engine/server/server.h b/r5dev/engine/server/server.h index cc05ac46..3f976c6d 100644 --- a/r5dev/engine/server/server.h +++ b/r5dev/engine/server/server.h @@ -111,13 +111,14 @@ static_assert(sizeof(CServer) == 0x25264C0); extern CServer* g_pServer; +extern ConVar sv_showconnecting; + +extern ConVar sv_pylonVisibility; +extern ConVar sv_pylonRefreshRate; + extern ConVar sv_globalBanlist; extern ConVar sv_banlistRefreshRate; -extern ConVar sv_statusRefreshRate; - -extern ConVar sv_showconnecting; - /* ==== CSERVER ========================================================================================================================================================= */ inline void(*CServer__FrameJob)(double flFrameTime, bool bRunOverlays, bool bUpdateFrame); inline void(*CServer__RunFrame)(CServer* pServer); diff --git a/r5dev/game/server/vscript_server.cpp b/r5dev/game/server/vscript_server.cpp index 2123d4d7..9120f395 100644 --- a/r5dev/game/server/vscript_server.cpp +++ b/r5dev/game/server/vscript_server.cpp @@ -83,6 +83,7 @@ namespace VScriptCode SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } + //----------------------------------------------------------------------------- // Purpose: shuts the server down and disconnects all clients //----------------------------------------------------------------------------- @@ -94,6 +95,21 @@ namespace VScriptCode SCRIPT_CHECK_AND_RETURN(v, SQ_OK); } + //----------------------------------------------------------------------------- + // Purpose: sets whether the server could auto reload at this time (e.g. if + // server admin has host_autoReloadRate AND host_autoReloadRespectGameState + // set, and its time to auto reload, but the match hasn't finished yet, wait + // until this is set to proceed the reload of the server + //----------------------------------------------------------------------------- + SQRESULT SetAutoReloadState(HSQUIRRELVM v) + { + SQBool state = false; + sq_getbool(v, 2, &state); + + g_hostReloadState = state; + SCRIPT_CHECK_AND_RETURN(v, SQ_OK); + } + //----------------------------------------------------------------------------- // Purpose: kicks a player by given name //----------------------------------------------------------------------------- @@ -282,6 +298,8 @@ void Script_RegisterCoreServerFunctions(CSquirrelVM* s) DEFINE_SERVER_SCRIPTFUNC_NAMED(s, CreateServer, "Starts server with the specified settings", "void", "string, string, string, string, int"); DEFINE_SERVER_SCRIPTFUNC_NAMED(s, DestroyServer, "Shuts the local server down", "void", ""); + + DEFINE_SERVER_SCRIPTFUNC_NAMED(s, SetAutoReloadState, "Set whether we can auto-reload the server", "void", "bool"); } //--------------------------------------------------------------------------------- diff --git a/r5dev/game/server/vscript_server.h b/r5dev/game/server/vscript_server.h index d75cd394..4bc7a82f 100644 --- a/r5dev/game/server/vscript_server.h +++ b/r5dev/game/server/vscript_server.h @@ -9,6 +9,8 @@ namespace VScriptCode SQRESULT CreateServer(HSQUIRRELVM v); SQRESULT DestroyServer(HSQUIRRELVM v); + SQRESULT SetAutoReloadState(HSQUIRRELVM v); + SQRESULT KickPlayerByName(HSQUIRRELVM v); SQRESULT KickPlayerById(HSQUIRRELVM v); SQRESULT BanPlayerByName(HSQUIRRELVM v);