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个单元格换行的方法给大家。 方法一: 首先我们先介绍一个,通过调整列宽的方式来达到显示全部内…...

SSD1306 oled显示屏的驱动SPI接口
有IIC接口 和SPI接口 还有8080,6080接口等 arduino SPI接口 直接使用u8g2库实现 //U8G2_SSD1306_128X64_NONAME_F_4W_SW_SPI u8g2(U8G2_R0, /* clock*/ 13, /* data*/ 11, /* cs*/ 10, /* dc*/ 9, /* reset*/ 8); asrpro(SPI接口按下方修改,IIC接口官方有驱动&…...

RSA:基于小加密指数的攻击方式与思维技巧
目录 目录 目录 零、前言 一、小加密指数爆破 [FSCTF]RSA签到 思路: 二、基于小加密指数的有限域开根 [NCTF 2019]easyRSA 思路: 三、基于小加密指数的CRT [0CTF 2016] rsa 思路: 零、前言 最近,发现自己做题思路比较…...

Vuex 和 Redux 的区别?
Vuex和Redux是两个流行的JavaScript状态管理库,它们有一些相似之处,但也有一些区别。 区别: 语言:Vuex是为Vue.js框架设计的,而Redux是一个独立的库,可用于多种JavaScript框架或库。生态系统:…...

软考高级系统架构师冲关预测
[ – 2023年10月27日 – ] 去年11月通过了软考高级系统架构师的考试,原本想立即分享下过关的总结回顾,但是随着软考新版大纲及教程的发布,也意味着题目及内容的复盘总结经验便不那么适用。在即将迎来今年的软考高架的时候,想着透…...

华为实验基础(1):交换机基础
一、交换机的分类 1、 根据交换方式划分: 存储转发式交换 (Store and Forward) 直通式交换 (Cut-through) 碎片过滤式交换 (Fragment Free) 2、 根据交换的协议层划分: 第二层交换:根据 MAC 地址进行交换 第三层交换&…...

bitlocker 加密锁定的固态硬盘,更换到别的电脑上,怎么把原密钥写进新电脑TPM芯片内,开启无需手动填密钥
环境: Win11 专业版 联想E14笔记本 512G ssd 问题描述: 一台笔记本因充电故障,需要拿去维修,不想重装系统,将bitlocker 加密锁定的固态硬盘拆下更换到别的笔记本电脑上,现在开机要手动填密钥,怎么把原密钥写进新电脑TPM芯片内,开启无需手动填密钥和之前那台电脑一…...

C语言之错误处理
在C语言中,错误处理是一种重要的编程技术,用于处理程序运行过程中可能出现的错误情况。C语言提供了几种处理错误的机制,包括返回错误码、使用全局变量、异常处理等。 1、返回错误码: 在函数执行过程中,如果发生错误&a…...

IO流框架,缓冲流
一.缓冲流有什么优点 Java中的缓冲流(Buffered Stream)具有以下优势: 提高效率:缓冲流通过在内存中缓存一部分数据,减少了直接从内存到磁盘或从磁盘到内存的频繁IO操作,从而提高了读写效率。缓冲区大小调整…...

数字音频工作站软件 Ableton Live 11 mac中文软件特点与功能
Ableton Live 11 mac是一款数字音频工作站软件,用于音乐制作、录音、混音和现场演出。它由Ableton公司开发,是一款极其流行的音乐制作软件之一。 Ableton Live 11 mac软件特点和功能 Comping功能:Live 11增加了Comping功能,允许用…...

【PyQt】调整子控件的层级以调整绘制的先后顺序
简述 qt中貌似没有直接设置z序的函数,但对应的有其他调整z序的方法: QWidget.raise_():置顶 QWidget.lower():置底 QWidget.stackUnder(wid):置于指定控件之下 其中关键函数是QWidget.stackUnder(wid),利…...