Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 72 additions & 55 deletions core/TimerSys.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@
#include "ConVarManager.h"
#include "logic_bridge.h"

#define TIMER_MIN_ACCURACY 0.1
#define TIMER_MIN_ACCURACY 0.1

TimerSystem g_Timers;
double g_fUniversalTime = 0.0f;
float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */
double g_fTimerThink = 0.0f; /* Timer's next think time */
float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */
double g_fTimerThink = 0.0f; /* Timer's next think time */
const double *g_pUniversalTime = &g_fUniversalTime;
ConVar *mp_timelimit = NULL;
int g_TimeLeftMode = 0;
Expand Down Expand Up @@ -144,9 +144,10 @@ class DefaultMapTimer :
* that a drastic jump in time will continue acting normally. Users
* 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 <= TIMER_MIN_ACCURACY)
const float intervalAccuracy = useTickInterval ? gpGlobals->interval_per_tick : TIMER_MIN_ACCURACY;
if (g_fUniversalTime - last - interval <= intervalAccuracy)
{
return last + interval;
}
Expand Down Expand Up @@ -234,10 +235,11 @@ void TimerSystem::GameFrame(bool simulating)
m_fLastTickedTime = gpGlobals->curtime;
m_bHasMapTickedYet = true;

if (g_fUniversalTime >= g_fTimerThink)
{
RunFrame();
const bool timerThink = g_fUniversalTime >= g_fTimerThink;
RunFrame(timerThink);

if (timerThink)
{
g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
}

Expand All @@ -249,12 +251,40 @@ void TimerSystem::GameFrame(bool simulating)
}
}

void TimerSystem::RunFrame()
void TimerSystem::ProcessRepeatTimers(double curtime, List<ITimer*>& timerList, bool isHighSpeed)
{
ITimer *pTimer;
ITimer *pTimer;
TimerIter iter;

double curtime = GetSimulatedTime();
ResultType res;
for (iter=timerList.begin(); iter!=timerList.end(); )
{
pTimer = (*iter);
if (curtime >= pTimer->m_ToExec)
{
pTimer->m_InExec = true;
res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
if (pTimer->m_KillMe || (res == Pl_Stop))
{
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
iter = timerList.erase(iter);
m_FreeTimers.push(pTimer);
continue;
}
pTimer->m_InExec = false;
pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval, isHighSpeed);
}
iter++;
}
}

void TimerSystem::RunFrame(bool timerThink)
{
const double curtime = GetSimulatedTime();

//// One-off timers
ITimer *pTimer;
TimerIter iter;
for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
{
pTimer = (*iter);
Expand All @@ -272,26 +302,15 @@ void TimerSystem::RunFrame()
}
}

ResultType res;
for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
{
pTimer = (*iter);
if (curtime >= pTimer->m_ToExec)
{
pTimer->m_InExec = true;
res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
if (pTimer->m_KillMe || (res == Pl_Stop))
{
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
iter = m_LoopTimers.erase(iter);
m_FreeTimers.push(pTimer);
continue;
}
pTimer->m_InExec = false;
pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval);
}
iter++;
}
//// Repeating timers
// Most repeating timers do not need to be updated every frame
if (timerThink)
{
ProcessRepeatTimers(curtime, m_LowSpeedLoopTimers, false);
}

// High speed repeating timers will always update
ProcessRepeatTimers(curtime, m_HighSpeedLoopTimers, true);
}

ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
Expand All @@ -312,7 +331,8 @@ ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void

if (flags & TIMER_FLAG_REPEAT)
{
m_LoopTimers.push_back(pTimer);
List<ITimer*>& timerList = pTimer->m_Flags & TIMER_FLAG_TICK_PRECISE ? m_HighSpeedLoopTimers : m_LowSpeedLoopTimers;
timerList.push_back(pTimer);
goto return_timer;
}

Expand Down Expand Up @@ -370,8 +390,10 @@ void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
pTimer->m_InExec = false;
return;
}

List<ITimer*>& timerList = pTimer->m_Flags & TIMER_FLAG_TICK_PRECISE ? m_HighSpeedLoopTimers : m_LowSpeedLoopTimers;
pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
m_LoopTimers.remove(pTimer);
timerList.remove(pTimer);
m_FreeTimers.push(pTimer);
}
}
Expand All @@ -389,12 +411,13 @@ void TimerSystem::KillTimer(ITimer *pTimer)
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);

if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
{
m_LoopTimers.remove(pTimer);
List<ITimer*>& timerList = pTimer->m_Flags & TIMER_FLAG_TICK_PRECISE ? m_HighSpeedLoopTimers : m_LowSpeedLoopTimers;
timerList.remove(pTimer);
} else {
m_SingleTimers.remove(pTimer);
}
Expand All @@ -405,26 +428,20 @@ void TimerSystem::KillTimer(ITimer *pTimer)
CStack<ITimer *> s_tokill;
void TimerSystem::RemoveMapChangeTimers()
{
ITimer *pTimer;
TimerIter iter;

for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
{
pTimer = (*iter);
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
{
s_tokill.push(pTimer);
}
}

for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
{
pTimer = (*iter);
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
{
s_tokill.push(pTimer);
}
}
const auto KillMapchangeTimers = [](List<ITimer*>& timerList) {
for (ITimer* pTimer : timerList)
{
if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
{
s_tokill.push(pTimer);
}
}
};

KillMapchangeTimers(m_SingleTimers);

KillMapchangeTimers(m_LowSpeedLoopTimers);
KillMapchangeTimers(m_HighSpeedLoopTimers);

while (!s_tokill.empty())
{
Expand Down
10 changes: 6 additions & 4 deletions core/TimerSys.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
using namespace SourceHook;
using namespace SourceMod;

typedef List<ITimer *> TimerList;
typedef List<ITimer *>::iterator TimerIter;

class SourceMod::ITimer
Expand Down Expand Up @@ -80,12 +79,15 @@ class TimerSystem :
bool GetMapTimeLeft(float *pTime);
IMapTimer *GetMapTimer();
public:
void RunFrame();
void RunFrame(bool timerThink);
void RemoveMapChangeTimers();
void GameFrame(bool simulating);
private:
List<ITimer *> m_SingleTimers;
List<ITimer *> m_LoopTimers;
void ProcessRepeatTimers(double curtime, List<ITimer*>& timerList, bool isHighSpeed);
private:
List<ITimer*> m_SingleTimers;
List<ITimer*> m_LowSpeedLoopTimers;
List<ITimer*> m_HighSpeedLoopTimers;
CStack<ITimer *> m_FreeTimers;
IMapTimer *m_pMapTimer;

Expand Down
1 change: 1 addition & 0 deletions plugins/include/timers.inc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@

#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_TICK_PRECISE (1<<2) /**< Timer will have tick level time precision */
#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 */

Expand Down
1 change: 1 addition & 0 deletions public/ITimerSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ namespace SourceMod

#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_TICK_PRECISE (1<<2) /**< Timer will have tick level time precision */

class ITimerSystem : public SMInterface
{
Expand Down
Loading