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