如何通过网络保持数据结构同步?


12

语境

在我正在开发的游戏中(一点点点击的图形冒险),游戏世界中发生的几乎所有事情都由动作管理器控制,该动作管理器的结构有点像:

在此处输入图片说明

因此,例如,如果检查对象的结果使角色打招呼,走了一下然后坐下,我只需生成以下代码:

var actionGroup = actionManager.CreateGroup();
actionGroup.Add(new TalkAction("Guybrush", "Hello there!");
actionGroup.Add(new WalkAction("Guybrush", new Vector2(300, 300));
actionGroup.Add(new SetAnimationAction("Guybrush", "Sit"));

这将创建一个新的操作组(上图中的整行)并将其添加到管理器中。所有组的并行执行,但动作各组链接在一起,使得第二一个只有第一个完成之后开始。当组中的最后一个动作完成时,该组将被销毁。

问题

现在,我需要在网络上复制此信息,以便在多人游戏会话中,所有玩家都可以看到同一件事。序列化各个动作不是问题。但是,在联网方面,我绝对是初学者,我有几个问题。为了简化讨论,我认为我们可以将动作管理器组件抽象为简单的:

var actionManager = new List<List<string>>();

如何继续使所有播放器之间的上述数据结构的内容保持同步?

除了核心问题之外,我还涉及其他一些问题(即,上述同一问题的所有可能含义):

  • 如果我使用服务器/客户端体系结构(其中一个玩家同时充当服务器和客户端),并且其中一个客户端产生了一组操作,那么他应该将它们直接添加到管理器中,还是只发送请求到服务器,这又将命令每个客户端添加该组
  • 丢包之类的呢?游戏是确定性的,但我认为客户执行的动作顺序中的任何差异都可能导致世界状态不一致。我该如何防范此类问题
  • 如果我一次添加太多操作怎么办,这不会引起连接问题吗?有什么缓解的办法吗?

5
强制性的“首先阅读此内容”链接gafferongames.com/networking-for-game-programmers尽管有一些代码,但技术性不是很高,但是罗word,可以很好地解释您的选择。另外,它将使您与阅读该链接的其他所有人处于平等的地位,这是一个快乐的地方。
Patrick Hughes 2012年

@PatrickHughes感谢您的链接!我一定会阅读所有内容。
David Gouveia

有些软件包可以为您管理这种事情。我不知道它的效率或速度如何,但是NowJS(nowjs.com)是一个有趣的示例。从开发人员的角度来看,您只需在客户端和服务器之间共享数据结构。改变一个,另一个改变。
蒂姆·霍尔特

Answers:


9

如果我使用服务器/客户端体系结构(其中一个玩家同时充当服务器和客户端),并且其中一个客户端产生了一组操作,那么他应该将它们直接添加到管理器中,还是只发送请求到服务器,哪个命令每个客户端添加该组?

在这种情况下,您有三个选择,您已经提到其中两个。您选择哪种选项取决于多种因素,只有您才能最好地判断:

1:客户端产生一组操作,将其直接添加,然后将其发送到服务器。服务器接受它而没有任何检查

*这种方法的好处是,就客户端而言,他们自己的行为可以独立于网络延迟为他们提供即时反馈。缺点是客户端的消息被服务器接受为事实(它们可能不是)*

2:客户端向服务器发送请求,服务器再将请求广播给所有人,包括发起请求的客户端。

这种方法的好处是服务器现在可以控制游戏中发生的所有事情,从而减轻了大多数非法活动的问题(这里的非法行为可以从客户端砍墙到执行现在非法的举动,因为服务器拥有客户做出特定举动时没有的更多信息)

3:客户端生成一组动作,将其直接添加,然后将其发送到服务器。服务器处理该请求,对这些动作进行自己的检查以确保它们不是非法的,然后将该动作广播给包括客户端在内的所有玩家。

它以1和2两者中的最佳者为代价,但带宽需求略有增加。好处是可以立即向客户端反馈自己的举动,如果客户端的举动是非法的,则服务器会采取适当的措施(强制纠正客户端)。

丢包之类的呢?游戏是确定性的,但我认为客户执行的动作顺序中的任何差异都可能导致世界状态不一致。我该如何防范此类问题?

数据包丢失和极端延迟是一个很难解决的问题。根据游戏的类型(例如死招),采用了不同的解决方案(没有一个是完美的)来解决该问题。我将假设您的游戏不必同步物理。

您应该做的第一件事就是对所有动作进行时间戳记。然后,您的系统应将这些因素考虑在内并进行相应的移动(也许服务器对此负有责任,然后拒绝非法移动)。实施此系统时,假设等待时间为0(本地测试)。

即使在本地网络上,0延迟当然也不是一个好的假设,所以既然所有动作都尊重时间,那么您信任哪个时间?这就是时间同步问题开始发挥作用的地方。在线上的许多文章/论文将指导您解决这一问题。

如果我一次添加太多操作怎么办,这不会引起连接问题吗?有什么缓解的办法吗?

首先是显而易见的东西。切勿发送字符串或序列化的对象。不要以游戏本身正在更新的速度发送消息。批量发送(例如每秒发送10次)。服务器/客户端偶尔会需要纠正错误(尤其是舍入错误)(因此,每秒,服务器都会发送所有信息[一切=所有对确保游戏中的内容正确无误至关重要的数据)状态],然后客户端将其捕捉到和/或将其考虑在内以更正其状态)。编辑:我的一位同事提到,如果您使用UDP [您应该使用,如果您有很多数据],则没有网络排序。每秒发送10个以上的数据包会增加乱序到达的数据包的变化。我看看是否可以援引该说法。至于乱序数据包本身,您可以丢弃它们或将其添加到系统的消息/移动队列中,该消息/移动队列旨在处理过去的更改以更正当前状态。

您应该有一个消息传递系统,它依赖于占用很少空间的内容。如果必须发送只能具有两个值的内容,则仅发送一位。如果您知道只能进行256次移动,请发送未签名的字符。有各种各样的旋转技巧可以尽可能紧密地打包消息。

您的消息传递系统很可能会使用大量枚举,以使您可以轻松地从传入消息中提取移动(如果您不明白我的意思,我建议您看一下RakNet及其示例)。

我确信您已经知道您无法解决因高延迟和数据包丢失而引起的问题,但是您可以使其影响不那么明显。祝好运!

编辑:帕特里克·休斯(Patrick Hughes)的上面带有链接的评论使此答案不完整,充其量只是针对OP的问题。我强烈建议改为阅读链接的主题


非常感谢您的详细答复,这几乎涵盖了我所有的担忧。关于您所说的部分,只有一个问题so every second, the server sends everything ... which the client then snaps to。我可以一次发送大量的信息吗?合理的最大尺寸是多少?
David Gouveia'4

一个合理的最大值将是一个不会引起延迟的数字:)...我知道您不是在寻找答案,但是我不想放一个数字,因为我对您的游戏不熟悉。
Samaursa'4

同样,没有硬性规定应该发送一大块,我只是举了一个例子。
Samaursa '04年

@Samaursa-我不同意“如果您正在使用UDP [如果有大量数据,应该使用UDP]”注释。由于它们是网络编程的新功能,因此TCP可以帮您处理大多数困难的事情,但它的速度要慢得多。在他们的情况下,我将使用TCP直至成为瓶颈。到那时,他们将对他们的应用程序如何使用网络有更好的了解,因此实现UDP的工作将更加容易。
克里斯(Chris)

@Chris:我不同意:) ...使用TCP和UDP之间的决定就像(imo)选择使用Python和C ++进行开发之间的决定。从理论上讲,这听起来不错,但是在实践中,很难简单地切换(同样是imo)。
Samaursa 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.