ouster开发笔记(五)

2014-05-23

科目三考完了,终于不用再浪费时间练车了,接下来专注ouster开发,还有一个月多一点。

先总结一下目前的开发进度:

  • scene+agent架构

大方向上基本上还是按照之前的设计实现的,每个地图,每个玩家,都由一个goroutine跑。

中间经历了一些代码上的重构,将代码全部写在gameserver,不再通过区分package,使用package的作用域来保证多goroutine读写的一致性。原本是想利用Go语言的package访问限制做一个强的约束的,scene/agent都是在不同的package中,不能访问到外部package的私有变量,这样当它们由不同goroutine运行,不必加锁,代码级别的保证。直到写monster.go的时候才反应过来,scene肯定要管理player的,它需要player数据结构,因此scene要引用agent包。agent与scene交互性是非常强的,agent肯定需要知道monster数据结构,但monster是写在scene包中,导致agent包又要引用scene包,循环依赖了。现在都写到一个gameserver中了,失去了代码级别的强约束,就必须自己使用的时候进行约束了:什么样的数据结构,只能在哪个goroutine写,其它的goroutine只能读。

scene向agent通信的方式,修改成了直接调用函数,而不是通过channel发消息。这个修改还处于待定阶段,做这个修改的原因在于,channel的阻塞特别容易把整个系统死锁。之前的scene到agent,agent到scene都是用channel通信,但是我用的是不带缓存channel,而消息流向比较复杂容易出错。agent处理一个封包,通过channel转发给scene处理,scene处理好再通过channel向agent发送消息,而这时agent此时还阻塞于上次的写channel,最终形成死锁。有两种方案可以处理,一种是使用带缓存的channel,另一种是单向的消息流动避免形成环路。暂时不好设置带缓存channel的缓存大小,所以临时选择了第二种方案,agent使用channel向scene发消息,而scene是直接调用agent函数。但是这样并发安全性上,还是有些东西需要更多地考虑。

  • aoi模块

aoi模块在接口方面是设计得比较满意了,实现上先重写了一个最简单的网格的aoi,接口定下来后面如果要优化或者改实现的,都或以再进行实验。目前这一块暂时没看到什么明显问题,待进一步测试。

  • 场景漫游

目前玩家已经可以在地图内行走了,滑步技能也做了。关于同步那一块的东西,之前是考虑了很多,但是后面由于决定改做darkeden模拟器,就跟着darkeden的做法来了,每走一个单位距离,客户端都会向服务端发送一条消息,收到服务端发回的响应后,可以继续移动一格距离。

这一块剩下的问题主要是碰撞的检测,暂时还没有做。

  • packet

packet模块也是接口方面先设计得可以了,packet/darkeden子目录专门用来解析darkeden封包。到目前为止,已经完成了大概70多种packet,而v2版本的darkeden大概有440多个packet。这一部分性能方面可能还需要优化的,目前大量地byte.Buffer{}直接使用了扔掉,内存分配释放太过频繁,可能会给gc带来压力。先实现再优化吧,如果后面有多的时间的话...

比较坑的几个地方,darkeden中每个对象实体都有一个编号ObjectID,这是个uint32类型的值,坑爹的地方是,它并不是随意的一个uint32,我之前准备利用高位来区分玩家和怪物之类的,结果发现,如果玩家的ObjectID大于10000之后,每次玩家移动,客户端对应显示就会消失掉,也就是ObjectID只能是小于10000的数。

然后就是加密,darkeden中有些包是加过密的,主要是像移动和使用技能相关的一些。加密的方式,有的是与一个数做了与运算,而有的还做了参数的混淆。

packet这一块,接下来的工作主要就是破译更多的包格式了,体力活。

  • skill

这一块刚开始看,darkeden把技能分了类,SkillToSelf/SkillToObject/SkillToTile,分别是对自己,对目标,对地技能。我先把技能的包格式给弄清楚了,并分别挑着每一类实现了一个,像隐身/麻痹/滑步。

darkeden的技能太多了,想完整支持是不可能的。所以接下来计划是挑重要的我觉得喜欢的技能实现。反正头几个实现起来麻烦一点,后面就是体力活了。优先计划的一些技能: 鬼:滑步/隐身/陨石/麻痹/沼泽/雾。战魔的:光冲/冰雹/精灵之怒/致命打击。医生:牙签/魔免/爆破之星。

  • 怪物AI

现在暂时怪物都是“死的”,攻击它都不还手的。怪物的heartbeat是一个空函数。我需要把怪物的AI做进去,移动/攻击等等动作,还有相关的寻路等。

角色数据持久化还是要搞的。再有其它很多像背包,交易,副本,可能都不一定有精力去做了。


反正东西还是很多的,时间却是有限的。自己定需求,自己做实现。核心的架构一块肯定是要做好的,毕竟是最积累技术的地方。至于上层逻辑,跟着darkeden来,做多做少看精力。

核心这一块比较头疼的问题是,保证数据的读写一致性问题。scene+agent这样架构的初衷是充分发挥出多核的性能。但是scene与agent的交互还是非常强的,而多个goroutine同时读写一份数据就会出现一致性问题。基本原则是每个数据,只有一个goroutine拥有写权限,其它的只能读。但是还是有很多不好划分的地方,比如角色的坐标,这个是aoi模块要使用了做广播的。它的数据在player结构体内,却是由scene进行写的。怪物就更复杂了,如果把怪物写权限放在scene,很多可以由agent完成的计算就必须移动到scene中,那么就失去了原来的用goroutine平摊计算量的优势。

现在我更加强调scene的功能是对象的管理以及广播消息,而属性伤害计算之类的希望更多地让agent平摊计算量。移动计算而不是移动数据。现在怪物相关的,有怪物攻击玩家和玩家攻击怪物。目前准备这么设计,平时怪物由scene所有,一旦怪物进入战斗,则把它的所有权移交给agent。一个怪物同时只会攻击一个玩家,那么只有这个玩家的goroutine能够对怪物属性进行写,其它goroutine只能读。

ouster