这是一篇拖延了2年多的文章…2017年10月份开始写的,直到这次过年才写完。。。
前言
随着王者荣耀的崛起,使用帧同步(Lockstep)的游戏也越来越多,关于帧同步和状态同步的讨论争论也有不少,那么到底该选哪种同步机制呢?两种机制都使用过,各有优缺点也都踩过不少坑,这里对帧同步和状态同步进行一下总结和讨论。
首先需要说明, 这里的帧同步其实是指LockStep,是指服务器按帧转发客户端的操作,客户端进行确定性运算和一致性模拟(同步操作两边客户端通过完全一致的操作计算出完全一致的状态)。每帧同步状态这里也认为是状态同步。
这两种同步机制都是为了达到即时同步不同客户端的状态的目的,帧同步需要参与者去管理和维护其自有的那份拷贝,通过施加一致的逻辑来推动所有的状态去同步地更新;状态同步则随着时间的流逝不断地比较和发送最小的状态变化和差异。
我们先看一些使用这两种同步机制的案例:
可以看到大部分类型游戏,两种同步方式都可以使用,但绝大部分游戏使用状态同步,大量玩家战斗的游戏只能使用状态同步。
网络模型发展历程
下面简单介绍一下网络同步模型的发展。我们可以从DOOM/QUAKE I/II/III的演化来看到同步模型的发展。可以参考DOOM3网络模型的演化与网络架构这篇文章。同步方式的历程大概是帧同步(Lockstep),快照同步(Snapshot synchronization),状态同步(State synchronization),目前绝大部分多人游戏都使用状态同步。
P2P 模型 (DOOM)
DOOM (1994) 的网络模型是基于P2P的帧同步。有着帧同步的各种问题,后面会介绍。并且因为没有主机,每个玩家直连其他所有玩家任何一个人卡所有人都卡,是一个非常古老的同步技术。
Packet Server (包的简单中继)
这个模型在原版 DOOM 的基础上增加了一个 Packet Server,负责转发所有的 tick command。玩家不再直连其他所有玩家,而是连到这个服务器 (某个玩家机器上) 以获取最新的状态。这样改进后,同步量降低了,一个玩家卡只会自己卡,当然如果服务器卡就会卡。体感可以参考魔兽争霸,主机卡了所有人都会卡。同时如果主机是服务器可以避免绝大部分作弊情况,但如果主机是玩家主机作弊就没办法了。帧同步的其他缺陷也没有得到解决。
Client Server (Quake I/II/III)CS架构
Quake I/II/III 实现了比较典型的 C/S 架构 (1996)。这个模型中服务器负责所有的逻辑判断,客户端本质上只是一个渲染终端。玩家把自己的操作和输入发送给服务器,收到一个实体列表用于渲染。服务器把压缩后的快照按照固定频率发送客户端 客户端使用这些快照来插值或推导出平滑连贯的体验 。
这时候同步机制已经变成了服务器同步操作客户端计算逻辑,服务器同步状态的状态同步了,解决了帧同步的大部分问题。但Quake I还做的比较简单,和帧同步不同的是把所有逻辑相关的放在了服务器,客户端在发送操作之后就要等服务器同步状态。延迟问题还是没有得到解决,同时因为要同步所有状态信息带宽占用很高,当游戏越复杂带宽就越高。
Quake III做了进一步的优化。客户端不是等待服务器而是会预测可能的游戏状态,预测状态和服务器端逻辑使用一套代码,如果服务器和客户端确实不一致,则服务器为准强同步。
预测也是降低延迟感的一个重要方式,对延迟要求很高的FPS特别重要。并且状态同步服务器永远所有信息也能允许玩家中途加入和退出了。但同样的开发复杂度也变的更高了,代码需要区分服务器和客户端,需要逻辑和表现分离,需要处理一些联调的问题(服务器和客户端处理时间不一致,预表现差值问题,强同步问题)。
半条命(基于Quake引擎开发)在这个基础上引入了一种延迟补偿 (lag compensation),当玩家向某个目标 (若干毫秒前的状态) 射击时,做实际检测的服务器会采用该目标若干毫秒前的状态来检验是否击中。这么做需要服务器把之前一小段时间的状态持续地保存下来,这样不仅增加了实现复杂度,而且导致了某种程度的不一致性。延时高的玩家反而更容易因为补偿获得更有利的判断,严重影响游戏体验 。这种补偿只能对目标的位置回滚,而所有其他环境状态的改变却已无法倒退,这也会影响实际的体验。
Quake III 里对同步信息做了进一步压缩和优化,只有在 PVS 内的实体才会被同步状态,而且被同步的是压缩后的与上一次同步的差值 (delta compressed relative to the entity states from a previous snapshot,Delta技术) 。
可以看到当玩家比较少的时候,帧同步只需要同步操作,流量会比较小,非常适合同屏大量小兵的情况(小兵不需要同步任何信息),极省带宽。但是当玩家多的时候每个玩家的操作都要互相同步,带宽就会指数增长,无法优化,反而状态同步可以通过分区域的方式同步支持更多的玩家。
与Quake III 不同,Doom III 的服务器和客户端使用同一份代码来更新/预测实体的状态,这样不用担心服务器和客户端逻辑的互相干扰,同时客户端和服务器也一相同的逻辑帧率运行60fps,每帧客户端上传玩家输入,服务器按固定间隔同步PVS范围内的状态快照,也可以理解为按帧同步的状态同步。
Doom III在网络上使用UDP,自己通过冗余包和滑动窗口保证服务器消息不丢失和有序并且允许客户端上行丢包,在弱网情况下比TCP延迟更低,不需要TimeOut机制。
两者的优缺点对比
那么我们再来对比一下帧同步和状态各自的优缺点: