core/TimerSys.cpp
author David Anderson <dvander@alliedmods.net>
Tue Sep 11 18:16:25 2007 +0000 (2007-09-11)
changeset 1409 84e9d3494603
parent 1408 2fd63cc4bd9c
child 1410 8feec8371000
permissions -rw-r--r--
added a case where CreateTimer() can return INVALID_HANDLE
fixed two initialization bugs in TimerSys
     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 <time.h>
    33 #include "TimerSys.h"
    34 #include "sourcemm_api.h"
    35 
    36 TimerSystem g_Timers;
    37 
    38 ConVar sm_time_adjustment("sm_time_adjustment", "0", 0, "Adjusts the server time in seconds");
    39 
    40 time_t GetAdjustedTime(time_t *buf)
    41 {
    42 	time_t val = time(NULL) + sm_time_adjustment.GetInt();
    43 	if (buf)
    44 	{
    45 		*buf = val;
    46 	}
    47 	return val;
    48 }
    49 
    50 inline float GetSimulatedTime()
    51 {
    52 	return engine->Time();
    53 }
    54 
    55 void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags)
    56 {
    57 	m_Listener = pCallbacks;
    58 	m_Interval = fInterval;
    59 	m_ToExec = fToExec;
    60 	m_pData = pData;
    61 	m_Flags = flags;
    62 	m_InExec = false;
    63 	m_KillMe = false;
    64 }
    65 
    66 TimerSystem::TimerSystem()
    67 {
    68 	m_fnTimeLeft = NULL;
    69 }
    70 
    71 TimerSystem::~TimerSystem()
    72 {
    73 	CStack<ITimer *>::iterator iter;
    74 	for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
    75 	{
    76 		delete (*iter);
    77 	}
    78 	m_FreeTimers.popall();
    79 }
    80 
    81 void TimerSystem::OnSourceModAllInitialized()
    82 {
    83 	g_ShareSys.AddInterface(NULL, this);
    84 }
    85 
    86 void TimerSystem::OnSourceModLevelChange(const char *mapName)
    87 {
    88 	MapChange(true);
    89 }
    90 
    91 void TimerSystem::RunFrame()
    92 {
    93 	ITimer *pTimer;
    94 	TimerIter iter;
    95 
    96 	float curtime = GetSimulatedTime();
    97 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
    98 	{
    99 		pTimer = (*iter);
   100 		if (curtime >= pTimer->m_ToExec)
   101 		{
   102 			pTimer->m_InExec = true;
   103 			pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   104 			pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   105 			iter = m_SingleTimers.erase(iter);
   106 			m_FreeTimers.push(pTimer);
   107 		} else {
   108 			break;
   109 		}
   110 	}
   111 
   112 	if (m_fnTimeLeft != NULL && m_MapEndTimers.size())
   113 	{
   114 		float time_left = m_fnTimeLeft();
   115 		for (iter=m_MapEndTimers.begin(); iter!=m_MapEndTimers.end();)
   116 		{
   117 			pTimer = (*iter);
   118 			if ((*iter)->m_Interval < time_left)
   119 			{
   120 				pTimer->m_InExec = true;
   121 				pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   122 				pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   123 				iter = m_MapEndTimers.erase(iter);
   124 				m_FreeTimers.push(pTimer);
   125 			} else {
   126 				break;
   127 			}
   128 		}
   129 	}
   130 
   131 	ResultType res;
   132 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
   133 	{
   134 		pTimer = (*iter);
   135 		if (curtime >= pTimer->m_ToExec)
   136 		{
   137 			pTimer->m_InExec = true;
   138 			res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   139 			if (pTimer->m_KillMe || (res == Pl_Stop))
   140 			{
   141 				pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   142 				iter = m_LoopTimers.erase(iter);
   143 				m_FreeTimers.push(pTimer);
   144 				continue;
   145 			}
   146 			pTimer->m_InExec = false;
   147 			pTimer->m_ToExec = curtime + pTimer->m_Interval;
   148 		}
   149 		iter++;
   150 	}
   151 
   152 	m_LastExecTime = curtime;
   153 }
   154 
   155 ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
   156 {
   157 	ITimer *pTimer;
   158 	TimerIter iter;
   159 	float to_exec = GetSimulatedTime() + fInterval;
   160 
   161 	if ((flags & TIMER_FLAG_BEFORE_MAP_END)
   162 		&& m_fnTimeLeft == NULL)
   163 	{
   164 		return NULL;
   165 	}
   166 
   167 	if (m_FreeTimers.empty())
   168 	{
   169 		pTimer = new ITimer;
   170 	} else {
   171 		pTimer = m_FreeTimers.front();
   172 		m_FreeTimers.pop();
   173 	}
   174 
   175 	pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
   176 
   177 	if (flags & TIMER_FLAG_REPEAT)
   178 	{
   179 		m_LoopTimers.push_back(pTimer);
   180 		goto return_timer;
   181 	}
   182 
   183 	if (flags & TIMER_FLAG_BEFORE_MAP_END)
   184 	{
   185 		for (iter=m_MapEndTimers.begin(); iter!=m_MapEndTimers.end(); iter++)
   186 		{
   187 			if (fInterval >= (*iter)->m_Interval)
   188 			{
   189 				m_MapEndTimers.insert(iter, pTimer);
   190 				goto return_timer;
   191 			}
   192 		}
   193 
   194 		m_MapEndTimers.push_back(pTimer);
   195 	} else {
   196 		if (m_SingleTimers.size() >= 1)
   197 		{
   198 			iter = --m_SingleTimers.end();
   199 			if ((*iter)->m_ToExec <= to_exec)
   200 			{
   201 				goto normal_insert_end;
   202 			}
   203 		}
   204 	
   205 		for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   206 		{
   207 			if ((*iter)->m_ToExec >= to_exec)
   208 			{
   209 				m_SingleTimers.insert(iter, pTimer);
   210 				goto return_timer;
   211 			}
   212 		}
   213 
   214 normal_insert_end:
   215 		m_SingleTimers.push_back(pTimer);
   216 	}
   217 
   218 return_timer:
   219 	return pTimer;
   220 }
   221 
   222 void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
   223 {
   224 	ResultType res;
   225 
   226 	if (pTimer->m_InExec)
   227 	{
   228 		return;
   229 	}
   230 
   231 	pTimer->m_InExec = true;
   232 	res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   233 
   234 	if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
   235 	{
   236 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   237 		if (pTimer->m_Flags & TIMER_FLAG_BEFORE_MAP_END)
   238 		{
   239 			m_MapEndTimers.remove(pTimer);
   240 		} else {
   241 			m_SingleTimers.remove(pTimer);
   242 		}
   243 		m_FreeTimers.push(pTimer);
   244 	} else {
   245 		if ((res != Pl_Stop) && !pTimer->m_KillMe)
   246 		{
   247 			if (delayExec)
   248 			{
   249 				pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
   250 			}
   251 			pTimer->m_InExec = false;
   252 			return;
   253 		}
   254 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   255 		m_LoopTimers.remove(pTimer);
   256 		m_FreeTimers.push(pTimer);
   257 	}
   258 }
   259 
   260 void TimerSystem::KillTimer(ITimer *pTimer)
   261 {
   262 	if (pTimer->m_KillMe)
   263 	{
   264 		return;
   265 	}
   266 
   267 	if (pTimer->m_InExec)
   268 	{
   269 		pTimer->m_KillMe = true;
   270 		return;
   271 	}
   272 
   273 	pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
   274 	pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   275 
   276 	if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
   277 	{
   278 		m_LoopTimers.remove(pTimer);
   279 	} else if (pTimer->m_Flags & TIMER_FLAG_BEFORE_MAP_END) {
   280 		m_MapEndTimers.remove(pTimer);
   281 	} else {
   282 		m_SingleTimers.remove(pTimer);
   283 	}
   284 
   285 	m_FreeTimers.push(pTimer);
   286 }
   287 
   288 CStack<ITimer *> s_tokill;
   289 void TimerSystem::MapChange(bool real_mapchange)
   290 {
   291 	ITimer *pTimer;
   292 	TimerIter iter;
   293 
   294 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   295 	{
   296 		pTimer = (*iter);
   297 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   298 		{
   299 			s_tokill.push(pTimer);
   300 		} else {
   301 			pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime();
   302 		}
   303 	}
   304 
   305 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
   306 	{
   307 		pTimer = (*iter);
   308 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   309 		{
   310 			s_tokill.push(pTimer);
   311 		} else {
   312 			pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime();
   313 		}
   314 	}
   315 
   316 	if (real_mapchange)
   317 	{
   318 		for (iter=m_MapEndTimers.begin(); iter!=m_MapEndTimers.end(); iter++)
   319 		{
   320 			pTimer = (*iter);
   321 			s_tokill.push(pTimer);
   322 		}
   323 	}
   324 
   325 	while (!s_tokill.empty())
   326 	{
   327 		KillTimer(s_tokill.front());
   328 		s_tokill.pop();
   329 	}
   330 
   331 	m_LastExecTime = GetSimulatedTime();
   332 }
   333 
   334 SM_TIMELEFT_FUNCTION TimerSystem::SetTimeLeftFunction(SM_TIMELEFT_FUNCTION fn)
   335 {
   336 	SM_TIMELEFT_FUNCTION old = m_fnTimeLeft;
   337 	m_fnTimeLeft = fn;
   338 	return old;
   339 }
   340