前言

谈到网络游戏,不可避免要谈到现有两种比较常见的网游同步技术:帧同步和状态同步
说到这两个名词,大家夸夸奇谈,都能讲上些许自己的见解,我反正啥也不懂
这篇文章就打算着重学习一下这两种技术的基础和原理

网络同步的目标就是时刻保证多台机器的游戏表现完全一致。

网络同步 = 实时的多端数据同步+实时的多端表现

同步战斗逻辑是包括技能逻辑、普攻、属性、伤害、移动、AI、检测、碰撞等等的一系列内容,这常常也被视为游戏开发过程中最难的部分。

网络同步按大类来分有两种做法:状态同步和帧同步。需要强调的是这两个概念并不是简单的对立概念,其中的差异包括:"数据格式与内容"、“逻辑的计算位置”和“是否有权威服务器”等。

一、帧同步【LockStep】

LockStep的翻译是锁步同步,是齐步行军的意思

1.1 基本原理

  • 帧同步的战斗逻辑在客户端
  • 在帧同步下,通信就比较简单了,服务端只转发操作,不做任何逻辑处理。
  • 客户端按照一定的帧速率(理解为逻辑帧,而不是客户端的渲染帧)去上传当前的操作指令,服务端将操作指令广播给所有客户端,
  • 当客户端收到指令后执行本地代码,如果输入的指令一致,计算的过程一致,那么计算的结果肯定是一致的,这样就能保证所有客户端的同步,这就是帧同步。

服务器对客户端指令进行收集和转发

1.2 帧同步缺陷

  • 由于帧同步战斗逻辑都在客户端,服务器没有验证,带来的问题就是外挂的产生(加速、透视、自动瞄准、数据修改等)
  • 网络条件较差的客户端会影响其他玩家的游戏体验。(优化方案:乐观帧锁定、渲染与逻辑帧分离、客户端预执行、指令流水线化、操作回滚等)
  • 不同机器浮点数精度问题、容器排序不确定性、RPC时序、随机数值计算不统一

1.3 乐观帧锁定

针对传统严格帧锁定算法中网速慢会卡到网速快的问题,实践中线上动作游戏通常用“定时不等待”的乐观方式再每次Interval时钟发生时固定将操作广播给所有用户,不依赖具体每个玩家是否有操作更新:

  1. 单个用户当前键盘上下左右攻击跳跃是否按下用一个32位整数描述,服务端描述一局游戏中最多8玩家的键盘操作为:int player_keyboards[8];
  2. 服务端每秒钟20-50次向所有客户端发送更新消息(包含所有客户端的操作和递增的帧号):
  3. update=(FrameID,player_keyboards)
  4. 客户端就像播放游戏录像一样不停的播放这些包含每帧所有玩家操作的 update消息。
  5. 客户端如果没有update数据了,就必须等待,直到有新的数据到来。
  6. 客户端如果一下子收到很多连续的update,则快进播放。
  7. 客户端只有按键按下或者放开,就会发送消息给服务端(而不是到每帧开始才采集键盘),消息只包含一个整数。服务端收到以后,改写player_keyboards

二、状态同步

状态同步顾名思义就是同步各个客户端的状态,保证每一次操作后的状态是一致的

通过开发服务端程序,把用户的操作作为输入实时上传到服务端,服务端通过计算返回结果给各个客户端,这样的过程就是状态同步。

2.1 基本原理

  • 状态同步的战斗逻辑在服务端
  • 在状态同步下,客户端更像是一个服务端数据的表现层
  • 一般的流程是
    • 客户端上传操作到服务器,
    • 服务器收到后计算游戏行为的结果,然后以广播的方式下发游戏中各种状态,
    • 客户端收到状态后再根据状态显示内容。

虚幻引擎的网络同步模型

如今的状态同步:增量同步、RPC(远程过程调用)两种同步手段。

目前状态同步多用于CS架构,客户端通过RPC向服务器发送指令信息,服务器通过属性同步(增量状态同步)向客户端发送各个对象的状态信息。我们可以才有预测回滚、延迟补偿、插值等优化方式

2.2 状态同步缺陷

  • 状态同步做回放系统的话会是个灾难。
  • 延迟过大、客户端性能浪费、服务端压力大
  • 对带宽的浪费。对于对象少的游戏,可以用快照保存整个游戏的状态发送,但一旦数量多起来,数量的占用就会直线上升。(优化:增量快照同步,协议同步指定数据)

三、区别(帧同步和状态同步)

  • 最大的区别就是战斗核心逻辑写在哪?状态同步的战斗逻辑在服务端,帧同步的战斗逻辑在客户端。
  • 状态同步比帧同步流量消耗大,例如一个复杂游戏的英雄属性可能有100多条,每次改变都要同步一次属性,这个消耗是巨大的,而帧同步不需要同步属性;
  • 帧同步的回放&观战比状态同步好做得多,因为只需要保存每局所有人的操作就好了,而状态同步的回放&观战,需要有一个回放&观战服务器,当一局战斗打响,战斗服务器在给客户端发送消息的同时,还需要把这些消息发给放&观战服务器,回放&观战服务器做储存,如果有其他客户端请求回放或者观战,则回放&观战服务器把储存起来的消息按时间发给客户端。
  • 状态同步的安全性比帧同步高很多,因为状态同步的所有逻辑和数值都是在服务端的,如果想作弊,就必须攻击服务器,而攻击服务器的难度比更改自己客户端数据的难度高得多,而且更容易被追踪,被追踪到了还会有极高的法律风险。而帧同步因为所有数据全部在客户端,所以解析客户端的数据之后,就可以轻松达到自己想要的效果。

区别表格

属性 帧同步(LockStep) 状态同步
确定性 严格确定 允许小误差,定时纠正误差数据
表现与响应速度 传统严格帧锁定要等其他客户端消息全部到达,响应比较慢;乐观帧锁定可以做到本地立刻响应,但是需要回滚的时候,体验就没那么好了 一般会做预测,可以做到立刻响应。不做预测的话,响应时间是一个往返时间(RTT)
带宽与流量 带宽随人数增加而增加,不适合MMO 需要发送各种状态数据,带宽占用比较高。可以通过压缩、裁剪、增量等方式优化。人数较少时候不如帧同步剩流量
网络延迟适应性 要求较低的延迟。如果延迟较高,所有玩家体验都不好。即使采用乐观帧锁定优化,高延迟下也容易产生卡顿 适应性较高,方便做各种插值优化。当然高延迟下,也容易产生位置突变
开发难度 初期开发减法,框架容易实现,但是后期解决bug和完善系统很困难。比如浮点数、随机数、执行顺序导致计算结果不一致,问题很难排查 框架比较复杂,客户端服务端一套代码,每个功能都需要客户端服务端联调。问题定位比较容易。也会出现时序问题
玩家数量 适合少量的玩家,比如ACT、MOBA 可多可少
跨平台 不适合跨平台,会有浮点数问题,可以用定点数来将误差控制在一个可接受范围,同时可以定时纠正结果 适合。有权威服务器
反外挂 P2P架构不适合反外挂,如果引入战斗服务器来校验各个客户端结果,可以解决常见外挂,但是透视和全图视野防不了 与服务器加入校验机制,可以起到比较好的反外挂效果。但是一样防不了透视外挂
中途加入和断线重连 比较复杂。可以在断线的时候,通过快捷播放服务器同步的帧数据来快速跟上游戏 容易。由于实时记录了各个对象的状态信息,所以重连的时候,直接创建这些对象,并同步信息即可
性能(客户端) 客户端要跑完整逻辑,还要执行渲染逻辑,开销比较大 可以灵活优化,客户端跑较少逻辑
回放(离线) 本身收集了所有玩家的输入信息进行逻辑推进,天然支持回放,且回放文件比较小 可以支持回放,但是逻辑比较复杂,需要不断记录状态信息,同时回放时候需要读取合适的时间。回放文件大
回放(实时) 比较复杂,客户端需要本地对全场状态进行序列化,才能回到目标时间。播完回放后还需要加速追上实时游戏状态 相对容易,可以方便的记录快照信息,并按照录制内容随时播放

帧同步和状态同步游戏

四、网络传输协议的选择

老话题,选择 TCP还是 UDP,答案是大部分时候,TCP打开 NODELAY即可,现在网络情况好了很多,没必要引入新的复杂度。即便是“帧锁定算法”上线的多人实时格斗游戏,也有在用 TCP跑着的。

当然,等到你的游戏发布出去了,开始挣钱了,你想改进你的游戏效果,特别是高峰期的卡顿比例(需要收集客户端统计),那么你可以使用 UDP来改进,《街霸4》和《英雄联盟》都是使用 UDP的,比如你可以使用 libenet(英雄联盟用的)。不过 libenet所采用的传输技术,是上世纪的标准 ARQ做法了,《街霸4》所采用的传输技术远远高过 libenet,如果你想采用更为现代的传输技术,赢得更低延迟的话,可以使用“快速传输协议-KCP”,被再若干上线项目和开源项目使用的协议,效果远远 PK libenet。

在使用 KCP时,你可以用在你 TCP的基础上,再登陆时服务端返回 UDP端口和密钥,客户端通过 TCP收到以后,向服务端的 UDP端口每隔一秒重复发送包含握手信息,直到服务端返回成功或者失败。服务端通过 UDP传上来的密钥得知该客户端 sockaddr对应的 TCP连接,这样就建立 TCP连接到 UDP连接的映射关系。为了保持连接和 NAT出口映射,客户端一般需要每 60秒就发送一个 UDP心跳,服务端收到后回复客户端,再在这个 UDP连接的基础上增加调用 KCP的逻辑,实现快速可靠传输,这样一套 TCP/UDP两用的传输系统就建立了。

中国的网络情况比较特殊,会存在有些网络 UDP连接不上的情况,因此都是先连接 TCP,然后试图 UDP,UDP不通的情况下,退回 TCP也能正常游戏,一旦 TCP断开,则认为 UDP也断开了。

不果归根结底,还是先上 TCP,再根据自己游戏的特点和是否出现传输问题,选择 UDP。

五、网络同步优化技术

下面对状态同步相关的一些优化技术进行学习。

首先问个问题:网络同步优化到底在优化什么?

  • 在单机游戏,我们从按下按键到画面响应,中间经历了:采样延迟、渲染流水线、刷新延迟、显示延迟
  • 在网络游戏,我们从按下按键到另一个机器收到指令,则会经历一个极为耗时的网络延迟
  • 网络延迟其实包括处理延迟、传输延迟(主要延迟)、排队延迟、传播延迟

我们进行网络同步优化,主要是想将网络延迟进行优化。

5.1 表现优化

表现优化主要是想弱化玩家对延迟的感受

5.1.1 插值优化

在状态同步中,由于客户端每次收到的是其他角色的位置信息,为了避免位置突变,客户端会采用插值技术,让表现更平滑,而不是跳帧。

  • 内插值的目的是解决客户端离散信息更新导致的突变问题。
  • 外插值的目的是解决网络延迟过大或者抖动导致间歇性收不到数据的卡顿问题。
  • 两种方案不冲突,可以同时采用

具体应用的时候,可以用使逻辑帧和渲染帧分离

5.1.2 客户端预测+回滚

  • 预测的目的是让玩家输入后,本地立刻收到反馈,提高游戏体验
  • 回滚是为了保证服务器的权威性

关于预测,就是本地先执行。

把玩家本地预执行的指令都记录好时间戳并存放到一个Move_Buffer列表里(类似滑动窗口)。在添加了时间戳条件下,收到了一条过时的服务器位置数据。

  • 如果玩家本地预测结果与服务器几乎一致,服务器回复一个ACKMOVE。客户端把Move_Buffer列表中对应数据清除
  • 如果预测结果与服务器不一致:需要本地回滚到服务器指定位置,把错误时刻后面的Move_Buffer列表指令执行一遍

5.2 延迟对抗

延迟对抗主要是想弱化玩家对延迟的感受

5.2.1 延迟补偿(Lag Compensation)

延迟补偿就是弥补客户端到服务器同步延迟的一项技术,核心就是 服务器在指定时刻对玩家角色进行位置回滚与计算处理。

实现方式:服务端会定时记录所有玩家位置。假设客户端到服务端的延迟为Xms,当服务端收到客户端操作后,服务端使用记录的Xms前所有玩家位置来计算是否命中,从而抵消延迟带来的问题

延迟补偿缺陷:使用延迟补偿要考虑游戏类型。不适合ACT网游

5.2.2 命令缓冲区

把远端数据缓存在一个buffer里面,然后按照固定频率从buffer里面取,可以解决客户端卡顿已经网络抖动问题。

不过缓冲区和延迟是有冲突的,缓冲区越大,证明我们缓存远端数据越多,延迟越大!

守望先锋的InputBuffer

5.2.3 假表现

真实开发环境下,我们可以根据游戏的具体情况,加一些前摇动画来掩盖延迟。比如在无敌状态前播一个前摇动画等。

5.3 丢包对抗

丢包对抗主要也是弱化玩家对延迟的感受

5.3.1 使用TCP

TCP不会丢包,对于延迟不敏感的游戏,优先采用TCP

5.3.2 冗余UDP数据包

一次性发送多个帧的数据来对抗丢包。

对于数据量比较小的游戏,可以采用冗余UDP的方案,即后续的UDP包会冗余一定量前面已发送的UDP包。

5.4 带宽优化

带宽优化的目的是减小客户端及服务器的同步压力。

5.4.1 同步对象裁剪

剔除不需要同步的对象。比如一个玩家离我很远,远到他的任何行为都不会影响到我,那我们就可以把他剔除,不进行同步给我,我不关心他的任何数据。

常见的裁剪方式有很多:SOI(Spheres Of Influence)、静态区域(把场景划分成多个区域,不在一个区域不同步)、视椎裁剪、八叉树裁剪、AOI(Area Of Interest)

着重讲一下AOI(Area Of Interest):根据玩家位置,维护一个动态的视野列表,视野外的对象会被完全忽略。实现方式有很多,常见的是基于格子的空间划分算法。虚幻引擎的大世界同步框架ReplicationGraph核心思想也是这个

5.4.2 分区、分房间

对于大型MMO来说,这是常见手段。将不同玩家分散到不同场景(不同服务器),这样可以减小服务器压力,降低延迟。但是也带来了跨服数据同步的问题

5.4.3 数据压缩和裁剪

坐标和旋转是我们场景的同步内容,但很多数据是不需要同步的。比如旋转的话,如果只需要一个Y轴旋转,我们可以传个float,而不是vector。比如我们可以压缩浮点数的精度。

然后对于状态同步,可以采用增量发送的方式来减少数据量

5.4.4 减少遍历和更细粒度的优化

对同步对象做优先级划分,发送频率调整等。

5.5 帧率优化

5.5.1 提升帧率

不同游戏的性能瓶颈不同,需要具体分析,包括:内存问题(GC、频繁申请与释放)、IO(资源加载、频繁读写文件,网络包发送频率过大,频繁读取数据库)、逻辑问题(大量遍历循环、无意义的Tick、过多的锁、高频率Log)、AI(寻路耗时)、物理问题(复杂模拟、碰撞检测)、语言特性等,客户端还有各种复杂的渲染问题(Draw Call太多,半透明,动态阴影),不断地优化,才能把帧率提高和稳定

5.5.2 保持帧率稳定和匹配

保持服务器帧率稳定

5.5.3 分摊计算压力

对于MMO这种服务器压力比较大的游戏,可以把一些复杂计算转交给客户端进行计算(甚至计算后返还给服务端),比如物理、寻路、AI等。

参考

再谈网游同步技术 - Skywind Inside

Mack:帧同步和状态同步该怎么选(上)

云影:两种同步模式:状态同步和帧同步

详细的同步历史介绍: 网络同步在游戏历史中的发展变化

网络同步在游戏历史中的发展变化

Jerish:使用虚幻引擎4年,我想再谈谈他的网络架构【经验总结】

“网络游戏同步法则”(最好给策划看看这篇,从玩法上规避)

UWA学堂 | Unity和Unreal游戏引擎的从业者学习交流平台

UWA学堂 | Unity和Unreal游戏引擎的从业者学习交流平台

https://gafferongames.com/post/networked_physics_in_virtual_reality/

《守望先锋》回放技术-阵亡镜头、全场最佳和亮眼表现-腾讯游戏学堂

【十一充电】《王者荣耀》技术总监复盘回炉历程:没跨过这三座大山,就是另一款MOBA霸占市场了

原文:【网络同步】浅析帧同步和状态同步 - 知乎

【网络同步】浅析帧同步和状态同步相关推荐

  1. 网络游戏数据同步的实现 一:状态同步、帧同步的基本原理概述

    什么是游戏的数据同步 数据同步是指使用某种方式让同在一局游戏中的多个客户端保持游戏进程同步. 什么游戏需要数据同步? 联机游戏(cs.饥荒.dead4ife2等) 网游(魔兽.天堂.传奇) 需要快照. ...

  2. 服务器解决了什么问题、状态同步和帧同步

    文章目录 一.服务器架构 二.两种同步模式:状态同步和帧同步 1.同步 2.状态同步和帧同步的区别 三.流量 四.回放&观战 七.开发效率 八.使用帧同步的知名游戏 九.断线重连 十.注意点 ...

  3. 多人网络游戏服务器开发基础学习笔记 II: 帧同步 | 游戏客户端预测原理分析 | FPS 游戏状态同步

    这篇是对书本 网络多人游戏架构与编程 的学习第二篇(第一篇:多人网络游戏服务器开发基础学习笔记 I:基本知识 | 游戏设计模式 | 网游服务器层次结构 | 游戏对象序列化 | 游戏 RPC 框架 | ...

  4. 多人网络游戏服务器开发基础学习笔记 I:基本知识 | 游戏设计模式 | 网游服务器层次结构 | 游戏对象序列化 | 游戏 RPC 框架 | 帧同步和状态同步

    今天继续开新坑,尽管过了很多 Unix 套接字编程的坑,但是实际还是有很多不同场景和性能的需求,以及最服务器架构的内容也就接触过 preforking 和 master 带 worker 而已. 所以 ...

  5. 游戏帧同步和状态同步

    在网络游戏中,服务器和客户端的同步技术是一个绕不开的话题,也是在技术选型时,首先需要确定的方案.网游中的同步技术主要有两个技术方向,帧同步和状态同步.本文简单讨论了帧同步和状态同步,整理并对比了他们的 ...

  6. 网络同步在游戏历史中的发展变化(三)—— 状态同步的发展历程与基本原理(上)...

    前言: 网络同步属于游戏开发中比较重要且复杂的一部分,但是由于网上的资料内容参差不齐,很多人直接拿别人的结论写文章,导致很多人对这一块的很多概念和理解都是错误的.本文参考了大量的相关论文和资料(花了半 ...

  7. 帧同步与状态同步:方案比较

    状态同步:大型的MMOARPG ,比如魔兽世界 帧同步:网络条件好的局域网,比如魔兽争霸,优化过的MMRPG, 比如王者荣耀 相比之下状态同步适用型更广,特别适合复杂度高,延迟要求高,玩家多的游戏,例 ...

  8. 【帧同步】关于状态同步的经验分享

    方案 低延迟环境下 比如国内,局域网情况下 写个同步那都不是难事 是个客户端看点书就会写了 难点在于 如何去处理 高延迟 以及及时响应的情况 我举个例子 fps或者tank游戏中 子弹和炮弹的射速是很 ...

  9. 主程技术分享: 游戏项目帧同步,状态同步如何选

    网络游戏开发项目中帧同步,状态同步如何选? 网络游戏的核心技术之一就是玩家的网络同步,主流的网络同步有"帧同步"与"状态同步".今天我们来分析一下这两种同步模式 ...

最新文章

  1. 【Sql Server】DateBase-视图
  2. Python正则表达式,看这一篇就够了
  3. 一个由跨平台产生的浮点数bug | 有你意想不到的结果
  4. golang多核的使用
  5. 如何以类似JSON的格式打印圆形结构?
  6. matlab基本运算与函数
  7. oracle创建表空间及用户,Oracle创建表空间和用户
  8. 西北纺织工学院97级计算机系学生毕业名单,原西北纺织工学院更名为西安工程大学...
  9. 拼多多派发“五五购物节”消费券 万张消费券30分钟被抢购一空
  10. 失范的数字货币量化市场:积弊成疾,洗牌将至 |链捕手
  11. linux学习命令总结⑩⑦
  12. 大数据行业最顶尖明星人才TOP20
  13. wordpress博客设置赞赏和内容会员可见以及跨境电商独立站
  14. 『工程项目实践』表格识别 — V2.0
  15. 斯坦福大学iOS应用开发教程学习笔记(第六课)故事版 StoryBoard
  16. 转一篇人生感悟,写的很好
  17. RISC-V Assembly Programmer's Manual
  18. 幅相曲线渐近线_若最小相位系统的低频段幅频特性的渐近线是一条斜率为20dB/dec的直线,则该系统( )。_学小易找答案...
  19. csol霸主永恒python_昔日霸主沦为下水道?黑客编程告诉你谁是剑网3新赛季最惨门派!...
  20. 2013 国家自然科学基金中标项目软件工程

热门文章

  1. CSS Sprites(精灵图)
  2. PHP的ES入门(三)—— 数据查询
  3. 项目添加到服务器报错,基于github+travis自动部署vue项目到远端服务器
  4. 多表(三个表)插入与删除操作
  5. 计算机网络谢希仁第七版课后习题答案(第四章)
  6. 超实用的油猴脚本推荐(持续更新)
  7. A081_Lucene_ElasticSearch
  8. [Java学习] 最小生成树——Prim算法
  9. 一个嵌入式牛人学习经历
  10. 将自己的网站(html页面)做成链接分享给他人(建议使用vercel)