同步客户端-服务器游戏状态

9
我正在制作一个客户端服务器的MMO风格游戏。到目前为止,我已经建立了框架,使得服务器和客户端相互交互以提供状态更新。服务器维护游戏状态并定期计算下一个状态,然后每隔一段时间(每n毫秒)向所有客户端发送新状态。用户可以在客户端上查看和响应这个新状态。然后将这些操作发送回服务器进行处理,并发送给下一个更新。
明显的问题是,这些更新在服务器和客户端之间传递需要时间。如果客户端试图攻击敌人,当更新返回到服务器时,很可能服务器已经推进了游戏状态,敌人已经不在原来的位置,超出了范围。
为了解决这个问题,我一直在努力想出一个好的解决方案。我已经查看了以下内容,它对我有所帮助,但并不完全:Mutli Player Game synchronization。我已经得出结论,不仅要传输游戏的当前状态,还可以传输其他信息,如方向(或AI移动的目标位置)和速度。从这里开始,在客户端上“猜测”实际状态所需的部分是通过将游戏状态向前推进n毫秒来实现的(就像服务器看到的那样)。
问题在于确定推进状态的时间,因为它取决于服务器和客户端之间的延迟时间,这可能会有很大的变化。此外,我应该将客户端状态推进到它查看它时的当前状态(即仅考虑更新传输到客户端所需的时间),还是应该将其推进到足够远,以便当其响应发送回服务器时,它将是正确的状态(考虑来回时间)。
有什么建议吗?
再次强调:
1)计算发送和接收之间的时间的最佳方法是什么?
2)我应该将客户端状态推进到整个往返路程中计数,还是仅计算从服务器到客户端获取数据所需的时间?
编辑:我已经想出了以下解决方案
由于我已经有许多数据包在客户端和服务器之间交换,如果必须添加,我不想增加流量。目前,客户端每150毫秒(仅在发生更改时)向服务器发送状态更新数据包(UDP),然后这些数据包被接收并处理。目前,服务器不会对这些数据包进行响应。
首先,我会让客户尝试估计他们的延迟时间。我会将其默认设置为50到100毫秒左右。我建议每2秒(每个客户端)服务器会立即响应其中一个包,并通过特殊的定时更新数据包将包索引发送回来。如果客户端接收到定时数据包,它将使用索引计算此数据包发送的时间,然后使用数据包之间的时间作为新的延迟时间。
这样可以使客户端相对及时地了解其延迟情况,而不会产生过多的网络流量。
听起来可行吗?还是有更好的方法?这仍然没有回答第二个问题。
3个回答

4
首先,如果你担心延迟少于1秒的问题,那么你开始脱离MMO的实际延迟范围。所有大型MMO游戏的处理方式基本上都是同时运行两个不同的“游戏” - 一个底层的游戏引擎负责处理所有的数学计算、角色状态、应用数字变化,另一个则是图形客户端。
第一个“游戏”,即数学和计算部分,在概念上更接近传统的控制台游戏(例如旧版MUD)。考虑消息来回传递,具有非常高的ACID隔离度。这些消息更加关注准确性,但你应该假设这些消息可能需要1-2秒或更长时间才能被处理和更新。这是“规则律师”,它确保命中点数被正确计算等。
第二个“游戏”是图形客户端。这个客户端真正专注于维护一种比第一个游戏更快的事情发生的幻觉,并将事件与图形外观同步。这个图形客户端经常会编造一些不重要的东西。这个客户端负责30帧/秒以上的图形。这就是为什么很多这样的图形客户端使用技巧,例如在用户按下按钮时启动攻击动画,但直到第一个游戏解决攻击后才实际解决动画。
我知道这与你问题的字面解释有些不同,但一旦你离开了网络上相邻的两台机器,100毫秒真的是非常乐观的...

我同意不能指望滞后时间在毫秒级别,但我确实需要知道它是多少。 - Nick Banks
小心让客户端进行计算 - 这为作弊者使用自动命中客户端欺骗提供了简单的途径。 - Will Iverson
1
我不让客户端进行那些计算,只做动画。 - Nick Banks

2

2) 我应该将客户端状态推进到整个往返过程的计数,还是仅在从服务器到客户端获取数据所需的时间内进行计数?

假设服务器在时间T0发送状态,客户端在时间T1看到它,玩家在时间T2做出反应,服务器在时间T3获得他们的答案,并立即处理它。 在这里,往返延迟为T1-T0 + T3-T2。在理想世界中,T0 = T1且T2 = T3, 并且观察时间和玩家行动处理之间唯一的延迟是玩家的反应时间,即T2-T1。 在现实世界中,它是T3-T0。 因此,为了模拟理想世界,您需要减去整个往返延迟:

T2-T1 = T3-T0 + (T1-T0 + T3-T2)

这意味着在网络较慢的玩家看来,游戏状态更加先进,而在网络较快的玩家看来则相对滞后。然而,这对他们来说并没有优势,因为它需要更长时间才能处理他们的反应。当然,在两个坐在一起且使用不同速度网络的玩家中间可能会有趣的情况发生。但这是相当不太可能的情况,不是吗?
整个过程存在问题:你在预测未来,这可能导致荒谬的情况发生。其中一些情况,比如撞墙,可以很容易地避免,但那些取决于玩家互动的情况则无法避免。
也许你可以把你的想法颠倒过来:不要预测,而是尝试在T3 - (T1-T0 + T3-T2)时评估玩家的动作。如果你确定一个角色会被这种方式击中,相应地减少其命中点数。这可能比原来的想法更容易实现和更真实,或者可能更糟糕,或根本不适用。这只是一个想法。
想象两个玩家彼此奔跑。按照预测,他们从右侧相互穿过。实际上,其中一个改变了方向,在最后他们从左侧相互穿过。

但这是相当不可能的情况,不是吗?如果一个设备使用蜂窝连接,另一个设备使用wifi呢?只是一个想法。无论如何,您是说我向用户显示游戏在他们的响应返回服务器时的状态,对吗? - Nick Banks
"蜂窝连接和WiFi" - 通常,两者都会尝试使用更快的连接。他们可以尝试这种方式来作弊,但对我来说似乎有点过于复杂了。这将是一种作弊行为,但在MMO中有更简单的作弊方式。 - maaartinus
是的,我说过这个。现在我要补充一点,你可以为所有玩家减少一个小常数来缩小时间差异。更少的外推可能意味着更少的问题;这只是一个想法。 - maaartinus

2
解决这种问题的一种方法是在客户端和服务器上运行游戏模拟。因此,不仅在服务器上模拟世界,还要在客户端上进行模拟。只需将客户端执行的操作(例如“玩家攻击怪物”)发送到服务器。服务器运行相同的模拟并检查事件。
如果它们不匹配(玩家作弊、延迟),则向客户端发送禁止信息,该操作不会被记录为服务器上的成功。这意味着其他客户端不会注意到它(服务器不会将该操作转发给其他客户端)。
这应该是处理延迟的一种非常有效的方式,特别是当您有很多PvM战斗时(而不是PvP)。由于怪物是一个模拟,所以客户端和服务器之间存在长时间的延迟并不重要。
话虽如此:大多数网络速度都非常快,延迟应该在几毫秒左右。这意味着您只需要让服务器足够快,以便能够在100ms内做出响应,那么玩家就不会注意到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接