core/TimerSys.cpp
author David Anderson <dvander@alliedmods.net>
Thu Sep 13 01:14:02 2007 +0000 (2007-09-13)
changeset 1415 edf4efe5707c
parent 1414 bb258bffc7df
child 1459 8132c2259758
permissions -rw-r--r--
finalized (i hope) new map timer api
     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 		if (atoi(old_value) == pVar->GetInt())
   124 		{
   125 			return;
   126 		}
   127 
   128 		g_Timers.MapTimeLeftChanged();
   129 	}
   130 
   131 private:
   132 	void Enable()
   133 	{
   134 		SH_ADD_HOOK_MEMFUNC(ICvar, CallGlobalChangeCallback, icvar, this, &DefaultMapTimer::GlobalChangeCallback, false);
   135 	}
   136 
   137 	void Disable()
   138 	{
   139 		SH_REMOVE_HOOK_MEMFUNC(ICvar, CallGlobalChangeCallback, icvar, this, &DefaultMapTimer::GlobalChangeCallback, false);
   140 	}
   141 
   142 private:
   143 	bool m_bInUse;
   144 } s_DefaultMapTimer;
   145 
   146 void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags)
   147 {
   148 	m_Listener = pCallbacks;
   149 	m_Interval = fInterval;
   150 	m_ToExec = fToExec;
   151 	m_pData = pData;
   152 	m_Flags = flags;
   153 	m_InExec = false;
   154 	m_KillMe = false;
   155 }
   156 
   157 TimerSystem::TimerSystem()
   158 {
   159 	m_pMapTimer = NULL;
   160 	m_bHasMapTickedYet = false;
   161 	m_fLastTickedTime = 0.0f;
   162 	m_LastExecTime = 0.0f;
   163 }
   164 
   165 TimerSystem::~TimerSystem()
   166 {
   167 	CStack<ITimer *>::iterator iter;
   168 	for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
   169 	{
   170 		delete (*iter);
   171 	}
   172 	m_FreeTimers.popall();
   173 }
   174 
   175 void TimerSystem::OnSourceModAllInitialized()
   176 {
   177 	g_ShareSys.AddInterface(NULL, this);
   178 	m_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL);
   179 }
   180 
   181 void TimerSystem::OnSourceModGameInitialized()
   182 {
   183 	mp_timelimit = icvar->FindVar("mp_timelimit");
   184 
   185 	if (m_pMapTimer == NULL && mp_timelimit != NULL)
   186 	{
   187 		SetMapTimer(&s_DefaultMapTimer);
   188 	}
   189 }
   190 
   191 void TimerSystem::OnSourceModShutdown()
   192 {
   193 	SetMapTimer(NULL);
   194 	g_Forwards.ReleaseForward(m_pOnGameFrame);
   195 }
   196 
   197 void TimerSystem::OnSourceModLevelChange(const char *mapName)
   198 {
   199 	MapChange(true);
   200 }
   201 
   202 void TimerSystem::OnSourceModLevelEnd()
   203 {
   204 	m_bHasMapTickedYet = false;
   205 }
   206 
   207 void TimerSystem::GameFrame(bool simulating)
   208 {
   209 	if (simulating && m_bHasMapTickedYet)
   210 	{
   211 		g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
   212 	}
   213 	else 
   214 	{
   215 		g_fUniversalTime += gpGlobals->interval_per_tick;
   216 	}
   217 
   218 	m_fLastTickedTime = gpGlobals->curtime;
   219 	if (!m_bHasMapTickedYet)
   220 	{
   221 		g_fMapStartTime = g_fUniversalTime;
   222 		m_bHasMapTickedYet = true;
   223 	}
   224 
   225 	if (g_fUniversalTime - m_LastExecTime >= 0.1)
   226 	{
   227 		RunFrame();
   228 	}
   229 
   230 	RunFrameHooks();
   231 
   232 	if (m_pOnGameFrame->GetFunctionCount())
   233 	{
   234 		m_pOnGameFrame->Execute(NULL);
   235 	}
   236 }
   237 
   238 void TimerSystem::RunFrame()
   239 {
   240 	ITimer *pTimer;
   241 	TimerIter iter;
   242 
   243 	float curtime = GetSimulatedTime();
   244 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
   245 	{
   246 		pTimer = (*iter);
   247 		if (curtime >= pTimer->m_ToExec)
   248 		{
   249 			pTimer->m_InExec = true;
   250 			pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   251 			pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   252 			iter = m_SingleTimers.erase(iter);
   253 			m_FreeTimers.push(pTimer);
   254 		} else {
   255 			break;
   256 		}
   257 	}
   258 
   259 	ResultType res;
   260 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
   261 	{
   262 		pTimer = (*iter);
   263 		if (curtime >= pTimer->m_ToExec)
   264 		{
   265 			pTimer->m_InExec = true;
   266 			res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   267 			if (pTimer->m_KillMe || (res == Pl_Stop))
   268 			{
   269 				pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   270 				iter = m_LoopTimers.erase(iter);
   271 				m_FreeTimers.push(pTimer);
   272 				continue;
   273 			}
   274 			pTimer->m_InExec = false;
   275 			pTimer->m_ToExec = curtime + pTimer->m_Interval;
   276 		}
   277 		iter++;
   278 	}
   279 
   280 	m_LastExecTime = curtime;
   281 }
   282 
   283 ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
   284 {
   285 	ITimer *pTimer;
   286 	TimerIter iter;
   287 	float to_exec = GetSimulatedTime() + fInterval;
   288 
   289 	if (m_FreeTimers.empty())
   290 	{
   291 		pTimer = new ITimer;
   292 	} else {
   293 		pTimer = m_FreeTimers.front();
   294 		m_FreeTimers.pop();
   295 	}
   296 
   297 	pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
   298 
   299 	if (flags & TIMER_FLAG_REPEAT)
   300 	{
   301 		m_LoopTimers.push_back(pTimer);
   302 		goto return_timer;
   303 	}
   304 
   305 	if (m_SingleTimers.size() >= 1)
   306 	{
   307 		iter = --m_SingleTimers.end();
   308 		if ((*iter)->m_ToExec <= to_exec)
   309 		{
   310 			goto normal_insert_end;
   311 		}
   312 	}
   313 
   314 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   315 	{
   316 		if ((*iter)->m_ToExec >= to_exec)
   317 		{
   318 			m_SingleTimers.insert(iter, pTimer);
   319 			goto return_timer;
   320 		}
   321 	}
   322 
   323 normal_insert_end:
   324 	m_SingleTimers.push_back(pTimer);
   325 
   326 return_timer:
   327 	return pTimer;
   328 }
   329 
   330 void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
   331 {
   332 	ResultType res;
   333 
   334 	if (pTimer->m_InExec)
   335 	{
   336 		return;
   337 	}
   338 
   339 	pTimer->m_InExec = true;
   340 	res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
   341 
   342 	if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
   343 	{
   344 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   345 		m_SingleTimers.remove(pTimer);
   346 		m_FreeTimers.push(pTimer);
   347 	} else {
   348 		if ((res != Pl_Stop) && !pTimer->m_KillMe)
   349 		{
   350 			if (delayExec)
   351 			{
   352 				pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
   353 			}
   354 			pTimer->m_InExec = false;
   355 			return;
   356 		}
   357 		pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   358 		m_LoopTimers.remove(pTimer);
   359 		m_FreeTimers.push(pTimer);
   360 	}
   361 }
   362 
   363 void TimerSystem::KillTimer(ITimer *pTimer)
   364 {
   365 	if (pTimer->m_KillMe)
   366 	{
   367 		return;
   368 	}
   369 
   370 	if (pTimer->m_InExec)
   371 	{
   372 		pTimer->m_KillMe = true;
   373 		return;
   374 	}
   375 
   376 	pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
   377 	pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
   378 
   379 	if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
   380 	{
   381 		m_LoopTimers.remove(pTimer);
   382 	} else {
   383 		m_SingleTimers.remove(pTimer);
   384 	}
   385 
   386 	m_FreeTimers.push(pTimer);
   387 }
   388 
   389 CStack<ITimer *> s_tokill;
   390 void TimerSystem::MapChange(bool real_mapchange)
   391 {
   392 	ITimer *pTimer;
   393 	TimerIter iter;
   394 
   395 	for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
   396 	{
   397 		pTimer = (*iter);
   398 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   399 		{
   400 			s_tokill.push(pTimer);
   401 		}
   402 	}
   403 
   404 	for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
   405 	{
   406 		pTimer = (*iter);
   407 		if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
   408 		{
   409 			s_tokill.push(pTimer);
   410 		}
   411 	}
   412 
   413 	while (!s_tokill.empty())
   414 	{
   415 		KillTimer(s_tokill.front());
   416 		s_tokill.pop();
   417 	}
   418 }
   419 
   420 IMapTimer *TimerSystem::SetMapTimer(IMapTimer *pTimer)
   421 {
   422 	IMapTimer *old = m_pMapTimer;
   423 
   424 	m_pMapTimer = pTimer;
   425 
   426 	if (m_pMapTimer)
   427 	{
   428 		m_pMapTimer->SetMapTimerStatus(true);
   429 	}
   430 
   431 	if (old)
   432 	{
   433 		old->SetMapTimerStatus(false);
   434 	}
   435 
   436 	return old;
   437 }
   438 
   439 IMapTimer *TimerSystem::GetMapTimer()
   440 {
   441 	return m_pMapTimer;
   442 }
   443 
   444 void TimerSystem::MapTimeLeftChanged()
   445 {
   446 
   447 }
   448 
   449 float TimerSystem::GetTickedTime()
   450 {
   451 	return g_fUniversalTime;
   452 }