core/TimerSys.cpp
author Peace-Maker <januk@freenet.de>
Tue Feb 05 21:45:11 2013 +0000 (2013-02-05)
changeset 3763 336283ba3a38
parent 2289 44045ead61cd
child 3872 4c93f74cae51
permissions -rw-r--r--
Updated for CS:S sync (r=asherkin).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 #include "ConVarManager.h"
    38 
    39 #define TIMER_MIN_ACCURACY		0.1
    40 
    41 TimerSystem g_Timers;
    42 double g_fUniversalTime = 0.0f;
    43 float g_fGameStartTime = 0.0f;	/* Game game start time, non-universal */
    44 double g_fTimerThink = 0.0f;		/* Timer's next think time */
    45 const double *g_pUniversalTime = &g_fUniversalTime;
    46 ConVar *mp_timelimit = NULL;
    47 int g_TimeLeftMode = 0;
    48 
    49 ConVar sm_time_adjustment("sm_time_adjustment", "0", 0, "Adjusts the server time in seconds");
    50 
    51 inline double GetSimulatedTime()
    52 {
    53 	return g_fUniversalTime;
    54 }
    55 
    56 time_t GetAdjustedTime(time_t *buf)
    57 {
    58 	time_t val = time(NULL) + sm_time_adjustment.GetInt();
    59 	if (buf)
    60 	{
    61 		*buf = val;
    62 	}
    63 	return val;
    64 }
    65 
    66 class DefaultMapTimer : 
    67 	public IMapTimer,
    68 	public SMGlobalClass,
    69 	public IConVarChangeListener
    70 {
    71 public:
    72 	DefaultMapTimer()
    73 	{
    74 		m_bInUse = false;
    75 	}
    76 
    77 	void OnSourceModLevelChange(const char *mapName)
    78 	{
    79 		g_fGameStartTime = 0.0f;
    80 	}
    81 
    82 	int GetMapTimeLimit()
    83 	{
    84 		return mp_timelimit->GetInt();
    85 	}
    86 
    87 	void SetMapTimerStatus(bool enabled)
    88 	{
    89 		if (enabled && !m_bInUse)
    90 		{
    91 			Enable();
    92 		} 
    93 		else if (!enabled && m_bInUse)
    94 		{
    95 			Disable();
    96 		}
    97 		m_bInUse = enabled;
    98 	}
    99 
   100 	void ExtendMapTimeLimit(int extra_time)
   101 	{
   102 		if (extra_time == 0)
   103 		{
   104 			mp_timelimit->SetValue(0);
   105 			return;
   106 		}
   107 
   108 		extra_time /= 60;
   109 
   110 		mp_timelimit->SetValue(mp_timelimit->GetInt() + extra_time);
   111 	}
   112 
   113 	void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
   114 	{
   115 		g_Timers.MapTimeLeftChanged();
   116 	}
   117 
   118 private:
   119 	void Enable()
   120 	{
   121 		g_ConVarManager.AddConVarChangeListener("mp_timelimit", this);
   122 	}
   123 
   124 	void Disable()
   125 	{
   126 		g_ConVarManager.RemoveConVarChangeListener("mp_timelimit", this);
   127 	}
   128 
   129 private:
   130 	bool m_bInUse;
   131 } s_DefaultMapTimer;
   132 
   133 /**
   134  * If the ticking process has run amok (should be impossible), we 
   135  * take care of this by "skipping" the in-between time, to prevent 
   136  * a bazillion times from firing on accident.  This has the result  
   137  * that a drastic jump in time will continue acting normally.  Users 
   138  * may not expect this, but... I think it is the best solution.
   139  */
   140 inline double CalcNextThink(double last, float interval)
   141 {
   142 	if (g_fUniversalTime - last - interval <= TIMER_MIN_ACCURACY)
   143 	{
   144 		return last + interval;
   145 	}
   146 	else
   147 	{
   148 		return g_fUniversalTime + interval;
   149 	}
   150 }
   151 
   152 void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags)
   153 {
   154 	m_Listener = pCallbacks;
   155 	m_Interval = fInterval;
   156 	m_ToExec = fToExec;
   157 	m_pData = pData;
   158 	m_Flags = flags;
   159 	m_InExec = false;
   160 	m_KillMe = false;
   161 }
   162 
   163 TimerSystem::TimerSystem()
   164 {
   165 	m_pMapTimer = NULL;
   166 	m_bHasMapTickedYet = false;
   167 	m_bHasMapSimulatedYet = false;
   168 	m_fLastTickedTime = 0.0f;
   169 }
   170 
   171 TimerSystem::~TimerSystem()
   172 {
   173 	CStack<ITimer *>::iterator iter;
   174 	for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
   175 	{
   176 		delete (*iter);
   177 	}
   178 	m_FreeTimers.popall();
   179 }
   180 
   181 void TimerSystem::OnSourceModAllInitialized()
   182 {
   183 	g_ShareSys.AddInterface(NULL, this);
   184 	m_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL);
   185 	m_pOnMapTimeLeftChanged = g_Forwards.CreateForward("OnMapTimeLeftChanged", ET_Ignore, 0, NULL);
   186 }
   187 
   188 void TimerSystem::OnSourceModGameInitialized()
   189 {
   190 	mp_timelimit = icvar->FindVar("mp_timelimit");
   191 
   192 	if (m_pMapTimer == NULL && mp_timelimit != NULL)
   193 	{
   194 		SetMapTimer(&s_DefaultMapTimer);
   195 	}
   196 }
   197 
   198 void TimerSystem::OnSourceModShutdown()
   199 {
   200 	SetMapTimer(NULL);
   201 	g_Forwards.ReleaseForward(m_pOnGameFrame);
   202 	g_Forwards.ReleaseForward(m_pOnMapTimeLeftChanged);
   203 }
   204 
   205 void TimerSystem::OnSourceModLevelEnd()
   206 {
   207 	m_bHasMapTickedYet = false;
   208 	m_bHasMapSimulatedYet = false;
   209 }
   210 
   211 void TimerSystem::GameFrame(bool simulating)
   212 {
   213 	if (simulating && m_bHasMapTickedYet)
   214 	{
   215 		g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
   216 		if (!m_bHasMapSimulatedYet)
   217 		{
   218 			m_bHasMapSimulatedYet = true;
   219 			MapTimeLeftChanged();
   220 		}
   221 	}
   222 	else 
   223 	{
   224 		g_fUniversalTime += gpGlobals->interval_per_tick;
   225 	}
   226 
   227 	m_fLastTickedTime = gpGlobals->curtime;
   228 	m_bHasMapTickedYet = true;
   229 
   230 	if (g_fUniversalTime >= g_fTimerThink)
   231 	{
   232 		RunFrame();
   233 
   234 		g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
   235 	}
   236 
   237 	RunFrameHooks(simulating);
   238 
   239 	if (m_pOnGameFrame->GetFunctionCount())
   240 	{
   241 		m_pOnGameFrame->Execute(NULL);
   242 	}
   243 }
   244 
   245 void TimerSystem::RunFrame()
   246 {
   247 	ITimer *pTimer;
   248 	TimerIter iter;
   249 
   250 	double curtime = GetSimulatedTime();
   251 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
   252 	{
   253 		pTimer = (*iter);
   254 		if (curtime >= pTimer->m_ToExec)
   255 		{
   256 			pTimer->m_InExec = true;
   257 			pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   258 			pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   259 			iter = m_SingleTimers.erase(iter);
   260 			m_FreeTimers.push(pTimer);
   261 		} 
   262 		else 
   263 		{
   264 			break;
   265 		}
   266 	}
   267 
   268 	ResultType res;
   269 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
   270 	{
   271 		pTimer = (*iter);
   272 		if (curtime >= pTimer->m_ToExec)
   273 		{
   274 			pTimer->m_InExec = true;
   275 			res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   276 			if (pTimer->m_KillMe || (res == Pl_Stop))
   277 			{
   278 				pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   279 				iter = m_LoopTimers.erase(iter);
   280 				m_FreeTimers.push(pTimer);
   281 				continue;
   282 			}
   283 			pTimer->m_InExec = false;
   284 			pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval);
   285 		}
   286 		iter++;
   287 	}
   288 }
   289 
   290 ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
   291 {
   292 	ITimer *pTimer;
   293 	TimerIter iter;
   294 	float to_exec = GetSimulatedTime() + fInterval;
   295 
   296 	if (m_FreeTimers.empty())
   297 	{
   298 		pTimer = new ITimer;
   299 	} else {
   300 		pTimer = m_FreeTimers.front();
   301 		m_FreeTimers.pop();
   302 	}
   303 
   304 	pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
   305 
   306 	if (flags & TIMER_FLAG_REPEAT)
   307 	{
   308 		m_LoopTimers.push_back(pTimer);
   309 		goto return_timer;
   310 	}
   311 
   312 	if (m_SingleTimers.size() >= 1)
   313 	{
   314 		iter = --m_SingleTimers.end();
   315 		if ((*iter)->m_ToExec <= to_exec)
   316 		{
   317 			goto normal_insert_end;
   318 		}
   319 	}
   320 
   321 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   322 	{
   323 		if ((*iter)->m_ToExec >= to_exec)
   324 		{
   325 			m_SingleTimers.insert(iter, pTimer);
   326 			goto return_timer;
   327 		}
   328 	}
   329 
   330 normal_insert_end:
   331 	m_SingleTimers.push_back(pTimer);
   332 
   333 return_timer:
   334 	return pTimer;
   335 }
   336 
   337 void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
   338 {
   339 	ResultType res;
   340 
   341 	if (pTimer->m_InExec)
   342 	{
   343 		return;
   344 	}
   345 
   346 	pTimer->m_InExec = true;
   347 	res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   348 
   349 	if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
   350 	{
   351 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   352 		m_SingleTimers.remove(pTimer);
   353 		m_FreeTimers.push(pTimer);
   354 	} 
   355 	else 
   356 	{
   357 		if ((res != Pl_Stop) && !pTimer->m_KillMe)
   358 		{
   359 			if (delayExec)
   360 			{
   361 				pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
   362 			}
   363 			pTimer->m_InExec = false;
   364 			return;
   365 		}
   366 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   367 		m_LoopTimers.remove(pTimer);
   368 		m_FreeTimers.push(pTimer);
   369 	}
   370 }
   371 
   372 void TimerSystem::KillTimer(ITimer *pTimer)
   373 {
   374 	if (pTimer->m_KillMe)
   375 	{
   376 		return;
   377 	}
   378 
   379 	if (pTimer->m_InExec)
   380 	{
   381 		pTimer->m_KillMe = true;
   382 		return;
   383 	}
   384 
   385 	pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
   386 	pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   387 
   388 	if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
   389 	{
   390 		m_LoopTimers.remove(pTimer);
   391 	} else {
   392 		m_SingleTimers.remove(pTimer);
   393 	}
   394 
   395 	m_FreeTimers.push(pTimer);
   396 }
   397 
   398 CStack<ITimer *> s_tokill;
   399 void TimerSystem::RemoveMapChangeTimers()
   400 {
   401 	ITimer *pTimer;
   402 	TimerIter iter;
   403 
   404 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   405 	{
   406 		pTimer = (*iter);
   407 		if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
   408 		{
   409 			s_tokill.push(pTimer);
   410 		}
   411 	}
   412 
   413 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
   414 	{
   415 		pTimer = (*iter);
   416 		if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
   417 		{
   418 			s_tokill.push(pTimer);
   419 		}
   420 	}
   421 
   422 	while (!s_tokill.empty())
   423 	{
   424 		KillTimer(s_tokill.front());
   425 		s_tokill.pop();
   426 	}
   427 }
   428 
   429 IMapTimer *TimerSystem::SetMapTimer(IMapTimer *pTimer)
   430 {
   431 	IMapTimer *old = m_pMapTimer;
   432 
   433 	m_pMapTimer = pTimer;
   434 
   435 	if (m_pMapTimer)
   436 	{
   437 		m_pMapTimer->SetMapTimerStatus(true);
   438 	}
   439 
   440 	if (old)
   441 	{
   442 		old->SetMapTimerStatus(false);
   443 	}
   444 
   445 	return old;
   446 }
   447 
   448 IMapTimer *TimerSystem::GetMapTimer()
   449 {
   450 	return m_pMapTimer;
   451 }
   452 
   453 void TimerSystem::MapTimeLeftChanged()
   454 {
   455 	m_pOnMapTimeLeftChanged->Execute(NULL);
   456 }
   457 
   458 void TimerSystem::NotifyOfGameStart(float offset)
   459 {
   460 	g_fGameStartTime = gpGlobals->curtime + offset;
   461 }
   462 
   463 float TimerSystem::GetTickedTime()
   464 {
   465 	return g_fUniversalTime;
   466 }
   467 
   468 bool TimerSystem::GetMapTimeLeft(float *time_left)
   469 {
   470 	if (!m_pMapTimer)
   471 	{
   472 		return false;
   473 	}
   474 
   475 	int time_limit;
   476 	if (!m_bHasMapSimulatedYet || (time_limit = m_pMapTimer->GetMapTimeLimit()) < 1)
   477 	{
   478 		*time_left = -1.0f;
   479 	}
   480 	else
   481 	{
   482 		*time_left = (g_fGameStartTime + time_limit * 60.0f) - gpGlobals->curtime;
   483 	}
   484 
   485 	return true;
   486 }
   487