6

我正在考虑用 C++ 编写多用户 RTS 游戏(部分)的可行性。我很快发现,一个硬性要求是游戏模拟必须完全确定服务器和所有客户端的最后一点,以便能够将网络通信限制为用户输入,而不是游戏状态本身. 由于每个人都有不同的计算机,这似乎是一个难题。

那么,是否有一些“神奇”的方法可以让 C++ 编译器创建一个在 Linux(服务器)、Windows 和 Mac 上完全确定的可执行文件?我认为两个主要的 OSS C++ 编译器是 GCC 和 Clang,所以我想知道在这方面是否一个比另一个表现更好。

我也会对任何可用于验证 C++ 确定性的测试套件感兴趣。

[编辑] 通过确定性,我的意思是编译后的程序,给定相同的初始状态,并以相同的顺序输入,将始终在它运行的任何平台上产生相同的输出。所以,也是跨网络。对我来说,一致听起来像是对这种行为的恰当定义,但我不是母语人士,所以我可能会误解确切的含义。

[EDIT#2] 虽然关于确定性/一致性是否重要的​​讨论,以及我是否应该在游戏引擎中以此为目标,以及它在 C++ 中通常有多大的问题,是非常有趣的,但它并没有以任何方式真正回答问题。到目前为止,没有人知道我是否应该使用 Clang 或 GCC 来获得最可靠/确定性/一致的结果。

[EDIT#3] 我突然想到有一种方法可以在 C++ 中获得与在 Java 中完全相同的结果。必须采用 JVM 的开源实现,并提取实现运算符和数学函数的代码。然后你把它变成一个独立的库,并在其中调用内联函数,而不是直接使用运算符。手工操作会很痛苦,但是如果生成代码,那么它是一个完美的解决方案。也许这甚至可以通过类和运算符重载来完成,所以它看起来也很自然。

4

5 回答 5

1

不要依赖未定义或未指定的行为,(特别是不要使用浮点),使用什么编译器都没有关系。

If ais 1, and bis 2, then a + bis 3. 这是由语言标准保证的。

C++ 不是一块在某些编译器中某些东西是“确定性的”而在其他编译器中不是。C++ 陈述了一些事实(如 1 + 2 == 3)并将一些事情留给编译器(如函数参数的评估顺序)。如果您的程序的输出仅取决于前者(和用户的输入),并且您使用符合标准的编译器,那么在给定相同用户输入的情况下,您的程序将始终产生相同的输出。

如果您的程序的输出也取决于(例如)用户的操作系统,那么程序仍然是确定性的,只是输出现在由用户的输入和他们的操作系统共同决定。如果您希望输出仅取决于用户的输入,则由您来确保用户的操作系统不是影响程序输出的因素。一种方法是仅依赖语言标准保证的行为,并使用符合该标准的编译器。

总之,所有代码都是确定性的,基于其输入。您只需要确保输入仅由您想要的内容组成。

于 2012-02-26T01:14:15.773 回答
1

由于每个人都有不同的计算机,这似乎是一个难题。

它不是。真的,这种网络非常简单,只要你不做任何规范未定义的事情。IEEE-754对如何进行浮点数学运算、如何进行舍入等非常清楚,并且跨平台实现相同。

您不需要做的最重要的事情是在需要确定性的代码中依赖 SIMD CPU 指令(注意:这是物理、AI 等:游戏状态。不是图形,这是您需要 SIMD 的地方)。这些类型的指令在浮点规则中运行得又快又松。所以你的游戏代码中没有 SIMD;仅在“客户端”代码(图形、声音等)中。

另外,你需要确保你的游戏状态不依赖于时间之类的东西;每个游戏状态时钟滴答应该是一个固定的时间间隔,而不是基于 PC 的时钟或任何类似性质的东西。

显然,你应该避免任何你没有代码的随机函数。但同样,仅适用于您的主要游戏循环;图形内容可以是特定于客户端的,因为它只是视觉效果,并不重要。

就保持两个游戏状态同步而言,差不多就是这样。您使用的编译器对您来说不会是一个大问题。

请注意,星际争霸和星际争霸 II 将此作为其网络模型的基础。它们都在 Mac 和 PC 上运行,并且都可以互相对抗。所以这是很有可能的,并且不需要 Clang。

虽然如果你喜欢 Clang,你应该使用它。但这应该是因为你喜欢它,而不是因为网络。

于 2011-07-10T11:41:14.320 回答
0

如果您开始使用浮点数,您的所有赌注都将被取消。即使在同一个平台上,您也会遇到难以发现/修复的问题,只需选择 Intel 或 AMD cpu。

许多运行时库为不同的芯片优化了代码路径。这些都在规范范围内,但有些比这更精确。这会导致细微的舍入误差,迟早会累积到可能破坏事物的差异。

您的目标应该是在没有 100% 确定性的情况下逃脱。毕竟:如果对手比它应该在左边多一个像素,这对玩家有影响吗?它不是。重要的是,客户端和服务器之间的微小差异不会破坏游戏玩法。

玩家在屏幕上看到的内容应该是确定性的,所以他不会感到被欺骗,但这绝不是必需的。

我从事的游戏通过不断地在所有客户端之间重新同步所有实体的游戏状态来存档这一点。然而,我们几乎从未发送过整个游戏状态,但我们在几秒钟内发送了每帧分配作业的少数对象的游戏状态。

只要给最重要的对象比其他对象更高的优先级就可以了。例如,在赛车游戏中,如果对手汽车离你很远,那么它的确切位置并不重要,只需要每 20 秒左右更新一次就可以了。

在这些更新之间,请相信小的舍入错误不会累积太多以至于您遇到麻烦。

于 2011-07-10T12:14:44.590 回答
0

我认为您使用的编译器并不重要。

例如,在 Doom 中使用了这种完全确定的方法。他们使用的是固定的“随机”数数组,而不是随机数生成器。游戏时间以游戏中的时间来衡量(如果我记得的话,大约是 1/30 秒)。

如果您通过游戏内机制来衡量所有内容,而不是将您的工作卸载到可能有各种版本的标准库中,我相信您应该在不同机器上实现良好的可移植性。当然,前提是这些机器的速度足以运行您的代码!

但是,网络通信本身可能会产生问题:延迟、掉线等。您的游戏应该能够处理延迟消息,并在必要时自行重新同步。例如,您可能希望不时发送完整(或至少:更详细)的游戏状态,而不是仅依赖用户输入。

还要考虑可能的漏洞:

  • 客户:我在扔手榴弹
  • 服务器:你没有手榴弹
  • 客户:我不在乎。尽管如此,我还是在扔手榴弹
于 2011-07-10T11:42:48.153 回答
0

这有点愚蠢。在大端机器与小端机器上,或在 64 位机器与 32 位机器与其他一些随机机器上,您的程序不会“完全确定”(无论如何)“到最后一点”。

说到随机,很多游戏都有随机的元素。如果您通过调用 c 标准函数 rand() 来实现这一点,那么所有的赌注都没有了。

于 2011-07-10T11:43:30.660 回答