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