游戏服务器之定时器

游戏服务器之定时器

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结束
					}
				}
			}
		}

猜你喜欢

转载自blog.csdn.net/asladfa/article/details/83506781