core/TimerSys.cpp
author David Anderson <dvander@alliedmods.net>
Sun Aug 26 17:41:02 2007 +0000 (2007-08-26)
changeset 1369 bd9ccced1b1a
parent 1330 ab94316bb52b
child 1371 d207f0e17532
permissions -rw-r--r--
fixed amb834 - command callbacks could potentially occur on invalid clients in very rare cases
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "TimerSys.h"
    33 
    34 TimerSystem g_Timers;
    35 TickInfo g_SimTicks;
    36 
    37 inline float GetSimulatedTime()
    38 {
    39 	if (g_SimTicks.ticking)
    40 	{
    41 		return gpGlobals->curtime;
    42 	} else {
    43 		return g_SimTicks.ticktime;
    44 	}
    45 }
    46 
    47 void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags)
    48 {
    49 	m_Listener = pCallbacks;
    50 	m_Interval = fInterval;
    51 	m_ToExec = fToExec;
    52 	m_pData = pData;
    53 	m_Flags = flags;
    54 	m_InExec = false;
    55 	m_KillMe = false;
    56 }
    57 
    58 TimerSystem::~TimerSystem()
    59 {
    60 	CStack<ITimer *>::iterator iter;
    61 	for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
    62 	{
    63 		delete (*iter);
    64 	}
    65 	m_FreeTimers.popall();
    66 }
    67 
    68 void TimerSystem::OnSourceModAllInitialized()
    69 {
    70 	g_ShareSys.AddInterface(NULL, this);
    71 }
    72 
    73 void TimerSystem::OnSourceModLevelChange(const char *mapName)
    74 {
    75 	MapChange(true);
    76 }
    77 
    78 void TimerSystem::RunFrame()
    79 {
    80 	ITimer *pTimer;
    81 	TimerIter iter;
    82 
    83 	float curtime = GetSimulatedTime();
    84 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
    85 	{
    86 		pTimer = (*iter);
    87 		if (curtime >= pTimer->m_ToExec)
    88 		{
    89 			pTimer->m_InExec = true;
    90 			pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
    91 			pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
    92 			iter = m_SingleTimers.erase(iter);
    93 			m_FreeTimers.push(pTimer);
    94 		} else {
    95 			break;
    96 		}
    97 	}
    98 
    99 	ResultType res;
   100 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
   101 	{
   102 		pTimer = (*iter);
   103 		if (curtime >= pTimer->m_ToExec)
   104 		{
   105 			pTimer->m_InExec = true;
   106 			res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   107 			if (pTimer->m_KillMe || (res == Pl_Stop))
   108 			{
   109 				pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   110 				iter = m_LoopTimers.erase(iter);
   111 				m_FreeTimers.push(pTimer);
   112 				continue;
   113 			}
   114 			pTimer->m_InExec = false;
   115 			pTimer->m_ToExec = curtime + pTimer->m_Interval;
   116 		}
   117 		iter++;
   118 	}
   119 
   120 	m_LastExecTime = curtime;
   121 }
   122 
   123 ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
   124 {
   125 	ITimer *pTimer;
   126 	TimerIter iter;
   127 	float to_exec = GetSimulatedTime() + fInterval;
   128 
   129 	if (m_FreeTimers.empty())
   130 	{
   131 		pTimer = new ITimer;
   132 	} else {
   133 		pTimer = m_FreeTimers.front();
   134 		m_FreeTimers.pop();
   135 	}
   136 
   137 	pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
   138 
   139 	if (flags & TIMER_FLAG_REPEAT)
   140 	{
   141 		m_LoopTimers.push_back(pTimer);
   142 		goto return_timer;
   143 	}
   144 
   145 	if (m_SingleTimers.size() >= 1)
   146 	{
   147 		iter = --m_SingleTimers.end();
   148 		if ((*iter)->m_ToExec <= to_exec)
   149 		{
   150 			goto insert_end;
   151 		}
   152 	}
   153 
   154 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   155 	{
   156 		if ((*iter)->m_ToExec >= to_exec)
   157 		{
   158 			m_SingleTimers.insert(iter, pTimer);
   159 			goto return_timer;
   160 		}
   161 	}
   162 
   163 insert_end:
   164 	m_SingleTimers.push_back(pTimer);
   165 
   166 return_timer:
   167 	return pTimer;
   168 }
   169 
   170 void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
   171 {
   172 	ResultType res;
   173 
   174 	if (pTimer->m_InExec)
   175 	{
   176 		return;
   177 	}
   178 
   179 	pTimer->m_InExec = true;
   180 	res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   181 
   182 	if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
   183 	{
   184 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   185 		m_SingleTimers.remove(pTimer);
   186 		m_FreeTimers.push(pTimer);
   187 	} else {
   188 		if ((res != Pl_Stop) && !pTimer->m_KillMe)
   189 		{
   190 			if (delayExec)
   191 			{
   192 				pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
   193 			}
   194 			pTimer->m_InExec = false;
   195 			return;
   196 		}
   197 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   198 		m_LoopTimers.remove(pTimer);
   199 		m_FreeTimers.push(pTimer);
   200 	}
   201 }
   202 
   203 void TimerSystem::KillTimer(ITimer *pTimer)
   204 {
   205 	TimerList *pList;
   206 
   207 	if (pTimer->m_KillMe)
   208 	{
   209 		return;
   210 	}
   211 
   212 	if (pTimer->m_InExec)
   213 	{
   214 		pTimer->m_KillMe = true;
   215 		return;
   216 	}
   217 
   218 	pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
   219 	pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   220 
   221 	pList = (pTimer->m_Flags & TIMER_FLAG_REPEAT) ? &m_LoopTimers : &m_SingleTimers;
   222 
   223 	pList->remove(pTimer);
   224 	m_FreeTimers.push(pTimer);
   225 }
   226 
   227 CStack<ITimer *> s_tokill;
   228 void TimerSystem::MapChange(bool real_mapchange)
   229 {
   230 	ITimer *pTimer;
   231 	TimerIter iter;
   232 
   233 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   234 	{
   235 		pTimer = (*iter);
   236 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   237 		{
   238 			s_tokill.push(pTimer);
   239 		} else {
   240 			pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime();
   241 		}
   242 	}
   243 
   244 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
   245 	{
   246 		pTimer = (*iter);
   247 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   248 		{
   249 			s_tokill.push(pTimer);
   250 		} else {
   251 			pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime();
   252 		}
   253 	}
   254 
   255 	while (!s_tokill.empty())
   256 	{
   257 		KillTimer(s_tokill.front());
   258 		s_tokill.pop();
   259 	}
   260 
   261 	m_LastExecTime = GetSimulatedTime();
   262 }