hostile-takeover/game/timer.cpp
2014-07-06 17:47:28 -07:00

246 lines
5.4 KiB
C++

#include "ht.h"
#include "base/messagequeue.h"
namespace wi {
//#define TIME_STEPPER
#ifdef TIME_STEPPER
// This makes time "stop" when in the debugger
class TimeStepper : public base::MessageHandler {
public:
TimeStepper() : m_ct(0), m_fInitialized(false) {}
long GetTickCount() {
if (!m_fInitialized) {
OneShot();
m_fInitialized = true;
}
return m_ct;
}
private:
void OneShot() {
base::Message msg;
msg.handler = this;
thread_.PostDelayed(&msg, 1);
}
virtual void OnMessage(base::Message *pmsg) {
m_ct++;
OneShot();
}
long m_ct;
bool m_fInitialized;
};
#endif
TimerMgr::TimerMgr()
{
m_fEnabled = true;
m_ptmrFirst = NULL;
m_ptmrNotifying = NULL;
m_ptmrTriggerNext = NULL;
m_fForceScan = true;
}
TimerMgr::~TimerMgr()
{
Assert(m_ptmrFirst == NULL);
}
long TimerMgr::GetTickCount()
{
#ifdef TIME_STEPPER
// Makes time stop while in the debugger, for easier debugging
static TimeStepper stepper;
return stepper.GetTickCount();
#else
return HostGetTickCount();
#endif
}
long TimerMgr::ScanDispatch(long tCurrent)
{
if (!m_fEnabled)
return -1;
// If currently scanning and being re-entered, disallow it.
Assert(m_ptmrNotifying == NULL);
if (m_ptmrNotifying != NULL)
return -1;
// If the timer state hasn't changed since the last scan, we can exit if not
// enough time has elapsed for a timer to go off.
if (!m_fForceScan) {
if (m_ptmrTriggerNext != NULL) {
long ctT = m_ptmrTriggerNext->m_tTrigger - tCurrent;
if (ctT > 0)
return ctT;
}
}
m_fForceScan = false;
// Scan timers to look for next timer to go off.
// Unfortunately these can't be sorted since the firing order is unique for any
// point in time.
Timer *ptmrT;
for (ptmrT = m_ptmrFirst; ptmrT != NULL; ) {
// If the timer has triggered, set its next trigger time to its current
// trigger time plus its rate. This is an attempt at keeping timing
// consistent and predictable. However, if this new time is less than
// or equal to the current real time we set it to the real time so we
// don't get stuck in a loop only processing timers.
bool fRemoved = false;
Timer *ptmrNext = ptmrT->m_ptmrNext;
if (ptmrT->m_tTrigger <= tCurrent) {
// Remember that this timer is notifying.
m_ptmrNotifying = ptmrT;
ptmrT->OnTimer(tCurrent);
m_ptmrNotifying = NULL;
fRemoved = ptmrT->m_tTrigger == -1;
if (!fRemoved) {
// Reset the rate after notification in case the rate was
// changed during notification.
ptmrT->m_tTrigger += ptmrT->m_ctRate;
// If the timer is falling behind real time catch it up
tCurrent = GetTickCount();
if (ptmrT->m_tTrigger < tCurrent)
ptmrT->m_tTrigger = tCurrent;
}
}
ptmrT = ptmrNext;
}
// Determine the next timer to trigger. This is done outside the
// above loop because timers, including the m_ptmrTriggerNext, may
// be removed during the OnTimer callback.
m_ptmrTriggerNext = m_ptmrFirst;
for (ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) {
if (ptmrT->m_tTrigger < m_ptmrTriggerNext->m_tTrigger)
m_ptmrTriggerNext = ptmrT;
}
// Return time till next timer
if (m_ptmrTriggerNext == NULL)
return -1;
long ct = tCurrent - m_ptmrTriggerNext->m_tTrigger;
if (ct < 0)
return 0;
return ct;
}
void TimerMgr::AddTimer(Timer *ptmr, long ct)
{
for (Timer *ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) {
Assert(ptmrT != ptmr, "Timer already added!");
if (ptmrT == ptmr)
return;
}
// Link it into the list
if (m_ptmrFirst != NULL)
m_ptmrFirst->m_ptmrPrev = ptmr;
ptmr->m_ptmrPrev = NULL;
ptmr->m_ptmrNext = m_ptmrFirst;
ptmr->m_ptimm = this;
m_ptmrFirst = ptmr;
SetTimerRate(ptmr, ct);
}
void TimerMgr::RemoveTimer(Timer *ptmr)
{
bool fFound = false;
for (Timer *ptmrT = m_ptmrFirst; ptmrT != NULL; ptmrT = ptmrT->m_ptmrNext) {
if (ptmrT == ptmr) {
fFound = true;
break;
}
}
Assert(fFound, "Timer not on list! (already removed?)");
if (!fFound)
return;
ptmr->m_tTrigger = -1;
ptmr->m_ptimm = NULL;
if (ptmr->m_ptmrPrev != NULL) {
ptmr->m_ptmrPrev->m_ptmrNext = ptmr->m_ptmrNext;
} else {
m_ptmrFirst = ptmr->m_ptmrNext;
}
if (ptmr->m_ptmrNext != NULL)
ptmr->m_ptmrNext->m_ptmrPrev = ptmr->m_ptmrPrev;
if (m_ptmrNotifying != ptmr && m_ptmrTriggerNext == ptmr) {
m_ptmrTriggerNext = NULL;
m_fForceScan = true;
}
}
void TimerMgr::SetTimerRate(Timer *ptmr, long ct)
{
// If the timer rate is decreasing, this may change the time till the next
// timer goes off, so force a scan next time.
ptmr->m_ctRate = ct;
ptmr->m_tTrigger = GetTickCount() + ct;
m_fForceScan = true;
}
void TimerMgr::BoostTimer(Timer *ptmr, long ct) {
ptmr->m_tTrigger += ct;
m_fForceScan = true;
}
// Force this timer to be dispatched at the next call to TimerMgr::ScanDispatch
// (presumably by EventMgr::GetEvent)
void TimerMgr::TriggerTimer(Timer *ptmr)
{
ptmr->m_tTrigger = GetTickCount();
m_fForceScan = true;
}
bool TimerMgr::IsAdded(Timer *ptmr)
{
Timer *ptmrT = m_ptmrFirst;
while (ptmrT != NULL) {
if (ptmrT == ptmr) {
break;
}
ptmrT = ptmrT->m_ptmrNext;
}
return ptmrT == ptmr;
}
Timer::~Timer() {
if (m_ptimm != NULL && m_ptimm->IsAdded(this)) {
Assert("Timer being destroyed, but still in list!");
}
}
} // namespace wi