卡盟,卡盟平台,卡盟排行榜,DMZ卡盟,蛋炒饭卡盟,绝地求生卡盟,和平精英卡盟,王者荣耀卡盟
智小呆卡盟 > 绝地求生辅助
绝地求生辅助

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

时间:2020-06-17 来源:网络采集

虚幻引擎已经使用了4年。我想再谈谈他的网络结构。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

作者|杰瑞什

来源|游戏开发

从16年前开始,我已经和虚幻接触了4年。最近,我读了很多关于网络同步的论文和书籍。我终于了解了像《末日审判》和《地震》这样的古代游戏的发展历史,并对它们的网络架构有了更深的了解。这一次,我想根据自己的工作和学习经验,从全局的角度来审视这个虚幻的网络模块,并总结一些常见的问题。我相信你会在阅读了用户设备同步的模糊细节后得到启发。

在开始之前,我会给初学者一个建议。如果你打算阅读UE4的同步源代码,你最好阅读这本书,网络多人游戏架构和编程,它基本上涵盖了UE4的同步框架,可以帮助你避免许多弯路。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

让我们言归正传:

网络同步是为了使每个客户端的角色表现一致,属于游戏引擎的高级功能,所以我们一般将其分为游戏模块。然而,具体的实施计划实际上会深深地影响底层网络架构(甚至整个游戏架构)。我们不仅要决定完成哪个网络协议,还要决定游戏中每个模块的循环执行顺序。这不仅仅是一个“游戏”级别。

虚幻引擎属于标准的CS架构(经过无数次修改),具有内置的状态同步功能。其同步频率与游戏帧速率相同,属于变长步长更新。由于帧速率完全受CPU和GPU性能的影响,网络同步的频率与整个项目的性能密切相关。然而,我们必须认识到,只要我们优化性能的所有方面,虚幻已经尽可能快地发送和接收数据。

带属性的远程过程控制同步

在虚幻中,有两种同步方法,即RPC和属性同步(许多服务器引擎都是如此)。与其说是一种同步手段,不如说是一种传输数据的方式。它的优点是,为了便于理解,它可以直接以类的函数的形式编写。同时,您不需要直接编写Socket,也不需要处理数据包和解包。在计算机网络的概念中,远程过程调用被称为“远程过程调用”,它本质上是一种传输数据的手段。它的实现可以是应用层的Http,也可以是传输层的TCP/UDP。在幻想中,由于许多游戏(如FPS)的同步需要更严格的网络延迟,我们放弃了需要三次握手的传输控制协议,转而使用UDP协议(更不可能考虑HTTP协议)。RPC可以标记为可靠或不可靠。可靠的远程过程控制最终会到达目标终端,但不可靠的远程过程控制可能会在拥挤的网络环境中丢失或在发动机电流限制的情况下提前停止。RPC本身不是一个持久对象。我们只能通过RPC参数将数据从一端“一次”发送到另一端,因此每个RPC调用只能“执行一次”(换句话说,它的生命周期只有一瞬间)。如果一个RPC消息从网络中丢失,它将会永久丢失(这里称为不可靠的RPC),因此它不适合游戏世界中各种对象的状态恢复,并且必须与能够保持对象状态的属性相结合。此外,UE4中的RPC不支持回调,所有的RPC函数都返回void。

属性同步本质上是上层的一个功能特性,并在逐个对象的基础上进行处理(不支持更精细的粒度同步,但理论上可以通过条件属性进行部分调整,有关详细信息,请参见AACtor::PreReplicate)。虚幻的服务器将以一定的频率执行同步对象属性的数据发送和接收,同时处理回调函数。生成属性同步是为了维护对象的状态。这是一个在概念上非常接近“同步”一词的功能。一旦服务器上的同步属性改变,它肯定会被发送到客户端(注意:属性同步只是服务器到客户端的同步,没有客户端到服务器的通信)。分组丢失可能会在中间延迟(当第一次同步时,actor是可靠的),但是它的内置机制将确保属性值最终被发送到客户端。借用一句经典的话,同步数据可能会很晚,但它永远不会缺席。

无论是RPC还是属性同步,您都会发现它是基于UObject的,或者更准确地说,是基于Actor(及其从属组件)。因为这两个函数之一是使用类中的函数,另一个是使用类对象的属性。他们都需要使用特定的对象作为媒介。在用户设备的体系结构中,设计是面向对象的,每个参与者都可以理解为游戏世界中的一个对象。

因为它是基于角色的,所以整个同步与游戏框架紧密相连。因为我们需要知道在发送同步数据时应该将数据发送到哪个客户端,以及链接信息(IP等)。)客户机和服务器之间是在Playercontroller中,同步逻辑与playercontroller密切相关。许多刚接触到虚幻的朋友经常会遇到无法发送或接收RPC数据的问题,也就是说,他们没有意识到playercontroller实际上包含了客户端和服务器之间的连接信息。最典型的情况是,如果洛克王国的雷神公司帮助你将10个玩家客户端连接到服务器,并在服务器上安装一辆汽车,让他执行客户端远程过程控制,他怎么知道要发送哪个客户端?当然,正是通过这辆车找到了控制他的游戏控制器,然后找到了相应客户端的IP。如果这辆车没有被任何客户控制,那么他不知道把它送给谁。

当然,由于不同的实现原则,RPC和属性同步之间有许多不同之处。由于属性同步遵循每个实例对象,因此不存在“按需发送”。换句话说,属性同步需要通过统一的引擎接口在每帧的特定时间写入发送缓冲区。问题在于,只有您在同一帧中修改的属性的最后一个值才会传递给客户端,从而导致回调函数只执行一次。另一方面,每次执行数据时,RPC会立即将数据插入发送缓存,从而确保不会丢失RPC调用(如果RPC是可靠的)。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

此外,还有一个深坑,是关于Actor和Component的同步序列。对象的同步必须首先将客户机上的对象与服务器上的对象相关联,这样当服务器上的对象发生变化时,客户机上的对象就会被告知发生变化。但是,A是一个对象,这些对象需要同步。场景中有如此多的对象,同步必须按顺序进行。这样,经常会出现很多指向A对象中的B对象的同步指针属性,但是当A对象出现时,B还没有同步,所以不可能在A的开始位置访问B。那么如何解决这个问题呢?答案是使用属性回调。一旦执行了属性回调,就可以确保A的B指针存在。然而,属性回调不能解决所有问题。如果B对象秘密地辅助C对象的指针,而C对象在回调时不同步,那么你要用B来访问C对象,发现它是空指针。目前,在虚幻引擎中没有完美的解决方案,所以我们应该尽力避免这种情况(我正在尝试实现一些可行的方法)。由此引发了许多更详细的问题,我将在后面列出其中一些。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

移动同步理解

现在已经介绍了两种同步方法,我们将重点讨论网络同步的解决方案。游戏中的同步本质上是同步客户端之间的性能,而RPC和属性同步只是数据同步。我们需要将它们与图像性能结合起来。坦率地说,图片表示是指对象、动画、位置等的显示和隐藏。其中,位置同步是最复杂的,因为游戏中的角色可能每一帧都在移动,而移动组件就是为了解决这个问题而诞生的。

移动组件非常复杂。他需要考虑各种情况下的延迟和抖动,解决不同客户和角色的流畅性问题,并实现各种插值方法。在网络同步中,总是有三种类型的角色,它们由本地玩家、服务器和其他玩家控制。在虚幻中,它们分别对应于自主性、权威性和模拟性。这三种类型的存在本质上代表了谁是角色的控制者(哪一端可以通过命令直接操作他),而从另一个角度来看,这种分类实际上代表了玩家的操作是否有网络延迟以及延迟的大小。对于本地控制的自治角色,他可以直接在本地响应您的操作。如果他想将操作发送到服务器,他需要经历客户端-服务器延迟,而服务器需要服务器-客户端延迟来将操作与其他客户端同步。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

同步中最困难的事情是如何有效地克服这种延迟。因此,将产生同步策略,如延迟补偿,即当本地客户端从其他客户端接收消息时,所有本地角色将在处理和计算消息之前回滚到[当前时间-网络延迟时间]的位置。

(UE4默认引擎没有这种操作,虚幻锦标赛。如下图所示,红色是当前结束的具体位置,黄色是回滚预测的位置)。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

移动组件使用从本地客户端到服务器的不可靠的RPC,而服务器使用到其他客户端的属性同步。为什么使用RPC?由于客户端只能通过RPC向服务器发送消息,属性同步仅用于服务器同步到客户端卡盟辅助平台。虚幻在同步位置时记录每个客户端和服务器的时间戳,通过位置缓存、每帧不停发送位置、时间戳调整位置的判断和回滚等操作达到理想的效果。本质上,手表先锋的帧同步+状态同步是相同的(详见:手表先锋架构和网络同步)。然而,虚幻不采用ECS,并且不能很好地支持体系结构中的所有逻辑回滚。

网络发展至今同步,但已基本成型。从早期的锁步到指令流水线再到预测性回滚时间扭曲,这些都是通用的同步优化方法。当前的趋势是状态同步和帧同步中的各种机制相互学习并相互促进。除了移动同步之外,其他要求(如运动同步和隐藏显示)通常并不严格,因为它们不需要处理每一帧,而RPC通常用于一次性通知修改。

关于同步,还有一个人们通常不太注意的细节,那就是同步频率。如前所述,UE4将尽快发送同步数据。如果客户端的性能非常好,并且帧的数量非常高,那么一个帧将生成大量的移动RPC。从理论上讲,如果没有丢包,即使服务器帧率很低,服务器也会根据客户端发送的数据逐个进行模拟,两端的结果是一样的,仍然是平滑的。但是,如果一些移动RPC在中间丢失(引擎将限制发送),可能会导致服务器的计算结果与客户端的计算结果不同,从而不断地拉回客户端,导致Caton。

通常,在某些情况下,RPC和属性同步可以相互替代。RPC可用于简单和低实时要求,而属性同步可用于要求服务器保持实时主控制并保持连续同步的状态。属性同步本身已经过优化,消耗也不是很大。您可以通过各种条件设置其同步规则。但是,请注意,数量变化会导致质量变化。如果所有属性都不受限制地同步使用,那么参与者(和属性)遍历的成本是相当大的,因此合理地使用它是非常重要的。理论上,有许多领域可以优化。例如,参与者可以设置同步的范围(类似于AOI)。远离玩家的对象不需要同步。Actor可以根据某些规则关闭某些客户端的属性复制功能(休眠),同时关闭ActorChannel并将其从网络连接中删除。复制图用于划分空间和消除弱相关对象,以减少占用的带宽(但该方案仅适用于大型世界游戏)。理论上,我们可以增加更多的优化方法和精细粒度来进行调整,但具体方案要根据游戏类型灵活处理。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

(复制图显示每个宝箱都被放置在他影响的所有格子中。玩家只有在进入宝箱时才会收到同步信息)。

记忆再现方式

回放似乎是一个非常高级的功能,但事实上它早在20世纪90年代就随着锁步算法诞生了。UE4内置了一个Demonetdriver系统来处理回放和录制,但是实现起来更复杂,因为它采用了状态同步而不是帧同步。基本思想是在本地创建一个虚拟服务器,它在录制时充当服务器,在回放时充当客户端。当游戏正在进行时,记录在本地开始,与回放相关的数据被序列化到数据流(可以是存储器、磁盘或网络包)中,然后在玩游戏时在相应的数据流中读出。尽管该框架是可用的,但它仍处于未完成阶段,而且有相当多的低成本卡津坑可供绝地生存(例如,过期的多播事件不会在回放中执行)。对于死亡回放和精彩场景的实时切换要求,所涉及的逻辑更加复杂(如现实世界和回放世界的切换和隐藏)。这次我会写一篇文章来阐述。

绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构

(官方射击游戏演示——射击游戏包含一个简单的回放演示功能)

底部框架

完成了上层的网络同步后,我们将简要讨论一下下层。虚幻引擎诞生于20世纪90年代,它肯定参考了许多其他游戏设计,比如《地震》和《部落》。Quake是当时第一批基于“CS架构状态同步”的游戏,而Tribe对模块进行了拆分和封装,这是第一款构建相对完善的网络同步架构的游戏。UE4的架构非常类似于部落,当前的同步方法是通过网络驱动+网络连接+通道+参与者/对象抽象层来实现的。许多人总是抱怨虚幻的引擎使底层过于复杂。然而,有许多历史原因和技术权衡。在过去的20年里,官方团队肯定无数次思考过这个问题,没有必要在这里重复。总之,从网络层面来看,UE4的高耦合网络框架不适合帧同步(这里是锁步),也很难转换成ECS架构。然而,我个人也觉得许多游戏不需要追求帧同步。这两种同步开发方法各有千秋。事实上,游戏不是那么容易制作的(也许踩在用户设备的官方坑上会让你更不舒服,毕竟,它不是你自己写的)。

关于网络协议,游戏界早就认识到,对于高频同步游戏,UDP同步比TCP更好。因此,虚幻使用的是UDP协议,但为了确保数据的可靠性,qq农场农场需要封装一个可靠的UDP协议,即网络驱动+网络连接+通道。里面的逻辑非常复杂,涉及许多模块。确实有一些冗余。此外,虽然它是可靠的,但属性同步和RPC的处理方法是不一样的。属性同步只确保最终数据是可靠的,中间结果可能会丢失,而RPC可以确保消息必须按顺序传递。对于其内置RUDP的重传机制,用户设备实际上已经进行了多次优化和调整。之前的任何数据包丢失和无序都会立即触发重传。在4.24中,增加了一个循环队列来接收数据包,以纠正接收数据包的顺序,从而在一定程度上减少了不必要的重传。默认情况下,消息的接收和发送仍然由主线程处理(我们可以决定是否启用多线程)。由于UDP不需要监听多个套接字,并且使用多线程来接收数据包没有意义,因此不采用iocp或其他异步IO方法。在虚幻引擎中,更新网络数据包的顺序是“接收数据-逻辑更新-卡盟平台-发送新数据”。但是,并非所有同步更新逻辑都是在接收数据包时完成的。UObject类型的同步属性的更新可以在合同发布之前更新(这是一个坑,请注意)。详情请参阅我的智虎文章《在UE4中探索》第5节第8节关于网络同步的原理。

为此,我对虚幻引擎的网络模块进行了较为全面的梳理。更多详情,请参考文章“游戏人物运动原理”和我的智虎专栏。"

https://zhuanlan.zhihu.com/c_164452593

“探索UE4”网络同步原理深化(上)

“UE4探索”深化网络同步原理(下)

UE4探索中对网络同步的理解与思考

在UE4中探索移动组件的详细说明

UE4中探索的回放系统分析(待定)

最后,让我们总结一下同步中遇到的一些常见问题。这些问题是在我踩了无数个坑之后才总结出来的。改变每个人的“关注”或“转发”并不过分。

1.不能保证在客户端上执行客户端的RPC。在服务器上,如果有一个参与者没有连接信息(例如,它不同步,完全由人工智能控制)。或者他的远程角色等于零),那么他的clientRPC将只在他自己的客户端上执行。最后一个可能的结果是函数调用堆栈的无限循环将会崩溃。

2 .如果在beginplay上生成了另一个参与者,则将在客户端服务器上执行beginplay。它可以触发客户端和服务器生成它们自己的参与者,导致客户端有两个参与者(一个是自己生成的,另一个是服务器生成的)。稍后,当调用RPC时,可能会发生RPC执行失败,因为本地生成的参与者没有任何连接信息。

3.在客户端上开始播放对象可能会执行多次。在不真实的情况下,如果一个参与者是由服务器创建并与客户端同步的,那么服务器可以随时关闭这个对象的同步。一旦对象远离玩家角色或服务器主动关闭同步,客户端上的对象将被删除。稍后,如果播放器再次接近该对象,它将重新与客户端同步,并再次开始播放。这样,一些数据被初始化两次,这可能不是我们想要的。

4.我们经常会遇到“游戏状态恢复”的情况,比如网络游戏中的断开和重新连接。然后,您可能会遇到一些对象在重新连接后没有处于正确的状态,因为许多对象更改是通过RPC进行的,这是一次性的。当您重新连接时,RPC将不再执行,因此客户端重新连接的状态实际上不同于服务器的状态。此时,您需要使用属性同步来解决这个问题,但是当您断开连接并重新连接时,您不一定想要执行属性回调,因此您需要重新检查回调函数的内容。

5.不要在任何时候将可能被破坏的对象传递到RPC参数中。RPC手动导航辅助卡盟参数不确定对象是否合法。如果对象在传递过程中被破坏,随后的序列化可能会触发一个相关的崩溃,在这个崩溃中找不到NETGUID。

6.一般来说,字符内的同步序列严格按照属性的声明序列,不同的参与者不能保证它。

7.一般回调将转移到的函数,注意是否有空返回。此时,其他参与者的指针可能为空。

8.UObject指针类型的数组属性可能会触发多个回调,最后一个可以确保所有指针都有值。

9.属性回调执行的前提是客户端和服务器具有不同的值。如果您在本地修改一个值,然后服务器修改与客户端相同的值,回调将不会被触发。

10.一般来说,当执行元和个人计算机被解开时,执行元不能保证远程过程控制的执行。这种情况经常发生在角色死后执行无代理时,所以此时应该注意RPC的执行。

11.如果属性没有与客户端同步,或者没有执行回调,请注意是否使用了自定义条件属性

12.用于设置计时器以确定是否接收到同步属性的逻辑不是标准的。一旦服务器或客户端更换了卡片(开始时没有出现,但随着游戏内容的增加,可能会出现各种奇怪的错误),信息可能会丢失。

CSDN 618程序员购物日:显示器、键盘、蓝牙耳机、扫地机器人、任天堂游戏机、AirPods Pro和其他许多it人士最喜欢的产品,都以非常低的价格出售,让1亿程序员开心!

文章标题:绝影辅助:使用虚幻引擎4年,我想再谈谈他的网络架构
文章地址:https://www.zhixiaodai.com/jdqsfz/41481.html