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