1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/core/TimerSys.cpp Sun Mar 30 07:00:22 2008 +0000
1.3 @@ -0,0 +1,487 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourceMod
1.8 + * Copyright (C) 2004-2008 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 +#include "ConVarManager.h"
1.41 +
1.42 +#define TIMER_MIN_ACCURACY 0.1
1.43 +
1.44 +TimerSystem g_Timers;
1.45 +double g_fUniversalTime = 0.0f;
1.46 +float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */
1.47 +double g_fTimerThink = 0.0f; /* Timer's next think time */
1.48 +const double *g_pUniversalTime = &g_fUniversalTime;
1.49 +ConVar *mp_timelimit = NULL;
1.50 +int g_TimeLeftMode = 0;
1.51 +
1.52 +ConVar sm_time_adjustment("sm_time_adjustment", "0", 0, "Adjusts the server time in seconds");
1.53 +
1.54 +inline double GetSimulatedTime()
1.55 +{
1.56 + return g_fUniversalTime;
1.57 +}
1.58 +
1.59 +time_t GetAdjustedTime(time_t *buf)
1.60 +{
1.61 + time_t val = time(NULL) + sm_time_adjustment.GetInt();
1.62 + if (buf)
1.63 + {
1.64 + *buf = val;
1.65 + }
1.66 + return val;
1.67 +}
1.68 +
1.69 +class DefaultMapTimer :
1.70 + public IMapTimer,
1.71 + public SMGlobalClass,
1.72 + public IConVarChangeListener
1.73 +{
1.74 +public:
1.75 + DefaultMapTimer()
1.76 + {
1.77 + m_bInUse = false;
1.78 + }
1.79 +
1.80 + void OnSourceModLevelChange(const char *mapName)
1.81 + {
1.82 + g_fGameStartTime = 0.0f;
1.83 + }
1.84 +
1.85 + int GetMapTimeLimit()
1.86 + {
1.87 + return mp_timelimit->GetInt();
1.88 + }
1.89 +
1.90 + void SetMapTimerStatus(bool enabled)
1.91 + {
1.92 + if (enabled && !m_bInUse)
1.93 + {
1.94 + Enable();
1.95 + }
1.96 + else if (!enabled && m_bInUse)
1.97 + {
1.98 + Disable();
1.99 + }
1.100 + m_bInUse = enabled;
1.101 + }
1.102 +
1.103 + void ExtendMapTimeLimit(int extra_time)
1.104 + {
1.105 + if (extra_time == 0)
1.106 + {
1.107 + mp_timelimit->SetValue(0);
1.108 + return;
1.109 + }
1.110 +
1.111 + extra_time /= 60;
1.112 +
1.113 + mp_timelimit->SetValue(mp_timelimit->GetInt() + extra_time);
1.114 + }
1.115 +
1.116 + void OnConVarChanged(ConVar *pConVar, const char *oldValue, float flOldValue)
1.117 + {
1.118 + g_Timers.MapTimeLeftChanged();
1.119 + }
1.120 +
1.121 +private:
1.122 + void Enable()
1.123 + {
1.124 + g_ConVarManager.AddConVarChangeListener("mp_timelimit", this);
1.125 + }
1.126 +
1.127 + void Disable()
1.128 + {
1.129 + g_ConVarManager.RemoveConVarChangeListener("mp_timelimit", this);
1.130 + }
1.131 +
1.132 +private:
1.133 + bool m_bInUse;
1.134 +} s_DefaultMapTimer;
1.135 +
1.136 +/**
1.137 + * If the ticking process has run amok (should be impossible), we
1.138 + * take care of this by "skipping" the in-between time, to prevent
1.139 + * a bazillion times from firing on accident. This has the result
1.140 + * that a drastic jump in time will continue acting normally. Users
1.141 + * may not expect this, but... I think it is the best solution.
1.142 + */
1.143 +inline double CalcNextThink(double last, float interval)
1.144 +{
1.145 + if (g_fUniversalTime - last - interval <= TIMER_MIN_ACCURACY)
1.146 + {
1.147 + return last + interval;
1.148 + }
1.149 + else
1.150 + {
1.151 + return g_fUniversalTime + interval;
1.152 + }
1.153 +}
1.154 +
1.155 +void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags)
1.156 +{
1.157 + m_Listener = pCallbacks;
1.158 + m_Interval = fInterval;
1.159 + m_ToExec = fToExec;
1.160 + m_pData = pData;
1.161 + m_Flags = flags;
1.162 + m_InExec = false;
1.163 + m_KillMe = false;
1.164 +}
1.165 +
1.166 +TimerSystem::TimerSystem()
1.167 +{
1.168 + m_pMapTimer = NULL;
1.169 + m_bHasMapTickedYet = false;
1.170 + m_bHasMapSimulatedYet = false;
1.171 + m_fLastTickedTime = 0.0f;
1.172 +}
1.173 +
1.174 +TimerSystem::~TimerSystem()
1.175 +{
1.176 + CStack<ITimer *>::iterator iter;
1.177 + for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++)
1.178 + {
1.179 + delete (*iter);
1.180 + }
1.181 + m_FreeTimers.popall();
1.182 +}
1.183 +
1.184 +void TimerSystem::OnSourceModAllInitialized()
1.185 +{
1.186 + g_ShareSys.AddInterface(NULL, this);
1.187 + m_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL);
1.188 + m_pOnMapTimeLeftChanged = g_Forwards.CreateForward("OnMapTimeLeftChanged", ET_Ignore, 0, NULL);
1.189 +}
1.190 +
1.191 +void TimerSystem::OnSourceModGameInitialized()
1.192 +{
1.193 + mp_timelimit = icvar->FindVar("mp_timelimit");
1.194 +
1.195 + if (m_pMapTimer == NULL && mp_timelimit != NULL)
1.196 + {
1.197 + SetMapTimer(&s_DefaultMapTimer);
1.198 + }
1.199 +}
1.200 +
1.201 +void TimerSystem::OnSourceModShutdown()
1.202 +{
1.203 + SetMapTimer(NULL);
1.204 + g_Forwards.ReleaseForward(m_pOnGameFrame);
1.205 + g_Forwards.ReleaseForward(m_pOnMapTimeLeftChanged);
1.206 +}
1.207 +
1.208 +void TimerSystem::OnSourceModLevelEnd()
1.209 +{
1.210 + m_bHasMapTickedYet = false;
1.211 + m_bHasMapSimulatedYet = false;
1.212 +}
1.213 +
1.214 +void TimerSystem::GameFrame(bool simulating)
1.215 +{
1.216 + if (simulating && m_bHasMapTickedYet)
1.217 + {
1.218 + g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime;
1.219 + if (!m_bHasMapSimulatedYet)
1.220 + {
1.221 + m_bHasMapSimulatedYet = true;
1.222 + MapTimeLeftChanged();
1.223 + }
1.224 + }
1.225 + else
1.226 + {
1.227 + g_fUniversalTime += gpGlobals->interval_per_tick;
1.228 + }
1.229 +
1.230 + m_fLastTickedTime = gpGlobals->curtime;
1.231 + m_bHasMapTickedYet = true;
1.232 +
1.233 + if (g_fUniversalTime >= g_fTimerThink)
1.234 + {
1.235 + RunFrame();
1.236 +
1.237 + g_fTimerThink = CalcNextThink(g_fTimerThink, TIMER_MIN_ACCURACY);
1.238 + }
1.239 +
1.240 + RunFrameHooks(simulating);
1.241 +
1.242 + if (m_pOnGameFrame->GetFunctionCount())
1.243 + {
1.244 + m_pOnGameFrame->Execute(NULL);
1.245 + }
1.246 +}
1.247 +
1.248 +void TimerSystem::RunFrame()
1.249 +{
1.250 + ITimer *pTimer;
1.251 + TimerIter iter;
1.252 +
1.253 + double curtime = GetSimulatedTime();
1.254 + for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); )
1.255 + {
1.256 + pTimer = (*iter);
1.257 + if (curtime >= pTimer->m_ToExec)
1.258 + {
1.259 + pTimer->m_InExec = true;
1.260 + pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
1.261 + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.262 + iter = m_SingleTimers.erase(iter);
1.263 + m_FreeTimers.push(pTimer);
1.264 + }
1.265 + else
1.266 + {
1.267 + break;
1.268 + }
1.269 + }
1.270 +
1.271 + ResultType res;
1.272 + for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); )
1.273 + {
1.274 + pTimer = (*iter);
1.275 + if (curtime >= pTimer->m_ToExec)
1.276 + {
1.277 + pTimer->m_InExec = true;
1.278 + res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
1.279 + if (pTimer->m_KillMe || (res == Pl_Stop))
1.280 + {
1.281 + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.282 + iter = m_LoopTimers.erase(iter);
1.283 + m_FreeTimers.push(pTimer);
1.284 + continue;
1.285 + }
1.286 + pTimer->m_InExec = false;
1.287 + pTimer->m_ToExec = CalcNextThink(pTimer->m_ToExec, pTimer->m_Interval);
1.288 + }
1.289 + iter++;
1.290 + }
1.291 +}
1.292 +
1.293 +ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags)
1.294 +{
1.295 + ITimer *pTimer;
1.296 + TimerIter iter;
1.297 + float to_exec = GetSimulatedTime() + fInterval;
1.298 +
1.299 + if (m_FreeTimers.empty())
1.300 + {
1.301 + pTimer = new ITimer;
1.302 + } else {
1.303 + pTimer = m_FreeTimers.front();
1.304 + m_FreeTimers.pop();
1.305 + }
1.306 +
1.307 + pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags);
1.308 +
1.309 + if (flags & TIMER_FLAG_REPEAT)
1.310 + {
1.311 + m_LoopTimers.push_back(pTimer);
1.312 + goto return_timer;
1.313 + }
1.314 +
1.315 + if (m_SingleTimers.size() >= 1)
1.316 + {
1.317 + iter = --m_SingleTimers.end();
1.318 + if ((*iter)->m_ToExec <= to_exec)
1.319 + {
1.320 + goto normal_insert_end;
1.321 + }
1.322 + }
1.323 +
1.324 + for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
1.325 + {
1.326 + if ((*iter)->m_ToExec >= to_exec)
1.327 + {
1.328 + m_SingleTimers.insert(iter, pTimer);
1.329 + goto return_timer;
1.330 + }
1.331 + }
1.332 +
1.333 +normal_insert_end:
1.334 + m_SingleTimers.push_back(pTimer);
1.335 +
1.336 +return_timer:
1.337 + return pTimer;
1.338 +}
1.339 +
1.340 +void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec)
1.341 +{
1.342 + ResultType res;
1.343 +
1.344 + if (pTimer->m_InExec)
1.345 + {
1.346 + return;
1.347 + }
1.348 +
1.349 + pTimer->m_InExec = true;
1.350 + res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData);
1.351 +
1.352 + if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT))
1.353 + {
1.354 + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.355 + m_SingleTimers.remove(pTimer);
1.356 + m_FreeTimers.push(pTimer);
1.357 + }
1.358 + else
1.359 + {
1.360 + if ((res != Pl_Stop) && !pTimer->m_KillMe)
1.361 + {
1.362 + if (delayExec)
1.363 + {
1.364 + pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval;
1.365 + }
1.366 + pTimer->m_InExec = false;
1.367 + return;
1.368 + }
1.369 + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.370 + m_LoopTimers.remove(pTimer);
1.371 + m_FreeTimers.push(pTimer);
1.372 + }
1.373 +}
1.374 +
1.375 +void TimerSystem::KillTimer(ITimer *pTimer)
1.376 +{
1.377 + if (pTimer->m_KillMe)
1.378 + {
1.379 + return;
1.380 + }
1.381 +
1.382 + if (pTimer->m_InExec)
1.383 + {
1.384 + pTimer->m_KillMe = true;
1.385 + return;
1.386 + }
1.387 +
1.388 + pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */
1.389 + pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData);
1.390 +
1.391 + if (pTimer->m_Flags & TIMER_FLAG_REPEAT)
1.392 + {
1.393 + m_LoopTimers.remove(pTimer);
1.394 + } else {
1.395 + m_SingleTimers.remove(pTimer);
1.396 + }
1.397 +
1.398 + m_FreeTimers.push(pTimer);
1.399 +}
1.400 +
1.401 +CStack<ITimer *> s_tokill;
1.402 +void TimerSystem::RemoveMapChangeTimers()
1.403 +{
1.404 + ITimer *pTimer;
1.405 + TimerIter iter;
1.406 +
1.407 + for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++)
1.408 + {
1.409 + pTimer = (*iter);
1.410 + if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
1.411 + {
1.412 + s_tokill.push(pTimer);
1.413 + }
1.414 + }
1.415 +
1.416 + for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++)
1.417 + {
1.418 + pTimer = (*iter);
1.419 + if (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)
1.420 + {
1.421 + s_tokill.push(pTimer);
1.422 + }
1.423 + }
1.424 +
1.425 + while (!s_tokill.empty())
1.426 + {
1.427 + KillTimer(s_tokill.front());
1.428 + s_tokill.pop();
1.429 + }
1.430 +}
1.431 +
1.432 +IMapTimer *TimerSystem::SetMapTimer(IMapTimer *pTimer)
1.433 +{
1.434 + IMapTimer *old = m_pMapTimer;
1.435 +
1.436 + m_pMapTimer = pTimer;
1.437 +
1.438 + if (m_pMapTimer)
1.439 + {
1.440 + m_pMapTimer->SetMapTimerStatus(true);
1.441 + }
1.442 +
1.443 + if (old)
1.444 + {
1.445 + old->SetMapTimerStatus(false);
1.446 + }
1.447 +
1.448 + return old;
1.449 +}
1.450 +
1.451 +IMapTimer *TimerSystem::GetMapTimer()
1.452 +{
1.453 + return m_pMapTimer;
1.454 +}
1.455 +
1.456 +void TimerSystem::MapTimeLeftChanged()
1.457 +{
1.458 + m_pOnMapTimeLeftChanged->Execute(NULL);
1.459 +}
1.460 +
1.461 +void TimerSystem::NotifyOfGameStart(float offset)
1.462 +{
1.463 + g_fGameStartTime = gpGlobals->curtime + offset;
1.464 +}
1.465 +
1.466 +float TimerSystem::GetTickedTime()
1.467 +{
1.468 + return g_fUniversalTime;
1.469 +}
1.470 +
1.471 +bool TimerSystem::GetMapTimeLeft(float *time_left)
1.472 +{
1.473 + if (!m_pMapTimer)
1.474 + {
1.475 + return false;
1.476 + }
1.477 +
1.478 + int time_limit;
1.479 + if (!m_bHasMapSimulatedYet || (time_limit = m_pMapTimer->GetMapTimeLimit()) < 1)
1.480 + {
1.481 + *time_left = -1.0f;
1.482 + }
1.483 + else
1.484 + {
1.485 + *time_left = (g_fGameStartTime + time_limit * 60.0f) - gpGlobals->curtime;
1.486 + }
1.487 +
1.488 + return true;
1.489 +}
1.490 +