Let's handle high precision timers separately

This commit is contained in:
Headline 2025-09-19 00:15:25 -07:00
parent 107e00c831
commit 833d727a71
4 changed files with 78 additions and 48 deletions

View File

@ -36,10 +36,12 @@
#include "ConVarManager.h" #include "ConVarManager.h"
#include "logic_bridge.h" #include "logic_bridge.h"
#define TIMER_MIN_ACCURACY 0.1
TimerSystem g_Timers; TimerSystem g_Timers;
double g_fUniversalTime = 0.0f; double g_fUniversalTime = 0.0f;
float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */ float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */
double g_fTimerThink = 0.0f; /* Timer's next think time */ double g_fTimerThink = 0.0f; /* Timer's next think time */
const double *g_pUniversalTime = &g_fUniversalTime; const double *g_pUniversalTime = &g_fUniversalTime;
ConVar *mp_timelimit = NULL; ConVar *mp_timelimit = NULL;
int g_TimeLeftMode = 0; int g_TimeLeftMode = 0;
@ -142,9 +144,10 @@ private:
* that a drastic jump in time will continue acting normally. Users * that a drastic jump in time will continue acting normally. Users
* may not expect this, but... I think it is the best solution. * may not expect this, but... I think it is the best solution.
*/ */
inline double CalcNextThink(double last, float interval) inline double CalcNextThink(double last, float interval, bool useTickInterval = false)
{ {
if (g_fUniversalTime - last - interval <= gpGlobals->interval_per_tick) const float intervalAccuracy = useTickInterval ? gpGlobals->interval_per_tick : TIMER_MIN_ACCURACY;
if (g_fUniversalTime - last - interval <= intervalAccuracy)
{ {
return last + interval; return last + interval;
} }
@ -163,6 +166,7 @@ void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec,
m_Flags = flags; m_Flags = flags;
m_InExec = false; m_InExec = false;
m_KillMe = false; m_KillMe = false;
m_HighSpeed = false;
} }
TimerSystem::TimerSystem() TimerSystem::TimerSystem()
@ -232,10 +236,11 @@ void TimerSystem::GameFrame(bool simulating)
m_fLastTickedTime = gpGlobals->curtime; m_fLastTickedTime = gpGlobals->curtime;
m_bHasMapTickedYet = true; m_bHasMapTickedYet = true;
if (g_fUniversalTime >= g_fTimerThink) const bool timerThink = g_fUniversalTime >= g_fTimerThink;
{ RunFrame(timerThink);
RunFrame();
if (timerThink)
{
g_fTimerThink = CalcNextThink(g_fTimerThink, gpGlobals->interval_per_tick); g_fTimerThink = CalcNextThink(g_fTimerThink, gpGlobals->interval_per_tick);
} }
@ -247,13 +252,12 @@ void TimerSystem::GameFrame(bool simulating)
} }
} }
void TimerSystem::RunFrame() void TimerSystem::ProcessTimerType(double curtime, TimerType& timerType, bool isHighSpeed)
{ {
ITimer *pTimer; ITimer *pTimer;
TimerIter iter; TimerIter iter;
double curtime = GetSimulatedTime(); for (iter=timerType.m_SingleTimers.begin(); iter!=timerType.m_SingleTimers.end(); )
for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
{ {
pTimer = (*iter); pTimer = (*iter);
if (curtime >= pTimer->m_ToExec) if (curtime >= pTimer->m_ToExec)
@ -261,7 +265,7 @@ void TimerSystem::RunFrame()
pTimer->m_InExec = true; pTimer->m_InExec = true;
pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
iter = m_SingleTimers.erase(iter); iter = timerType.m_SingleTimers.erase(iter);
m_FreeTimers.push(pTimer); m_FreeTimers.push(pTimer);
} }
else else
@ -271,7 +275,7 @@ void TimerSystem::RunFrame()
} }
ResultType res; ResultType res;
for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); ) for (iter=timerType.m_LoopTimers.begin(); iter!=timerType.m_LoopTimers.end(); )
{ {
pTimer = (*iter); pTimer = (*iter);
if (curtime >= pTimer->m_ToExec) if (curtime >= pTimer->m_ToExec)
@ -281,17 +285,31 @@ void TimerSystem::RunFrame()
if (pTimer->m_KillMe || (res == Pl_Stop)) if (pTimer->m_KillMe || (res == Pl_Stop))
{ {
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
iter = m_LoopTimers.erase(iter); iter = timerType.m_LoopTimers.erase(iter);
m_FreeTimers.push(pTimer); m_FreeTimers.push(pTimer);
continue; continue;
} }
pTimer->m_InExec = false; pTimer->m_InExec = false;
pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval); pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval, isHighSpeed);
} }
iter++; iter++;
} }
} }
void TimerSystem::RunFrame(bool timerThink)
{
const double curtime = GetSimulatedTime();
// Most timers do not need to be updated every frame
if (timerThink)
{
ProcessTimerType(curtime, m_LowSpeedTimers, false);
}
// High speed timers will
ProcessTimerType(curtime, m_HighSpeedTimers, true);
}
ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags) ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
{ {
ITimer *pTimer; ITimer *pTimer;
@ -307,33 +325,34 @@ ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void
} }
pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags); pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
TimerType& timerType = flags & TIMER_FLAG_TICK_PRECISE ? m_HighSpeedTimers : m_LowSpeedTimers;
if (flags & TIMER_FLAG_REPEAT) if (flags & TIMER_FLAG_REPEAT)
{ {
m_LoopTimers.push_back(pTimer); timerType.m_LoopTimers.push_back(pTimer);
goto return_timer; goto return_timer;
} }
if (m_SingleTimers.size() >= 1) if (timerType.m_SingleTimers.size() >= 1)
{ {
iter = --m_SingleTimers.end(); iter = --timerType.m_SingleTimers.end();
if ((*iter)->m_ToExec <= to_exec) if ((*iter)->m_ToExec <= to_exec)
{ {
goto normal_insert_end; goto normal_insert_end;
} }
} }
for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++) for (iter=timerType.m_SingleTimers.begin(); iter!=timerType.m_SingleTimers.end(); iter++)
{ {
if ((*iter)->m_ToExec >= to_exec) if ((*iter)->m_ToExec >= to_exec)
{ {
m_SingleTimers.insert(iter, pTimer); timerType.m_SingleTimers.insert(iter, pTimer);
goto return_timer; goto return_timer;
} }
} }
normal_insert_end: normal_insert_end:
m_SingleTimers.push_back(pTimer); timerType.m_SingleTimers.push_back(pTimer);
return_timer: return_timer:
return pTimer; return pTimer;
@ -350,11 +369,12 @@ void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
pTimer->m_InExec = true; pTimer->m_InExec = true;
res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
TimerType& timerType = pTimer->m_Flags & TIMER_FLAG_TICK_PRECISE ? m_HighSpeedTimers : m_LowSpeedTimers;
if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT)) if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
{ {
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
m_SingleTimers.remove(pTimer); timerType.m_SingleTimers.remove(pTimer);
m_FreeTimers.push(pTimer); m_FreeTimers.push(pTimer);
} }
else else
@ -369,7 +389,7 @@ void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
return; return;
} }
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
m_LoopTimers.remove(pTimer); timerType.m_LoopTimers.remove(pTimer);
m_FreeTimers.push(pTimer); m_FreeTimers.push(pTimer);
} }
} }
@ -387,14 +407,15 @@ void TimerSystem::KillTimer(ITimer *pTimer)
return; return;
} }
pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */ pTimer->m_InExec = true; /* The timer is not really executed but this check needs to be done */
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
TimerType& timerType = pTimer->m_Flags & TIMER_FLAG_TICK_PRECISE ? m_HighSpeedTimers : m_LowSpeedTimers;
if (pTimer->m_Flags & TIMER_FLAG_REPEAT) if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
{ {
m_LoopTimers.remove(pTimer); timerType.m_LoopTimers.remove(pTimer);
} else { } else {
m_SingleTimers.remove(pTimer); timerType.m_SingleTimers.remove(pTimer);
} }
m_FreeTimers.push(pTimer); m_FreeTimers.push(pTimer);
@ -403,26 +424,21 @@ void TimerSystem::KillTimer(ITimer *pTimer)
CStack<ITimer *> s_tokill; CStack<ITimer *> s_tokill;
void TimerSystem::RemoveMapChangeTimers() void TimerSystem::RemoveMapChangeTimers()
{ {
ITimer *pTimer; auto KillMapchangeTimers = [](TimerList& timerList) {
TimerIter iter; for (ITimer* pTimer : timerList)
{
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
{
s_tokill.push(pTimer);
}
}
};
for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++) KillMapchangeTimers(m_LowSpeedTimers.m_SingleTimers);
{ KillMapchangeTimers(m_LowSpeedTimers.m_LoopTimers);
pTimer = (*iter);
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
{
s_tokill.push(pTimer);
}
}
for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++) KillMapchangeTimers(m_HighSpeedTimers.m_SingleTimers);
{ KillMapchangeTimers(m_HighSpeedTimers.m_LoopTimers);
pTimer = (*iter);
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
{
s_tokill.push(pTimer);
}
}
while (!s_tokill.empty()) while (!s_tokill.empty())
{ {

View File

@ -41,7 +41,6 @@
using namespace SourceHook; using namespace SourceHook;
using namespace SourceMod; using namespace SourceMod;
typedef List<ITimer *> TimerList;
typedef List<ITimer *>::iterator TimerIter; typedef List<ITimer *>::iterator TimerIter;
class SourceMod::ITimer class SourceMod::ITimer
@ -55,12 +54,23 @@ public:
int m_Flags; int m_Flags;
bool m_InExec; bool m_InExec;
bool m_KillMe; bool m_KillMe;
bool m_HighSpeed;
};
class TimerList : public List<ITimer*>
{
}; };
class TimerSystem : class TimerSystem :
public ITimerSystem, public ITimerSystem,
public SMGlobalClass public SMGlobalClass
{ {
public:
struct TimerType {
TimerList m_SingleTimers;
TimerList m_LoopTimers;
};
public: public:
TimerSystem(); TimerSystem();
~TimerSystem(); ~TimerSystem();
@ -80,12 +90,14 @@ public: //ITimerSystem
bool GetMapTimeLeft(float *pTime); bool GetMapTimeLeft(float *pTime);
IMapTimer *GetMapTimer(); IMapTimer *GetMapTimer();
public: public:
void RunFrame(); void RunFrame(bool timerThink);
void RemoveMapChangeTimers(); void RemoveMapChangeTimers();
void GameFrame(bool simulating); void GameFrame(bool simulating);
private: private:
List<ITimer *> m_SingleTimers; void ProcessTimerType(double curtime, TimerType& timerType, bool isHighSpeed);
List<ITimer *> m_LoopTimers; private:
TimerType m_LowSpeedTimers;
TimerType m_HighSpeedTimers;
CStack<ITimer *> m_FreeTimers; CStack<ITimer *> m_FreeTimers;
IMapTimer *m_pMapTimer; IMapTimer *m_pMapTimer;

View File

@ -39,6 +39,7 @@
#define TIMER_REPEAT (1<<0) /**< Timer will repeat until it returns Plugin_Stop */ #define TIMER_REPEAT (1<<0) /**< Timer will repeat until it returns Plugin_Stop */
#define TIMER_FLAG_NO_MAPCHANGE (1<<1) /**< Timer will not carry over mapchanges */ #define TIMER_FLAG_NO_MAPCHANGE (1<<1) /**< Timer will not carry over mapchanges */
#define TIMER_FLAG_TICK_PRECISE (1<<2) /**< Timer will have tick level time precision */
#define TIMER_HNDL_CLOSE (1<<9) /**< Deprecated define, replaced by below */ #define TIMER_HNDL_CLOSE (1<<9) /**< Deprecated define, replaced by below */
#define TIMER_DATA_HNDL_CLOSE (1<<9) /**< Timer will automatically call CloseHandle() on its data when finished */ #define TIMER_DATA_HNDL_CLOSE (1<<9) /**< Timer will automatically call CloseHandle() on its data when finished */

View File

@ -107,6 +107,7 @@ namespace SourceMod
#define TIMER_FLAG_REPEAT (1<<0) /**< Timer will repeat until stopped */ #define TIMER_FLAG_REPEAT (1<<0) /**< Timer will repeat until stopped */
#define TIMER_FLAG_NO_MAPCHANGE (1<<1) /**< Timer will not carry over mapchanges */ #define TIMER_FLAG_NO_MAPCHANGE (1<<1) /**< Timer will not carry over mapchanges */
#define TIMER_FLAG_TICK_PRECISE (1<<2) /**< Timer will have tick level time precision */
class ITimerSystem : public SMInterface class ITimerSystem : public SMInterface
{ {