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