Alt-F4 #26 - 给游戏添加多人 2021-03-05
翻译 Ph.X
在本周的第 26 期 Alt-F4 中(半周年!),of2win2 前往异星工厂多人游戏,并解释了它背后的一些技术机理。如果你曾想知道什么是 Desync,或者游戏是如何同时处理数百名玩家和数千个实体的,欢迎深入阅读!
异星工厂服务器 oof2win2
大多数玩家可能至少连接过一次异星工厂服务器,可能与朋友一起玩,或只是查看一些其他人的世界。在今天的 Alt-F4 中,我将简述多人游戏的历史,然后我会深入解释多人游戏在技术上 如何 工作的。其中我将解释完全确定性算法和锁步算法的使用。
多人游戏发展史
异星工厂在 2014 年 10 月的推出 0.11.0 版本中引入了多人游戏功能,该功能的开发是从异星工厂 0.9.4 开始的。这个多人游戏和你今天看到的不一样,你不能比如说通过 Steam 轻松的“加入好友”或者使用服务器浏览器——你需要知道服务器的确切 IP 地址。当多人游戏最初发布时,有不少 Bug,比如这个 Bug 不允许多人游戏持续超过 20 秒。当然,它被修复了,仅仅 3 个小时之后,经典的 Wube 式更新。还有这个 Bug,它不允许一次连接超过三个人——不像六年后的这个 500 多个玩家的多人游戏。多人游戏开发上投入的海量工作才让 500 名玩家能够同时连接。
作为一项主要功能,异星工厂从 0.12.0 开始支持无头服务器。这意味着服务器现在可以在没有 GPU 的机器上运行,这大大降低了异星工厂服务器的成本,提高了可访问性。它还允许在一台机器上同时运行多个服务器实例,这在某些情况下非常有用。
在异星工厂 0.14.0 版本中,服务器不再因为某个玩家的电脑处理更新时间过长而暂停所有玩家的游戏。这意味着,如果你的电脑比较老,服务器将不再等待你赶上处理。这在大型服务器上非常有用,因为大型服务器可以同时有几十到几百名玩家在线,这样就不需要让所有人为了一个人去等待。
完全确定性方法
正如 FFF-30 中提到的那样,所有的客户端和服务器必须以相同的方式模拟游戏,在完全相同的时间进行相同的操作。这意味着,如果一个人在电脑上做了什么,其他人的异星工厂实例也需要做同样的动作。实例是指某件存在的事物,例如,一个篮子里的苹果或 Chrome 中的标签页可以有很多实例。异星工厂与大多数多人游戏有很大的不同,比如反恐精英:全球攻势或守望先锋,所以开发人员不能直接从这些游戏中提取多人游戏的实现模式,然后移植到异星工厂中,这是无法正常工作的。
相反,在创建多人游戏的过程中,开发者使用锁步协议创建多人游戏。在异星工厂中,与服务器的连接开始时,服务器只给你地图。然后,服务器只有在因用户输入的而发生变化时才会告诉你,比如有玩家在某个坐标上放了一个传送带,死于虫群(或火车)等等。你只被告知 该事件 发生了,然后你的游戏必须自己更新其本地模拟。它并不在每一个 Tick(游戏状态更新的单位)都详细更新当前发生的每一个事件,比如机器人移动和火车停止。
在每一个 Tick 传输发生的每一个事件,将需要大量的网络带宽,因为你需要传输诸如“某个物流机器人移动到某处”这样的信息,在一个大存档中每一个 Tick 都会发生数万次的事件。更不用说一些其他的信息了,这将导致每一个 Tick 都要传输整个存档,导致在某些情况下需要每秒传输 1500 MB。相反,你只被告知真正重要的信息,这主要是玩家与游戏的互动,然后你的客户端就像没有其他人一样运行游戏。
游戏界还有很多其他处理多人游戏的方式。例如,守望先锋是一款几乎跟踪一切的游戏,在游戏服务器上中心化,监控每一个物品、玩家、子弹等等,如果出了问题会主动纠正你的客户端。异星工厂只监控玩家的输入,如果出了问题就会抛出一个 Desync(失去同步)。我稍后会解释什么是 Desync。这两种实现是不同的,因为游戏是完全不同的:在守望先锋中,当你最初下载游戏时,你可以下载所有的地图,所以你只需要转移玩家和子弹的位置。然而在异星工厂中,地图一直在变化。
在异星工厂中,你有着位于不同位置的组装机、灯、电线杆、传送带、机械臂,以及其他几乎所有的东西,因为每个基地都是独一无二的。这也是为什么在异星工厂中,只传输由玩家引起的变化,因为异星工厂可以像单人游戏一样模拟游戏,只是接收来自服务器的玩家变化。这比传输整个地图要容易得多,只需要在客户端连接时给他们地图,并告诉他们任何其他会改变模拟的输入,例如玩家向右移动了 10 格等。请看下面的图片。对于任何好奇的人来说,守望先锋的多人游戏已经由守望先锋的开发者在这里(较短的视频)和这里进行了更详细的解释。
玩家:嘿,你好!我可以加入异星工厂服务器吗?
服务器:当然没问题!这是目前的地图,下载吧。[地图在附件]
服务器:你出现在 x=0, y=3
服务器:你的伙伴“potatoman”将他的物流筛选槽 33 设置为“高速传送带”。你把它们也设置为“高速传送带”,并进一步模拟
服务器:你的伙伴“potatoman”向右移动 3 格
玩家:我向左移动 4 格
服务器:已确认,转发中
— Factorio Server
异星工厂使用了确定性算法,当给定相同的输入时,这类算法将产生相同的输出。这意味着结果中不存在随机性,在如异星工厂的情况下正需如此。当异星工厂的多个实例运行时,需要采用完全确定性的算法,这样所有的实例都以锁步算法运行并保持同步。使用完全确定性算法的原因是,如果你有产生随机输出的函数,你就不能使用锁步架构,因为如果处理事件的函数对每个客户端,每次给出的结果都不一样,整个系统就会被搞砸。一个完全确定性的算法是由以下定义的:
- 除算法的输入外,不得使用任何其他数据。不允许使用的数据:随机数、存储的磁盘数据、全局变量、计时器(即程序启动后的时间)
- 算法的运行不能是时间敏感的
一个反例是,如果一个程序的多个实例写入一个 Excel 电子表格,而另一个程序将读取表格的最后一行。这将使程序具有时间敏感性,因为如果一个写程序的实例延迟了几秒钟,就会产生完全不同的 Excel 行顺序,使读取最后一行的程序获得完全不同的输入。
锁步和完全确定性算法的一个例子是,客户端拍下来的蓝图。当你点击一个蓝图将蓝图导入共享库时,蓝图图标就不会再变灰了,比如下面的右图。这是因为当你点击它们时,你选择将它们转移到游戏的共享库中。然后当你把它放置在某个地方时,你的客户端就会告诉服务器,你把蓝图放置在某个XY坐标上。然后服务器会告诉所有其他连接的客户端,它已经被放置在这些相同的坐标上。然后每个单独的客户端模拟所有的机器人从他们的机器人端口出来,获得资源,放置他们所拥有的实体,然后回到他们选择的机器人端口。所有的客户端都是自己模拟的,没有任何进一步的输入,并且使用前述的完全确定性算法以相同的方式进行模拟。
失去同步(’Desync’)是指两台计算机根据完全确定性算法应该在同一时间做一些事情并获得相同结果,但没有做到。通常情况下,当客户端和服务器在同一时间做同样的事情时,它们会很高兴,因为它们是同步的(’In sync’)。当两个客户端计算更新的结果不同,通常是由于编程错误,就会出现失去 Desync 的情况。请看下面的图片,这是一个 Desync 的例子。如果一个 Mod 作者或场景作者没有很好地管理他们的数据,也可能会导致 Desync。Desync 会强行让你的客户端从服务器上注销,并生成一个 Desync 报告,这是开发人员用来调查这些 Desync 的东西。
玩家:嘿,我计算的 Tick 33859 时刻净功率是 348。对吗?
服务器:什么?我算的是 936。你搞错了。我把地图发给你,然后断开连接,你可以稍后重连
— 异星工厂服务器
你可能会好奇,当机器人在地图上移动时,怎么不会发生 Desync?当然,如果它们都在工作,并选择一些机器人来执行上述任务,不同的客户端可能会选择不同的机器人来执行这些任务,不是吗?不是的。每个客户端总是会在同一时刻选择同一个机器人,因为选择机器人的算法是 完全确定的。两列火车从候车区进站?永远是同一列火车,因为这 也是完全确定的。一个喷吐虫决定攻击你的采矿前哨的哪个炮塔?也是完全确定的。这些只是几个例子,但游戏中的 一切 都是完全确定性的。如果不是这样,你就会在这里有一个 Desync,在那里又有一个 Desync,多人游戏就根本没法玩了。在多人游戏中,解同步的原因有很多,比如机器人建造、虫群的 AI 模拟,最重要的是,Mod 作者自己造成的。
即使你想使用像 math.random()
这样简单的方法在你的 Mod 或场景中得到一个 随机 数,也会有一致的结果——所有的客户端都会得到该函数的相同结果。这是因为异星工厂的随机数生成器是 有种子的。它从预先赋予的一个数字开始,然后用这个数字在之后产生随机数。如果你让每一个客户端都以同样的方式使用种子,你的 随机 数也能保持同步。需要注意的是,它是一个 伪随机数 生成器,因此不是真正的随机,因为它是用一个预先确定的数字初始化的,这使得结果可以在任何地方重现。参见这里来了解更多关于随机种子的信息。
现在你知道更多关于当你点击服务器列表中的服务器,通过 IP,通过 Steam 或通过局域网加入时会发生什么。异星工厂的开发者一直在非常努力地开发多人游戏,让我们可以创建大型游戏,如超过 500 人的多人游戏或复杂的 Clusterio 集群,为创作者提供他们开发有趣的东西所需的工具。你能做的限制越来越少,庞大的基地,海量的玩家,甚至可能两者兼而有之!所有这些都取决于你和你的设置。
征稿
一如既往的,我们正在召集任何想要为 Alt-F4 做出贡献的人,无论是提交文章还是帮助翻译都可以。如果您有些有趣的想法,并乐于与社区分享,这里就是一个好地方。如果您没有太大把握,我们会很乐意帮助您讨论内容创意和结构问题。如果您有意参与,从加入 Discord 开始吧!