您的位置:首页 > 编程语言 > Go语言

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中容器中的所有玩家,把消息广播给他们。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  服务器