From 5fe8bed25673db3f6b30b4e4dbce016e504f6151 Mon Sep 17 00:00:00 2001 From: Borja Ferrer Date: Sat, 10 Mar 2007 13:16:19 +0000 Subject: [PATCH] Added timer system implementation --HG-- extra : convert_revision : svn%3A39bc706e-5318-0410-9160-8a85361fbb7c/trunk%40593 --- core/CTimerSys.cpp | 199 +++++++++++++++++++++++++++++++++ core/CTimerSys.h | 65 +++++++++++ core/msvc8/sourcemod_mm.vcproj | 14 ++- core/sourcemod.cpp | 8 +- public/ITimerSystem.h | 13 +-- 5 files changed, 288 insertions(+), 11 deletions(-) create mode 100644 core/CTimerSys.cpp create mode 100644 core/CTimerSys.h diff --git a/core/CTimerSys.cpp b/core/CTimerSys.cpp new file mode 100644 index 00000000..166b54ed --- /dev/null +++ b/core/CTimerSys.cpp @@ -0,0 +1,199 @@ +/** +* vim: set ts=4 : +* =============================================================== +* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. +* =============================================================== +* +* This file is not open source and may not be copied without explicit +* written permission of AlliedModders LLC. This file may not be redistributed +* in whole or significant part. +* For information, see LICENSE.txt or http://www.sourcemod.net/license.php +* +* Version: $Id$ +*/ + +#include "CTimerSys.h" + +CTimerSystem g_Timers; + +void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags) +{ + m_Listener = pCallbacks; + m_Interval = fInterval; + m_ToExec = fToExec; + m_pData = pData; + m_Flags = flags; + m_InExec = false; + m_KillMe = false; +} + +void CTimerSystem::OnSourceModAllInitialized() +{ + g_ShareSys.AddInterface(NULL, this); +} + +void CTimerSystem::RunFrame() +{ + ITimer *pTimer; + TimerIter iter; + + for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); ) + { + pTimer = (*iter); + if (gpGlobals->curtime >= pTimer->m_ToExec) + { + pTimer->m_InExec = true; + pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); + if (pTimer->m_KillMe) + { + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); + } + iter = m_SingleTimers.erase(iter); + m_FreeTimers.push(pTimer); + } else { + break; + } + } + + ResultType res; + for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); ) + { + pTimer = (*iter); + if (gpGlobals->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 = gpGlobals->curtime + pTimer->m_Interval; + } + iter++; + } + + m_LastExecTime = gpGlobals->curtime; +} + +ITimer *CTimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags) +{ + ITimer *pTimer; + TimerIter iter; + float to_exec = gpGlobals->curtime + fInterval; + + if (m_FreeTimers.empty()) + { + pTimer = new ITimer; + } else { + pTimer = m_FreeTimers.front(); + m_FreeTimers.pop(); + } + + pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags); + + if (flags & TIMER_FLAG_REPEAT) + { + m_LoopTimers.push_back(pTimer); + goto return_timer; + } + + if (m_SingleTimers.size() >= 1) + { + iter = --m_SingleTimers.end(); + if ((*iter)->m_ToExec <= to_exec) + { + goto insert_end; + } + } + + for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++) + { + if ((*iter)->m_ToExec >= to_exec) + { + m_SingleTimers.insert(iter, pTimer); + goto return_timer; + } + } + +insert_end: + m_SingleTimers.push_back(pTimer); + +return_timer: + return pTimer; +} + +void CTimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec) +{ + ResultType res; + + if (pTimer->m_InExec) + { + return; + } + + pTimer->m_InExec = true; + res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); + + if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT)) + { + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); + m_SingleTimers.remove(pTimer); + m_FreeTimers.push(pTimer); + } else { + if (delayExec && (res != Pl_Stop) && !pTimer->m_KillMe) + { + pTimer->m_ToExec = gpGlobals->curtime + pTimer->m_Interval; + pTimer->m_InExec = false; + return; + } + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); + m_LoopTimers.remove(pTimer); + m_FreeTimers.push(pTimer); + } +} + +void CTimerSystem::KillTimer(ITimer *pTimer) +{ + TimerList *pList; + + if (pTimer->m_KillMe) + { + return; + } + + if (pTimer->m_InExec) + { + pTimer->m_KillMe = true; + return; + } + + pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */ + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); + + pList = (pTimer->m_Flags & TIMER_FLAG_REPEAT) ? &m_LoopTimers : &m_SingleTimers; + + pList->remove(pTimer); + m_FreeTimers.push(pTimer); +} + +void CTimerSystem::MapChange() +{ + ITimer *pTimer; + TimerIter iter; + + for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++) + { + pTimer = (*iter); + pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + gpGlobals->curtime; + } + + for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++) + { + pTimer = (*iter); + pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + gpGlobals->curtime; + } +} diff --git a/core/CTimerSys.h b/core/CTimerSys.h new file mode 100644 index 00000000..520a33f1 --- /dev/null +++ b/core/CTimerSys.h @@ -0,0 +1,65 @@ +/** +* vim: set ts=4 : +* =============================================================== +* SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved. +* =============================================================== +* +* This file is not open source and may not be copied without explicit +* written permission of AlliedModders LLC. This file may not be redistributed +* in whole or significant part. +* For information, see LICENSE.txt or http://www.sourcemod.net/license.php +* +* Version: $Id$ +*/ + +#ifndef _INCLUDE_SOURCEMOD_CTIMERSYS_H_ +#define _INCLUDE_SOURCEMOD_CTIMERSYS_H_ + +#include "ShareSys.h" +#include +#include +#include +#include "sourcemm_api.h" + +using namespace SourceHook; +using namespace SourceMod; + +typedef List TimerList; +typedef List::iterator TimerIter; + +class SourceMod::ITimer +{ +public: + void Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags); + ITimedEvent *m_Listener; + void *m_pData; + float m_Interval; + float m_ToExec; + int m_Flags; + bool m_InExec; + bool m_KillMe; +}; + +class CTimerSystem : + public ITimerSystem, + public SMGlobalClass +{ +public: //SMGlobalClass + void OnSourceModAllInitialized(); +public: //ITimerSystem + ITimer *CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags); + void KillTimer(ITimer *pTimer); + void FireTimerOnce(ITimer *pTimer, bool delayExec=false); +public: + void RunFrame(); + void MapChange(); +private: + List m_SingleTimers; + List m_LoopTimers; + CStack m_FreeTimers; + float m_LastExecTime; +}; + +extern CTimerSystem g_Timers; + +#endif //_INCLUDE_SOURCEMOD_CTIMERSYS_H_ diff --git a/core/msvc8/sourcemod_mm.vcproj b/core/msvc8/sourcemod_mm.vcproj index 5ee357db..76298884 100644 --- a/core/msvc8/sourcemod_mm.vcproj +++ b/core/msvc8/sourcemod_mm.vcproj @@ -1,7 +1,7 @@ + + @@ -313,6 +317,10 @@ RelativePath="..\CTextParsers.h" > + + @@ -421,6 +429,10 @@ RelativePath="..\..\public\ITextParsers.h" > + + diff --git a/core/sourcemod.cpp b/core/sourcemod.cpp index 950793d1..4271d6d3 100644 --- a/core/sourcemod.cpp +++ b/core/sourcemod.cpp @@ -26,6 +26,7 @@ #include "CPlayerManager.h" #include "CTranslator.h" #include "ForwardSys.h" +#include "CTimerSys.h" SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, false, bool, const char *, const char *, const char *, const char *, bool, bool); SH_DECL_HOOK0_void(IServerGameDLL, LevelShutdown, SH_NOATTRIB, false); @@ -193,6 +194,7 @@ bool SourceModBase::LevelInit(char const *pMapName, char const *pMapEntities, ch g_LastAuthCheck = 0.0f; g_Logger.MapChange(pMapName); + g_Timers.MapChange(); /* Refresh language stuff */ char path[PLATFORM_MAX_PATH]; @@ -221,7 +223,7 @@ void SourceModBase::GameFrame(bool simulating) * precious CPU cycles. */ float curtime = gpGlobals->curtime; - if (curtime - g_LastTime > 0.1f) + if (curtime - g_LastTime >= 0.1f) { if (m_CheckingAuth && (gpGlobals->curtime - g_LastAuthCheck > 0.7f)) @@ -229,9 +231,11 @@ void SourceModBase::GameFrame(bool simulating) g_LastAuthCheck = gpGlobals->curtime; g_Players.RunAuthChecks(); } + + g_Timers.RunFrame(); g_LastTime = curtime; } - + if (g_pOnGameFrame && g_pOnGameFrame->GetFunctionCount()) { g_pOnGameFrame->Execute(NULL); diff --git a/public/ITimerSystem.h b/public/ITimerSystem.h index 2291087f..23d19353 100644 --- a/public/ITimerSystem.h +++ b/public/ITimerSystem.h @@ -20,11 +20,10 @@ #define _INCLUDE_SOURCEMOD_TIMER_SYSTEM_H_ #include +#include -//:TODO: this is a placeholder and not yet implemented -//remove these lines and uncomment once we're done! -//#define SMINTERFACE_TIMERSYS_NAME "ITimerSys" -//#define SMINTERFACE_TIMERSYS_VERSION 1 +#define SMINTERFACE_TIMERSYS_NAME "ITimerSys" +#define SMINTERFACE_TIMERSYS_VERSION 1 namespace SourceMod { @@ -41,9 +40,7 @@ namespace SourceMod * * @param pTimer Pointer to the timer instance. * @param pData Private pointer passed from host. - * @return Pl_Handle to stop timer, Pl_Continue to continue. - * Passing Pl_Continue when a timer's repeat count - * has been exhausted will not extend it. + * @return Pl_Stop to stop timer, Pl_Continue to continue. */ virtual ResultType OnTimer(ITimer *pTimer, void *pData) =0; @@ -53,7 +50,7 @@ namespace SourceMod * @param pTimer Pointer to the timer instance. * @param pData Private data pointer passed from host. */ - virtual void OnTimerEnd() =0; + virtual void OnTimerEnd(ITimer *pTimer, void *pData) =0; }; #define TIMER_FLAG_REPEAT (1<<0)