提供实时游戏状态更新的服务器

问题描述:

目前我的游戏服务器很小(一个区域和〜50 AI),并且每次发送状态更新数据包(UDP)时,都会向每个客户端发送一个完整状态。这会创建大约1100字节的数据包大小。几乎所有的发送是所有实体的以下信息:提供实时游戏状态更新的服务器

int uid 
int avatarImage 
float xPos 
float yPos 
int direction 
int attackState 
24 bytes 

编辑:更高效的结构

int uid 
byte avatarImage 
float xPos 
float yPos 
byte direction & attackState 
14 bytes 

但我将需要发送最终的详细信息的实体。比如我加入到这个:

float targetXPos 
float targetYPos 
float speed 

由于需要更多的数据为每个实体发送,我很快就要到来,很可能已经通过数据包的最大尺寸。所以我试图想到一些可能的方法来解决我的问题:

1)只是建立状态更新包,直到我用完了房间,然后剩下的其余部分。非常糟糕的客户视图。不是一个真正的选择。

2)只将N个最近实体的数据发送给客户端。这要求每个状态更新计算出每个客户端最近的N.这可能非常耗时。

3)一些如何设计数据包,以便我可以发送多个相同的更新。目前,客户端假定数据包的结构如下:

int currentMessageIndex 
int numberOfPCs 
N * PC Entity data 
int numberOfNPCs 
N * NPS Entity data 

然后客户端接受这个新数据并完全覆盖其状态副本。由于数据包是完全独立的,即使客户端错过了一个数据包,也是可以的。我不知道我将如何实现同一更新的多个数据包的想法,因为如果我错过其中的一个,那么呢?我无法用更新,部分状态覆盖完整,过时的状态。

4)只发送改变的实际变量。例如,对于每个实体,我添加一个int,这是每个字段的位掩码。诸如速度,目标,方向和avatarImage等事物不需要在每次更新时发送。我仍然回到如果客户端错过实际需要更新其中一个值的数据包时会发生什么情况的问题。我不知道这将是多么重要。这还需要在客户端和服务器端创建/读取数据包时进行更多的计算,但不要太多。

有更好的想法吗?

+0

我喜欢4号的想法。但是,如果任何信息相互连接(如速度和方向)。确保它们一起发送。如果在这两种情况下客户端都会丢失数据包......它仍然会错过更新......数据是一次全部发送还是仅在发生更改时发送。 – 2011-04-20 06:44:33

+0

压缩呢?或某种自定义编码?如果事先知道某些值的范围,则可以在每个字段上丢失一些比特... – Dan 2011-04-20 06:58:28

+1

我也喜欢数字4。除了发送* update *包之外,您还可以根据客户端的要求发送* whole_status *包。当你在* update *包中包含序列号时,客户端知道它应该何时请求* whole_status *。 – 2011-04-20 06:59:34

我会用数字4和数字2去。 正如你已经意识到的那样,通常只发送更新而不是完整的游戏状态。但要确保你总是发送绝对值而不是增量,这样在丢弃数据包时不会丢失任何信息。您可以在客户端使用航位推算法,在糟糕的网络条件下尽可能平滑地制作动画。 你必须为此仔细设计,以便丢失数据包并不重要。

至于2号,如果你为它设计的话,它不必费时。例如,您可以将您的游戏区域划分为一个正方形网格,其中每个实体总是在一个特定的正方形中,让游戏世界跟踪这一点。在这种情况下,计算9个surronding网格中的实体是O(1)操作。

+0

这不是一个网格坐标系,你可以通过float xPos,yPos看到。没有网格,我允许个人电脑重叠,所以你可以有50人正确的对方。 – 2011-04-20 07:23:53

一个问题,我跑进发送增量更新(基本上是你的第4个选项),是数据能进能出的顺序或只是老了,这取决于你的服务器如何并发是,更新在服务器上的多个客户端的主要竞争条件同一时间。

我的解决方案是更新通知发送给所有的客户,有位掩码设置为位已更新的每个项目。

然后在客户端请求基于掩码的具体数据的当前值,这也允许客户端只请求数据是感兴趣的

这样做的好处是避免了竞争条件和客户端始终获得最新的价值。

的缺点是它需要一个往返,获得真正的价值。

更新来演示我试图做的点。

假定4个客户机A,B,C,d。 A和B同时向服务器Xa和Xb上的可变状态X发送更新。由于B比A晚得多,服务器上X的最终状态是X = Xb。

服务器发送更新后的状态给所有的客户端,所以C和D得到X的更新状态,因为交付顺序是不确定的C得到Xa然后Xb和D得到Xb然后Xa,所以在这一点上,客户端C和D对X的状态有不同的想法,一个反映了服务器有另一个没有的东西,它已经被弃用(或旧的)数据。

另一方面,如果服务器发出一个X已经更改为所有客户端的通知,C和D将获得两个X的状态更改通知。它们都请求X的当前状态,并且它们两者都以Xb的服务器上的最终状态X结束。

由于状态通知的顺序是无关紧要的,因为在它没有数据,以及客户端发出的每个他们都结了一致的数据通知更新状态的请求,

我希望这是我想说的更清楚些。

是它会增加延迟,但设计者必须决定什么更重要的是,延迟或者让所有客户反映可变数据的相同状态。这将取决于数据和游戏。

+0

客户端数据始终是旧的,因此强制客户端请求它并不能解决此问题。唯一真正的问题是多大。如果服务器正确地管理世界状态,那么它应该能够向任何感兴趣的客户发送更新而没有问题。 – Kylotan 2011-04-20 11:41:11

+0

旧的含义不推荐使用。如果多个客户端同时更新相同的数据,则无法保证一个客户端不会收到过期的数据。并发意味着消息以非确定性顺序接收。我的方法意味着你总是得到服务器知道的最新信息,并且永远不会得到错误的数据,尽管它可能需要紧接着执行两个请求。想想看,这很复杂。 – 2011-04-20 18:15:43

+1

在实时更新的情况下,您应该在发生更改时将更改后的状态发送给感兴趣的客户。这是网络上经典的观察者模式。然后,无需往返即可获得最新的价值,因为您已拥有它或正在向您前进。无论采用哪种方式,您都必须假设您可能拥有旧数据,因为信息需要时间才能从服务器传输到客户端,并且自上次通知以来数据可能会再次发生更改。额外的往返行程只意味着您可以得到旧的数据。 – Kylotan 2011-04-20 18:29:32

这种类型的系统是使用Dead Reckoningpredictive contract算法通常解决。您不能依赖所有客户端同时获取更新,因此您需要根据先前已知的值预测位置,然后根据服务器生成的结果验证这些预测。

+0

我已经知道了,但问题仍然是如何获取所有数据到客户端,以便它可以使用它来执行Dead Rechoning。它不能以某种速度和方向来完成,或者在这种情况下它正在寻找一个目标。 – 2011-04-20 20:18:27

+0

您无法保证每个客户都会同时更新;从而进行推测。你的服务器应该是实体定位的权威来源;但每个客户都可以根据您已经提供的数据(位置,方向,速度和目标)预测它和所有其他实体的去向。每x毫秒,你的服务器应该发送最新的位置变化量,你的客户将用它来纠正他们的预测。这个数据包可以简单地为{uid,deltax,deltay}。您会看到单独的数据包以了解速度,方向和目标更改。 – 2011-04-20 21:26:27

+0

你正在浪费原始数据包结构中的位。您可能没有INT_MAX头像图片或攻击状态。您可以通过使用位掩码而不是整数来减少这些内容。你也许可以对你的使用和方向也做同样的事情。 – 2011-04-20 21:27:46