diff --git a/core/TimerSys.cpp b/core/TimerSys.cpp index 2084b153..abb6a60c 100644 --- a/core/TimerSys.cpp +++ b/core/TimerSys.cpp @@ -35,6 +35,7 @@ #include "frame_hooks.h" #include "ConVarManager.h" #include "logic_bridge.h" +#include #define TIMER_MIN_ACCURACY 0.1 @@ -170,9 +171,8 @@ void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, TimerSystem::TimerSystem() { m_pMapTimer = NULL; - m_bHasMapTickedYet = false; - m_bHasMapSimulatedYet = false; m_fLastTickedTime = 0.0f; + OnSourceModLevelEnd(); } TimerSystem::~TimerSystem() @@ -213,29 +213,28 @@ void TimerSystem::OnSourceModLevelEnd() { m_bHasMapTickedYet = false; m_bHasMapSimulatedYet = false; + m_bWasSimulating = false; + m_uFramesAhead = 0; } -void TimerSystem::GameFrame(bool simulating) +/* Think is called before gpGlobals is updated every frame, even if the server is hibernating */ +void TimerSystem::Think(bool unused) { - if (simulating && m_bHasMapTickedYet) - { - g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime; - if (!m_bHasMapSimulatedYet) - { - m_bHasMapSimulatedYet = true; - MapTimeLeftChanged(); - } - } - else - { + m_uFramesAhead++; + bool simulating = m_bWasSimulating && m_uFramesAhead == 1; + + if (m_bHasMapTickedYet) { + g_fUniversalTime += gpGlobals->realtime - m_fLastTickedTime; + } else { g_fUniversalTime += gpGlobals->interval_per_tick; } - m_fLastTickedTime = gpGlobals->curtime; + m_fLastTickedTime = gpGlobals->realtime; m_bHasMapTickedYet = true; - if (g_fUniversalTime >= g_fTimerThink) - { + logicore.callbacks->OnThink(simulating); + + if (g_fUniversalTime >= g_fTimerThink) { RunFrame(); g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY); @@ -243,9 +242,19 @@ void TimerSystem::GameFrame(bool simulating) RunFrameHooks(simulating); - if (m_pOnGameFrame->GetFunctionCount()) + m_pOnGameFrame->Execute(); +} + +/* GameFrame is called after gpGlobals is updated, and may not be called when the server is hibernating */ +void TimerSystem::GameFrame(bool simulating) +{ + m_bWasSimulating = simulating; + m_uFramesAhead = 0; + + if (simulating && !m_bHasMapSimulatedYet) { - m_pOnGameFrame->Execute(NULL); + m_bHasMapSimulatedYet = true; + MapTimeLeftChanged(); } } diff --git a/core/TimerSys.h b/core/TimerSys.h index c5a9b758..62ee5e58 100644 --- a/core/TimerSys.h +++ b/core/TimerSys.h @@ -82,6 +82,7 @@ public: //ITimerSystem public: void RunFrame(); void RemoveMapChangeTimers(); + void Think(bool unused); void GameFrame(bool simulating); private: List m_SingleTimers; @@ -92,6 +93,8 @@ private: /* This is stuff for our manual ticking escapades. */ bool m_bHasMapTickedYet; /** Has the map ticked yet? */ bool m_bHasMapSimulatedYet; /** Has the map simulated yet? */ + bool m_bWasSimulating; /** Was the last GameFrame simulating */ + unsigned m_uFramesAhead; /** Number of frames Think is ahead of GameFrame */ float m_fLastTickedTime; /** Last time that the game currently gave us while ticking. */ diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index eb55ed75..a46cd1cb 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -46,7 +46,6 @@ #include #include #include -#include #include SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); @@ -291,6 +290,7 @@ bool SourceModBase::InitializeSourceMod(char *error, size_t maxlength, bool late void SourceModBase::StartSourceMod(bool late) { SH_ADD_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false); + SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false); SH_ADD_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false); enginePatch = SH_GET_CALLCLASS(engine); @@ -362,8 +362,6 @@ void SourceModBase::StartSourceMod(bool late) { g_pSourcePawn2->InstallWatchdogTimer(atoi(timeout) * 1000); } - - SH_ADD_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false); } static bool g_LevelEndBarrier = false; @@ -598,8 +596,8 @@ void SourceModBase::ShutdownServices() } SH_REMOVE_HOOK(IServerGameDLL, LevelShutdown, gamedll, SH_MEMBER(this, &SourceModBase::LevelShutdown), false); + SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::Think), false); SH_REMOVE_HOOK(IServerGameDLL, GameFrame, gamedll, SH_MEMBER(&g_Timers, &TimerSystem::GameFrame), false); - SH_REMOVE_HOOK(IServerGameDLL, Think, gamedll, SH_MEMBER(logicore.callbacks, &IProviderCallbacks::OnThink), false); } void SourceModBase::LogMessage(IExtension *pExt, const char *format, ...)