May 15, 2012

开发笔记(19) : 怪物行走控制

这段时间项目进展还算顺利,叮当同学在盯项目进度,我专心解决程序上的各种小问题。

最近我在协助解决 NPC (包括地图上的怪物)的行为控制以及 AI 的问题。

目前,我们的进度还处在玩家可以通过客户端登陆到服务器,可以在场景上漫游,以及做一些简单的战斗和技能动作的阶段。按最初的设计原则,我们的每个玩家是在服务器上有一个独立的 agent 服务的。目前写到现在,和中间想过的一些实现方法有些差异,但大体上还是按这个思路进行的。 关键是在于去除大部分的回调方式的异步调用;编写的控制流程自然完整,不需要太多的去考虑 agent 行为之外的交互性。

如果丝毫不考虑性能问题,我很想把每单个怪物和 NPC 都放在独立服务中。但是估算后,觉得不太现实。在 这一篇 的最后一段已经提过这个问题了。

今天展开来谈谈我的方案。

我想把怪物的移动行为独立出来做,以减少 AI 的压力。也就是说,地图上所有的怪,在设定的时候,都可以设定他们的巡逻路径,或是仅仅站立不动。我希望在没有外力干扰的时候,处理这些行为对系统压力最小。

我不想让怪在没有任何玩家看见的时候就让它静止不动,因为这样可能会增加实现的复杂性,并在怪物行为较为复杂时,无法贯彻策划的意图。

最好的方法还是把之隔离,使其对系统的负荷受控。同时也可以通过分离,减小实现的复杂性。

这个子系统是这样的:

阅读全文 "开发笔记(19) : 怪物行走控制" »

May 04, 2012

开发笔记(18) : 读写锁与线程安全

最近一段时间,我正解决的技术问题都是和多线程以及并发有关。

我期望在产品上线后,可以用的上至少 32 核的机器。这样,让多核平摊计算负荷就比较重要。单机承载的人数可以不高,但不希望玩家卡在一起特定的操作上。

这里强调的是单机上的并发,而把跑在不同机器的进程会分开考虑。所以,尽量利用共享内存以及单机锁来解决信息交换和状态同步问题。

上个月花了很多时间解决在 Lua 中并发读取同一份配置数据 的问题。一开始是想用一个 Lua State 储存一份结构化数据,然后让其它的 State 对他进行并发的读操作。我以为这种并发读的行为是线程安全的,但是我错了。因为读 Lua State 是需要对 Lua Stack 做修改,所以在没有锁的情况并不能做到线程安全。

随之我实现了一个改进方案。一开始就初始化好若干 Lua Thread ,在不同的 OS Thread 中使用独立的 Stack 空间操作。因为我们的框架只会启动有限的 OS 线程来跑更多的 Lua State ,这样做是可行且线程安全的。不过还是有一点小问题。如果用户向 Lua State 压入并不存在的 key (string 类型) ,修改 Lua 中 string pool 的操作又有线程安全问题了。当然,这个问题还可以用更为复杂的方法规避(比如再给每个线程配置一个独立的 state 做 string 合法性校验)。

我最后放弃了直接用 Lua State 储存这份共享数据,而改用自己实现的一个线程读安全的 hash 表。Lua 仅作为数据解析和加载过程的工具而存在。整套代码我已经开源了

阅读全文 "开发笔记(18) : 读写锁与线程安全" »

April 26, 2012

pbc 优化

最近几天优化了一下 pbc

这是一个大改动,所以写 blog 记录一下。

首先,我为 rmessage 定制了一个 heap alloc ,在使用 rmessage 解包的时候不再调用系统的 malloc 。而是从一个连续内存 heap 上取用内存。这样在删除 rmessage 对象时也会更快。因为只需要把 heap 回收即可。

当然这样会导致 rmessage 解包时用到的内存增加。对于内存紧张,性能关键部分,我还是推荐 pattern 模式。虽然比较难用,但可以保证时间和空间性能。

另外,我增加了 upb 的 Event-based parsing 模式,见新增接口 pbc_decode

不过我认为这个 api 不适合直接在 C 里调用,但是用来做动态语言的 binding 不错。现在 lua binding 中的 decode 就改用这个实现了。这样每次解包就把所有项都解出来,而不用附着一个 userdata 。回避了手动调用 close_decoder 的问题。

btw, 根据一个同学使用的反馈,他们大多不主动调用 close_decoder ,而依赖 gc 回收 decode 过程中产生的 C 对象。但是这些 C 对象申请的内存不会通知 lua ,所以 lua 的 gc 触发条件不会及时触发。这使得 pbc 的 lua binding 可能占用大量内存。我这次的修改主要针对这个问题。

April 19, 2012

让多个 Lua state 共享一份静态数据

如果你在同一个进程里有多个 lua state , 它们需要共享大量的只读数据, 那么可能就不希望在每个 state 启动的时候都加载和解析一遍这些数据.

所以我们需要一个共享只读数据的方法。

前段时间,我实现了一个 共享内存服务 ,这个可以保证共享内存的安全读写。不过,如果数据是只读的,那么就不需要这么复杂了。

我们只需要把数据加载到一个 lua state 中,其它的同一进程内的 state 通过 C 接口去读数据就可以了。

今天,我做了简单的实现,放在了 github 上。

阅读全文 "让多个 Lua state 共享一份静态数据" »

April 18, 2012

开发笔记(17) : 策划表格公式处理

前段时间为策划提供过一些技术上的支持, 设计过一个简单的 DSL 。但随着更多策划加入团队,我觉得这个思路不能很好的贯彻下去。

策划更喜欢通过 excel 表格来表达他心中的数值关系,而不是通过代码语言。我需要找到另一种更切合实际的方案来将策划的想法转换为可以运行的代码。

研究了一下策划历史项目上的 excel 表格后,我归纳了一下其需求。我们需要把问题分步解决,excel 表格到程序可以利用的数据结构是一项工作,而从表达了数据之间联系的结构到代码又是另一项工作。

对于数值运算,有怎样的需求呢?

我更多看到的是一种声明式的表达,而不是过程式的。比如,策划会定义,“血量”这个属性,实际上是等价于“耐力 * 10”。我们把后者定义为一个公式。

许多表格其实就是在不同的位置表达了这种公式推导关系:一个属性等价于另一些属性组成的表达式。而在运行时,根据人物的一些基础属性,可以通过一系列的公式推导,得到最终的一系列属性值。满足这个需求并不难,读出表格里的对应项,做简单的解析就可以了。(这里涉及到另一个问题,表格里的对应项在哪里,今天暂且不谈)

对于这种声明式表达,程序要做的工作是进行一次拓扑排序,把需要先求值的属性排在前面,有依赖关系的属性求解放在后面。这样就可以转换为过程式的指令。

另一种表格称为查表。其实就是表达一种映射关系。如下表:

法术伤害物理伤害
战士0.51
法师10.5

阅读全文 "开发笔记(17) : 策划表格公式处理" »

April 11, 2012

Lua int64 的支持

虽然今天发了 twitter ,以及向 lua mailling list 里投递了消息,不过想想还是写一篇 blog 记录一下。

Lua 只支持一种 number ,默认是 double 类型。虽然你可以通过修改 luaconf.h 里的定义,把 lua number 改成 int64 。但是为了 int64 类型而放弃浮点数,恐怕不是大多数人想要的。

int64 通常用在 uuid 上,也就是说不需要对其数学运算,只需要可以比较就好了。我以前最喜欢的做法是用 8 bytes 长的 string 来表示一个 int64 。这样,即可以做唯一的 key 用,又不用做复杂的扩展。

pbc 的 lua binding 库 中,对 fixed64 类型,我就是这样处理的。

阅读全文 "Lua int64 的支持" »

April 09, 2012

如何更准确的网络对时

这个周末 想到这个问题, 是由另一个问题引起的.

为了模拟复杂的网络环境,我们在内网安装了模拟环境

怪物公司同学周末调试客户端时,修改了自己机器的网关,增加了模拟延迟。奇怪的是,他的客户端在切换网关时并没有断开连接。可延迟也果真发生了。

我和他探讨了一下,觉得这个模拟延迟是单向的。当游戏服务器发送数据包回桌面时,由于服务器和他的桌面机在同一个网段,所以 IP 包被直接发回了。TCP 连接也不因为修改了过去的通路和断开。

当然,这种模拟并不是我们想要的。正确的方法应该是在修改网关(指向延迟模拟机器)的同时,也修改桌面机的 IP ,或是给自己机器绑定两个 IP ,使用模拟环境网段的 IP 来重新建立 TCP 连接。或者在模拟网关上做一次 NAT 。反正方法有很多,不展开讨论了。只有正确的模拟双向延迟(或网络颠簸)才好得到接近现实情况的场景。

不过这次错误,引出我另一个思考。如果 TCP 上行和下行延迟差距较大,有没有什么特别糟糕的事情发生呢?我的第一反应是,网络对时不准了。

我们的对时协议一般都遵从这样一个假定。我们的桌面机发送一个数据包到服务器所需要消耗的时间,大约等于服务器发一个数据包回桌面机的时间。这样,我们测试出一个数据包来回的时间,除 2 ,就得到了单程时间。这样就可以根据时间服务送来的服务器时间,把桌面时间和服务器时间基本校准了。

可一旦上下行速度不一致,甚至偏差较大时,这个假定被破坏掉了,时间也无法校准了。

阅读全文 "如何更准确的网络对时" »

Misc

Categories

Recent Comments