diff --git a/core/TimerSys.cpp b/core/TimerSys.cpp index 3ce7839d..667ac3b9 100644 --- a/core/TimerSys.cpp +++ b/core/TimerSys.cpp @@ -15,6 +15,17 @@ #include "TimerSys.h" TimerSystem g_Timers; +TickInfo g_SimTicks; + +inline float GetSimulatedTime() +{ + if (g_SimTicks.ticking) + { + return gpGlobals->curtime; + } else { + return g_SimTicks.ticktime; + } +} void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags) { @@ -52,10 +63,11 @@ void TimerSystem::RunFrame() ITimer *pTimer; TimerIter iter; + float curtime = GetSimulatedTime(); for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); ) { pTimer = (*iter); - if (gpGlobals->curtime >= pTimer->m_ToExec) + if (curtime >= pTimer->m_ToExec) { pTimer->m_InExec = true; pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); @@ -71,7 +83,7 @@ void TimerSystem::RunFrame() for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); ) { pTimer = (*iter); - if (gpGlobals->curtime >= pTimer->m_ToExec) + if (curtime >= pTimer->m_ToExec) { pTimer->m_InExec = true; res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); @@ -83,19 +95,19 @@ void TimerSystem::RunFrame() continue; } pTimer->m_InExec = false; - pTimer->m_ToExec = gpGlobals->curtime + pTimer->m_Interval; + pTimer->m_ToExec = curtime + pTimer->m_Interval; } iter++; } - m_LastExecTime = gpGlobals->curtime; + m_LastExecTime = curtime; } ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags) { ITimer *pTimer; TimerIter iter; - float to_exec = gpGlobals->curtime + fInterval; + float to_exec = GetSimulatedTime() + fInterval; if (m_FreeTimers.empty()) { @@ -160,7 +172,7 @@ void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec) { if (delayExec) { - pTimer->m_ToExec = gpGlobals->curtime + pTimer->m_Interval; + pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval; } pTimer->m_InExec = false; return; @@ -203,12 +215,14 @@ void TimerSystem::MapChange() for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++) { pTimer = (*iter); - pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + gpGlobals->curtime; + pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime(); } for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++) { pTimer = (*iter); - pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + gpGlobals->curtime; + pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime(); } + + m_LastExecTime = GetSimulatedTime(); } diff --git a/core/TimerSys.h b/core/TimerSys.h index 4078b6a3..884bdaf2 100644 --- a/core/TimerSys.h +++ b/core/TimerSys.h @@ -27,6 +27,13 @@ using namespace SourceMod; typedef List TimerList; typedef List::iterator TimerIter; +struct TickInfo +{ + bool ticking; /* true=game is ticking, false=we're ticking */ + unsigned int tickcount; /* number of simulated ticks we've done */ + float ticktime; /* tick time we're maintaining */ +}; + class SourceMod::ITimer { public: @@ -64,5 +71,6 @@ private: }; extern TimerSystem g_Timers; +extern TickInfo g_SimTicks; #endif //_INCLUDE_SOURCEMOD_CTIMERSYS_H_ diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index aeaaa1ff..15c09a05 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -47,6 +47,8 @@ float g_LastAuthCheck = 0.0f; IForward *g_pOnGameFrame = NULL; IForward *g_pOnMapEnd = NULL; bool g_Loaded = false; +int g_StillFrames = 0; +float g_StillTime = 0.0f; typedef int (*GIVEENGINEPOINTER)(ISourcePawnEngine *); typedef unsigned int (*GETEXPORTCOUNT)(); @@ -263,6 +265,10 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch m_ExecPluginReload = true; g_LastTime = 0.0f; g_LastAuthCheck = 0.0f; + g_SimTicks.ticking = true; + g_SimTicks.tickcount = 0; + g_StillTime = 0.0f; + g_StillFrames = 0; /* Notify! */ SMGlobalClass *pBase = SMGlobalClass::head; @@ -297,6 +303,27 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch RETURN_META_VALUE(MRES_IGNORED, true); } +void StartTickSimulation() +{ + g_SimTicks.ticking = false; + g_SimTicks.tickcount = 0; + g_SimTicks.ticktime = gpGlobals->curtime; +} + +void StopTickSimulation() +{ + g_SimTicks.ticking = true; + g_Timers.MapChange(); + g_StillFrames = 0; + g_LastTime = gpGlobals->curtime; +} + +void SimulateTick() +{ + g_SimTicks.tickcount++; + g_SimTicks.ticktime = g_StillTime + (g_SimTicks.tickcount * gpGlobals->interval_per_tick); +} + void SourceModBase::GameFrame(bool simulating) { /** @@ -304,6 +331,46 @@ void SourceModBase::GameFrame(bool simulating) * precious CPU cycles. */ float curtime = gpGlobals->curtime; + int framecount = gpGlobals->framecount; + + /* Verify that we're still ticking */ + if (g_SimTicks.ticking) + { + if (g_StillFrames == 0) + { + g_StillFrames = framecount; + g_StillTime = curtime; + } else { + /* Try to detect when we've stopped ticking. + * We do this once 10 frames pass and there have been no ticks. + */ + if (g_StillTime == curtime) + { + if (framecount - g_StillFrames >= 5) + { + StartTickSimulation(); + return; + } + } else { + /* We're definitely ticking we get here, + * but update everything as a precaution */ + g_StillFrames = framecount; + g_StillTime = curtime; + } + } + } else { + /* We need to make sure we should still be simulating. */ + if (g_StillTime != curtime) + { + /* Wow, we're ticking again! It's time to revert. */ + StopTickSimulation(); + return; + } + /* Nope, not ticking. Simulate! */ + SimulateTick(); + curtime = g_SimTicks.ticktime; + } + if (curtime - g_LastTime >= 0.1f) { if (m_CheckingAuth