core/TimerSys.cpp
author David Anderson <dvander@alliedmods.net>
Tue Sep 11 18:08:49 2007 +0000 (2007-09-11)
changeset 1408 2fd63cc4bd9c
parent 1407 bb6516a50eea
child 1409 84e9d3494603
permissions -rw-r--r--
timelimit function now gets initialized
     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 (m_FreeTimers.empty())
   162 	{
   163 		pTimer = new ITimer;
   164 	} else {
   165 		pTimer = m_FreeTimers.front();
   166 		m_FreeTimers.pop();
   167 	}
   168 
   169 	pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
   170 
   171 	if (flags & TIMER_FLAG_REPEAT)
   172 	{
   173 		m_LoopTimers.push_back(pTimer);
   174 		goto return_timer;
   175 	}
   176 
   177 	if (flags & TIMER_FLAG_BEFORE_MAP_END)
   178 	{
   179 		for (iter=m_MapEndTimers.begin(); iter!=m_MapEndTimers.end(); iter++)
   180 		{
   181 			if (fInterval >= (*iter)->m_Interval)
   182 			{
   183 				m_MapEndTimers.insert(iter, pTimer);
   184 				goto return_timer;
   185 			}
   186 		}
   187 
   188 		m_MapEndTimers.push_back(pTimer);
   189 	} else {
   190 		if (m_SingleTimers.size() >= 1)
   191 		{
   192 			iter = --m_SingleTimers.end();
   193 			if ((*iter)->m_ToExec <= to_exec)
   194 			{
   195 				goto normal_insert_end;
   196 			}
   197 		}
   198 	
   199 		for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   200 		{
   201 			if ((*iter)->m_ToExec >= to_exec)
   202 			{
   203 				m_SingleTimers.insert(iter, pTimer);
   204 				goto return_timer;
   205 			}
   206 		}
   207 
   208 normal_insert_end:
   209 		m_SingleTimers.push_back(pTimer);
   210 	}
   211 
   212 return_timer:
   213 	return pTimer;
   214 }
   215 
   216 void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
   217 {
   218 	ResultType res;
   219 
   220 	if (pTimer->m_InExec)
   221 	{
   222 		return;
   223 	}
   224 
   225 	pTimer->m_InExec = true;
   226 	res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   227 
   228 	if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
   229 	{
   230 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   231 		if (pTimer->m_Flags & TIMER_FLAG_BEFORE_MAP_END)
   232 		{
   233 			m_MapEndTimers.remove(pTimer);
   234 		} else {
   235 			m_SingleTimers.remove(pTimer);
   236 		}
   237 		m_FreeTimers.push(pTimer);
   238 	} else {
   239 		if ((res != Pl_Stop) && !pTimer->m_KillMe)
   240 		{
   241 			if (delayExec)
   242 			{
   243 				pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
   244 			}
   245 			pTimer->m_InExec = false;
   246 			return;
   247 		}
   248 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   249 		m_LoopTimers.remove(pTimer);
   250 		m_FreeTimers.push(pTimer);
   251 	}
   252 }
   253 
   254 void TimerSystem::KillTimer(ITimer *pTimer)
   255 {
   256 	TimerList *pList;
   257 
   258 	if (pTimer->m_KillMe)
   259 	{
   260 		return;
   261 	}
   262 
   263 	if (pTimer->m_InExec)
   264 	{
   265 		pTimer->m_KillMe = true;
   266 		return;
   267 	}
   268 
   269 	pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
   270 	pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   271 
   272 	if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
   273 	{
   274 		m_LoopTimers.remove(pTimer);
   275 	} else if (pTimer->m_Flags & TIMER_FLAG_BEFORE_MAP_END) {
   276 		m_MapEndTimers.remove(pTimer);
   277 	} else {
   278 		m_SingleTimers.remove(pTimer);
   279 	}
   280 
   281 	pList->remove(pTimer);
   282 	m_FreeTimers.push(pTimer);
   283 }
   284 
   285 CStack<ITimer *> s_tokill;
   286 void TimerSystem::MapChange(bool real_mapchange)
   287 {
   288 	ITimer *pTimer;
   289 	TimerIter iter;
   290 
   291 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   292 	{
   293 		pTimer = (*iter);
   294 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   295 		{
   296 			s_tokill.push(pTimer);
   297 		} else {
   298 			pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime();
   299 		}
   300 	}
   301 
   302 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
   303 	{
   304 		pTimer = (*iter);
   305 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   306 		{
   307 			s_tokill.push(pTimer);
   308 		} else {
   309 			pTimer->m_ToExec = pTimer->m_ToExec - m_LastExecTime + GetSimulatedTime();
   310 		}
   311 	}
   312 
   313 	if (real_mapchange)
   314 	{
   315 		for (iter=m_MapEndTimers.begin(); iter!=m_MapEndTimers.end(); iter++)
   316 		{
   317 			pTimer = (*iter);
   318 			s_tokill.push(pTimer);
   319 		}
   320 	}
   321 
   322 	while (!s_tokill.empty())
   323 	{
   324 		KillTimer(s_tokill.front());
   325 		s_tokill.pop();
   326 	}
   327 
   328 	m_LastExecTime = GetSimulatedTime();
   329 }
   330 
   331 SM_TIMELEFT_FUNCTION TimerSystem::SetTimeLeftFunction(SM_TIMELEFT_FUNCTION fn)
   332 {
   333 	SM_TIMELEFT_FUNCTION old = m_fnTimeLeft;
   334 	m_fnTimeLeft = fn;
   335 	return old;
   336 }
   337