第 23 章

网络的影子世界

把时钟拨回2019年的一场引擎架构会议。网络延迟不是技术问题,是物理常数——当这个判断被说出来时,它引发的不是恍然大悟,而是一阵压抑的沉默。在场的每个人都懂。从客户端-服务器模型在1990年代成为多人游戏主流架构的那一天起,光速限制下的信号传播时间、路由跳转的队列延迟、最后一英里的带宽波动——这三层时间鸿沟就始终嵌在网络通信的物理底层。

沉默的原因不在于无知,而在于承认的代价:一旦承认延迟是不可消除的物理常数,引擎架构在过去二十年间建立起来的整座延迟补偿大厦,地基就打在流沙上。而当游戏世界从封闭房间扩展到数百平方公里的开放地形,当同时在线玩家从数十人跃升至数百人,当物理模拟的复杂度使每一帧的计算结果都高度敏感于初始条件时,这座大厦的裂缝开始从地基向上蔓延,一直蔓延到引擎架构的最底层。

把时间拨回2017年秋天。Epic Games位于北卡罗来纳州卡里的总部,一名网络程序员正在追踪一个让整个《堡垒之夜》团队困惑了数周的问题。

问题本身很简单:两名玩家在同一时刻目睹了同一枚火箭弹的爆炸,但爆炸摧毁的建筑物在两人的屏幕上呈现出不同的破碎形态。差异不是视觉上的——破碎的瓦砾堆位置偏差了不到一米,但这一米的偏差意味着其中一名玩家以为自己有掩体,而服务器判定他已经暴露在开阔地带。这不是代码错误。程序员反复检查了复制逻辑、物理计算的输入参数、网络包的序列化顺序,一切都在按照设计运行。

问题出在设计本身无法解决的层面:火箭弹爆炸的物理计算在服务器上完成,结果通过网络发送给两个客户端,但两个客户端与服务器之间的往返延迟不同——一个是四十二毫秒,一个是七十八毫秒。在这三十六毫秒的窗口中,两个客户端各自的本地预测算法基于不同的已知状态推演了爆炸后的碎片轨迹。当服务器的权威裁决到达时,本地预测被校正,但校正发生在那位以为自己有掩体的玩家已经做出下一个战术决策之后。Epic内部的网络团队给这类问题起了一个专门的名字:时间窗口分歧。

它不是传统意义上的软件缺陷——无法通过修复某一行代码来消除。它是网络延迟这个物理常数与玩家对公平的期望之间的结构性冲突。而《堡垒之夜》的百人同图竞技模式,正在以前所未有的强度将这种冲突暴露出来。

要理解为什么《堡垒之夜》会成为这个问题的极限测试场,需要回到Unreal Engine 4的网络复制架构本身。UE4的网络模型建立在经典的服务器权威原则之上:服务器拥有游戏世界的唯一真实状态,所有客户端都是这个状态的近似副本。引擎的复制系统负责决定哪些Actor的属性变化需要从服务器发送给哪些客户端,以及以什么频率发送。在2017年之前,这个系统的核心是一个相对简单的相关性判断机制。每个客户端在服务器端都有一个对应的PlayerController,复制系统遍历场景中的所有Actor,检查每个Actor是否与该PlayerController相关——通常基于距离、视线和所属关系——然后将相关Actor的属性变化打包发送。

在大厅对战游戏、小型多人合作游戏或早期的大逃杀模式中,这个机制运转良好。当场景中的Actor数量在几百个以内,同时在线玩家在几十人以内时,遍历判断的CPU开销和网络带宽消耗都在可控范围内。

但《堡垒之夜》的百人模式将这两个数字同时推向了临界点。一百名玩家同时降落在一张十二平方公里的地图上,每个人都在移动、建造、射击、拾取物品。场景中需要网络同步的Actor数量——包括玩家角色、武器、建筑结构、可拾取物品、载具、环境破坏效果——轻松突破数千个。如果复制系统仍然逐个判断每个Actor对每个客户端的相关性,那么每帧需要执行的相关性检查次数就是数千个Actor乘以一百个客户端。这个数字让CPU预算直接崩溃。

更致命的是,《堡垒之夜》的核心玩法机制——即时建造——给网络复制增加了一个前所未有的维度。在传统射击游戏中,地图是静态的,需要同步的主要是玩家位置、武器状态和弹道。

但在《堡垒之夜》中,任何一名玩家都可以在任何位置瞬间建造墙壁、斜坡、地板和屋顶,这些建筑结构立即成为其他玩家需要与之交互的物理实体。建造行为不仅改变了视觉状态,还改变了碰撞体积、视线遮挡和弹道轨迹。这意味着网络复制系统必须在极低延迟下同步高度动态的空间拓扑变化,而这个变化对物理模拟和战术决策的影响是直接的。

Epic的网络团队在2017年底开始意识到,他们面对的不再是优化现有复制系统的问题。他们需要一种全新的架构来管理这个规模的网络状态同步。这个架构最终被命名为ReplicationGraph,在Unreal Engine 4.20版本中首次公开,随后在4.22版本中成为正式功能。

ReplicationGraph的核心思想是将网络复制的决策从一个一个Actor分别判断转变为空间网格驱动的批量策略。它不是简单地优化相关性检查的性能,而是从根本上改变了复制系统的组织原则。

在传统的UE4复制模型中,每个Actor自己决定它需要被复制给谁。这个决策是分散的、个体化的。

ReplicationGraph取而代之的是一个集中式的图结构——一个在服务器上运行的节点网络,每个节点负责管理一类Actor或一个空间区域的复制策略。最关键的节点类型是空间网格节点:它将游戏世界划分成固定大小的网格单元,每个单元跟踪其中的Actor集合,然后基于客户端当前所在的网格位置来决定哪些单元的Actor需要复制。这个设计带来的性能提升是数量级的。在传统模型中,复制系统的复杂度与Actor数量乘以客户端数量成正比。在ReplicationGraph模型中,复杂度降为网格单元数量加上客户端数量。因为网格单元的数量远小于Actor数量,而且网格单元的复制决策可以被缓存和复用,CPU开销从指数增长变成了线性增长。

但ReplicationGraph的真正意义不在于性能优化。它标志着Epic对网络架构在引擎中地位的根本性重新认识。

在ReplicationGraph之前,网络复制是引擎的附加模块——它坐在物理模拟、动画系统和游戏逻辑之上,负责将这些系统的输出打包发送。ReplicationGraph之后,网络架构开始向引擎的底层渗透。空间网格的划分方式开始影响关卡设计——地图被设计成便于网格划分的形状。Actor的归类方式开始影响游戏逻辑的编写方式——开发者需要理解哪些Actor会被同一个网格节点管理,从而设计它们的交互模式。复制优先级开始影响物理模拟的精度分配——靠近网格边界的Actor可能获得更高的同步频率。网络架构不再是引擎的外挂。它正在成为引擎的骨架。

就在Epic在卡里总部重构网络复制架构的同时,另一条平行线在旧金山的Unity Technologies总部展开。Unity面对的问题与Epic有相似之处,但压力来源和应对策略截然不同。Unity的起点决定了它的网络架构困境具有不同的性质。

作为一款以降低游戏开发门槛为使命的引擎,Unity的用户群体从独立开发者到中型工作室,从手游到PC游戏,从单人体验到多人联机,跨度极大。在2019年之前,Unity对网络功能的支持一直处于一种松散联邦的状态:引擎核心提供底层网络API——主要是对Socket的封装和对UNET的基本支持——而真正的多人游戏网络栈则由第三方资产商店的插件来提供。Photon、Mirror、DarkRift、Forge——这些第三方解决方案各自实现了不同的网络模型、同步策略和服务器架构。这种状态在Unity的早期阶段是合理的。当引擎的用户群体高度分散时,让市场来选择最适合特定游戏类型的网络方案,比引擎团队自己维护一套通用方案更有效率。

但到了2019年,这种松散的联邦开始显露出结构性缺陷。缺陷的核心在于:第三方网络插件无法触及引擎底层。

一个Photon或Mirror的开发者可以在Unity的GameObject层级上实现客户端-服务器的状态同步,可以编写插值和外推算法来平滑其他玩家的移动,可以设计RPC调用来触发远程事件。但他们无法修改Unity的物理引擎来支持服务器端的确定性模拟,无法修改动画系统的状态机来区分本地预测动画和权威动画,无法修改输入系统来在本地响应和服务器确认之间做出毫秒级的权衡。他们只能在使用引擎提供的现有接口的前提下,在应用层搭建网络同步的逻辑。当游戏规模较小时,这种限制并不致命。

但当开放世界、大规模多人和跨平台联机三股力量同时施压时,应用层的网络方案开始从各个方向漏风。一个典型的案例是物理交互:如果服务器和客户端使用不同版本的PhysX引擎——这在跨平台场景中几乎不可避免,因为不同平台的PhysX实现存在微妙差异——那么同一个物理碰撞在两端可能产生不同的结果。第三方网络插件对此无能为力,因为物理引擎的确定性是引擎底层的事情。

Unity的网络架构团队在2019年开始着手解决这个问题。他们的策略与Epic形成了一种镜像关系:Epic从网络架构出发,向下渗透引擎底层;Unity从引擎底层出发,向上构建网络栈。这个策略的核心是一个名为Netcode for GameObjects的新网络框架,以及它与Unity正在推进的面向数据的技术栈DOTS的深度整合。Netcode for GameObjects在2021年作为正式包发布,标志着Unity首次提供了一套由引擎团队维护的、与引擎底层深度耦合的网络解决方案。Netcode for GameObjects的设计哲学与ReplicationGraph有本质区别。ReplicationGraph是为《堡垒之夜》这种特定类型的大世界游戏优化的,它的空间网格策略假设了某种特定的游戏空间结构。

Netcode for GameObjects则需要服务于Unity的整个用户光谱——从两人的合作解谜游戏到百人的大逃杀,从移动端的回合制游戏到PC端的实时射击。这种通用性要求意味着Netcode for GameObjects不能采用任何过于激进的、绑定特定游戏类型的架构假设。

Unity的解决方案是让网络栈成为DOTS架构的一个自然延伸。在DOTS中,游戏逻辑被组织成运行在实体上的系统,实体是纯粹的数据容器,系统是无状态的函数。Netcode for GameObjects将网络同步建模为一种特殊的系统:它在服务器和客户端之间同步实体的组件数据,同步策略由开发者通过配置来决定——哪些组件需要同步、同步给谁、以什么频率同步、在同步之前是否需要进行量化或压缩。这个设计的精妙之处在于,它避免了ReplicationGraph必须做出的那种全局性空间假设。

Netcode for GameObjects不需要知道世界被划分成网格,它只需要知道哪些实体的哪些组件需要被复制。空间的复杂性被留给了开发者——他们可以在自己的系统中实现任何空间划分策略,然后告诉Netcode for GameObjects哪些实体需要基于这个策略进行同步。但代价同样明显。ReplicationGraph通过集中式的空间网格节点实现了性能的数量级提升,因为它可以批量处理同一网格单元中的所有Actor。Netcode for GameObjects的实体级粒度意味着它无法进行这种批量优化——每个实体的同步决策是独立的,即使两个实体在空间上相邻,Netcode for GameObjects也不会自动将它们合并到一个复制批次中。对于《堡垒之夜》这种规模的游戏,Netcode for GameObjects的架构可能无法提供与ReplicationGraph相当的性能。

但对于Unity所服务的大部分游戏类型,这种灵活性比极致的性能更重要。这两条平行线——Epic的自上而下渗透与Unity的自下而上构建——在2021年交汇于同一个节点:它们都承认了网络架构不再是引擎的附加模块,而是引擎的基础约束。

但第三条平行线的出现,将这个问题推向了更激进的维度。2019年3月19日,Google在旧金山游戏开发者大会上正式发布了Stadia云游戏平台。发布会的核心承诺是激进的:消除客户端-服务器边界。在Stadia的架构中,客户端不再是一台运行游戏引擎实例的本地设备,而是一个视频流解码器和输入信号发送器。游戏的全部计算——渲染、物理、AI、网络同步——都在Google的数据中心完成,玩家设备只负责显示画面和回传输入。如果这个承诺能够兑现,那么本章所讨论的所有网络同步问题——时间窗口分歧、复制策略、空间网格划分、本地预测与权威裁决的权衡——都将被一笔勾销。

因为在Stadia的世界里,不存在多个客户端各自维护游戏状态的近似副本这回事。整个游戏世界只存在于Google的数据中心,所有玩家都是通过视频流接入这个单一世界的终端。延迟不再存在于客户端与服务器之间,而是存在于玩家设备与数据中心之间。网络同步问题被替换为视频传输问题。

这是一个在架构层面极具诱惑力的愿景。如果游戏状态只存在于一个地方,那么物理模拟可以是完全确定性的,因为不存在需要同步的多个物理世界。动画系统不需要区分本地预测和权威动画,因为不存在本地。输入处理不需要在即时响应和服务器确认之间权衡,因为所有输入都直接到达那个唯一的游戏实例。

引擎的网络层可以从核心架构中被完全移除——不再需要ReplicationGraph,不再需要Netcode for GameObjects,不再需要任何形式的复制策略、插值算法或延迟补偿。Google在这个愿景上投入了数十亿美元。

他们建造了遍布全球的数据中心,开发了专门的GPU服务器硬件,收购了多家游戏工作室来为Stadia开发独占内容,甚至在2019年E3期间包下了整个洛杉矶会展中心的南大厅来展示这个平台。但物理常数不会因为投资规模而让步。Stadia在2019年11月正式上线后,玩家和评测者很快发现了一个无法回避的问题:延迟。

Google声称其数据中心的处理能力加上预测性输入算法可以将端到端延迟控制在可接受的范围内,但实际体验取决于玩家与最近数据中心之间的物理距离。对于居住在主要城市附近、拥有高速光纤连接的玩家,Stadia的体验确实接近本地游戏。

但对于其他玩家——那些距离数据中心数百公里、使用普通宽带连接、或者通过WiFi而非有线网络接入的人——延迟从可感知变成不可玩。更致命的是,Stadia的架构将延迟问题从游戏引擎可以部分补偿变成了游戏引擎完全无法介入。

在传统的客户端-服务器模型中,引擎可以通过本地预测让玩家的输入得到即时反馈,即使这个反馈最终可能被服务器校正。在Stadia的模型中,玩家的输入必须首先通过互联网到达数据中心,在数据中心完成计算,然后将视频帧通过互联网传回——整个往返过程中的每一毫秒延迟都直接转化为玩家感知到的操作迟滞。引擎没有任何手段来掩盖这个迟滞,因为它根本不在玩家的设备上运行。

2021年2月1日,Google宣布关闭Stadia内部游戏开发工作室。2022年9月29日,Google宣布将于2023年1月18日关闭Stadia平台。

从发布到关闭,三年零两个月。Stadia的失败不是技术执行的问题——Google拥有足够的工程能力和资金来优化视频编码、部署边缘节点、改进预测算法。Stadia的失败是架构假设的问题:它假设网络延迟可以通过足够多的数据中心和足够快的网络连接来消除,但这个假设违背了物理常数。光速是有限的。

信号在光纤中的传播速度大约是每秒二十万公里——比真空中的光速慢约三分之一。从纽约到洛杉矶的往返时间,即使忽略所有路由跳转和队列延迟,理论上也无法低于大约四十毫秒。对于回合制游戏,四十毫秒无关紧要。对于需要精确时机的动作游戏,四十毫秒是生与死的距离。而在Stadia失败的同一时期,另一家公司的云游戏服务以更谦卑的姿态存活了下来。NVIDIA的GeForce Now于2020年2月正式脱离测试阶段,开始商业运营。与Stadia不同,GeForce Now没有试图重新定义客户端-服务器边界。它将自己定位为一个远程游戏PC:玩家在NVIDIA的数据中心租用一台虚拟机,在这台虚拟机上安装和运行他们已经拥有的游戏——来自Steam、Epic Games Store或其他数字分发平台。

游戏的网络架构保持不变——如果游戏本身使用客户端-服务器模型,那么GeForce Now上的实例就是那个客户端,它与游戏服务器之间的通信与任何本地PC上的客户端完全相同。这个设计的代价是:GeForce Now无法消除客户端-服务器之间的网络同步问题。

如果一名GeForce Now玩家和一名本地PC玩家在同一个《堡垒之夜》服务器中对战,GeForce Now玩家不仅面临与游戏服务器之间的延迟,还面临与NVIDIA数据中心之间的额外延迟——他们的输入需要先到达数据中心,然后从数据中心到达游戏服务器,服务器的响应需要先到达数据中心,然后从数据中心到达他们的屏幕。这个双层延迟在某些情况下会让GeForce Now玩家处于明显的竞争劣势。

但这个设计的收益是:GeForce Now不需要重写任何游戏的网络架构。它不需要说服游戏开发者采用新的API、新的复制模型或新的同步策略。

它只需要提供足够好的视频编码和足够近的数据中心,让双层延迟的总和在大多数情况下保持在可接受的范围内。它承认了网络延迟的物理常数,然后在这个常数之内寻找生存空间。2023年初,当Stadia正式关闭时,GeForce Now拥有超过两千万注册用户。

这个数字与本地游戏平台的用户规模相比仍然很小,但它证明了云游戏的一种可能路径:不是取代客户端-服务器模型,而是在其边缘寄生。这三条平行线——Epic的ReplicationGraph重构、Unity的Netcode演进、云游戏的边界重定义——在2023年交汇于同一个结论,尽管这个结论从未在任何GDC演讲或技术博客中被明确说出:在联网的世界里,没有任何一个客户端拥有完整的真相。这不是一个可以被工程手段消除的问题。它是一个必须被引擎架构接受的前提条件。

ReplicationGraph接受了这个前提,所以它不再试图让所有客户端看到完全相同的世界状态,而是通过空间网格划分来管理谁需要看到什么——它将真相的碎片分配给不同的客户端,每个客户端只获得与其当前空间位置相关的部分真相。Netcode for GameObjects接受了这个前提,所以它不再试图提供一种大一统的同步方案,而是将同步策略的配置权交给开发者——它承认不同类型的游戏需要不同形状的真相。

GeForce Now接受了这个前提,所以它不再试图消除客户端-服务器边界,而是在这个边界之上叠加了一层新的边界——它承认玩家与游戏世界之间永远隔着至少一台服务器,无论那台服务器在物理上位于何处。Stadia是唯一一个拒绝接受这个前提的参与者。它的整个架构建立在一个反事实的假设之上:如果网络足够快,延迟就可以被消除。物理常数证明了它的错误。

这个结论在2023年产生了一个具体的后果,它将在接下来的几年中持续发酵,重塑引擎开发工具链的信任基础。这个后果不是技术上的——ReplicationGraph和Netcode for GameObjects已经证明了引擎可以在技术层面适应网络延迟的物理常数。这个后果是制度上的:引擎必须在其架构中显式地标记出哪些计算结果是本地真相、哪些是权威真相、以及在两者发生冲突时以什么规则来解决。

这意味着引擎不再只是一个世界模拟器。它必须同时成为一个真相管理器——一个在多个相互矛盾的世界状态之间做出裁决的系统。

而裁决的规则,无论引擎团队如何精心设计,最终都会偏向某些类型的游戏体验而牺牲另一些类型。ReplicationGraph的空间网格策略偏向大世界射击游戏,牺牲了需要全局状态一致性的策略游戏。Netcode for GameObjects的实体级粒度偏向灵活性,牺牲了大规模场景下的批量优化。

GeForce Now的寄生策略偏向拥有高速网络连接的玩家,牺牲了网络条件较差地区的可及性。当引擎成为真相管理器时,它就不再是中立工具。

它开始拥有立场。而这个立场的选择,将构成引擎纪元下一个压力点的核心。在联网的世界里,引擎不仅要回答世界如何运作,还要回答谁的版本的世界算数。