1. 程式人生 > >遊戲伺服器之定時器

遊戲伺服器之定時器

遊戲伺服器之定時器

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.

If the installed hardware doesn’t support a high-resolution performance counter, this parameter can be zero (this will not occur on systems that run Windows XP or later。
    從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結束
					}
				}
			}
		}