多人网络游戏服务器开发基础学习笔记 II: 帧同步 | 游戏客户端预测原理分析 | FPS 游戏状态同步
这篇是对书本 网络多人游戏架构与编程 的学习第二篇(第一篇:多人网络游戏服务器开发基础学习笔记 I:基本知识 | 游戏设计模式 | 网游服务器层次结构 | 游戏对象序列化 | 游戏 RPC 框架 | 帧同步和状态同步_我说我谁呢 --CSDN博客),内容还是以基础为主。第一篇主要是讲解了网络多人游戏的一些最基础的知识。同时因为一些点书本内容太简略,所以参照学习了 GDC 2017 守望先锋对 ECS 架构涉及和网络同步的视频分享结合讲解加深理解。尝试提供所有必要基础知识理解游戏客户端预测(但是只是基础角度的分析,有需要深入学习的时候直接看视频)。对于守望先锋 ECS 架构部分这里不涉及,那部分内容属于 如何设计对象系统的部分,可以看云风大佬的分析。GDC 的视频连接下文会给出。
对于游戏中的各种帧,我之前专门总结了一下,从显卡,操作系统的硬件中断,定时器和 gameloop 来讲到,这里放个连接:游戏开发基础笔记:逻辑帧和物理帧辨析 | Gameloop | 游戏循环_我说我谁呢 --CSDN博客
帧同步(命令同步)和状态同步概念澄清(OW 是帧+状态同步)
- 前面其实讲过一次,但是书本的概念不是很清晰,这里补充一下。
- 帧同步的实现是定义逻辑帧(锁同步?),服务器的作用是用来同步指令,然后发送,客户端本地按顺序演算,支持战斗回放。就是前面说的确定锁同步网络模型。
- 状态同步是服务器运算说有点状态,然后计算好下一个的状态再返回给客户端同步全局状态。
- 但是状态同步和客户端先行不是同等级别的概念,为了防止客户端看到操作和结果之间时间点的割裂,必须再画面表现上优化,客户端先行、平滑插值等在表现上降低对延迟的感受。这里的机器猫完全没有这个实现。RPG游戏中,动画的特效一般做的比较长时间,看着好看同时延长网络响应时间, 攻击的时候给人感觉是击中了。放技能也有一个前摇,同时将攻击请求提交给服务器。等服务器结果返回时,动画也播放完毕了,之后就是更新状态和 HUD 而已。
- 还有一个问题是反作弊,对于 RTS 和帧同步的方案,只需要定时服务器(或者某个客户的托管服务器,或者大家)校验一下就行了,但是这个只是防止了数据作弊,对于信息泄漏的话可能无解,因为运算和所有数据都在客户端,完全可以全部 dump 出来,包括视野信息,以及透视等。而状态同步不会泄漏运算流程。
- 然而对于手游,客户端运算(帧同步)才能保证在各种无线网络下的延迟问题,这是和网络因素决定的(就和 TCP 的基础设施反而和网络不相适应导致要定制 UDP 一样)。这种反作弊的确就用校验可以解决,最常用的做法验算,确定客户端随机数种子,还有客户端操作。那么记录正常战斗的数据,放到其他客户端去验算,看看验算能否通过即可。不过这样针对泄漏游戏内部运行逻辑信息的外挂的确没办法了。
- 帧同步的好处是,客户端可以像单机那样运行,只需要把 Input sampling 和实际的 input(其他玩家的input来自网络,自己的input从 input sampling 里面拿)拆开就行了,这样就等于用网络输入 hook 在单机上面。状态同步的客户端开发是不能这样的。
- Realword 部分,王者荣耀,皇室战争是帧同步,魔兽用的是帧同步,lol 可能是状态同步(没有相关信息,存疑),这部分资料有些难找,腾讯游戏 gameplay 大佬的文章的对现有各种著名游戏的同步方式以及 CS 还是 p2p 的总结 图片来自这里:网络游戏同步技术概述 - 知乎 (zhihu.com)(但是这个主要是理论说明没有涉及实现,所以我就只摘这两幅图了,由于原因,这里不放图片了,请点进连接阅读)。
C/S 服务器架构实例
这篇内容是第六章第一部分,讲解的是一个 C/S 架构的状态同步的例子。(由于主要还是以书本学习为主,需要这部分了解了才能讲到下面的内容,这部分也是前面说的提供必要的基础属于了)。
权威服务器,专用服务器和监听服务器(托管,比如通过某种云服务提供的),对于托管服务器意思是客户端(其中一个玩家)本身充当服务器。然后托管服务器可以实现 host migration 专用服务器需要配置备用服务器。对等网络的时候除了伪随机数,还要考虑状态一致性。
书本的 demo 机器猫行动MultiplayerBook/MultiplayerBook (github.com) ch6 代码,有 VS 的读者可以下载来感受一下,只需要运行 win32 的生成,使用 SDL2 多媒体 2d 库开发的。下面说一下这个怎么运行:
编译好之后在 Debug 目录下面应该会有 RoboCatServer 和 RoboCatClient 的可运行 exe,这个时候进入 cmd 里面输入这个运行一个 S 两个 C:
RoboCatServer 45000
RoboCatClient 127.0.0.1:45000 Aohn
RoboCatClient 127.0.0.1:45000 Bohn
sdl 是接口而已,实现可以用 opngl 的接口进一步封装的,带有窗口和输出输出管理。注意事项是这个游戏必须要用 win32 编译。提供一些必要的上下文信息供读者(我)阅读时用,这个例子讲的是一个 CS 架构的游戏,玩家是两个猫,然后猫按键盘移动和发射射线攻击其他猫。所有的运算都在 server 上进行,读者(我)应该可以无障碍阅读下面内容了。
这个是一个 UDP 来的,而且第六章基本是 minimum code,所以没有流控和重传。
代码分离
- 首先是 CS 的代码分离,对于整盘游戏实际是在服务器演算的,这样就涉及两套东西,一套是给 UI 用的,一套是给服务器运算的。对于 common attribute 或者 member function 可能就要做一个 base class 。
- 游戏逻辑的共享,以及 socket helper 的共享,从而做出层次结构来。比如网络库会有公共都要用到的发包接包的逻辑,抽象一个 manager 出来,然后 client 主要是封装 C 请求解析 S 响应,然后 override 就行了。
- 服务器跨平台的问题,对于这个我的想法是只要他发包和收包序列化的逻辑相同就行了,所以我完全可以搞到 linux 上,不过前台进程转 daemon 的代码要另外做而已。最好还是用第三方的已经搞过适配层的,实在不行可以自己造轮子。
UDP 握手
- 对于 socket helper 的类这里不再看了,但是感觉还是得自己写一套熟悉 socket api 和各种选项。不过 windows 下又没有 epoll 这种,高性能保证需要用 windows 提供的 api,这个实际不靠谱,所以服务器和客户端肯定得分开来开发的。
- 机器猫全部用的静态工厂模式,禁用了默认构造,这个可能是一种完全委托给 shared ptr 管理的
- 就是用之前说到 4 char int 来标识一个包的头部
- hello 包和 welcome 包。
- 服务器分发 client id 过程,使用 autoincrement variable 就行了。
- 原来 NONBLOCK 这么消耗 CPU 的,如果开了 NONBLOCK 然后线程又不 sleep,结果就是一直 context switch 来去,没有用户的情况下都会占满 CPU(属于是死循环了)。这还只是 UDP recvfrom 而已。额,所以为什么标准的服务器进行 event loop 不会过大的 CPU 占用率呢?nginx 这种没什么连接的时候也就几,有连接也稳定十几,这是因为 epoll 这种阻塞吧。这里这个例子直接死循环 recvfrom 的确不太靠谱。
- robust 1,对于 UDP 而言,必须处理hello 失败的情况,就是没有收到 welcome,然后他会重新发hello,但是又不能连续发 hello 因为 hello 发多了等于引发 congestion,并且之后对于迟来的 welcome 会增加(那还不如用 tcp 呢),所以需要等待一定的间隔。当然最好的方法是首先通过某种方法测量 rtt(然而 hello 是第一个包),然后对于迟来的 welcome,应该丢弃他,所以要判断当前的状态是不是已经被 welcomed 了。
- 吐槽一下 C++ 头文件分离,太坑了,什么 intellisence 的跳转都不好用,太难受了。然后 editor 一般有跳转功能,主要是这个功能各家快捷键都不一样,vscode 的一个 f1 打开命令栏可太好用了。这里mark 一下 clion 是 ctrl shift +n, vs 是 ctrl+,
状态机
- 通过状态机的方法控制发包的类型,全程用一个成员变量 mstate 来做这个控制,这样不用传参(就是对于一个类来说的)。
- 状态机模式能保证只有在当前的状态可以被数据包转移才进行转移,于是顺利完成了迟来的重复 welcome 的的丢弃和正确的转移。而且不用各种参数传递,只需要维护当前状态就行了。
circular buffer
- 解耦 Socket 层和 application 层,中间用一个 NetworkManager 来,直接操作 Packet 类而不是操作流(UDP packet 可能包含多个 application packet,所以本质还是当成流看待的)。
客户端 IO
- 这里客户端会涉及一个问题是多个事件等待,然而windows 又没有 epoll,看看他是怎么解决的。
- 因为用到了 SDL2 来做多媒体 IO 处理,这里键盘事件是由 SDL 负责的,SDL 提供 wait event 和 poll event 两种调用来处理各种游戏设备输入,由于我们需要同时处理键盘事件和网络事件,所以这里只能用 non block 的 sdl poll,正如其名就是 polling 所有设备看看有没有事件(内部可以用 queue 实现,可能会是高效率的)然后返回。
- 非阻塞能让我们自己实现多路 IO 复用了属于。。。不过有点空转了。不过游戏本来就不可能不占用 CPU。
- 这里的思路是,先 poll 一下键盘,如果有键盘就响应键盘(更新 inputstate)。然后没有键盘才 do frame。我很好奇这里的时序问题,因为如果处理了事件,会不会引发一个消息发给 server 呢?后面能看到不会,这里是用锁同步的方法的。
- doframe 做的事情有很多。
- 首先是更新当前捕获的状态,但是会先判断一下是否到达采样点。再更新到 move 里,所以 move 是 input 的采样,而 move 在之后会发给 server。
- 然后接收一个包(真实实现是从网络读一个包进 queue,然后再从 queue 里面接收一个包的),这个包理论上是 FIFO 的,但是对于 UDP 可能过程寻路的问题,导致顺序不一样。这样搞你要决议了,必须约定处理顺序,这样很麻烦。另外一种方法是 1:1 ack ,这种太坑了,不过是必要的。
- 上面这个包会更新服务器的最新运算成果,然后把他 render 出来。之后再把这次的 move 发出去(当然的,如果没达到 buffer 的操作的限制是不会发送的,等于什么都不做)。
- 这里的 move 只是一个 采样,但是实际编写应该要累积所有操作(额,不过对于可覆盖的操作的确没必要,比如 sprite 的 replication 的确是采样就行了)。第七章继续研究。
- 这里还讲解了一个冗余数据的东西一个操作发三个 UDP(我感觉还是 ACK 靠谱,或者这个也有道理的,如果是你的网络不好你丢包了,那你放的技能没有发出来也是正常的,但是我觉得可能还是会引发拥塞,这个不能想当然,得实测才知道。而且必须考虑到整个路径上会有很多重传的请求的,比如玩家不断地在那里点他,理论也会触发多次发送(除非客户端进行某种缓冲),这个和 TCP 的网页那个差不多了属于,因为某个地方网络差了,一直刷新,这种问题要让客户端做一套防护,同时服务端,cdn 什么都得做,特别是查询数据库)。
服务端结算(即状态同步)
- ClientProxy 类的作用,server 必须维护所有玩家的信息,这样才能路由包到 handler?(我感觉因为包里面有 uid 了,所以用 ip port 来做这个没必要)。不过根据异步处理的思想,这个用 Proxy 类来管理动作方便一点,对于他的 move,只需要 serever 进行游戏演算的时候调用 proxy 的时间然后引发就行了,而不用解析到包马上就执行游戏演算。
- 帧率同步的问题,对于积累的多个动作演算,服务器运行的时间也要同步,这个还没搞懂。对于运算完全在 server 进行,这样小猫移动的时候不久不能即时看到反馈了?但是实际网络运行很快的,这个应该不成问题。
- 结算之后要发回去,server 的主要工作是这样的(doframe):
- 首先是读包,检查掉线问题。
- 如果需要重建猫猫(比如倒计时复活)
- 更新全世界(调用所有对象的 update),调用 update 函数,这个在 server
多人网络游戏服务器开发基础学习笔记 II: 帧同步 | 游戏客户端预测原理分析 | FPS 游戏状态同步相关推荐
- 多人网络游戏服务器开发基础学习笔记 I:基本知识 | 游戏设计模式 | 网游服务器层次结构 | 游戏对象序列化 | 游戏 RPC 框架 | 帧同步和状态同步
今天继续开新坑,尽管过了很多 Unix 套接字编程的坑,但是实际还是有很多不同场景和性能的需求,以及最服务器架构的内容也就接触过 preforking 和 master 带 worker 而已. 所以 ...
- ASP.Net MVC开发基础学习笔记(5):区域、模板页与WebAPI初步
http://blog.jobbole.com/85008/ ASP.Net MVC开发基础学习笔记(5):区域.模板页与WebAPI初步 2015/03/17 · IT技术 · .Net, Asp. ...
- ASP.Net MVC开发基础学习笔记(1):走向MVC模式
原文出处: Edison Chou的博客(@周旭龙EdisonChou) 一.ASP.Net的两种开发模式 1.1 ASP.Net WebForm的开发模式 (1)处理流程 在传统的WebFor ...
- [转]ASP.Net MVC开发基础学习笔记(3):Razor视图引擎、控制器与路由机制学习
[出处]http://www.cnblogs.com/edisonchou/p/3923475.html 关于机制的介绍,讲得不错,觉得可以参考着学习一下 1.1 千呼万唤始出来的MVC3.0 在MV ...
- ASP.Net MVC开发基础学习笔记:五、区域、模板页与WebAPI初步
一.区域-麻雀虽小,五脏俱全的迷你MVC项目 1.1 Area的兴起 为了方便大规模网站中的管理大量文件,在ASP.NET MVC 2.0版本中引入了一个新概念-区域(Area). 在项目上右击创建新 ...
- ASP.Net MVC开发基础学习笔记:三、Razor视图引擎、控制器与路由机制学习
一.天降神器"剃须刀" - Razor视图引擎 1.1 千呼万唤始出来的MVC3.0 在MVC3.0版本的时候,微软终于引入了第二种模板引擎:Razor.在这之前,我们一直在使用W ...
- ASP.Net MVC开发基础学习笔记(3):Razor视图引擎、控制器与路由机制学习
一.天降神器"剃须刀" - Razor视图引擎 1.1 千呼万唤始出来的MVC3.0 在MVC3.0版本的时候,微软终于引入了第二种模板引擎:Razor.在这之前,我们一直在使用W ...
- ASP.Net MVC开发基础学习笔记:四、校验、AJAX与过滤器
一.校验 - 表单不是你想提想提就能提 1.1 DataAnnotations(数据注解) 位于 System.ComponentModel.DataAnnotations 命名空间中的特性指定对数据 ...
- ASP.Net MVC开发基础学习笔记(4):校验、AJAX与过滤器
原文出处: Edison Chou的博客(@周旭龙EdisonChou) 一.校验 - 表单不是你想提想提就能提 1.1 DataAnnotations(数据注解) 位于 System.Comp ...
最新文章
- VS2010运行速度优化汇总
- 使用let替换var实现块级作用域的小发现
- 我什么时候应该使用结构而不是类?
- 如何在Anaconda中安装Pytorch
- 个性化服务谋定移动电子商务-李玉庭:经信研究重整购物
- 我是怎么利用微信做兼职月入1W的
- the NTP socket is in use, exiting
- cardsui-for-android
- Set的5种遍历方式
- java判断zip包的编码格式_java解压zip包出现乱码
- 【项目管理】------九大项目管理框架 (
- Postman POST方式提交json数据,PHP接收
- STM32示波器设计
- mac系统postman+newman生成测试报告
- Python之禅——个人翻译
- 信息安全实验:信息摘要函数的设计与验证
- 阿里云轻量级GPU计算型vgn6i云服务器配置性能详解
- Oracle 全文索引
- 【hh】我胡汉三又回来了
- PBN飞越转弯Flyover衔接DF航段保护区组图
热门文章
- 深入理解JVM-内存模型(jmm)和GC
- 这些是 Python 官方推荐的最好书籍(推荐)
- 利用云服务器搭建hadoop集群
- ASP.NET MVC 实现页落网资源分享网站+充值管理+后台管理(8)之文章管理
- 管理员技术(二): 访问练习用虚拟机、 命令行基础技巧 、 挂载并访问光盘设备、ls列表及文档创建、复制删除移动...
- Python生成声音波形、模拟钢琴音色
- Python爬取12306车票信息
- MyBatis 的级联查询
- 按键android手机排行榜,【直板全键盘手机推荐】直板键盘手机排行榜
- ESPG和OGC、SRS、SRID指的是什么
- 多人网络游戏服务器开发基础学习笔记 I:基本知识 | 游戏设计模式 | 网游服务器层次结构 | 游戏对象序列化 | 游戏 RPC 框架 | 帧同步和状态同步