dvander@592: /** ds@653: * vim: set ts=4 : ds@1330: * ============================================================================= dvander@1231: * SourceMod dvander@1231: * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. ds@1330: * ============================================================================= ds@653: * ds@1330: * This program is free software; you can redistribute it and/or modify it under ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the ds@1330: * Free Software Foundation. dvander@1231: * ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1330: * details. dvander@1231: * ds@1330: * You should have received a copy of the GNU General Public License along with ds@1330: * this program. If not, see . dvander@1231: * ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1330: * this exception to all derivative works. AlliedModders LLC defines further ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1330: * or . ds@653: * ds@653: * Version: $Id$ ds@653: */ dvander@592: dvander@1371: #include dvander@592: #include "TimerSys.h" dvander@1412: #include "ForwardSys.h" dvander@1371: #include "sourcemm_api.h" dvander@1412: #include "frame_hooks.h" dvander@592: dvander@1414: SH_DECL_HOOK2_void(ICvar, CallGlobalChangeCallback, SH_NOATTRIB, false, ConVar *, const char *); dvander@1414: dvander@616: TimerSystem g_Timers; dvander@1412: float g_fUniversalTime = 0.0f; dvander@1459: float g_fGameStartTime = 0.0f; /* Game game start time, non-universal */ dvander@1412: const float *g_pUniversalTime = &g_fUniversalTime; dvander@1414: ConVar *mp_timelimit = NULL; dvander@1459: int g_TimeLeftMode = 0; dvander@727: dvander@1371: ConVar sm_time_adjustment("sm_time_adjustment", "0", 0, "Adjusts the server time in seconds"); dvander@1371: dvander@1412: inline float GetSimulatedTime() dvander@1412: { dvander@1412: return g_fUniversalTime; dvander@1412: } dvander@1410: dvander@1371: time_t GetAdjustedTime(time_t *buf) dvander@1371: { dvander@1371: time_t val = time(NULL) + sm_time_adjustment.GetInt(); dvander@1371: if (buf) dvander@1371: { dvander@1371: *buf = val; dvander@1371: } dvander@1371: return val; dvander@1371: } dvander@1371: dvander@1414: class DefaultMapTimer : dvander@1414: public IMapTimer, dvander@1414: public SMGlobalClass dvander@1414: { dvander@1414: public: dvander@1414: DefaultMapTimer() dvander@1414: { dvander@1414: m_bInUse = false; dvander@1414: } dvander@1414: dvander@1459: void OnSourceModLevelChange(const char *mapName) dvander@1459: { dvander@1459: g_fGameStartTime = 0.0f; dvander@1459: } dvander@1459: dvander@1414: int GetMapTimeLimit() dvander@1414: { dvander@1414: return mp_timelimit->GetInt(); dvander@1414: } dvander@1414: dvander@1414: void SetMapTimerStatus(bool enabled) dvander@1414: { dvander@1414: if (enabled && !m_bInUse) dvander@1414: { dvander@1414: Enable(); dvander@1414: } dvander@1414: else if (!enabled && m_bInUse) dvander@1414: { dvander@1414: Disable(); dvander@1414: } dvander@1414: m_bInUse = enabled; dvander@1414: } dvander@1414: dvander@1414: void ExtendMapTimeLimit(int extra_time) dvander@1414: { dvander@1414: extra_time /= 60; dvander@1414: dvander@1414: if (extra_time == 0) dvander@1414: { dvander@1414: mp_timelimit->SetValue(0); dvander@1414: return; dvander@1414: } dvander@1414: dvander@1414: mp_timelimit->SetValue(mp_timelimit->GetInt() + extra_time); dvander@1414: } dvander@1414: dvander@1414: void GlobalChangeCallback(ConVar *pVar, const char *old_value) dvander@1414: { dvander@1414: if (pVar != mp_timelimit) dvander@1414: { dvander@1414: return; dvander@1414: } dvander@1414: dvander@1415: if (atoi(old_value) == pVar->GetInt()) dvander@1415: { dvander@1415: return; dvander@1415: } dvander@1415: dvander@1415: g_Timers.MapTimeLeftChanged(); dvander@1414: } dvander@1414: dvander@1414: private: dvander@1414: void Enable() dvander@1414: { dvander@1414: SH_ADD_HOOK_MEMFUNC(ICvar, CallGlobalChangeCallback, icvar, this, &DefaultMapTimer::GlobalChangeCallback, false); dvander@1414: } dvander@1414: dvander@1414: void Disable() dvander@1414: { dvander@1414: SH_REMOVE_HOOK_MEMFUNC(ICvar, CallGlobalChangeCallback, icvar, this, &DefaultMapTimer::GlobalChangeCallback, false); dvander@1414: } dvander@1414: dvander@1414: private: dvander@1414: bool m_bInUse; dvander@1414: } s_DefaultMapTimer; dvander@1414: dvander@592: void ITimer::Initialize(ITimedEvent *pCallbacks, float fInterval, float fToExec, void *pData, int flags) dvander@592: { dvander@592: m_Listener = pCallbacks; dvander@592: m_Interval = fInterval; dvander@592: m_ToExec = fToExec; dvander@592: m_pData = pData; dvander@592: m_Flags = flags; dvander@592: m_InExec = false; dvander@592: m_KillMe = false; dvander@592: } dvander@592: dvander@1408: TimerSystem::TimerSystem() dvander@1408: { dvander@1414: m_pMapTimer = NULL; dvander@1412: m_bHasMapTickedYet = false; dvander@1462: m_bHasMapSimulatedYet = false; dvander@1412: m_fLastTickedTime = 0.0f; dvander@1412: m_LastExecTime = 0.0f; dvander@1408: } dvander@1408: faluco@641: TimerSystem::~TimerSystem() faluco@641: { faluco@641: CStack::iterator iter; faluco@641: for (iter=m_FreeTimers.begin(); iter!=m_FreeTimers.end(); iter++) faluco@641: { faluco@641: delete (*iter); faluco@641: } faluco@641: m_FreeTimers.popall(); faluco@641: } faluco@641: dvander@616: void TimerSystem::OnSourceModAllInitialized() dvander@592: { dvander@592: g_ShareSys.AddInterface(NULL, this); dvander@1412: m_pOnGameFrame = g_Forwards.CreateForward("OnGameFrame", ET_Ignore, 0, NULL); dvander@1459: m_pOnMapTimeLeftChanged = g_Forwards.CreateForward("OnMapTimeLeftChanged", ET_Ignore, 0, NULL); dvander@1412: } dvander@1412: dvander@1414: void TimerSystem::OnSourceModGameInitialized() dvander@1414: { dvander@1414: mp_timelimit = icvar->FindVar("mp_timelimit"); dvander@1414: dvander@1414: if (m_pMapTimer == NULL && mp_timelimit != NULL) dvander@1414: { dvander@1414: SetMapTimer(&s_DefaultMapTimer); dvander@1414: } dvander@1414: } dvander@1414: dvander@1412: void TimerSystem::OnSourceModShutdown() dvander@1412: { dvander@1414: SetMapTimer(NULL); dvander@1412: g_Forwards.ReleaseForward(m_pOnGameFrame); dvander@1459: g_Forwards.ReleaseForward(m_pOnMapTimeLeftChanged); dvander@592: } dvander@592: dvander@673: void TimerSystem::OnSourceModLevelChange(const char *mapName) dvander@673: { dvander@1142: MapChange(true); dvander@673: } dvander@673: dvander@1412: void TimerSystem::OnSourceModLevelEnd() dvander@1412: { dvander@1412: m_bHasMapTickedYet = false; dvander@1462: m_bHasMapSimulatedYet = false; dvander@1412: } dvander@1412: dvander@1412: void TimerSystem::GameFrame(bool simulating) dvander@1412: { dvander@1412: if (simulating && m_bHasMapTickedYet) dvander@1412: { dvander@1412: g_fUniversalTime += gpGlobals->curtime - m_fLastTickedTime; dvander@1462: if (!m_bHasMapSimulatedYet) dvander@1462: { dvander@1462: m_bHasMapSimulatedYet = true; dvander@1462: MapTimeLeftChanged(); dvander@1462: } dvander@1412: } dvander@1412: else dvander@1412: { dvander@1412: g_fUniversalTime += gpGlobals->interval_per_tick; dvander@1412: } dvander@1412: dvander@1412: m_fLastTickedTime = gpGlobals->curtime; dvander@1459: m_bHasMapTickedYet = true; dvander@1412: dvander@1412: if (g_fUniversalTime - m_LastExecTime >= 0.1) dvander@1412: { dvander@1412: RunFrame(); dvander@1412: } dvander@1412: dvander@1412: RunFrameHooks(); dvander@1412: dvander@1412: if (m_pOnGameFrame->GetFunctionCount()) dvander@1412: { dvander@1412: m_pOnGameFrame->Execute(NULL); dvander@1412: } dvander@1412: } dvander@1412: dvander@616: void TimerSystem::RunFrame() dvander@592: { dvander@592: ITimer *pTimer; dvander@592: TimerIter iter; dvander@592: dvander@727: float curtime = GetSimulatedTime(); dvander@592: for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); ) dvander@592: { dvander@592: pTimer = (*iter); dvander@727: if (curtime >= pTimer->m_ToExec) dvander@592: { dvander@592: pTimer->m_InExec = true; dvander@592: pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); faluco@612: pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); dvander@592: iter = m_SingleTimers.erase(iter); dvander@592: m_FreeTimers.push(pTimer); dvander@592: } else { dvander@592: break; dvander@592: } dvander@592: } dvander@592: dvander@592: ResultType res; dvander@592: for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); ) dvander@592: { dvander@592: pTimer = (*iter); dvander@727: if (curtime >= pTimer->m_ToExec) dvander@592: { dvander@592: pTimer->m_InExec = true; dvander@592: res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); dvander@592: if (pTimer->m_KillMe || (res == Pl_Stop)) dvander@592: { dvander@592: pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); dvander@592: iter = m_LoopTimers.erase(iter); dvander@592: m_FreeTimers.push(pTimer); dvander@592: continue; dvander@592: } dvander@592: pTimer->m_InExec = false; dvander@727: pTimer->m_ToExec = curtime + pTimer->m_Interval; dvander@592: } dvander@592: iter++; dvander@592: } dvander@592: dvander@727: m_LastExecTime = curtime; dvander@592: } dvander@592: dvander@616: ITimer *TimerSystem::CreateTimer(ITimedEvent *pCallbacks, float fInterval, void *pData, int flags) dvander@592: { dvander@592: ITimer *pTimer; dvander@592: TimerIter iter; dvander@727: float to_exec = GetSimulatedTime() + fInterval; dvander@592: dvander@592: if (m_FreeTimers.empty()) dvander@592: { dvander@592: pTimer = new ITimer; dvander@592: } else { dvander@592: pTimer = m_FreeTimers.front(); dvander@592: m_FreeTimers.pop(); dvander@592: } dvander@592: dvander@592: pTimer->Initialize(pCallbacks, fInterval, to_exec, pData, flags); dvander@592: dvander@592: if (flags & TIMER_FLAG_REPEAT) dvander@592: { dvander@592: m_LoopTimers.push_back(pTimer); dvander@592: goto return_timer; dvander@592: } dvander@592: dvander@1414: if (m_SingleTimers.size() >= 1) dvander@592: { dvander@1414: iter = --m_SingleTimers.end(); dvander@1414: if ((*iter)->m_ToExec <= to_exec) dvander@592: { dvander@1414: goto normal_insert_end; dvander@592: } dvander@1414: } dvander@1414: dvander@1414: for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++) dvander@1414: { dvander@1414: if ((*iter)->m_ToExec >= to_exec) dvander@1407: { dvander@1414: m_SingleTimers.insert(iter, pTimer); dvander@1414: goto return_timer; dvander@1407: } dvander@1414: } dvander@1407: dvander@1407: normal_insert_end: dvander@1414: m_SingleTimers.push_back(pTimer); dvander@592: dvander@592: return_timer: dvander@592: return pTimer; dvander@592: } dvander@592: dvander@616: void TimerSystem::FireTimerOnce(ITimer *pTimer, bool delayExec) dvander@592: { dvander@592: ResultType res; dvander@592: dvander@592: if (pTimer->m_InExec) dvander@592: { dvander@592: return; dvander@592: } dvander@592: dvander@592: pTimer->m_InExec = true; dvander@592: res = pTimer->m_Listener->OnTimer(pTimer, pTimer->m_pData); dvander@592: dvander@592: if (!(pTimer->m_Flags & TIMER_FLAG_REPEAT)) dvander@592: { dvander@592: pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); dvander@1414: m_SingleTimers.remove(pTimer); dvander@592: m_FreeTimers.push(pTimer); dvander@592: } else { faluco@612: if ((res != Pl_Stop) && !pTimer->m_KillMe) dvander@592: { faluco@612: if (delayExec) faluco@612: { dvander@727: pTimer->m_ToExec = GetSimulatedTime() + pTimer->m_Interval; faluco@612: } dvander@592: pTimer->m_InExec = false; dvander@592: return; dvander@592: } dvander@592: pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); dvander@592: m_LoopTimers.remove(pTimer); dvander@592: m_FreeTimers.push(pTimer); dvander@592: } dvander@592: } dvander@592: dvander@616: void TimerSystem::KillTimer(ITimer *pTimer) dvander@592: { dvander@592: if (pTimer->m_KillMe) dvander@592: { dvander@592: return; dvander@592: } dvander@592: dvander@592: if (pTimer->m_InExec) dvander@592: { dvander@592: pTimer->m_KillMe = true; dvander@592: return; dvander@592: } dvander@592: dvander@592: pTimer->m_InExec = true; /* The timer it's not really executed but this check needs to be done */ dvander@592: pTimer->m_Listener->OnTimerEnd(pTimer, pTimer->m_pData); dvander@592: dvander@1407: if (pTimer->m_Flags & TIMER_FLAG_REPEAT) dvander@1407: { dvander@1407: m_LoopTimers.remove(pTimer); dvander@1407: } else { dvander@1407: m_SingleTimers.remove(pTimer); dvander@1407: } dvander@592: dvander@592: m_FreeTimers.push(pTimer); dvander@592: } dvander@592: dvander@1108: CStack s_tokill; dvander@1142: void TimerSystem::MapChange(bool real_mapchange) dvander@592: { dvander@592: ITimer *pTimer; dvander@592: TimerIter iter; dvander@592: dvander@592: for (iter=m_SingleTimers.begin(); iter!=m_SingleTimers.end(); iter++) dvander@592: { dvander@592: pTimer = (*iter); dvander@1142: if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)) dvander@1108: { dvander@1108: s_tokill.push(pTimer); dvander@1108: } dvander@592: } dvander@592: dvander@592: for (iter=m_LoopTimers.begin(); iter!=m_LoopTimers.end(); iter++) dvander@592: { dvander@592: pTimer = (*iter); dvander@1142: if (real_mapchange && (pTimer->m_Flags & TIMER_FLAG_NO_MAPCHANGE)) dvander@1108: { dvander@1108: s_tokill.push(pTimer); dvander@1108: } dvander@1108: } dvander@1108: dvander@1108: while (!s_tokill.empty()) dvander@1108: { dvander@1108: KillTimer(s_tokill.front()); dvander@1108: s_tokill.pop(); dvander@592: } dvander@592: } dvander@1407: dvander@1414: IMapTimer *TimerSystem::SetMapTimer(IMapTimer *pTimer) dvander@1407: { dvander@1414: IMapTimer *old = m_pMapTimer; dvander@1414: dvander@1414: m_pMapTimer = pTimer; dvander@1414: dvander@1414: if (m_pMapTimer) dvander@1414: { dvander@1414: m_pMapTimer->SetMapTimerStatus(true); dvander@1414: } dvander@1414: dvander@1414: if (old) dvander@1414: { dvander@1414: old->SetMapTimerStatus(false); dvander@1414: } dvander@1414: dvander@1407: return old; dvander@1407: } dvander@1407: dvander@1414: IMapTimer *TimerSystem::GetMapTimer() dvander@1414: { dvander@1414: return m_pMapTimer; dvander@1414: } dvander@1414: dvander@1415: void TimerSystem::MapTimeLeftChanged() dvander@1414: { dvander@1459: m_pOnMapTimeLeftChanged->Execute(NULL); dvander@1459: } dvander@1415: dvander@1459: void TimerSystem::NotifyOfGameStart(float offset) dvander@1459: { dvander@1459: g_fGameStartTime = gpGlobals->curtime + offset; dvander@1414: } dvander@1415: dvander@1415: float TimerSystem::GetTickedTime() dvander@1415: { dvander@1415: return g_fUniversalTime; dvander@1415: } dvander@1459: dvander@1459: bool TimerSystem::GetMapTimeLeft(float *time_left) dvander@1459: { dvander@1462: if (!m_pMapTimer) dvander@1459: { dvander@1459: return false; dvander@1459: } dvander@1459: dvander@1462: int time_limit; dvander@1462: if (!m_bHasMapSimulatedYet || (time_limit = m_pMapTimer->GetMapTimeLimit()) < 1) dvander@1462: { dvander@1462: *time_left = -1.0f; dvander@1462: } dvander@1462: else dvander@1462: { dvander@1462: *time_left = (g_fGameStartTime + time_limit * 60.0f) - gpGlobals->curtime; dvander@1462: } dvander@1459: dvander@1459: return true; dvander@1459: } dvander@1462: