Erlang开发MMORPG的思考

(原文转至:http://www.myexception.cn/program/125249.html 稍作编辑整理)
还未见到有实时的MMORPG采用erlang作为后台。原因不外乎端游几乎是C++一脉相承,从客户端延伸到服务端,当然是同种语言方便。此外计算性能和员工招聘也是重要的考量。

而页游和手游的客户端往往采用flash、Java、oc、C#等,除java、C#外都很少用于服务端开发,所以服务端语言的选择就有了更多自由。

早些年erlang技术人员还很少,现在已经有一定的量了。出于兴趣,我重新考虑用erlang开发实时战斗的MMORPG的方法。

有利的部分


erlang是好的,吸引了很多人的兴趣。不少人甚至将erlang体系的影响带到了自己所在的其他语言项目中,构建一些类似erlang的基础设施。包括我。

随意挥霍的process

端游从早期的单进程支持一个游戏服,发展到现在的多进程支持一个游戏服。其中涉及多个进程协作的逻辑越来越多,异步任务相互之间由于时间差带来的问题也越来越频繁。

通常我总会利用状态机或者类似的办法管理异步任务,但并不能顾及到每一处,而且状态机增加了理解代码的难度。

erlang轻量的process使我能为每个异步任务创建一个独立的执行过程,同时变量不可变的机制几乎消除了多线程中的资源共用的问题。

发送消息太简单

用C++,发送消息从socket干起,还只能发给一个真正的系统进程,然后switch分发处理。

在异步任务逐渐增多的环境下,有时候很希望语法糖是:能将消息发送给某个异步任务。需要消息平台是一种逻辑上的抽象,而不是底层的socket。

C++可以包装一个,而erlang提供了一个极其完善的面向process的消息系统,process又可以映射到异步任务中。

ETS以及Mnesia,it’s great!

在多进程支持一个游戏功能的情况下,有大把的数据需要在不同的进程之间同步共享。也用C++包装一个类似的系统能降低很多重复的数据同步工作。

erlang提供了ETS甚至Mnesia,几乎无条件的为我们提供了数据的分布、持久和恢复。

困难的部分


用erlang开发游戏最困难的地方就是实现场景逻辑。这里没有异步任务,这里是效率、指针和角色相互操作的天堂,但是对erlang来说可能是地狱。

常见的是player/NPC角色之间的立即相互修改,从地图区域查询一批player/NPC角色等。在指针环境下这些操作非常有效率。

一个实时战斗的MMORPG要求核心逻辑要达到25帧/S,区域地图在线1000人。

如果我们为每个player/NPC映射到process用于交互,那么他们必然使用消息去操作访问对方。 这很美,但是效率不行。因为player/NPC的操作访问实在太频繁了,这一来erlang就变成一个消息繁忙的系统,真正的游戏计算反而耽误了。

每个场景一个process,场景中所有的player/NPC集中保存到tuple、list中,通过参数传递。这样具体操作player/NPC的数据确实快了,但是erlang的原生list类型不是利于搜索的类型,这一趟趟查下来也就将计算时间花光光了。另外通过参数传递也会导致每个相关的子函数都要留一个口子。

每个场景一个process,场景中的所有player/NPC都保存到进程字典中。进程字典的存取速度在erlang中算快了,插入100ns,查询40ns。ETS的速度是us。(参见进程字典到底有多快)

游戏的三种逻辑


如果要用erlang来做MMORPG,那规划一下游戏的基本逻辑模块,根据模块的特性选择不同的erlang模式,是有意义的。像C++那样一种模式就行得通的做法,并不适用于erlang。

自我修改逻辑

比如升级,强化之类,就是改改自己的属性,改改自己的技能。

这种逻辑erlang可以为每个player/NPC创建process负责,也可以一个process处理多个
player/NPC 的类似逻辑。客户端对于这种操作的延时总能容忍1、2秒,慢慢来,性能没关系。

低频角色交互逻辑

比如组队,领取任务,NPC思考等等,角色需要相互操作对方数据。同样也是延时容忍较高,可以尽情发挥erlang在这种交互逻辑中的异步任务支持能力。

高频角色交互逻辑

同一个场景中,角色交互频率最高的就是移动和战斗。

移动影响相互间的视野,战斗则直接读取修改彼此的数据。不要忘了,1000人25帧/S的计算

移动和战斗


这一块比较纯粹,由一个process为一个场景的player/NPC提供服务,基本上有下面几样内容就搞定计算:

  • pos-角色坐标
  • atb-角色属性
  • skill-技能列表
  • state-状态列表
  • 同屏表
  • 地图数据
  • 事件列表

前4条属于角色数据,简单操作即可。

同屏表通过四叉树管理角色的空间,为各种类型的群攻判定提供快目标的快速查询。

地图数据提供角色检测技能释放的空间合法性,以及战斗寻路。

事件列表则每帧更新技能、状态等数值的计算。

效率优化


仅仅是纯粹的技能效果计算,erlang在虚拟机上的效率肯定不及C++。不过属性计算这块要求的性能并不高,还是能扛下这个计算。

费时、难写的计算可能有两块:同屏表和地图数据。

erlang的数据类型很难高效的处理关系型结构,空间四叉树和地图三角关系用erlang实现起来相当凑合。那么,同屏表和地图的功能到底作为场景战斗process的一个功能,还是以独立process的形式服务呢?

process发送消息的性能大概是1us(参见erlang进程间发送消息的性能)。通常一个角色的一次战斗计算需要访问同屏表和地图功能各一次,4us。1000人的话是4ms。1帧=40ms,那么每帧大概有30ms左右的时间用于完整的战斗计算,包括同屏计算和地图计算。

为了进一步提高同屏计算和地图计算的性能,可以用C++(I~Kao,为毛不提C)实现erlang的内置函数。此外,原先逐个角色串行计算同屏功能和地图功能,可以改并行:

先收集全部角色的同屏计算需求;
将全部计算需求均匀发送到多个同屏表process进行计算;
一边收集结果一边继续计算剩下的战斗部分。

地图计算也采用类似的并行处理,即可提高效率。

你可能感兴趣的:(Erlang开发MMORPG的思考)