大多数实时网络游戏,将 server 的时间和 client 的时间校对一致是可以带来许多其他系统设计上的便利的。这里说的对时,并非去调整 client 的 os 中的时钟,而是把 game client 内部的逻辑时间调整跟 server 一致即可。
一个粗略的对时方案可以是这样的,client 发一个数据包给 server,里面记录下发送时刻。server 收到后,立刻给这个数据包添加一个server 当前时刻信息,并发还给 client 。因为大部分情况下,game server 不会立刻处理这个包,所以,可以在处理时再加一个时刻。两者相减,client 可以算得包在 server 内部耽搁时间。
client 收到 server 发还的对时包时,因为他可以取出当初发送时自己附加的时刻信息,并知道当前时刻,也就可以算出这个数据包来回的行程时间。这里,我们假定数据包来回时间相同,那么把 server 通知的时间,加上行程时间的一半,则可以将 client 时间和 server 时间校对一致。
这个过程用 udp 协议做比用 tcp 协议来的好。因为 tcp 协议可能因为丢包重发引起教大误差,而 udp 则是自己控制,这个误差要小的多。只是,现在网络游戏用 tcp 协议实现要比 udp 有优势的多,我们也不必为对时另起一套协议走 udp 。
一般的解决方法用多次校对就可以了。因为,如果双方时钟快慢一致的情况下,对时包在网络上行程时间越短,就一定表明误差越小。这个误差是不会超过包来回时间的一半的。我们一旦在对时过程中得到一个很小的行程时间,并在我们游戏逻辑的时间误差允许范围内,就不需要再校对了。
或者校对多次,发现网络比较稳定(虽然网速很慢),也可以认为校对准确。这种情况下,潜在的时间误差可能比较大。好在,一般,我们在时间敏感的包上都会携带时间戳。当双方时间校对误差很小的时候,client 发过来的时间戳是不应该早于 server 真实时刻的。(当时间校对准确后,server 收到的包上的时间戳加上数据包单行时间,应该等于 server 当前时刻)
一旦 server 发现 client 的包“提前”收到了,只有一种解释:当初校对时间时糟糕的网络状态带来了很多的时间误差,而现在的网络状态要明显优于那个时候。这时,server 应该勒令 client 重新对时。同理,client 发现 server 的数据包“提前”到达,也可以主动向 server 重新对时。
一个良好的对时协议的设定,在协议上避免 client 时间作弊(比如加速器,或者减速器)是可行的。这里不讨论也不分析更高级的利用游戏逻辑去时间作弊的方式,我们给数据包打上时间戳的主要目的也非防止时间作弊。
校对时间的一般用途是用来实现更流畅的战斗系统和位置同步。因为不依赖网络传输的统一时间参照标准可以使游戏看起来更为实时。
首先谈谈位置同步。
好的位置同步一定要考虑网络延迟的影响,所以,简单把 entity 的坐标广播到 clients 不是一个好的方案。我们应该同步的是一个运动矢量以及时间信息。既,无论是 client 还是 server ,发出和收到的信息都应该是每个 entity 在某个时刻的位置和运动方向。这样,接收方可以根据收到的时刻,估算出 entity 的真实位置。对于 server 一方的处理,只要要求 client 按一个频率(一般来说战斗时 10Hz 即可,而非战斗状态或 player 不改变运动状态时可以更低) 给它发送位置信息。server 可以在网络状态不好的情况下依据最近收到的包估算出现在 player 位置。而 client 发出的每次 player 位置信息,都应该被 server 信任,用来去修正上次的估算值。而 server 要做的只是抽查,或交给另一个模块去校验数据包的合法性(防止作弊)。
在 server 端,每个 entity 的位置按 10Hz 的频率做离散运动即可。
client 因为涉及显示问题,玩家希望看到的是 entity 的连续运动,所以处理起来麻烦一点。server 发过来的位置同步信息也可能因为网络延迟晚收到。client 同样根据最近收到的包做估算,但是再收到的包和之前已经收到的信息估算结果不同的时候,应该做的是运动方向和速度的修正,尽可能的让下次的估算更准确。
关于战斗指令同步,我希望是给所有战斗指令都加上冷却时间和引导时间,这正是 wow 的设计。这样,信任 client 的时间戳,就可以得到 client 准确的指令下达时间。引导时间(或者是公共冷却时间)可以充当网络延迟时间的缓冲。当然我们现在的设计会更复杂一些,这里不再列出。对于距离敏感的技能,例如远程攻击和范围魔法,我们的设计是有一个模糊的 miss 判定公式,解决距离边界的判定问题。
这里, server 对攻击目标的位置做估算的时候,可以不按上次发出包的运动方向去做位置估计,而选择用最有利于被攻击者的运动方向来做。这样,可以减少网络状况差的玩家的劣势。
对于 PVE 的战斗,甚至可以做更多的取舍,达到游戏流畅的效果。比如一个网络状态差的玩家去打 npc,他攻击 npc 的时刻,npc 是处于攻击范围之内的。但是由于网络延迟,数据包被 server 收到的时候,npc 已经离开。这个时候 server 可以以 client 的逻辑来将 npc 拉会原来的坐标。
虽然,这样做,可能会引起其他玩家(旁观者) client 上表现的不同。但是,网络游戏很多情况下是不需要严格同步的。在不影响主要游戏逻辑的情况下,player 的手感更为重要。
转自:http://blog.codingnow.com/2006/04/sync.html