KBEngine奇技淫巧<三>

本文详细介绍了KBEngine中各种Handler的运行机制和组织框架,包括BaseApp的Handler继承、Task的处理流程、EventDispatcher的调度以及不同类型的Handler如InitProgressHandler和BaseMessagesForwardClientHandler的工作方式。文章还探讨了Handler的添加时机和设计上的灵活性与不足。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

各种Handler

研读KBEngine源码的时候,你经常会看到各种Handler结尾的类,搞懂这些类是很有必要的。我们以BaseApp程序为例来梳理这些Handler

class ServerApp : 
	public SignalHandler, 
	public TimerHandler, 
	public ShutdownHandler,
	public Network::ChannelTimeOutHandler,
	public Network::ChannelDeregisterHandler,
	public Components::ComponentsNotificationHandler

BaseApp一通继承会继承到这里,这里有6个Handler,先记下,
还有:

class DataDownload : public thread::TPTask
class EntityMessagesForwardCellappHandler : public Task
class BaseMessagesForwardClientHandler : public Task
class InitProgressHandler : public Task
class ProxyForwarder : public TimerHandler
class RestoreEntityHandler : public Task
class SyncEntityStreamTemplateHandler : public Task

运行机制

可以看到,在BaseApp一级目录里面就能罗列出这么多Handler,而KBEngine有将近十个程序,每一个程序都大同小异,虽然程序使用同一套代码,不过梳理这些也够喝一壶的了。那么这些handler是如何运行的呢。万变不离其宗,根上理论还是Win32消息机制,或者叫观察者模式。

框架组织

bool ServerApp::run(void)
{
	dispatcher_.processUntilBreak();
	return true;
}

万恶之源的运行函数,我们知道所有的App都会一通继承最后继承到ServerApp,而ServerApp最重要的两个成员变量就是:

	Network::EventDispatcher& 								dispatcher_;	
	Network::NetworkInterface&								networkInterface_;

这两个东西虽然都是Network名空间,功能也是大同小异,不过标哥应该是从逻辑上划分开来了。networkInterface_ 主管网络, dispatcher_ 主管各种操作事件。
那么好了,EventDispatcher就是我们今天需要研究的东西了。看最重要的两个函数:

//-------------------------------------------------------------------------------------
void EventDispatcher::processUntilBreak()
{
	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING)
		breakProcessing_ = EVENT_DISPATCHER_STATUS_RUNNING;

	while(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING)
	{
		this->processOnce(true);
	}
}

//-------------------------------------------------------------------------------------
int EventDispatcher::processOnce(bool shouldIdle)
{
	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING)
		breakProcessing_ = EVENT_DISPATCHER_STATUS_RUNNING;

	this->processTasks();

	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING){
		this->processTimers();
	}

	this->processStats();
	
	if(breakProcessing_ != EVENT_DISPATCHER_STATUS_BREAK_PROCESSING){
		return this->processNetwork(shouldIdle);
	}

	return 0;
}

什么意思呢?
processUntilBreak:我不说停你就给我一直跑
processOnce:跑一步,游戏里面可以说跑一帧,或者心跳一下,那么在这个心跳里面就会有我们说到的各种handler。

处理任务
处理定时器
处理各种状态
处理网络

handler属于任务,我们也可以从类的声明里面看到,很多handler都是继承到了task,再看处理任务,其他的三个技巧可以留着以后再谈。

处理任务逻辑

void EventDispatcher::processTasks()
{
	pTasks_->process();
}

因为标哥喜欢把某种对象的容器重新封装为一个类,所以还得再跟进一层:
类是这样子的:

/**
 *	任务容器
 */
class Tasks
{
public:
	Tasks();
	~Tasks();

	void add(Task * pTask);
	bool cancel(Task * pTask);
	void process();
private:
	
	typedef std::vector<KBEngine::Task *> Container;
	Container container_;
};

那么最主要的函数就是process

void Tasks::process()
{
	Container::iterator iter = container_.begin();

	while (iter != container_.end())
	{
		Task * pTask = *iter;
		if(!pTask->process())
			iter = container_.erase(iter);
		else
			++iter;
	}
}

逻辑倒是很简单,遍历调用容器里所有task的process,这样我们就知道在游戏运行中可以调用到,也就是处理对应的handler。留下一个问题就是什么时机添加task,很明显标哥在考虑这个添加的时候没有考究,各种添加方式都有,造成代码的美感有点欠缺,有的handler声明一个start里面添加的,有的在构造函数里面添加的,有的是attach之类的添加的,设计有欠缺,也可以说灵活性好些吧,毕竟标哥在写引擎的时候考虑很多。
我们举几个例子:

InitProgressHandler

这个是声明了一个start函数,在函数内添加到任务管理器,坐等心跳回调:

void InitProgressHandler::start()
{
	networkInterface_.dispatcher().addTask(this);
}

BaseMessagesForwardClientHandler

BaseMessagesForwardClientHandler::BaseMessagesForwardClientHandler(Entity* pEntity, COMPONENT_ID cellappID):
Task(),
pEntity_(pEntity),
completed_(false),
startForward_(false),
cellappID_(cellappID),
createTime_(0)
{
	DEBUG_MSG(fmt::format("BaseMessagesForwardClientHandler::BaseMessagesForwardClientHandler() : cellappID({}), entityID({})!\n", 
		cellappID_, (pEntity_ ? pEntity_->id() : 0)));
	
	Baseapp::getSingleton().networkInterface().dispatcher().addTask(this);

	createTime_ = timestamp();
}

这个是在构造函数里面添加到任务管理器,然后就坐等心跳回调。

有的时候标哥会重新封装一层,重新声明一个handler和task,task添加到任务管理器,然后task里面再来一个handler,这样在回调的时候再调用handler的对应操作处理,例如:
InitProgressHandler里面有一个EntityAutoLoader* pEntityAutoLoader_;,虽然这个东西不是handler结尾的,但是处理机制是一样的。

自成系统的handler

有一些handler并没有继承task,就是自己孤零零的来一个,标哥这个散装类还是不少的。
典型的就是各种超时handler,这个就是对应时机调用对应的回调。

比如:

class ChannelTimeOutHandler
{
public:
	virtual void onChannelTimeOut(Channel * pChannel) = 0;
};

这个东西就是在

class NetworkInterface : public TimerHandler

里面声明一个这个类型的变量,

	ChannelTimeOutHandler *					pChannelTimeOutHandler_;	// 超时的通道可被这个句柄捕捉, 例如告知上层client断开
	ChannelDeregisterHandler *				pChannelDeregisterHandler_;

然后在对应的时机调用对应的回调

void NetworkInterface::onChannelTimeOut(Channel * pChannel)
{
	if (pChannelTimeOutHandler_)
	{
		pChannelTimeOutHandler_->onChannelTimeOut(pChannel);
	}
	else
	{
		ERROR_MSG(fmt::format("NetworkInterface::onChannelTimeOut: "
					"Channel {} timed out but no handler is registered.\n",
				pChannel->c_str()));
	}
}

这个纯粹就是封装了一个函数的感觉,设计美感倒是没有提升什么。那么handler就扯到这里,

总结

一种是直接是task
一种是task里面又包含了一层handler
一种是直接散装handler
在研读标哥写的代码的时候如果遇到这种类型还是需要稍微注意一下属于那种handler,就会顺畅一些了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

当当小螳螂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值