mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Game will check scripts/levels/mapname.json and load all rpaks in the rpak field. Game now also unloads pak files loaded by the SDK preventing crashes and unnecessary memory usage. VPK's and STBSP's are planned to be added as well.
341 lines
11 KiB
C++
341 lines
11 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose: Runs the state machine for the host & server.
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "core/stdafx.h"
|
|
#include "tier0/cmd.h"
|
|
#include "tier0/cvar.h"
|
|
#include "tier0/commandline.h"
|
|
#include "tier1/NetAdr2.h"
|
|
#include "tier2/socketcreator.h"
|
|
#ifdef DEDICATED
|
|
#include "engine/sv_rcon.h"
|
|
#else //
|
|
#include "engine/cl_rcon.h"
|
|
#endif // DEDICATED
|
|
#include "engine/gl_screen.h"
|
|
#include "engine/host_state.h"
|
|
#include "engine/net_chan.h"
|
|
#include "engine/sys_engine.h"
|
|
#include "engine/sys_utils.h"
|
|
#include "engine/cmodel_bsp.h"
|
|
#include "rtech/rtech_game.h"
|
|
#ifndef DEDICATED
|
|
#include "vgui/vgui_baseui_interface.h"
|
|
#endif // DEDICATED
|
|
#include "client/IVEngineClient.h"
|
|
#include "networksystem/pylon.h"
|
|
#include "public/include/bansystem.h"
|
|
#include "game/server/gameinterface.h"
|
|
|
|
std::chrono::time_point<std::chrono::steady_clock> tpPylonStartClock = std::chrono::steady_clock::now();
|
|
std::chrono::time_point<std::chrono::steady_clock> tpBanListStartClock = std::chrono::steady_clock::now();
|
|
bool g_bLevelResourceInitialized = false;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: state machine's main processing loop
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::FrameUpdate(void* rcx, void* rdx, float time)
|
|
{
|
|
static bool bInitialized = false;
|
|
if (!bInitialized)
|
|
{
|
|
g_pHostState->Setup();
|
|
bInitialized = true;
|
|
}
|
|
#ifdef DEDICATED
|
|
g_pRConServer->RunFrame();
|
|
#else //
|
|
g_pRConClient->RunFrame();
|
|
#endif // DEDICATED
|
|
|
|
HostStates_t oldState{};
|
|
void* placeHolder = nullptr;
|
|
if (setjmpFn(*host_abortserver, placeHolder))
|
|
{
|
|
CHostState_InitFn(g_pHostState);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
*g_ServerAbortServer = true;
|
|
do
|
|
{
|
|
Cbuf_Execute();
|
|
oldState = g_pHostState->m_iCurrentState;
|
|
|
|
switch (g_pHostState->m_iCurrentState)
|
|
{
|
|
case HostStates_t::HS_NEW_GAME:
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Loading level: '%s'\n", g_pHostState->m_levelName);
|
|
g_pHostState->State_NewGame();
|
|
break;
|
|
}
|
|
case HostStates_t::HS_CHANGE_LEVEL_SP:
|
|
{
|
|
g_pHostState->State_ChangeLevelSP();
|
|
break;
|
|
}
|
|
case HostStates_t::HS_CHANGE_LEVEL_MP:
|
|
{
|
|
g_pHostState->State_ChangeLevelMP();
|
|
break;
|
|
}
|
|
case HostStates_t::HS_RUN:
|
|
{
|
|
g_pHostState->Think();
|
|
State_RunFn(&g_pHostState->m_iCurrentState, nullptr, time);
|
|
break;
|
|
}
|
|
case HostStates_t::HS_GAME_SHUTDOWN:
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Shutdown host game\n");
|
|
|
|
g_bLevelResourceInitialized = false;
|
|
Host_Game_ShutdownFn(g_pHostState);
|
|
break;
|
|
}
|
|
case HostStates_t::HS_RESTART:
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Restarting state machine\n");
|
|
g_bLevelResourceInitialized = false;
|
|
#ifndef DEDICATED
|
|
CL_EndMovieFn();
|
|
#endif // !DEDICATED
|
|
SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway.
|
|
g_pEngine->SetNextState(EngineState_t::DLL_RESTART);
|
|
break;
|
|
}
|
|
case HostStates_t::HS_SHUTDOWN:
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Shutdown state machine\n");
|
|
g_bLevelResourceInitialized = false;
|
|
#ifndef DEDICATED
|
|
CL_EndMovieFn();
|
|
#endif // !DEDICATED
|
|
SendOfflineRequestToStryderFn(); // We have hostnames nulled anyway.
|
|
g_pEngine->SetNextState(EngineState_t::DLL_CLOSE);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
} while ((oldState != HostStates_t::HS_RUN || g_pHostState->m_iNextState == HostStates_t::HS_LOAD_GAME && g_pCVar->FindVar("single_frame_shutdown_for_reload")->GetBool())
|
|
&& oldState != HostStates_t::HS_SHUTDOWN
|
|
&& oldState != HostStates_t::HS_RESTART);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: setup state machine
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::Setup(void) const
|
|
{
|
|
g_pHostState->LoadConfig();
|
|
g_pConVar->ClearHostNames();
|
|
#ifdef DEDICATED
|
|
g_pRConServer->Init();
|
|
#else //
|
|
g_pRConClient->Init();
|
|
#endif // DEDICATED
|
|
|
|
*(bool*)m_bRestrictServerCommands = true; // Restrict commands.
|
|
ConCommandBase* disconnect = (ConCommandBase*)g_pCVar->FindCommand("disconnect");
|
|
disconnect->AddFlags(FCVAR_SERVER_CAN_EXECUTE); // Make sure server is not restricted to this.
|
|
|
|
if (net_userandomkey->GetBool())
|
|
{
|
|
HNET_GenerateKey();
|
|
}
|
|
|
|
g_pCVar->FindVar("net_usesocketsforloopback")->SetValue(1);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: think
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::Think(void) const
|
|
{
|
|
std::chrono::time_point<std::chrono::steady_clock> tpCrrentClock = std::chrono::steady_clock::now();
|
|
|
|
if (std::chrono::duration_cast<std::chrono::seconds>(tpCrrentClock - tpBanListStartClock).count() >= 1)
|
|
{
|
|
g_pBanSystem->BanListCheck();
|
|
tpBanListStartClock = std::chrono::steady_clock::now();
|
|
}
|
|
if (std::chrono::duration_cast<std::chrono::seconds>(tpCrrentClock - tpPylonStartClock).count() >= 5)
|
|
{
|
|
KeepAliveToPylon();
|
|
tpPylonStartClock = std::chrono::steady_clock::now();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: load and execute configuration files
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::LoadConfig(void) const
|
|
{
|
|
if (!g_pCmdLine->CheckParm("-devsdk"))
|
|
{
|
|
IVEngineClient_CommandExecute(NULL, "exec \"autoexec_server.cfg\"");
|
|
IVEngineClient_CommandExecute(NULL, "exec \"rcon_server.cfg\"");
|
|
#ifndef DEDICATED
|
|
IVEngineClient_CommandExecute(NULL, "exec \"autoexec_client.cfg\"");
|
|
IVEngineClient_CommandExecute(NULL, "exec \"rcon_client.cfg\"");
|
|
#endif // !DEDICATED
|
|
IVEngineClient_CommandExecute(NULL, "exec \"autoexec.cfg\"");
|
|
}
|
|
else // Development configs.
|
|
{
|
|
IVEngineClient_CommandExecute(NULL, "exec \"autoexec_server_dev.cfg\"");
|
|
IVEngineClient_CommandExecute(NULL, "exec \"rcon_server_dev.cfg\"");
|
|
#ifndef DEDICATED
|
|
IVEngineClient_CommandExecute(NULL, "exec \"autoexec_client_dev.cfg\"");
|
|
IVEngineClient_CommandExecute(NULL, "exec \"rcon_client_dev.cfg\"");
|
|
#endif // !DEDICATED
|
|
IVEngineClient_CommandExecute(NULL, "exec \"autoexec_dev.cfg\"");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: shutdown active game
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::GameShutDown(void)
|
|
{
|
|
g_bLevelResourceInitialized = false;
|
|
if (m_bActiveGame)
|
|
{
|
|
g_pServerGameDLL->GameShutdown();
|
|
m_bActiveGame = 0;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: unloads all pakfiles loaded by the SDK
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::UnloadPakFile(void)
|
|
{
|
|
for (int i = 0; i < sizeof(g_nLoadedPakFileId); i++)
|
|
{
|
|
if (g_nLoadedPakFileId[i] > 0)
|
|
{
|
|
RTech_UnloadPak(g_nLoadedPakFileId[i]);
|
|
}
|
|
else
|
|
{
|
|
memset(g_nLoadedPakFileId, '\0', sizeof(g_nLoadedPakFileId));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: initialize new game
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::State_NewGame(void)
|
|
{
|
|
g_bLevelResourceInitialized = false;
|
|
m_bSplitScreenConnect = false;
|
|
if (!g_ServerGameClients) // Init Game if it ain't valid.
|
|
{
|
|
SV_InitGameDLLFn();
|
|
}
|
|
|
|
if (!CModelLoader_Map_IsValidFn(g_CModelLoader, m_levelName) // Check if map is valid and if we can start a new game.
|
|
|| !Host_NewGameFn(m_levelName, nullptr, m_bBackgroundLevel, m_bSplitScreenConnect, nullptr) || !g_ServerGameClients)
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Error: Map not valid\n");
|
|
#ifndef DEDICATED
|
|
SCR_EndLoadingPlaque();
|
|
#endif // !DEDICATED
|
|
GameShutDown();
|
|
}
|
|
|
|
m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run.
|
|
|
|
// If our next state isn't a shutdown or its a forced shutdown then set next state to run.
|
|
if (m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCVar->FindVar("host_hasIrreversibleShutdown")->GetBool())
|
|
{
|
|
m_iNextState = HostStates_t::HS_RUN;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: change singleplayer level
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::State_ChangeLevelSP(void)
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Changing singleplayer level to: '%s'\n", m_levelName);
|
|
m_flShortFrameTime = 1.5; // Set frame time.
|
|
g_bLevelResourceInitialized = false;
|
|
|
|
if (CModelLoader_Map_IsValidFn(g_CModelLoader, m_levelName)) // Check if map is valid and if we can start a new game.
|
|
{
|
|
Host_ChangelevelFn(true, m_levelName, m_mapGroupName); // Call change level as singleplayer level.
|
|
}
|
|
else
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Error: Unable to find map: '%s'\n", m_levelName);
|
|
}
|
|
|
|
m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run.
|
|
|
|
// If our next state isn't a shutdown or its a forced shutdown then set next state to run.
|
|
if (m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCVar->FindVar("host_hasIrreversibleShutdown")->GetBool())
|
|
{
|
|
m_iNextState = HostStates_t::HS_RUN;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: change multiplayer level
|
|
//-----------------------------------------------------------------------------
|
|
FORCEINLINE void CHostState::State_ChangeLevelMP(void)
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Changing multiplayer level to: '%s'\n", m_levelName);
|
|
m_flShortFrameTime = 0.5; // Set frame time.
|
|
g_bLevelResourceInitialized = false;
|
|
|
|
g_pServerGameDLL->LevelShutdown();
|
|
if (CModelLoader_Map_IsValidFn(g_CModelLoader, m_levelName)) // Check if map is valid and if we can start a new game.
|
|
{
|
|
#ifndef DEDICATED
|
|
using EnabledProgressBarForNextLoadFn = void(*)(void*);
|
|
(*reinterpret_cast<EnabledProgressBarForNextLoadFn**>(g_pEngineVGui))[31](g_pEngineVGui); // EnabledProgressBarForNextLoad
|
|
#endif // !DEDICATED
|
|
Host_ChangelevelFn(false, m_levelName, m_mapGroupName); // Call change level as multiplayer level.
|
|
}
|
|
else
|
|
{
|
|
DevMsg(eDLL_T::ENGINE, "Error: Unable to find map: '%s'\n", m_levelName);
|
|
}
|
|
|
|
m_iCurrentState = HostStates_t::HS_RUN; // Set current state to run.
|
|
|
|
// If our next state isn't a shutdown or its a forced shutdown then set next state to run.
|
|
if (m_iNextState != HostStates_t::HS_SHUTDOWN || !g_pCVar->FindVar("host_hasIrreversibleShutdown")->GetBool())
|
|
{
|
|
m_iNextState = HostStates_t::HS_RUN;
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
void CHostState_Attach()
|
|
{
|
|
DetourAttach((LPVOID*)&CHostState_FrameUpdate, &CHostState::FrameUpdate);
|
|
}
|
|
|
|
void CHostState_Detach()
|
|
{
|
|
DetourDetach((LPVOID*)&CHostState_FrameUpdate, &CHostState::FrameUpdate);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
CHostState* g_pHostState = reinterpret_cast<CHostState*>(p_CHostState_FrameUpdate.FindPatternSelf("48 8D ?? ?? ?? ?? 01", ADDRESS::Direction::DOWN, 100).ResolveRelativeAddressSelf(0x3, 0x7).GetPtr());
|