1.1 --- a/core/TimerSys.cpp Fri Mar 28 06:21:34 2008 +0000
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,514 +0,0 @@
1.4 -/**
1.5 - * vim: set ts=4 :
1.6 - * =============================================================================
1.7 - * SourceMod
1.8 - * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
1.9 - * =============================================================================
1.10 - *
1.11 - * This program is free software; you can redistribute it and/or modify it under
1.12 - * the terms of the GNU General Public License, version 3.0, as published by the
1.13 - * Free Software Foundation.
1.14 - *
1.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 - * details.
1.19 - *
1.20 - * You should have received a copy of the GNU General Public License along with
1.21 - * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 - *
1.23 - * As a special exception, AlliedModders LLC gives you permission to link the
1.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 - * by the Valve Corporation. You must obey the GNU General Public License in
1.27 - * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 - * this exception to all derivative works. AlliedModders LLC defines further
1.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 - * or <http://www.sourcemod.net/license.php>.
1.31 - *
1.32 - * Version: $Id$
1.33 - */
1.34 -
1.35 -#include <time.h>
1.36 -#include "TimerSys.h"
1.37 -#include "ForwardSys.h"
1.38 -#include "sourcemm_api.h"
1.39 -#include "frame_hooks.h"
1.40 -
1.41 -#define TIMER_MIN_ACCURACY 0.1
1.42 -
1.43 -#if !defined ORANGEBOX_BUILD
1.44 -#define CallGlobalChangeCallbacks CallGlobalChangeCallback
1.45 -#endif
1.46 -
1.47 -#if defined ORANGEBOX_BUILD
1.48 -SH_DECL_HOOK3_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *, float);
1.49 -#else
1.50 -SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallbacks, SH_NOATTRIB, false, ConVar *, const char *);
1.51 -#endif
1.52 -
1.53 -TimerSystem g_Timers;
1.54 -double g_fUniversalTime = 0.0f;
1.55 -float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */
1.56 -double g_fTimerThink = 0.0f; /* Timer's next think time */
1.57 -const double *g_pUniversalTime = &g_fUniversalTime;
1.58 -ConVar *mp_timelimit = NULL;
1.59 -int g_TimeLeftMode = 0;
1.60 -
1.61 -ConVar sm_time_adjustment("sm_time_adjustment", "0", 0, "Adjusts the server time in seconds");
1.62 -
1.63 -inline double GetSimulatedTime()
1.64 -{
1.65 - return g_fUniversalTime;
1.66 -}
1.67 -
1.68 -time_t GetAdjustedTime(time_t *buf)
1.69 -{
1.70 - time_t val = time(NULL) + sm_time_adjustment.GetInt();
1.71 - if (buf)
1.72 - {
1.73 - *buf = val;
1.74 - }
1.75 - return val;
1.76 -}
1.77 -
1.78 -class DefaultMapTimer :
1.79 - public IMapTimer,
1.80 - public SMGlobalClass
1.81 -{
1.82 -public:
1.83 - DefaultMapTimer()
1.84 - {
1.85 - m_bInUse = false;
1.86 - }
1.87 -
1.88 - void OnSourceModLevelChange(const char *mapName)
1.89 - {
1.90 - g_fGameStartTime = 0.0f;
1.91 - }
1.92 -
1.93 - int GetMapTimeLimit()
1.94 - {
1.95 - return mp_timelimit->GetInt();
1.96 - }
1.97 -
1.98 - void SetMapTimerStatus(bool enabled)
1.99 - {
1.100 - if (enabled && !m_bInUse)
1.101 - {
1.102 - Enable();
1.103 - }
1.104 - else if (!enabled && m_bInUse)
1.105 - {
1.106 - Disable();
1.107 - }
1.108 - m_bInUse = enabled;
1.109 - }
1.110 -
1.111 - void ExtendMapTimeLimit(int extra_time)
1.112 - {
1.113 - if (extra_time == 0)
1.114 - {
1.115 - mp_timelimit->SetValue(0);
1.116 - return;
1.117 - }
1.118 -
1.119 - extra_time /= 60;
1.120 -
1.121 - mp_timelimit->SetValue(mp_timelimit->GetInt() + extra_time);
1.122 - }
1.123 -
1.124 -#if defined ORANGEBOX_BUILD
1.125 - void GlobalChangeCallback(ConVar *pVar, const char *old_value, float flOldValue)
1.126 -#else
1.127 - void GlobalChangeCallback(ConVar *pVar, const char *old_value)
1.128 -#endif
1.129 - {
1.130 - if (pVar != mp_timelimit)
1.131 - {
1.132 - return;
1.133 - }
1.134 -
1.135 - if (atoi(old_value) == pVar->GetInt())
1.136 - {
1.137 - return;
1.138 - }
1.139 -
1.140 - g_Timers.MapTimeLeftChanged();
1.141 - }
1.142 -
1.143 -private:
1.144 - void Enable()
1.145 - {
1.146 - SH_ADD_HOOK_MEMFUNC(ICvar, CallGlobalChangeCallbacks, icvar, this, &DefaultMapTimer::GlobalChangeCallback, false);
1.147 - }
1.148 -
1.149 - void Disable()
1.150 - {
1.151 - SH_REMOVE_HOOK_MEMFUNC(ICvar, CallGlobalChangeCallbacks, icvar, this, &DefaultMapTimer::GlobalChangeCallback, false);
1.152 - }
1.153 -
1.154 -private:
1.155 - bool m_bInUse;
1.156 -} s_DefaultMapTimer;
1.157 -
1.158 -/**
1.159 - * If the ticking process has run amok (should be impossible), we
1.160 - * take care of this by "skipping" the in-between time, to prevent
1.161 - * a bazillion times from firing on accident. This has the result
1.162 - * that a drastic jump in time will continue acting normally. Users
1.163 - * may not expect this, but... I think it is the best solution.
1.164 - */
1.165 -inline double CalcNextThink(double last, float interval)
1.166 -{
1.167 - if (g_fUniversalTime - last - interval <= TIMER_MIN_ACCURACY)
1.168 - {
1.169 - return last + interval;
1.170 - }
1.171 - else
1.172 - {
1.173 - return g_fUniversalTime + interval;
1.174 - }
1.175 -}
1.176 -
1.177 -void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags)
1.178 -{
1.179 - m_Listener = pCallbacks;
1.180 - m_Interval = fInterval;
1.181 - m_ToExec = fToExec;
1.182 - m_pData = pData;
1.183 - m_Flags = flags;
1.184 - m_InExec = false;
1.185 - m_KillMe = false;
1.186 -}
1.187 -
1.188 -TimerSystem::TimerSystem()
1.189 -{
1.190 - m_pMapTimer = NULL;
1.191 - m_bHasMapTickedYet = false;
1.192 - m_bHasMapSimulatedYet = false;
1.193 - m_fLastTickedTime = 0.0f;
1.194 -}
1.195 -
1.196 -TimerSystem::~TimerSystem()
1.197 -{
1.198 - CStack<ITimer *>::iterator iter;
1.199 - for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
1.200 - {
1.201 - delete (*iter);
1.202 - }
1.203 - m_FreeTimers.popall();
1.204 -}
1.205 -
1.206 -void TimerSystem::OnSourceModAllInitialized()
1.207 -{
1.208 - g_ShareSys.AddInterface(NULL, this);
1.209 - m_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL);
1.210 - m_pOnMapTimeLeftChanged = g_Forwards.CreateForward("OnMapTimeLeftChanged", ET_Ignore, 0, NULL);
1.211 -}
1.212 -
1.213 -void TimerSystem::OnSourceModGameInitialized()
1.214 -{
1.215 - mp_timelimit = icvar->FindVar("mp_timelimit");
1.216 -
1.217 - if (m_pMapTimer == NULL && mp_timelimit != NULL)
1.218 - {
1.219 - SetMapTimer(&s_DefaultMapTimer);
1.220 - }
1.221 -}
1.222 -
1.223 -void TimerSystem::OnSourceModShutdown()
1.224 -{
1.225 - SetMapTimer(NULL);
1.226 - g_Forwards.ReleaseForward(m_pOnGameFrame);
1.227 - g_Forwards.ReleaseForward(m_pOnMapTimeLeftChanged);
1.228 -}
1.229 -
1.230 -void TimerSystem::OnSourceModLevelChange(const char *mapName)
1.231 -{
1.232 - MapChange(true);
1.233 -}
1.234 -
1.235 -void TimerSystem::OnSourceModLevelEnd()
1.236 -{
1.237 - m_bHasMapTickedYet = false;
1.238 - m_bHasMapSimulatedYet = false;
1.239 -}
1.240 -
1.241 -void TimerSystem::GameFrame(bool simulating)
1.242 -{
1.243 - if (simulating && m_bHasMapTickedYet)
1.244 - {
1.245 - g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
1.246 - if (!m_bHasMapSimulatedYet)
1.247 - {
1.248 - m_bHasMapSimulatedYet = true;
1.249 - MapTimeLeftChanged();
1.250 - }
1.251 - }
1.252 - else
1.253 - {
1.254 - g_fUniversalTime += gpGlobals->interval_per_tick;
1.255 - }
1.256 -
1.257 - m_fLastTickedTime = gpGlobals->curtime;
1.258 - m_bHasMapTickedYet = true;
1.259 -
1.260 - if (g_fUniversalTime >= g_fTimerThink)
1.261 - {
1.262 - RunFrame();
1.263 -
1.264 - g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
1.265 - }
1.266 -
1.267 - RunFrameHooks();
1.268 -
1.269 - if (m_pOnGameFrame->GetFunctionCount())
1.270 - {
1.271 - m_pOnGameFrame->Execute(NULL);
1.272 - }
1.273 -}
1.274 -
1.275 -void TimerSystem::RunFrame()
1.276 -{
1.277 - ITimer *pTimer;
1.278 - TimerIter iter;
1.279 -
1.280 - double curtime = GetSimulatedTime();
1.281 - for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
1.282 - {
1.283 - pTimer = (*iter);
1.284 - if (curtime >= pTimer->m_ToExec)
1.285 - {
1.286 - pTimer->m_InExec = true;
1.287 - pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
1.288 - pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.289 - iter = m_SingleTimers.erase(iter);
1.290 - m_FreeTimers.push(pTimer);
1.291 - }
1.292 - else
1.293 - {
1.294 - break;
1.295 - }
1.296 - }
1.297 -
1.298 - ResultType res;
1.299 - for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
1.300 - {
1.301 - pTimer = (*iter);
1.302 - if (curtime >= pTimer->m_ToExec)
1.303 - {
1.304 - pTimer->m_InExec = true;
1.305 - res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
1.306 - if (pTimer->m_KillMe || (res == Pl_Stop))
1.307 - {
1.308 - pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.309 - iter = m_LoopTimers.erase(iter);
1.310 - m_FreeTimers.push(pTimer);
1.311 - continue;
1.312 - }
1.313 - pTimer->m_InExec = false;
1.314 - pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval);
1.315 - }
1.316 - iter++;
1.317 - }
1.318 -}
1.319 -
1.320 -ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
1.321 -{
1.322 - ITimer *pTimer;
1.323 - TimerIter iter;
1.324 - float to_exec = GetSimulatedTime() + fInterval;
1.325 -
1.326 - if (m_FreeTimers.empty())
1.327 - {
1.328 - pTimer = new ITimer;
1.329 - } else {
1.330 - pTimer = m_FreeTimers.front();
1.331 - m_FreeTimers.pop();
1.332 - }
1.333 -
1.334 - pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
1.335 -
1.336 - if (flags & TIMER_FLAG_REPEAT)
1.337 - {
1.338 - m_LoopTimers.push_back(pTimer);
1.339 - goto return_timer;
1.340 - }
1.341 -
1.342 - if (m_SingleTimers.size() >= 1)
1.343 - {
1.344 - iter = --m_SingleTimers.end();
1.345 - if ((*iter)->m_ToExec <= to_exec)
1.346 - {
1.347 - goto normal_insert_end;
1.348 - }
1.349 - }
1.350 -
1.351 - for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
1.352 - {
1.353 - if ((*iter)->m_ToExec >= to_exec)
1.354 - {
1.355 - m_SingleTimers.insert(iter, pTimer);
1.356 - goto return_timer;
1.357 - }
1.358 - }
1.359 -
1.360 -normal_insert_end:
1.361 - m_SingleTimers.push_back(pTimer);
1.362 -
1.363 -return_timer:
1.364 - return pTimer;
1.365 -}
1.366 -
1.367 -void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
1.368 -{
1.369 - ResultType res;
1.370 -
1.371 - if (pTimer->m_InExec)
1.372 - {
1.373 - return;
1.374 - }
1.375 -
1.376 - pTimer->m_InExec = true;
1.377 - res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
1.378 -
1.379 - if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
1.380 - {
1.381 - pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.382 - m_SingleTimers.remove(pTimer);
1.383 - m_FreeTimers.push(pTimer);
1.384 - }
1.385 - else
1.386 - {
1.387 - if ((res != Pl_Stop) && !pTimer->m_KillMe)
1.388 - {
1.389 - if (delayExec)
1.390 - {
1.391 - pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
1.392 - }
1.393 - pTimer->m_InExec = false;
1.394 - return;
1.395 - }
1.396 - pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.397 - m_LoopTimers.remove(pTimer);
1.398 - m_FreeTimers.push(pTimer);
1.399 - }
1.400 -}
1.401 -
1.402 -void TimerSystem::KillTimer(ITimer *pTimer)
1.403 -{
1.404 - if (pTimer->m_KillMe)
1.405 - {
1.406 - return;
1.407 - }
1.408 -
1.409 - if (pTimer->m_InExec)
1.410 - {
1.411 - pTimer->m_KillMe = true;
1.412 - return;
1.413 - }
1.414 -
1.415 - pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
1.416 - pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.417 -
1.418 - if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
1.419 - {
1.420 - m_LoopTimers.remove(pTimer);
1.421 - } else {
1.422 - m_SingleTimers.remove(pTimer);
1.423 - }
1.424 -
1.425 - m_FreeTimers.push(pTimer);
1.426 -}
1.427 -
1.428 -CStack<ITimer *> s_tokill;
1.429 -void TimerSystem::MapChange(bool real_mapchange)
1.430 -{
1.431 - ITimer *pTimer;
1.432 - TimerIter iter;
1.433 -
1.434 - for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
1.435 - {
1.436 - pTimer = (*iter);
1.437 - if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
1.438 - {
1.439 - s_tokill.push(pTimer);
1.440 - }
1.441 - }
1.442 -
1.443 - for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
1.444 - {
1.445 - pTimer = (*iter);
1.446 - if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE))
1.447 - {
1.448 - s_tokill.push(pTimer);
1.449 - }
1.450 - }
1.451 -
1.452 - while (!s_tokill.empty())
1.453 - {
1.454 - KillTimer(s_tokill.front());
1.455 - s_tokill.pop();
1.456 - }
1.457 -}
1.458 -
1.459 -IMapTimer *TimerSystem::SetMapTimer(IMapTimer *pTimer)
1.460 -{
1.461 - IMapTimer *old = m_pMapTimer;
1.462 -
1.463 - m_pMapTimer = pTimer;
1.464 -
1.465 - if (m_pMapTimer)
1.466 - {
1.467 - m_pMapTimer->SetMapTimerStatus(true);
1.468 - }
1.469 -
1.470 - if (old)
1.471 - {
1.472 - old->SetMapTimerStatus(false);
1.473 - }
1.474 -
1.475 - return old;
1.476 -}
1.477 -
1.478 -IMapTimer *TimerSystem::GetMapTimer()
1.479 -{
1.480 - return m_pMapTimer;
1.481 -}
1.482 -
1.483 -void TimerSystem::MapTimeLeftChanged()
1.484 -{
1.485 - m_pOnMapTimeLeftChanged->Execute(NULL);
1.486 -}
1.487 -
1.488 -void TimerSystem::NotifyOfGameStart(float offset)
1.489 -{
1.490 - g_fGameStartTime = gpGlobals->curtime + offset;
1.491 -}
1.492 -
1.493 -float TimerSystem::GetTickedTime()
1.494 -{
1.495 - return g_fUniversalTime;
1.496 -}
1.497 -
1.498 -bool TimerSystem::GetMapTimeLeft(float *time_left)
1.499 -{
1.500 - if (!m_pMapTimer)
1.501 - {
1.502 - return false;
1.503 - }
1.504 -
1.505 - int time_limit;
1.506 - if (!m_bHasMapSimulatedYet || (time_limit = m_pMapTimer->GetMapTimeLimit()) < 1)
1.507 - {
1.508 - *time_left = -1.0f;
1.509 - }
1.510 - else
1.511 - {
1.512 - *time_left = (g_fGameStartTime + time_limit * 60.0f) - gpGlobals->curtime;
1.513 - }
1.514 -
1.515 - return true;
1.516 -}
1.517 -