// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
/*
Intro:
------
This file contains a complete timer system.
It is robust and can be used widely.
The user creates classes derived from CTimerEvent to represent their event handlers.
When the classes' set() or setRemaining() methods are called an entry is added to the event system provoking
a callback of the user's timerCallback() method at the given time.
If a CTimer object is destroyed its callback is automatically canceled.
A single timer may only have a single time set for it. Setting the time more than once will cancel
the previous time.
NOTE: The only requirement for the system is that the CTimerManager::tickUpdate() method is called every tick
Example 1: - a class containing a timer
----------
class CMyClass
{
public:
void startTimer()
{
// set the timer to trigger in 50 ticks
_Timer.setRemaining(50);
}
void update()
{
if (_Timer.isActive())
{
nlinfo("time remaining: ",_Timer.getTimeRemaining());
}
else
{
// set the timer to trigger in 20 ticks
_Timer.setRemaining(20);
}
}
private:
CTimer _Timer;
};
Example 2: - a class using a timer and a custom event handler to trigger regular actions
----------
class CMyClass;
class CMyTimerEvent;
class CMyTimerEvent: public CTimerEvent
{
public:
CMyTimerEvent(CMyClass* parent);
void timerCallback(CTimer* owner);
private:
CMyClass* _Parent;
}
class CMyClass
{
public:
void startTimer()
{
// set the timer to trigger in 50 ticks
_Timer.setRemaining(50,new CMyTimerEvent(this));
}
void doSomething(CTimerEvent* event)
{
nlinfo("hello world");
// set the timer to trigger in 20 ticks with the same event handler
_Timer.setRemaining(20,event);
}
private:
CMyTimer _Timer;
};
void CMyTimerEvent::CMyTimerEvent(CMyClass* parent)
{
_Parent=parent;
}
void CMyTimerEvent::timerCallback(CTimer* owner)
{
_Parent->doSomething(this);
}
*/
#ifndef TIMER_H
#define TIMER_H
//-------------------------------------------------------------------------------------------------
// includes
//-------------------------------------------------------------------------------------------------
// stl
#include
// misc
#include "nel/misc/types_nl.h"
#include "nel/misc/time_nl.h"
#include "nel/misc/common.h"
#include "nel/misc/smart_ptr.h"
// game share
#include "utils.h"
#include "tick_event_handler.h"
#include "singleton_registry.h"
//-------------------------------------------------------------------------------------------------
// forward class declarations
//-------------------------------------------------------------------------------------------------
class CTimer;
class CTimerEvent;
class CTimerManager;
//-------------------------------------------------------------------------------------------------
// class CTimer
//-------------------------------------------------------------------------------------------------
// generic timer class
class CTimer
{
public:
// ctor and dtor
CTimer();
virtual ~CTimer();
// set the timer for a given target time & clear the custom event object
void set(NLMISC::TGameCycle time);
// set the timer for somewhere between (time) and (time + variation)
// also clear the custom event object
void set(NLMISC::TGameCycle time,uint32 variation);
// set the timer for a given target time with given custom event handler object
void set(NLMISC::TGameCycle time,CTimerEvent* eventObject);
// set the timer for somewhere between (time) and (time + variation)
// also set given custom event handler object
void set(NLMISC::TGameCycle time,CTimerEvent* eventObject,uint32 variation);
// set the timer for (current time + time) & clear the custom event object
void setRemaining(NLMISC::TGameCycle time);
// set the timer for somewhere between (current time + time) and (current time + time + variation)
// also clear the custom event object
void setRemaining(NLMISC::TGameCycle time,uint32 variation);
// set the timer for (current time + time) with given custom event handler object
void setRemaining(NLMISC::TGameCycle time,CTimerEvent* eventObject);
// set the timer for somewhere between (current time + time) and (current time + time + variation)
// also set given custom event handler object
void setRemaining(NLMISC::TGameCycle time,CTimerEvent* eventObject,uint32 variation);
// test whether timer is running
bool isActive() const;
// reset the timer and clear the custom timer event
void reset();
// get the target time for the timer - return 0 if the event is not active
NLMISC::TGameCycle getTime() const;
// get the time remaining before the target time for the timer - return 0 if the event is not active
NLMISC::TGameCycle getTimeRemaining() const;
// get a pointer to the current custom event object
CTimerEvent* getEvent();
private:
// prohibit copy
CTimer(const CTimer&);
CTimer& operator=(const CTimer&);
// a smart pointer to the timer event - the event is reffed by a second smart ptr from the event system
NLMISC::CSmartPtr _Event;
};
//-------------------------------------------------------------------------------------------------
// class CTimerEvent
//-------------------------------------------------------------------------------------------------
// specialisable timer event class
class CTimerEvent: public NLMISC::CRefCount
{
NL_INSTANCE_COUNTER_DECL(CTimerEvent);
public:
// callback to be specialised
virtual void timerCallback(CTimer* owner) {}
public:
// ctor
CTimerEvent();
// dtor
virtual ~CTimerEvent();
// read accessors
NLMISC::TGameCycle getTime() const;
// get hold of the object that created the event (or NULL if the object has been deleted)
CTimer* getOwner() const;
// return true if the event is active otherwise false
bool isActive() const;
private:
// the following interface is reserved for use by the CTimer class
friend class CTimer;
// setup the event
void set(CTimer* owner,NLMISC::TGameCycle time);
// setup the event, selecting a time between 'time' and 'time'+'variation'
void set(CTimer* owner,NLMISC::TGameCycle time,uint32 variation);
// clear the event - mark it for deletion
void clear();
private:
// the following interface is reserved for use by the CTimerManager class
friend class CTimerManager;
// method used by the event system to call the timer callback and kill off the event
void processEvent();
private:
// the correct time for executing the event
NLMISC::TGameCycle _Time;
// a pointer to the owner object - is NULL if owner object has been deleted or this event has been invalidated
CTimer* _Owner;
};
//-------------------------------------------------------------------------------------------------
// class CTimerManager
//-------------------------------------------------------------------------------------------------
// singleton timer manager
class CTimerManager: public IServiceSingleton
{
public:
// update called each tick in service update
// updates events and cleans out event vectors
virtual void tickUpdate();
// get the singleton instance...
static CTimerManager* getInstance();
// callback called when the tick service connects - used to ajust time values of event objects
void syncTick();
private:
// this is a singleton so prohibit construction
CTimerManager();
// the type of the event vector for a given time hash
typedef std::vector > TEventVector;
// the event vector is filled directly by CTimerEvent objects
friend class CTimerEvent;
// singleton encapsulation of event vector set
TEventVector& getEventVector(NLMISC::TGameCycle time);
// data
NLMISC::TGameCycle _LastTick;
TEventVector _EventVectors[256];
};
//-------------------------------------------------------------------------------------------------
// class CTimer
//-------------------------------------------------------------------------------------------------
inline CTimer::CTimer()
{
}
inline CTimer::~CTimer()
{
reset();
}
inline void CTimer::set(NLMISC::TGameCycle time)
{
set(time,new CTimerEvent);
}
inline void CTimer::set(NLMISC::TGameCycle time,uint32 variation)
{
set(time,new CTimerEvent,variation);
}
inline void CTimer::set(NLMISC::TGameCycle time,CTimerEvent* eventObject)
{
if (_Event!=NULL)
_Event->clear();
_Event= eventObject;
eventObject->set(this,time);
}
inline void CTimer::set(NLMISC::TGameCycle time,CTimerEvent* eventObject,uint32 variation)
{
if (_Event!=NULL)
_Event->clear();
_Event= eventObject;
eventObject->set(this,time,variation);
}
inline void CTimer::setRemaining(NLMISC::TGameCycle time)
{
set(CTickEventHandler::getGameCycle()+time);
}
inline void CTimer::setRemaining(NLMISC::TGameCycle time,uint32 variation)
{
set(CTickEventHandler::getGameCycle()+time,variation);
}
inline void CTimer::setRemaining(NLMISC::TGameCycle time,CTimerEvent* eventObject)
{
set(CTickEventHandler::getGameCycle()+time,eventObject);
}
inline void CTimer::setRemaining(NLMISC::TGameCycle time,CTimerEvent* eventObject,uint32 variation)
{
set(CTickEventHandler::getGameCycle()+time,eventObject,variation);
}
inline void CTimer::reset()
{
if (_Event==NULL)
return;
_Event->clear();
_Event=NULL;
}
inline bool CTimer::isActive() const
{
return (_Event!=NULL) && _Event->isActive();
}
inline NLMISC::TGameCycle CTimer::getTime() const
{
if (!isActive())
return 0;
return _Event->getTime();
}
inline NLMISC::TGameCycle CTimer::getTimeRemaining() const
{
if (!isActive())
return 0;
return getTime()-CTickEventHandler::getGameCycle();
}
inline CTimerEvent* CTimer::getEvent()
{
return _Event;
}
//-------------------------------------------------------------------------------------------------
// inlines CTimerEvent
//-------------------------------------------------------------------------------------------------
inline CTimerEvent::CTimerEvent()
{
_Owner = NULL;
_Time = 0;
}
inline CTimerEvent::~CTimerEvent()
{
}
inline void CTimerEvent::set(CTimer* owner,NLMISC::TGameCycle time)
{
BOMB_IF(owner==NULL,"Impossible to set a timer with a NULL owner",return);
BOMB_IF(_Owner!=NULL && _Owner!=owner,"Attempt to change owner of an active event",return);
_Owner = owner;
_Time = time;
CTimerManager::getInstance()->getEventVector(time).push_back(this);
}
inline void CTimerEvent::set(CTimer* owner,NLMISC::TGameCycle time,uint32 variation)
{
BOMB_IF(variation==0,"shouldn't call this method with variation value of 0", set(owner,time));
BOMB_IF(variation>256,"shouldn't call this method with variation value of >256", variation=256);
BOMB_IF(owner==NULL,"Impossible to set a timer with a NULL owner", return);
BOMB_IF(_Owner!=NULL && _Owner!=owner,"Attempt to change owner of an active event", return);
_Owner = owner;
CTimerManager* mgr=CTimerManager::getInstance();
CTimerManager::TEventVector *best=NULL;
uint32 bestLength=~0u;
for (uint32 i=0;igetEventVector(time+i);
uint32 length=(uint32)vect.size();
if (length<=bestLength)
{
bestLength= length;
best=&vect;
_Time = time + i;
}
}
BOMB_IF(best==NULL,"BUG: This can never happen!",return)
best->push_back(this);
}
inline NLMISC::TGameCycle CTimerEvent::getTime() const
{
return _Time;
}
inline CTimer* CTimerEvent::getOwner() const
{
return _Owner;
}
inline bool CTimerEvent::isActive() const
{
return _Owner!=NULL;
}
inline void CTimerEvent::clear()
{
_Owner=NULL;
}
inline void CTimerEvent::processEvent()
{
CTimer* owner=_Owner;
BOMB_IF(owner==NULL,"Attempt to process an event that no longer has a valid owner",return)
// mark the event as expired - the state may be chnaged during the timer callback...
// NOTE: This operation results in '_Owner' being set to NULL
_Owner->reset();
// call the virtual callback
timerCallback(owner);
}
//-------------------------------------------------------------------------------------------------
// inlines CTimerManager
//-------------------------------------------------------------------------------------------------
inline CTimerManager::CTimerManager()
{
_LastTick= CTickEventHandler::getGameCycle();
}
inline CTimerManager* CTimerManager::getInstance()
{
static CTimerManager* instance= NULL;
if (instance==NULL)
{
instance=new CTimerManager;
}
return instance;
}
inline CTimerManager::TEventVector& CTimerManager::getEventVector(NLMISC::TGameCycle time)
{
return _EventVectors[uint8(time&0xff)];
}
//-------------------------------------------------------------------------------------------------
#endif