遊戲伺服器之定時器
遊戲伺服器之定時器
1.1 簡單介紹
在遊戲中的關於定時任務的應用很多,在此就不舉例了。所謂定時任務,簡單來說就是t毫秒後執行相應的任務。因為這裡用的例子都是博主工作時用到的遊戲伺服器中的,所以相關程式碼會比較詳盡,如若只是瞭解定時器的大概實現,按照博主所標註的順序,只看重點即可。
1.2 相關api介紹
在windows平臺下有兩個相關的函式:
BOOL WINAPI QueryPerformanceFrequency(
Out LARGE_INTEGER *lpFrequency
);
Parameters:lpFrequency [out]
A pointer to a variable that receives the current performance-counter frequency, in counts per second.
從MSDN上的手冊可以看到這個引數,用來接收當前硬體平臺的每秒鐘的計數頻率。博主猜測這個頻率跟晶振頻率CPU頻率啥的有關係。
Return value
the return value is nonzero.If the function fails, the return value is zero.
可以看到函式成功返回非零值,否則返回0
BOOL WINAPI QueryPerformanceCounter(
Out LARGE_INTEGER *lpPerformanceCount
);
Parameters:lpPerformanceCount [out]
A pointer to a variable that receives the current performance-counter value, in counts.
該引數用來接受平臺的計數值。
Return value
返回值也是成功返回一個非零值,否則返回0.
1.3 基本原理
由上可知,有了每秒鐘的計數頻率,和當前計數。那麼我們就可以愉快計算時間間隔了。
時間間隔 = 當前計數 - 之前計數/計數頻率
現在還有一個是需要在一個while迴圈中檢測各項定時器是否到達,在我們的遊戲伺服器中也就是隨著伺服器的心跳檢測。每個心跳的過程中去檢測相關object的的定時器是否到達。
1.4 相關程式碼
標頭檔案.h
class TimerFix
{
public:
TimerFix();
void Restart();
double Elapsed(bool restart=false);
private:
//std::clock_t m_StartTime;
LARGE_INTEGER m_StartTime;//獲取定時器開始計數
LARGE_INTEGER m_Freq; //頻率
LARGE_INTEGER m_EndTime; //獲取當前計數
};
實現.cpp
namespace MMOLibrary
{
TimerFix::TimerFix()
{
QueryPerformanceFrequency(&m_Freq);//獲取頻率
QueryPerformanceCounter(&m_StartTime);//獲取當前計數
m_EndTime = m_StartTime;
}
void TimerFix::Restart()
{
QueryPerformanceCounter(&m_StartTime);//重新計數時,重新獲取計數
m_EndTime = m_StartTime;
}
double TimerFix::Elapsed(bool restart) //獲取時間間隔
{
//std::clock_t c = std::clock();
QueryPerformanceCounter(&m_EndTime);
double dtime = (double)(m_EndTime.QuadPart - m_StartTime.QuadPart) / (double)m_Freq.QuadPart;
if (restart) m_StartTime = m_EndTime;
return dtime;
}
}
1.5應用舉例
下面舉一個栗子,來解釋說明在我們的遊戲中是如何具體使用的。
//相關宣告,便於讀者理解程式碼具體含義
//GamePlayer中成員變數及函式
typedef std::map<int, double> TimerMap;
TimerMap mTimer;
GameServerLib* mGSL;
int SetPlayerTimer(int i,int esp);
//GameServerLi中的成員變數及成員函式
//TimerFix的具體實現上面已有
TimerFix* mTimerFix;
TimerFix* GetTimerFix(){return mTimerFix;}
//mGSL為GamePlayer的基類中的GameServerLib * 型別的成員變數
player:set_timer(10,2000) //player這裡的player是遊戲中的一個GamePlayer的物件
//這裡的第一個引數標識哪個定時器,因為遊戲中可能會有多種定時任務,第二個引數是時間,單位是毫秒
//下面是set_timer的實現
int GamePlayer::SetPlayerTimer(int i,int esp)
{
if( i >=1 && i<= 25)
{
//這裡的基本操作是,先獲取當前時間距離定時器開始計時或者初始化時的時間間隔
//然後加上需要的時間間隔,將相應的時間間隔存進map裡,map的key標識時具體哪個定時器
mTimer[i-1] = mGSL->GetTimerFix()->Elapsed() + esp/1000.0;
if( esp == 0 )
{
mTimer[i-1] = 0;
}
return i;
}
return 0;
}
1.6隨著伺服器心跳,update的時候檢測定時器
下面是我們遊戲中非常詳細的相關實現的具體過程
class Application:
{
virtual void OnUpdate()=0;//純虛擬函式
}
class CGameServerApp :
public LEUD::Application,//繼承過來
void CGameServerApp::OnUpdate()
//相關具體OnUpdate具體實現及有關宣告
//下面根據數字順序理解即可
while(WAIT_OBJECT_0 != WaitForMultipleObjects(size,ev,FALSE,m_HeartBeat)){
__try{
OnUpdate(); //這裡是數字順序 1
RunCommand();
}__except(UnhandledExceptionFilter(GetExceptionInformation())){
}
}
void CGameServerApp::OnUpdate()
{
PERF_NODE("m_pGameServerLib");
//GameServerLib* m_pGameServerLib;
m_pGameServerLib->Update(); //這裡是數字順序 2
}
void GameServerLib::Update()
{
double tim = GetTimerFix()->Elapsed(); //注意這裡
PERF_NODE("mSessionsUpdate");
if( mUpdateTime + mUpdateDuration < tim ) //這裡是數字順序 3
{
mUpdateTime = tim;
SessionUpdateFuntor funtor;
funtor.This = this;
funtor.time = tim;
//這裡是數字順序4
mSessions.erase(std::remove_if(mSessions.begin(),mSessions.end(),funtor),mSessions.end());
}
}
class SessionUpdateFuntor
{
public:
GameServerLib* This;
double time;
bool operator() (GameSession* & session)
{
//需要判斷當前session是否斷開連線
if( session->GetState()==GameSession::SESSION_STATE_IS_END ) //
{
This->DestroySession(session);
return true;
}
session->Update(time); //這裡是數字順序5
return false;
}
};
void GameSession::Update(double time)
{
if( mState != SESSION_STATE_IS_RUN ) return ;
mPlayer->Update(time); //這裡是數字順序6
}
void GamePlayer::Update(double ti)
{
{
PERF_NODE("mTimer");
for( TimerMap::iterator pos = mTimer.begin(); //這裡的mTimer就是我們當初set_timer時所用的
pos != mTimer.end();
++ pos )
{
if( pos->second > 0.0 && ti > pos->second )
{
pos->second = 0.0;
//這裡用map的key去拼成個字串
char ss[512];_snprintf_s(ss,_countof(ss),511,"player.onTimer%d",(pos->first+1));ss[511]=0;
//執行相應的player的對應的定時器,呼叫相應的lua指令碼
mGSL->GetCtoLuaEngine()->OnlyPlayer(this,ss);//這裡時數字7結束
}
}
}
}