网络游戏实时动作同步方案手记(1)

和MMORPG不同,实时动作型网络游戏追求操作的响应要求极高(<150ms)。

动作型网络游戏的制作人希望做到单机游戏的体验,网络游戏的服务。

网络指令在多客户端间的同步算法,从原理上来说,围绕两种特性的取舍而定:

*牺牲局部实时性:某程度的互等待,保证各客户端间指令集在指定时间段一致。

*牺牲局部一致性:容许客户端本机先行模拟,等待后续指令到达纠正。(DR)

网络的存在导致鱼与熊掌不可兼得,所以现在市面上的动作网络游戏都有

如下的妥协和折衷实现:

*在客户端本机上算大量关键运算来保证手感

*玩法上对直接进行的决定性互动的要求不高

*接受时不时网络延迟导致等待的缺点来维持复杂精确的玩法

*策划维持可玩性的底线下对一些一致性的要求求减低

实时性需求非常高的网络游戏,舍是一种关键艺术。

FPS,赛车,RTS,泡泡堂/QQ堂,模拟器联网等类型的网络游戏在一定程度上

都有以上的一个或多个取舍抉择。

---------------------------------------------------------------------

在很多实时同步的网络游戏中,都用到UDP而不是MMORPG中的TCP进行

非关键消息的同步(更有些赛车类型的游戏可能直接起用p2p网络互联):

非关键消息允许丢包,可对物体状态做客户端预测补偿;UDP甚至可方便地穿墙;

所以UDP比TCP在这种情况下更合适。Dead Reckoning是常用的客户端

预测的技术,而更细节的客户端预测对不同类型的游戏而言有不同的扩展。

这方面的技术在之前的HalfLife / Quake3 / CounterStrike等UDP中心

服务器做动作认证的FPS游戏中应用得比较成熟。

虽然觉得真正的p2p网状互联的动作同步不适合网游(一致性和防外挂考虑),

但观察了几个游戏后发现泡泡堂,跑跑卡丁车,QQ飞车,名将三国等一些游戏

确实不同程度上使用网状p2p(有些在公共场景组队就启用p2p进行位置同步),

而且有些的收发包分别都达到20个/秒的量,几乎赶上渲染帧率的一半。

直观的猜想是它们玩家间动作显示上具实时性,但关键交互结果是不具实时性的。

而道具的使用大部分都提倡范围化时延化,以减少网络延迟的影响。

这样可以简单地提高udp发包频率和数量,客户端尽情模拟后拉扯:

如果大家不作弊,如果平均每秒接到的包够多,简单插值和预测一下自然很平滑。

而关键的属性改变,战场物品掉落,死亡,胜利等,则用非实时的中心服务器

信息保证保证结果唯一性。

虽然如此,还是觉得p2p的体系应该有一定的peer间同步法则。

之前没了解过p2p体系的同步知识,所以先查阅了一些比较容易找到的paper,

所描述的分布式状态同步算法大致有如下几种。

---------------------------------------------------------------------

Lockstep Synchronization

最直观同步方案:任何peer的action要得到其他所有peer的同意才能发起。

见下图:

如果使用非网状的p2p模型,提供peer master来做统一状态运算,

那lockstep模型便成为严格的帧同步模型,这是大多RTS,模拟器联网,

以及一些提供建主机后连接的休闲动作游戏采用的同步模型;通过频繁

对时,便可以像编写串行单机指令一样来进行多个peer的事件指令同步。

lockstep原是监狱用语,指犯人列队步行时的同步串链式行进,非常形象。

最初并非设计用于游戏,而是军方的军事互动仿真程序。IEEE DIS标准指出:

100-300ms的双程总延时,对军事性互动仿真模拟是可接受的。而根据一些统计,

第一人称射击游戏中的玩家双程延迟普遍在50-300ms的区间内。

而如果玩家都在一个LAN内进行p2p连接通信,双程延迟可能控制在20ms以内。

优点:简单易实现,不会出现任何的不一致性。

在延迟小(round-trip < 100ms)且稳定的环境下非常合适。

在实时性要求不高的玩法(比如回合制玩法)中也非常合适。

缺点:游戏节奏受最慢的peer影响巨大。一人卡机,所有人受影响。

恶意的peer可以用伪造大延迟的方式来获得好处,从而破坏游戏公平性。

---------------------------------------------------------------------

Bucket Synchronization

见下图:

原paper很长,这里只按照个人理解编排步骤:

(原文是彻底分布式的网状p2p,为方便理解,下面将player B称为peer master,

并假设它负责对时任务,以省掉NTP对时系统的理解过程)

1. 设定最大容忍延迟值为 PlayoutDelay(如120ms)。

PlayoutDelay为:peer master执行指令N的时刻 - 远程peer的指令N发出时刻所对应的bucket时间段起始时间

2. 设定游戏时间轴上每隔BucketFrequency(如40ms)归一个新bucket来存放动作指令。

3. 开始游戏前,peer master发送对时命令给所有peer进行对时。

4. 因为peer master知道所有peer的平均延迟 AvgLag,那它执行步骤3后,

延迟 AvgLag 时间后,开始游戏逻辑。

5. peer master自身发出的操作指令,虽然不需要经过网络,但也需要强行延迟

投递到Ti对应的bucket中,其中:

Ti = 当前时刻所属bucket时间段的起始时间 + PlayoutDelay

6. 如果peer master接到一个动作指令,但:

peer master当前时间 - 动作指令携带的发送时间 > PlayoutDelay

那peer master会直接丢弃这个动作指令(也就是将超时到达的指令当作无效)

7. 如果peer master接到的动作指令不超过上述6中描述的范围,那会把这个指令

放入Ti所对应的bucket中,其中:

Ti = 指令包携带的发送时间戳所在的bucket时间段起始时间 + PlayoutDelay

8. 每个bucket对应的时间段末端,peer master执行这个bucket中所有的指令,

将结果状态更新到画面。

9. 因为有网络的存在,可能某些bucket中没有指令,原版MiMaze游戏实现是不处理。

但paper中提供了基于Dead Reckoning的预测状态显示方法。

10. 游戏过程中,peer master以某种时间间隔作频率进行各peer对时,

保证将各peer机器时间的误差控制在一个可接受范围内。

优点:不依赖最慢peer来决定游戏流畅度。流畅度是维持在平均预定义水平的。

缺点:对超过MaxLag的指令做丢弃。除了原paper中的3D吃豆人迷宫式游戏这种

经过特殊剪裁的游戏,动作游戏对延迟玩家的操作做丢弃,似乎很难想象。

---------------------------------------------------------------------

TimeWrap Synchronization

它是一个基于某些状态支持回滚(rollback)的同步算法。有点类似HL的做法。

简言之,就是对每个操作指令的执行后保存一个状态快照(snapshot),

各个peer按照自己的预测先行显示,但在发生一致性冲突的情况下,

回滚到上一个状态,并重新将指令序列在基于回滚后的快照的基础上再

执行一次,以获得正确的当前状态。

---------------------------------------------------------------------

Trailing State Synchronization

对TimeWrap Synchronization的一种改进。TimeWrap方案中建立snapshot是

以指令数量(1或少量几个指令)间隔为单位;而TSS方案则以某种延迟值(100ms)

间隔为单位对游戏做snapshot(比如100ms前做一个,200ms前做一个...)。

当发生一致性冲突时,寻找最远需要开始计算的snapshot,并将该snapshot到

现在为止的时间内的指令重新执行,得到正确的最新状态。

---------------------------------------------------------------------

参考文章:

<>

<>

<>

<>

---------------------------------------------------------------------

你可能感兴趣的:(网络游戏实时动作同步方案手记(1))