粉丝36获赞1326


你屏幕上怎么地震了?别担心,这可是个异形工厂的视频。我保证。但在铺设第一条传送带之前,我们需要谈谈为何这种混乱恰恰是你钟爱的自动化游戏的反面。 这不仅仅是在游戏玩法层面的意义。在经典的敌人称射击游戏中,服务器基本上每秒都在用所谓的快照架构向你大喊大叫。它持续追踪游戏状态,只发送你上一课所见与当下发生之事的差异, 那就是差分压缩。但服务器很聪明,它不会向你发送整个地图的数据,那绝对是个噩梦。 相反,它计算一个 pvs 或潜在可见机,想象一个在你角色周围不断变化的气泡,服务器只告诉你那个气泡内理论上你能看到的东西。 多年来,这一直是应对网络三难困境的唯一途径,即在宽带延迟和可信之间进行残酷权衡。 一星工厂的运行方式截然不同,它采用了确定性的锁布架构,这意味着玩家指向服务器或其他玩家发送操作,输入绘画中的所有成员,随后自行模拟游戏进程,几乎不占用待宽。 然而,你可以看到,双方都必须考虑并收集所有输入之后才能模拟每一帧, 这会导致帧落后于最新输入,造成操作延迟。但关键在于这一点。 在一个拥有十万台活跃机器的百万级基地中,服务器并不关心它们的位置或状态,它只关心你的鼠标点击,这将整个负担从你的互联网贷款和服务器赋载转移到了你的 cpu 上。 让我们更深入的谈谈实施这种架构所需满足的先决条件,它带来的问题,以及异形工厂的网络代码如何解决这些问题。 首要且最重要的是,正如开头名字所示,确定性首要要求是必须严格且按位一致,因为每台机器在给定相同输入时,每个时钟周期都必须计算出完全相同的结果, 否则游戏模拟将不同步,最终导致同一回。话中的两名玩家看到的是两个完全不同的游戏世界。 任何涉及浮点运算的游戏在不同平台和计算指令下都会产生差异,尤其是向量化指令,它们能将多个数据打包并一次性处理。这方面的一个例子是 r c p p s 指令,它一次性计算四个浮点数的倒数 或者 r s q r t 指令它计算一组数的平方根倒数。 关键在于该实线并未被任何标准严格定义。尽管英特尔和 amd 都声称相对误差小于一点五乘以二的负十二次方,但这仍可能导致精度出现偏差。 在这种情况下, amd 平均精度更高。正如你所想,哪怕这样微小的差异也能荒谬的影响两次模拟之间的不同步。许多游戏解决此问题的方法是在为不同架构编解期游戏时使用严格模式。 这告诉编解器不要在代码的关键部分执行任何严重优化,以确保浮点计算以可预测且可重复的方式进行。 然而,还有更多令人担忧的问题,因为不同的变易器可能会执行自动向量化或按不同顺序重新排列指令。 像星际争霸二这类游戏选择了更简单的路线完全切换到定点运算,而异形工厂的开发团队却选择了一条更为残酷的道路。 他们决定保留浮点数以获取其精度和范围。但标准化了每一项运算。他们不仅实现了自己的三角函数库,他们确保了所有平台上的每一项数学计算都按严格定义的顺序执行。 这意味着绕过标准系统库转而使用一套统一的数学历程。无论是在 amd 处理器还是基于 arm 的 mac 上,都能产生完全相同的微模式结果。 这是一项显著额外的开销,但它确保了位极一致性,同时未丢失如此大规模模拟所需的高精度。 另一个主要的非确定性来源是随机性,计算机无法生成真正的随机数。相反,他们使用的是伪随机数生成器, 把它想象成一个预定义的无限数字列表。它看起来随机,却遵循严格的数学公式。 只要每位玩家都从完全相同的种子开始,也就是列表上的那个起点,他们就会抽取完全相同的一系列随机结果。然而,即便种子相同,实现也可能出错。在许多编程语言中,函数参数的求值顺序并未严格定义。 调用随机数生成器时,传入两个参数的做法是一个潜在陷阱。一个变异器可能从左边的参数开始取第一个数,而另一个则从右边开始。训练中。这一细微变化会导致伪随机数生成器状态发散,最终引发不同步。 锁部架构还需要固定的客率,以匹配玩家之间模拟的节奏。正如你所见,一星工厂将其锁定在每秒六十次更新。这 conveniently 提供了 fps 与 ups 比例以衡量你电脑或网络的性能。 即使你的配置能输出更高的性能,你也被锁定在六十 fps。 这对于这类游戏来说是可接受的解决方案,因为你并不需要像玩射击游戏那样依靠极高的帧数来对事件作出反应。这表明我们可以通过牺牲某些东西来节省待宽,提升玩家的网络体验。 话虽如此,开发者是如何解决输入延迟的?这就是延迟状态缓冲登场之处。这本质上是你典型游戏中客户端预测的一个版本。 他通过复制当前的游戏状态,并在等待服务器或所有绘画方完成同步时应用本地输入,从而隐藏了输入延迟。缓冲区满可能意味着 cpu 无法跟上模拟速度或者同步耗时过长,且网络吞吐量不足。 目前为止,我只谈到了游戏的对等网络版本。正如你可能注意到的那样, factor 又改变了其多人游戏的策略,这在 ff 一 百四十七中有所体现。开发者将这种从点对点模型到某种人群点对点的服务端客户端模型的改写称为重写。 你看,当时是点对点方案时,你会面临多重问题?你懂生活的道理吧,永远是无穷的问题,却只有有限的解法。 对于这类游戏而言, top 结构难以维护。你必须利用诸多 udp 网络孔洞穿透之类的技巧让连接变得可行。这相当聪明, 你必须连接两方,同时仍使用一个不转发任何流量也不进行计算的终极服务器。他允许一方进行一次穿透,然后让另一方知道该敲哪里,然后处理加入或断开连接的事件, 就让代码变得复杂。正如特里戴维斯所说,白痴仰慕复杂性,天才则欣赏简单。复杂的代码往往会产生更多难以调试的 bug。 另一件事是如何存储状态以便传递给新加入的玩家。 搭建服务器就能解决这个问题。他保存状态发送给玩家,在玩家尝试追赶的过程中累积新操作,并接收带有其余输入的地图。他还决定了输入延迟,且不会随着玩家数量增加而成指数级恶化。 当没有服务器时,想象一下发送的数据包数量。每位玩家每次生成输入时都需向所有人发送数据包,而每个人也都必须接收一分。这完全与我们关于史上最安静网络的言论相背。 但服务器客户端模型引入了巨型数据包,这是一种单一的压缩广播,与其让数十条微小且碎片化的消息堵塞待宽,服务器会在每个时钟周期内收集所有玩家的全部输入,将它们分装进一个优化的数据结构中,然后一次性向所有人发送这一权威数据源。 这将混乱的连接网转化为清晰同步的节奏。无论有多少工程师在建设工厂,每个客户端只需与一个实体通信, 那就是服务器。为了维持连接,玩家必须时不时发送心跳包。而在建立连接时,他需发起握手,先发送连接请求,接着接收回复,然后客户端确认已收到该回复,最后由服务器发出连接确认。 这乍一看可能很复杂,但这是一种实施对抗带毒 s 攻击措施的基本方法。 t k 闭合的概念也在 f f f 开发日之中被引入。这样客户端就能说明它在当前帧内生成了固定数量的输入。 因此,作为服务器提供了一个清晰的窗口,用于验证客户端是如何完成模拟游戏状态的作业的 发送。一个时隙闭合后会进行循环溶于检查,简称 crc。 每个客户端都必须计算一个哈希值,这是一个与其输入对应的唯一短值,也常被称为校验和代表其当前的游戏状态。 当然,便利,拥有数百万实体、散落物品和飞行无人机的整个状态会耗费大量时间。 相反,它采用启发式方法,仅计算游戏关键部分的校验和例如逻辑网络或库存状态。 此后,若客户端未能通过此类检查,就必须重新下载完整状态,仿佛作为新玩家加入绘画一般。这个服务器的概念随后被称为无头服务器。 最终,一心工厂是双方基于数学信任所取得的成就之一。这体现了高效的网络代码,因为他并非向网络中盲目抛出数据,而是传递每个绘画成员清晰且简明的意图。 这种程度的纪律性使游戏能够处理数千个活跃实体而不会压垮连接。在一个规模通常导致不同步和延迟的类型中,一星工厂证明了一个确定性的基础,可以让庞大的模拟在数百小时内保持完美同步。 他提醒我们,有时最高效的网络恰恰是最沉默的那一个。