3

ouster开发笔记(五)

 3 years ago
source link: https://www.zenlife.tk/ouster-dev5.md
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

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

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

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK