【网络同步】浅析帧同步和状态同步_帧同步如何保证客户端同步-程序员宅基地

技术标签: 帧同步  

前言

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

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

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

同步战斗逻辑是包括技能逻辑、普攻、属性、伤害、移动、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等。

原文地址:【网络同步】浅析帧同步和状态同步 - 知乎 (zhihu.com)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/oSKyTonight/article/details/129848135

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法