mangos 消息广播
2014-02-20 16:54
211 查看
先上图。下面是文字描述。
我在研究mangos游戏服务器mangosd的时候,在看到用定时器来更新某一区域的天气对象时,看到了mangos在基于地图对象的消息广播流程。下面是我的分析,欢迎指正。
mangos游戏服务器把一张地图(也就是代码中的Map对象)分成了64*64个Grid对象。也就是64*64个格子。每个格子的尺寸是533*533.然后又把每一个格子分成了8*8个Cell.
所以每一个cell的尺寸是533/8*533/8. 整个地图系统以cell为最基本的单位。所以当更新某一区域的天气时,都是把天气对象广播给属于这一区域的cell中的所有Player。Player
对象只要进入了一个cell区域就会被加入到cell中的模板容器中。
上面是消息广播的UML类图。如果看不懂的话,请去补习一下UML,挺简单的 。下面是广播的主干代码。
void Map::MessageBroadcast(Player *player, WorldPacket *msg, bool to_self, bool own_team_only)
{
CellPair p = MaNGOS::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
if(p.x_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP || p.y_coord >= TOTAL_NUMBER_OF_CELLS_PER_MAP )
{
sLog.outError("Map::MessageBroadcast: Player (GUID: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", player->GetGUIDLow(), player->GetPositionX(), player->GetPositionY(), p.x_coord, p.y_coord);
return;
}
Cell cell = RedZone::GetZone(p);
cell.data.Part.reserved = ALL_DISTRICT;
if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
return;
MaNGOS::MessageDeliverer post_man(*player, msg, to_self, own_team_only);
TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > message(post_man);
CellLock<ReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *this);
}
在上面的代码中,先通过找到这一区域的某一个玩家,然后计算得到该玩家所属位置的CellPair对象。这个对象表示的是玩家是在整个地图的哪一个cell中.注:整个地图有
512*512个 cell,也就是 (64*8)*(64*8)。这个CellPair就是玩家处于512*512个cell中的的哪一个cell。
然后计算得到一个cell对象.这个cell对象描述的信息是,玩家处于地图中的哪一个Grid中的哪一个cell里面。
MaNGOS::MessageDeliverer是一个消息邮递员,也就是下面要提到的访问器(visitor),就是通过它来把一条消息广播给某一个或多个cell中所有玩家.
而TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > 是一个类型容器访问器。这个模板对象起到一个中间人的作用,通过他来引导一个消息邮递
员把某一消息传递给该模板的第二个类型指定的容器中的所有对象。
原来我一直在思考TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > 这个类模板存在的意义。通过上面的UML图。我们可以看到Map,
NGrid,Grid这个三个对象和这个类模板的关系是依赖关系。这里要说明一点NGrid对象代表一张地图上64*64个格子中的其中一个。Grid对象代表一个格子中的8*8个cell中的其中
以个。cell通过这个类模板中间人对象来调用MessageDeliverer来把消息广播给cell中容器里的玩家。其实,cell自己可以直接让MessageDeliverer来访问cell中容器中的所
有玩家,为什么需要TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > 这个中间人来调用呢?在下认为原因是下面的描述:
在mangos基于地图的消息广播中,会有不同的被广播群体,上面的代码被广播的群体仅仅是玩家对象,这个被广播的群体有个专门的实际意义上的对象MessageDeliverer来负责
广播操作,并且这个广播操作需要的数据也是特定的。所以根据不同的被广播对象,就会 有一个专门的对象负责把消息广播给他们。根据面向对象的设计原则,要面向接口编
程而不要面向实现编程,让具体的实现隐藏在接口的背后,这样应用层就和具体的实现通过接口进行解耦。当具体的实现发生变化的时候不影响应用层的代码。避免以后修改而
带来的影响。所以,我们肯定想定义一个接口类叫消息邮递基类,负责广播给不同群体的消息邮递员对象都从这个接口继承,然后再定义一个接口叫类型容器基类,所有被广播
的容器类型都从他继承。这是我们广泛的做法。但是mangos不是这样做的,在mangos的源代码中大量使用模板替代这种继承来实现多态。多态要用到虚函数,而虚函数的执
行是在运行期指定的。如果用模板来代替继承实现多态,则可以把虚函数的指定从运行期提前到编译器。这对运行中的程序来说提高了些许效率。所以综上所述,之所以要用
TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > 这个中间人角色的对象原因就是,把具体的<广播者对象,具体的被广播者对象> 与 <地图对象
(map),格子对象(NGrid),cell对象(Grid)进行解耦.并提高程序执行的效率.
小结:通过以上的UML图片可以看到,广播消息的流程是:通过地图对象找到某一玩家所在哪一个格子的哪一个cell中。整个地图以cell为基本单位.cell对象中有个
TypeMapContainer.这类型容器是个极具技巧性的容器,这个容器可以放任何类型的对象。其实他只是一个普通的类,容器中的操作不是他来进行的,他封装了一个
ContainerMapList的类模板,这个类模板才是真正的存放多类型对象的容器。比如你要在这个容器中存放 Player对象,Creature对象,Corpse对象,则ContainerMapList会产生
3个GridRefManager对象来存放Player对象中的GridRef对象(一一对应),Creature中的GridRef对象(一一对应), Corpse对象中的GridRef对象(一一对应)。每个
GridRefManager其实是个链表,GridRef是能存放到链表中的元素。咋一看实际上mangos的这个能存放多种类型对象的容器,是通过曲线救国的方式实现的。cell对象一旦 有
了这个万能容器,则当一个玩家进入一个cell区域的时候,这个玩家就被加入到cell的容器中,所以,这个时候,cell通过TypeContainerVisitor<MaNGOS::MessageDeliverer,
WorldTypeMapContainer >来让消息邮递对象遍历cell中容器中的所有玩家,把消息广播给他们。
相关文章推荐
- android消息广播Intent传递数据
- Intent发送广播消息
- Consul实现原理系列文章2: 用Gossip来做集群成员管理和消息广播
- android 限制广播消息的接收者
- VC 利用DLL共享区间在进程间共享数据及进程间广播消息
- 接受系统广播消息 BroadcastReceiver
- Android自定义一个广播接收器BroadcastReceiver监听本地消息
- Android 中的消息传递,详解广播机制
- RabbitMQ(python实现)学习之二:Producer发送消息至多个消息队列queue(广播消息)
- RabbitMQ-消息广播
- RabbitMQ~广播消息
- RabbitMQ~广播消息
- Android中接收系统广播消息
- C#对象间的协作和信息交换(六)利用事件(Event)广播消息
- android 如何屏蔽接收来自某些信道的小区广播消息
- Android动作广播类别消息类型
- C#使用UdpClient发送广播消息介绍
- 接收系统广播消息之监听系统开机
- Remoting异步回调,向在线用户广播消息
- 一种在不同窗体之间进行消息广播的方法