网络游戏的对时以及同步问题

大多数实时网络游戏,将 的时间和 client 的时间校对一致是可以带来许多其他系统设计上的便利的。这里说的对时,并非去调整 client 的 os 中的时钟,而是把 game client 内部的逻辑时间调整跟 一致即可。

一个粗略的对时方案可以是这样的,client 发一个数据包给 server,里面记录下发送时刻。server 收到后,立刻给这个数据包添加一个server 当前时刻信息,并发还给 client 。因为大部分情况下,game server 不会立刻处理这个包,所以,可以在处理时再加一个时刻。两者相减,client 可以算得包在 server 内部耽搁时间。

client 收到 server 发还的对时包时,因为他可以取出当初发送时自己附加的时刻信息,并知道当前时刻,也就可以算出这个数据包来回的行程时间。这里,我们假定数据包来回时间想同,那么把 server 通知的时间,加上行程时间的一半,则可以将 client 时间和 server 时间校对一致。

这个过程用 udp 协议做比用 tcp 协议来的好。因为 tcp 协议可能因为丢包重发引起教大误差,而 udp 则是自己控制,这个误差要小的多。只是,现在网络游戏用 tcp 协议实现要比 udp 有优势的多,我们也不必为对时另起一套协议走 udp 。 (更多…)

标签Tags:, , , , , , , , , , ,

动态加载资源

如今很多游戏engine宣称自己支持动态加载地图,也就是说可以作到跨地图时的零读取时间。听起来很高深的技术,实际不难实现。最近我正在考虑更加通用的解决方案。

先说说基本的思路。也就是我们需要把地图数据切割成小块,让每一块的数据读取解析量并不太大。然后,就可以根据玩家所在坐标读取最小数据量的数据。当玩家移动的时候,利用一些机器闲置时间去读周围的场景,或者进一步可以根据玩家移动方向把前方数据预读的优先级别调高。

其实,这个方案可以被推广到所有的游戏资源。其实,我们可以给每个资源文件都放置一个通用的数据域存放一些建议关联文件列表。如果是地图块的话,它的建议依赖文件就是它周围的地图块,或者是人为设置过跳转点的目标块。

而模型文件则可以把它依赖的贴图,骨骼文件做为建议依赖资源。地图场景文件可以建议依赖一些摆放在上面的模型文件。

有了这些依赖关系,我们在读取某个指定资源文件后,可以用一个独立线程预先加载一些可能下一步要加载数据,也可以由逻辑层主动通知一些预测信息。例如我向东向移动,可以通知 I/O 线程,东边的相关资源优先级别高一些,I/O 线程则去调整预读队列的次序。

I/O 线程的设计是比较复杂的,我的最初想法是建立一个大的 , 以内存页对齐(IA32 下是 4K)。如果数据被预读了,主线程索要数据的时候,用共享内存的方式,将 中的数据块交换出去。

经过和同事的讨论,一个做 OS 的同事提示我,设计可以更简洁一些。因为现在 OS 对 file I/O 的 cache 机制都非常完善了,预读只需要去真的读一下文件即可,不需要再去自己做 cache,自己做 cache 只能节省一些数据 copy 的操作,但是其逻辑的复杂程度还有页表操作的负担不一定划算,所以我们只用专心做好预读逻辑即可。

这样的动态加载的资源管理模块,完全可以做的对主线程完全透明。我们的主线程只需要按阻塞模式去读取文件就够了。

但是,如果读取文件还伴随着耗时的解码过程。比如解 jpeg,,解密。那么我们最好自己做 cache 了。

标签Tags:, , , , , , , ,

游戏中的货币

究竟如何用游戏规则来稳定虚拟货币。

首先,我认为现在的网络游戏中,货币并不完全等同于现实社会中的货币。现实社会中,货币只是一种能直接起交换手段或支付媒介作用的东西,它本身没有价值;货币的投放由央行控制。但在大多数网游中,货币本身有了实用价值,它可以从系统那买到各种补给品,或是可以直接增加虚拟角色的能力。而且货币本身也多由玩家自己的主动行为生产。

同时,在健康的虚拟经济环境中,虚拟货币也起到了现实货币同样的作用——作为支付媒介。 (更多…)

标签Tags:, , , , , , , , , , , , , ,

游戏服务器内的组播

游戏服务器在设计时,会有许多组播的需求。比如一个 NPC 需要向周围的观察者播出它的状态信息。无出不在的各种聊天频道中需要向频道中的参于者广播聊天消息等。

通常,我们会为每个组播的请求维护一张列表,然后再把需要的信息包发送给指定列表上的多个连接。这个过程在很多地方都会遇到,一个设计的不太好的引擎,再这里有可能滋生诸多 bug 。尤其在多服务器的设计时尤其如此。

这两天,我试图寻找一种简洁统一的解决方案。

前几天的一篇 blog 讨论了一组服务器设置多个外部接入点的设计。就着这个思路我们可以引申一下。

当每台连接服务器设置一个不大的上限,比如最多同时处理 8192 个外部连接,那么管理分组倒不会带来太大的负担。

我们只需要在逻辑服务器与连接服务器间定义四条协议。

把一个连接加入指定分组。
让一个连接退出指定分组。
向一个分组广播消息。
删除一个分组。
这里,后两条协议是冗余的。

对于第三条协议,如果表示连接号的容量足够大,我们完全可以留一部分号码用于组播。这在 IP 协议中定义 IP 地址的构成时就是用的类似方案。不过我们的设计为了节省带宽,全部用的 2 字节做内部连接号,资源比较紧张,所以就单独设计一条协议了。

对于第四条协议,当最后一个连接退出时,分组就自然取消了。增加一个专门的删除协议,纯粹是使用上的方便。

在连接服务器上维护一个分组,仅仅需要一个 1K 的数组(8192 bits)。而有组播需求时,只用遍历这个数组。在 32 位系统下,只是一个 256 次的循环而已。因为大部分分组的成员都不会太多。数组中充斥着大量的 0 。扫描一下就非常的快了。

连接服务器用一个简单的 freelist + hash 表的技术,就可以高效且动态的管理不限量的分组,无论从 CPU 还是内存的负载来看,都是可以接受的。

我们额外需要做的只是一个分组编号分配管理器。为每个进程分配唯一独立的分组号就够了。在我们现在的设计中,为了尽量避免交互通讯。分配新组号采用不需要反馈的形式。每个需要申请组号的进程,都有独立的组号 id 空间。由分组管理器对若干独立的分组号做一次统一映射。

换句话说,每个服务器进程都可以直接从 0 号分组用起,然后直接用 1 号,2 号,3 号 …… 类似操作系统管理物理内存一样。独立进程拥有的只是虚拟分组号,由管理器映射到全局唯一分组号上。这样就完美的解决了不同服务器进程间的分组编号冲突问题。

标签Tags:, , , , , , , ,

随机数有多随机?

作为一个常识,每个程序员在做入门学习时,都会被老师谆谆教导:我们用的编程语言中的随机函数,只能产生出伪随机数。它有它的内在规律,只能作为对显示世界的随机事件的近似模拟。接下来,我们通常会被传授随机种子的概念。以及用物理上更随机的量做种子。比如系统时间、两次敲击键盘的时间间隔、多次移动鼠标的偏移、甚至系统出错的出错信息码等等。

作为游戏数值策划,除了加减乘除,用的最多的数学概念恐怕就是随机数了。有经验的数值策划或许从他的前辈那得知计算机中程序产生的随机数并不太可靠;或者他本身就受过程序方面的训练。如果游戏项目更幸运一点,担当数值策划的他是一个数学爱好者,并读过诸如《计算机程序设计艺术》这样的技术书籍,那么事情会好的多。可惜大多数境遇下,策划们从不深究计算机随机数背后的细节,也不太关心所谓“伪”随机数究竟“伪”到什么程度。 (更多…)

标签Tags:, , , , , , , , , , , , , , , , ,

角色动作控制接口的设计

本质上,图象 engine 是一帧帧渲染角色精灵的。渲染这个操作必须被隐藏起来为应用层不可见。3d engine 有更高的表现力,比如可以做骨骼动画,以此做动作间融合。这些应该归到渲染层面做。绝大多数情况下,应用层不应该关心。

以前,我们的引擎接口提供了方法设置角色当前应该播放的动画系列。其参数有播放指定次数还是循环播放。仔细考虑,其实不太合理。 (更多…)

标签Tags:, , , , , , , , , , , , ,

游戏的帧率控制

我曾在不同场合,多次向人表达我对游戏程序设计的一个重要观点:时间控制,对于游戏程序设计至关重要。

这是因为,大多数电子游戏,都是一个实时人机互动系统。在非人机互动的软件中,软件及硬件一起是一个封闭系统,时间参数对这个系统是否正确工作是无意义的量。这样的系统,我们尽量应该设计成自动化工作模式,只要有正确的输入,得到正确的输出即可。通常,这样的系统的开发,还应该有自动化测试的流程去驱动它。

但实时人机互动软件则不一样,我们得把人看成系统的一部分。人和机器的交互是通过人眼、人耳、键盘、鼠标等完成信息交换的。如果把人脑看成一个线程、计算机看成另一个线程,我们几乎没有能力实现一个资源锁,利用锁的方式保证系统工作永远正常。人脑在全速运转时,适时得不到正确的信息输入,整个系统就可认为出现了故障。

可见,在这样的系统中,时间控制显得多么的重要。 (更多…)

标签Tags:, , , , , , , , , , , , , , , , ,

游戏数值公式的表象与本质

拿魔兽世界(后面全简写作 wow )举例说事,恐怕是唯一不容易遭人非议的了。我们注意到, wow 之前有很多网络游戏,甚至暴雪之前也有一款很接近现代网游的游戏—— diablo 的 battle net 版,都采用了一种让玩家展现个性化角色的设计。那就是随着升级,为角色自由分配属性点,再由属性点影响角色的能力。

我可以举出长长的例子证明这一点,甚至电脑 RPG 的鼻祖,D&D ,也是有自由加点的设定的。虽然 D&D 里玩家可以加的点很少,暴雪在 diablo 里加强了这个设计。

为什么在这么多已有的网游中,大家都沿袭了这个设定,把它作为提供玩家个性化的必要手段时,暴雪绝然抛弃了它,把个性化转移到了天赋系统中。是为了创新吗?我不是暴雪的游戏设计师,我没有答案。但我们知道,暴雪从来不是一个以创新为名的游戏制作公司。而天赋树从外观上看,就是 diablo 中技能树的延续。 (更多…)

标签Tags:, , , , , , , , , , , ,

ASP常用函数表(新手们的好工具)

Array()
函数返回一个数组
表达式 Array(list)
允许数据类型: 字符,数字均可
实例: <%
Dim myArray()
For i = 1 to 7
Redim Preserve myArray(i)
myArray(i) = WeekdayName(i)
Next
%>
返回结果: 建立了一个包含7个元素的数组myArray
myArray("Sunday","Monday", … … "Saturday")

CInt()
函数将一个表达式转化为数字类型
表达式 CInt(expression)
允许数据类型: 任何有效的字符均可(不大于32767)
实例: <%
f = "234"
response.write cINT(f) + 2
%>
返回结果: 236
转化字符"234"为数字"234",如果字符串为空,则返回0值

CreateObject()
函数建立和返回一个已注册的ACTIVEX组件的实例。
表达式 CreateObject(objName)
允许数据类型: objName 是任何一个有效、已注册的ACTIVEX组件的名字.
实例: <%
Set con = .CreateObject("ADODB.Connection")
%>
(更多…)

标签Tags:, , , , ,

病情加重~

  昨晚喝了点啤酒,本来就不怎么样了的身体好像加重了,多了点头痛,不能通宵了…

  一起来就看到手机塞满了短信,表姐又用泡泡给我手机发了N条短信,还叫我不要生气sleepy…全部东西点下全部删除就行了,根本骚扰不了我,而且,卡穷到了连一条一毛钱的短信都发不出,好可怜的手机,如今成了我的昂贵的电子表…

  部落格刚开始不久,没什么内容,还一直想不到首页的设计,仅当我的一个私有的空间先吧~

标签Tags:, , , ,