Games104现代游戏引擎笔记 网络游戏进阶架构

Character Movement Replication 角色位移同步

玩家2的视角看玩家1的移动是起伏一截一截,并且滞后的
interpolation:内插值,在两个旧的但已知的状态计算
extrapolation:外插值,本质是预测
内插值:但网络随着时间不停地给我信息包时,信息包可以不均匀(由于网络波动等因素),客户端可以根据给的时间将中间值插出来,保证平滑性。如用catmull曲线插值
做内插值时,从服务器来的数据包,要cache到内存,加上一些offset时间,这样在s1和s2之间插值时,有足够的时间等待s3
这样在客户端看到对方的移动是足够的平滑的。
内插值的延迟是会被加剧的
有个问题是,真正在移动的玩家1,如果做跑停跑停的状态,玩家2看到的玩家1可能会把停的状态smooth掉
人眼更倾向于连续的状态
内插值的问题:
1.加剧延迟
2.两个客户端看到的状态是严格不一样的。对于碰撞不敏感的个人移动游戏还可以接受,否则会出现双方产生不同的碰撞视觉效果

外插值:根据现在的速度,加速度,方向,预测未来一段时间,没有任何其他事情发生时,将会处于的位置
当收到一个网络包时,知道我现在已经跟真实状态不同时,如何校准。
如果与此时的网络包校准,追到时,实际上真实世界已经到了另一个状态。因此,要预判别人的动作

换气算法:
我发现我在点p,速度v,加速度a,在t0收到网络包,告知真实位置在p0,速度是v0,加速度a0。
经过一个固定时间tb,我的新的真实位置pb,等于当前的收到包告知的真实当前位置p0加上真实当前速度v0,加上真实当前加速度a0的时间平方的一半(牛顿第一第二定理)
此时客户端自身的移动预测轨迹是p,v,a确定的。次轨迹在tb时间后的预测位置是pt
此时需要一条曲线让客户端自身的移动预测位置pt能追上真实预测位置pb
需要对速度进行插值,在tb时间是,客户端速度v追上真实速度v0
同时对位置进行插值,如果没有收到新包时,自身的p,v本身也会收到自身的a影响,走到自身复制的pt位置(如上图红线),因为收到了新包,所以知道在t时间后会在新的真实pb位置(如上图绿线)。线性的在两个位置之间依次进行插值(如上图蓝线,即客户端真实展现的运动轨迹)
这样处理的好处在于,不会看到物体的位置瞬间变换,是逐步追上目标点的位置。并不符合动力学,实际就是对位置进行生硬的插值



碰撞问题:虽然可以检测到两辆车已经发生碰撞,但位置是外插值进来的,会发现车直接插进另一辆车里面。server发现两个刚体已经穿插时,很多物理引擎会给两个刚体一个很大的力来推开。导致实际上不是很重的碰撞直接弹飞了。
不同的游戏需要不同的策略

一个简单的思想:在客户端提前进行物理的检测,如果检测发生碰撞,会把位置同步的权利从网络同步和外插值的算法切换回物理引擎的算法。一段时间后再切回(如看门狗是1秒之后)。则在这1秒内完全交给本地的client的物理来结算。
有个问题是,因为输入的不同会导致不同客户端物理模拟的结果不同。这里面有很多很难的trick。
总结:做外插值时,需要考虑各种各样的边界情况
内插值:多用于fps,moba游戏。会应用在玩家会经常进行各种瞬移,或者突然有很大的加速度(注意,赛车这种其实加速度并不大,真正加速度大的是controller,即玩家自己。因为需要角色灵活有响应感。这实际上都是反物理的,几乎都是瞬移)。内插值相对比较稳定(外插可能看到其他人穿墙又来回,可以本地做物理检测解决)

外插值:适合整个运动符合物理学规律的,如开船开车的游戏
真实游戏里,可能会把两种算法结合到一起。在角色移动时,使用内插,保证安全性。上了载具后,又使用外插
Hit Registration 命中判定


难题1:敌人在哪。灰色真实位置,半透明是服务器位置,然后是客户端看到的位置

难点2:对方不停移动,怎样算打中。以及我看到对方在掩体外,但对方实际在掩体内
在一个网络不确定的环境下,让所有的客户端对是否命中目标达成共识(不是真实)
解决方法:
第一种,客户端决定,hit detection
第二种,服务器处理,hit-registration

客户端判定的游戏,大地图,许多玩家,如PUBG,非常动态非常复杂的世界(可以拆屋子,有大量破坏),如战地

就是一个hitscan,去扫描一下能否打中。
3种弹道:
1.速度无限快的直线,开枪的瞬间就判定直线位置
2.有飞行轨迹的直线
3.抛物线
客户端3种都行,只以本地看到的为准,非常准确,符合人的直觉。都是射击手感比较好的游戏

服务器会做一个验证,但是验证通常不会特别麻烦,客户端会发送开枪的位置,及击中的目标,击中的位置给服务器和服务器记录的自身的位置做验证,是否距离过远。如果符合距离,验证射击直线上是否有障碍物。
真实游戏,如守望先锋,服务器以受击者的位置(上图黄圈),周围红框的范围,只要打中这个box,就判定打中。除此还有一些其他的防外挂判定
如果选择客户端判定检测,服务器很多的校验只是做一些基本情况的验证。

优点:
1.高效,很多计算在客户端做完
2.准确
缺点:
1.非常不安全,客户端或者网络包被破解会有很多外挂
2.lag switches:客户端主动断开网络,则没有新的网络包接收,客户端的敌人就会暂时停住。此时射击敌人,然后1s或者0.5s后,重连网络。因为服务器没有判定客户端掉线。服务器会收到一个相对真实的射击消息
3.客户端检测,弹药可以无限,进行无限的射击判定
服务器判定
最基本的问题:服务器看到的人位置会比客户端看到的人位置更快(服务器人已经跑进掩体,客户端并没有),客户端无法对移动的真实位置进行锁定
延迟补偿
当服务器收到客户端一个开枪的消息时,知道客户端射击的位置,方向。服务器不会用现在的受击者的状态做判定。会把状态的时钟返回退,退回一个延迟的时钟,以当时的受击者状态做判定。
即服务器根据延迟,算法猜测客户端射击瞬间时客户端的世界是什么状态。
因此服务器要对世界在每一个tick时将state做一个快照,保存一段时间,形成一个buffer,来对不同延迟的客户端进行处理
fps很多时候不止网络延迟,内插值还会延迟一段时间,才得到对方的位置
服务器时间需要减掉网络延迟(Packet Latency),以及客户端的内插值延迟(Client View Interpolation Offset) (即上图蓝色box)
红色box是客户端本地看到的状态
根据不同算法,内插值还是外插值,需要单独的处理延迟补偿。甚至本地可能用了物理检测
服务器检测问题
掩体问题:
1.我认为我躲进了掩体,但是由于延迟在对方客户端我仍为躲进掩体,服务器是以开枪人的世界做判定。我仍被击中
2.我已经在掩体后,观察别人。因为延迟的原因,其实已经看到别人,但因为我的位置仍未同步过去,在对方世界里,我仍在掩体后,无论是以发现还是射击作为收益,都会有先手优势。网络延迟越大,优势越大。
职业的fps射击比赛,会选择用局域网,控制延迟足够小

攻击前摇:攻击加起手动作,几帧的动作(50ms/100ms)能有效降低在网络波动,延迟情况下导致的客户端和服务器状态不同步延迟
因此很多动作游戏,建议做攻击的前摇,为网络延迟争取时间

虽然是服务器做命中检测,但是可以客户端也做命中的判定,如果命中,可以先播放击中的特效。只是纯粹的受击效果展示,真实的伤害结算仍以服务器计算为准
MMOG Network Architecture MMOG网络架构

MMO:很多玩家在一个世界online在一起,MMORPG,MMOFPS,MMOMOBA
游戏子系统
User management:玩家管理器
Matchmaking:匹配系统
Trading system:交易系统
Social system:社交系统
Data storage:数据系统
。。。

简易架构:
连接层:login,gateway。管理用户链接
服务层:各种玩家服务
数据层:各种DB

Login Server:https,完成链接,验证账号密码
Gateway:网关服务,把服务器内外网隔绝开,用户永远只跟gateway交流,gateway负责和内部的角色服务器,大厅服务器。。。去talk。类似防火墙 。验证客户端发送的所有信息的合法性,有效性,包括拦截一些攻击。玩家的链接的加密解密,数据解压缩都在Gateway。通常会用户数量增长越开越多
大厅:多数时候作为一个缓冲池,让玩家在等待例如matchmaking的时候,彼此间能够被管理起来。有时不一定要有真实的场景,可以是个虚拟大厅

管理所有玩家的真正属性、数据。包括玩家的邮件,装备,道具

有非常重的金融属性,保证绝对的安全性,原子性。每一笔交易全部可以rollback。保证任何情况下,做的每一个transaction,都能被准确记录下来,不用担心出现异常数据
有时候会变成一个独立的server。邮件,聊天有自己专门的server。一个单一的server在很多人在一起操作时,可能会炸掉
不仅要考虑玩家间的ranking值,还要考虑彼此间的延迟,尽可能把延迟相近的玩家匹配在一起,保证大家的体验是一致的。
一般开黑的玩家会被扔到一个单独的池子匹配



3类数据存储方式:
1.关系数据库:如mysql。实际上在网游的海量数据下,采用的一般是分布式的数据架构。同时写的时候会写到很多个并行的数据表里,确保写的效率足够高。万一有一个服务器挂掉,数据不会丢掉
2.非关系数据库:访问速度更快,更轻量的数据库,如mongoDB。很多大量的log信息,或者一些临时的state,不需要特别及时,复杂的查询,但又希望访问。
3.内存数据库:如redis。许多服务器同时在跑,可能产生很多的游戏的中间数据,需要一个效率非常高,能够管理数据,但不需要特别重的读写磁盘,只需要在内存中保存下来


大量玩家时,服务器采用分布式架构。
主要都是一些服务器的开发

负载均衡



一致性哈希
服务器和player都用一个哈希算法,算出一个0-2的32次方的值,将数域分布成一个环,服务器也分布在一个环。所有的玩家数据,逆时针方向找到最近的server作为存储
如果s2服务器被释放掉,s2服务器的数据,逆时针顺沿存储到s3
注意,哈希算法设计的要使服务器分布的尽可能均衡



Bandwidth Optimization 带宽优化

带宽优化的意义:数据量过大,会产生拥塞,造成延迟。延迟过大时,有些网关会主动掐断
带宽计算:
n 玩家的数量,f 更新频率,s 更新的数据包体大小


数据压缩
最常用的一种数据压缩方法:将浮点数转成定点数。如果世界不是特别大,用一个16位的定点数可以精确到以厘米位单位的位置
为了配合这种方法,会对游戏地图进行分区。每个区不会特别大,以便于定点数存储位置

只更新玩家相关的object,不需要同步全地图
把世界分成一个个静态zone,将玩家放在zone里

如果世界是开放的,不希望有传送门的概念,这试验AOI
以我为中心,我只关注我周围的object
最简单的AOI:定义一个半径,在整个世界里进行一个query


对空间划格子:
只关注我周围3x3的格子,形成一个AOI列表。当有entity进入或离开zone,通过event更新。同理我进入或离开一个zone,也会notify其他人。
本质是空间换时间



十字链表法
假设空间是2D分布,以x轴为方向,所有的object排个序,再沿着y轴排个序。
如果我的AOI是150m,在x轴上,前后各走150m,得到一群id,在y轴上同样操作。同时满足x轴与y轴的id就是AOI里的entity。
当有任何物体在移动时,会去notify其他人,更新各自的AOI
PVS:根据所在的位置,预先生成好了visiblity的set,不在pvs里的object不关注。对空间分割感更强的游戏更实用
降低server传递数据的频率,尤其对于近战游戏,近处的object同步频率高,远处的object同步频率低
Anti-Cheat 反作弊

直接修改游戏主体,修改内存,破解客户端
破解底层sdk
劫持网络包,发生假消息
作弊方式:查内存,定位内存中的敏感数据。修改数据


反作弊:
加密客户端,运行时再解密。“给客户端加壳”。现在的这种方式几乎都被破解
内存混淆:高度敏感中的数据,在内存中加密,使用的瞬间解密读取
作弊方式:修改本地的文件资源。例如fps游戏,把资源改成发光的材质
反作弊:客户端不停地算本地文件的哈希值,上传哈希值到服务器进行对比校验
作弊方式:网络包劫取


反作弊:
加密网络包:
1.对称算法加密:客户端与服务器共享一个密钥,根据密钥通讯。客户端可能被破解,暴露密钥。
2.非对称算法加密:客户端拥有一个公钥,服务器有一个私钥。 速度较慢,成本较高。一般只登录时使用一次。当网络建立好,用加密的方式把对称的钥匙传递给客户端。接下来服务器与客户端的通讯用一次性的对称加密钥匙
作弊方式:软件注入

反作弊软件:扫描内存中游戏的签名,如果内存中被注入一些奇怪的代码,或者checksum,哈希值不对。检查内存是否被更改过
作弊方式;ai作弊。 比较难防
监察者模式:检查到可疑视频,上传给监察者,让其他玩家打分管理,有主观因素。不准确。是与技术无关的反作弊方法

通过大数据的深度学习,去判定玩家的行为模型。正常玩家的模型是有pattern的
在内存中扫描已知的外挂程序。对商业性质的外挂有较好的判断
Build a Scalable World

开放世界的构建分成3种模型
1.zoning:将世界分成一个个zone
2.instancing:副本
3.replication:将世界分成很多个虚拟的层

玩家的分布是不均匀的,所以zone是动态的划分的。一般用四叉树

玩家跨边界:每个角色的AOI有关注的半径,zone之间会做一个边界,一个角色到边界时,另一个zone的玩家需要能看到边界的角色
只要一个entityA在zoneA的border区里,会在zoneB做一个对应的ghost entityA,虽然真正的entityA在zoneA里,但是zoneB的玩家仍然能看见。只不过真正的逻辑,行为仍由entityA去处理
跨越边界的瞬间,本来在zoneA的实体entityA,把数据迁移一下,将zoneB里的ghostA变成entityA,将zoneA里的entityA变成ghostA。
实际做的时候,会有一个缓冲区,设定一个阈值,穿过边界一段距离后才迁移数据,避免边界线来回跑
每一个character扔到多个游戏的镜像去处理,其他层的玩家对于本层的玩家都是ghost
相关文章:
Games104现代游戏引擎笔记 网络游戏进阶架构
Character Movement Replication 角色位移同步 玩家2的视角看玩家1的移动是起伏一截一截,并且滞后的 interpolation:内插值,在两个旧的但已知的状态计算 extrapolation:外插值,本质是预测 内插值:但网络随着…...
Apollo 快速上手指南:打造自动驾驶解决方案
快速上手 概述云端体验登录云端仿真环境 打开DreamView播放离线数据包PNC Monitor 内置的数据监视器cyber_monitor 实时通道信息视图福利活动 主页传送门:📀 传送 概述 Apollo 开放平台是一个开放的、完整的、安全的平台,将帮助汽车行业及自…...
C现代方法(第14章)笔记——预处理器
文章目录 第14章 预处理器14.1 预处理器的工作原理14.2 预处理指令14.3 宏定义14.3.1 简单的宏14.3.2 带参数的宏14.3.3 #运算符14.3.4 ##运算符14.3.5 宏的通用属性14.3.6 宏定义中的圆括号14.3.7 创建较长的宏14.3.8 预定义宏14.3.9 C99中新增的预定义宏14.3.10 空的宏参数(C…...
Kafka KRaft模式探索
1.概述 Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。其核心组件包含Producer、Broker、Consumer,以及依赖的Zookeeper集群。其中Zookeeper集群是Kafka用来负责集群元数据的管理、控制器的选举等。 2.内容…...
LVS-keepalived实现高可用
概念: 本章核心: Keepalived为LVS应运而生的高可用服务。LVS的调度无法做高可用,预算keepalived这个软件,实现了调度器的高可用。 但是:Keeplived不是专门为LVS集群服务的,也可以做其他服务器的高可用 LVS…...
Linux内核驱动开发的需要掌握的知识点
Linux内核驱动开发是一项复杂而有挑战性的任务,需要掌握多方面的知识和技能。下面是一些需要掌握的关键知识点,这些知识将有助于你成功地开发Linux内核驱动程序。 1. Linux内核基础知识 首先,了解Linux内核的基础知识至关重要。这包括Linux…...
nginx 动静分离 防盗链
一、动静分离环境准备静态资源配置(10.36.192.169)安装nginx修改配置文件重启nginx 动态资源配置(192.168.20.135)yum安装php修改nginx配置文件重启nginx nginx代理机配置(192.168.20.134)修改nginx子自配置文件重启nginx 客户端访问 二、防盗链nginx防止…...
MYSQL(索引篇)
一、什么是索引 索引是一种数据结构,它用来帮助MYSQL更高效的获取数据 采用索引可以提高数据检索的效率,降低IO成本 通过索引对数据排序,降低数据排序成本,降低CPU消耗 常见的有:B树索引、B树索引、哈希索引。其中Inno…...
Java API访问HDFS
一、下载IDEA 下载地址:https://www.jetbrains.com/idea/download/?sectionwindows#sectionwindows 拉到下面使用免费的IC版本即可。 运行下载下来的exe文件,注意安装路径最好不要安装到C盘,可以改成其他盘,其他选项按需勾选即可…...
高三高考免费试卷真题押题知识点合集
发表于安徽 温馨提示:有需要的真题试卷可联系本人,百卷内上免费资源。 感觉有用的下方三连,谢谢 。 免费版卷有6-60卷每卷平均4-30页 高三免费高三地理高三英语高三化学高三物理高三语文高三历史高三政治高三数学高三生物 付费版卷有1…...
css 计算函数属性:calc() 不起效 原因
踩坑:注意事项(- 减号或加号前后需要空格!!!) calc(100% - 251px); 这里错误写法中-两边没加空格,导致width不生效。但并不是所有运算符间都需要加空格,只有 和 - 需要加空格,因为运算允许负…...
2、TB6600驱动器介绍【51单片机控制步进电机-TB6600系列】
摘要:本节介绍TB6600驱动器界面及关键参数设置 一、驱动器功能界面 二、关键参数 输入电压:DC9-42V 输出电流:0.5-4A 最大功耗:160W 细分设置:1,2/A,2/B,4,8,16,32 工作温度:-10~45C 信号口驱动电流&…...
Vue3:将表格数据下载为excel文件
需求 将表格数据或者其他形式的数据下载为excel文件 技术栈 Vue3、ElementPlus、 实现 1、安装相关的库 下载xlsx 和 file-saver 库 npm install -S file-saver npm install -S xlsx引入XLSX库和FileSaver库 import XLSX from xlsx; import FileSaver from file-saver;…...
vue+Fullcalendar
vueFullcalendar: vueFullcalendar项目代码https://gitee.com/Oyxgen404/vue--fullcalendar.git...
Spring定时任务+webSocket实现定时给指定用户发送消息
生命无罪,健康万岁,我是laity。 我曾七次鄙视自己的灵魂: 第一次,当它本可进取时,却故作谦卑; 第二次,当它在空虚时,用爱欲来填充; 第三次,在困难和容易之…...
C语言学习笔记(六):数组(1)
0,问题的引入 怎么保存一个学生的成绩 float a; 怎么保存一个班(10人)的学生的成绩 float a,b,c,d......; float a1,a2,a3,........; 这样太麻烦了 -》“数组” 1,数组 什么是数组ÿ…...
apk反编译修改教程系列-----修改apk中的图片 任意更换apk桌面图片【三】
往期教程: apk反编译修改教程系列-----修改apk应用名称 任意修改名称 签名【一】 apk反编译修改教程系列-----任意修改apk版本号 版本名 防止自动更新【二】 这次实例演示下如何更换apk安装后的桌面图标图片。其实这个步骤前面我有一个教程贴。这次针对步骤做个补…...
【IO面试题 五】、 Serializable接口为什么需要定义serialVersionUID变量?
文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 面试官: Serializable接口为什么…...
san.js源码解读之模版解析(parseTemplate)篇——readIdent函数
一、源码分析 /*** 读取ident* 这里的 ident 指标识符(identifier),也就是通常意义上的变量名* 这里默认的变量名规则为:由美元符号($)、数字、字母或者下划线(_)构成的字符串** inner* param {Walker} walker 源码读取对象* return {string}*/ functio…...
【excel技巧】excel单元格内如何换行?
Excel表格,在制作完成之后,在输入数据的时候,总是会遇到内容长度太长导致无法全部显示或者破坏表格整体格式。几天分享4个单元格换行的方法给大家。 方法一: 首先我们先介绍一个,通过调整列宽的方式来达到显示全部内…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
从零实现富文本编辑器#5-编辑器选区模型的状态结构表达
先前我们总结了浏览器选区模型的交互策略,并且实现了基本的选区操作,还调研了自绘选区的实现。那么相对的,我们还需要设计编辑器的选区表达,也可以称为模型选区。编辑器中应用变更时的操作范围,就是以模型选区为基准来…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...
