4

当您在多人游戏中服务器从客户端接收移动(位置)信息时,您希望验证此信息作为反作弊措施。

这可以这样做:

maxPlayerSpeed = 300; // = 300 pixels every 1 second
if ((1000 / (getTime() - oldTimestamp) * (newPosX - oldPosX)) > maxPlayerSpeed)
{
   disconnect(player); //this is illegal!
}

这是一个简单的例子,只考虑了 X 坐标。这里的问题是,一旦服务器收到最后的位置更新,就会存储 oldTimestamp 。这意味着,如果当时有延迟峰值,则旧时间戳将比服务器接收到的新位置更新晚得多。这意味着时差将不准确。

例子:

  1. 客户说:我现在在位置 5x10
  2. 滞后峰值:服务器在时间戳 500 收到此消息(它通常应该在 30 时到达)
  3. ....1 秒动作...
  4. 客户说:我现在在 20x15 的位置
  5. 无延迟峰值:服务器在时间戳 1530 处接收消息

服务器现在会认为这两个位置的时间差是1030,但是实际的时间差是1500。这可能会导致反作弊检测认为1030不够长,从而踢掉客户端。

可能的解决方案:让客户端在发送时发送时间戳,以便服务器可以使用这些时间戳代替

问题:这个解决方案的问题是玩家可以操纵客户端发送一个不合法的时间戳,所以反作弊系统不会启动。这不是一个好的解决方案。

也可以简单地允许 maxPlayerSpeed * 2 速度(例如),但是这基本上允许速度达到正常速度的两倍。这也不是一个好的解决方案。

那么:您对如何解决这个“服务器时间戳和延迟”问题有什么建议,以使我的反作弊措施值得吗?

4

3 回答 3

4

不不不..恕我直言,这都是错误的,以及如何不这样做。

补救措施是不信任您的客户。不要让客户发送他们的位置,让他们发送他们的按钮状态!将按钮状态视为客户说“我正在前进,除非你反对”的请求。如果客户端发送“向前移动”消息并且无法向前移动,服务器可以忽略它或做任何它喜欢的事情以确保一致性。在这种情况下,客户只会自欺欺人。

至于通过数据包泛洪实现的速度黑客,请保留一个数据包计数器。弹出在特定时间范围内发送的数据包多于允许设置的客户端。客户端应在每个滴答/帧/世界时间步长发送一个数据包。在整个时间步长增量中根据时间命名数据包很方便。然后可以识别并忽略相同时间步长的过多数据包。请注意,在使用 UDP 时多次发送相同的数据包是一个好主意,以防止数据包丢失。

同样,永远不要相信客户。这一点怎么强调都不过分。

于 2009-11-02T19:37:48.760 回答
3

通过过滤消除滞后尖峰。或者换一种说法,不是总是将他们的新位置与之前的位置进行比较,而是将其与几次更新前的位置进行比较。这样,任何短期抖动都会被平均化。在您的示例中,服务器可以查看延迟峰值之前的位置,并查看玩家总体上以合理的速度移动。

对于每个玩家,您可以简单地持有最后 X 个位置,或者您可能持有很多最近的位置加上一些较旧的位置(例如 2、3、5、10 秒前)。

通常,您无论如何都会在正常移动速度范围内在服务器上执行插值/外推,以隐藏其他玩家的抖动 - 您所做的只是将其扩展到您的作弊检查机制。所有合法的加速都会在明显的减速之后出现,而插值有助于弥补这种错误。

于 2009-11-01T13:48:38.120 回答
2

无论对方法的看法如何,您正在寻找的是被认为是“作弊”的速度阈值。给定距离和时间增量,您可以根据您的作弊阈值轻松查看它们是否“移动得太远”。

time = thisTime - lastTime;
speed = distance / time;
If (speed > threshold) dudeIsCheating();

用于测量的时间是服务器接收数据包的时间。虽然看起来微不足道,但它正在计算每个角色移动的距离,这最终可能会非常昂贵。最佳路线是服务器根据速度计算位置,即角色的位置。客户端从不传达位置或绝对速度,相反,客户端发送“最大百分比”速度。


澄清一下:这只是为了作弊检查。您的代码有可能在服务器上延迟或长时间处理影响您的结果。公式应该是:

maxPlayerSpeed = 300; // = 300 pixels every 1 second
if (maxPlayerSpeed < 
    (distanceTraveled(oldPos, newPos) / (receiveNewest() - receiveLast()))
{
   disconnect(player); //this is illegal!
}

这将玩家的旅行速度与最大旅行速度进行比较。时间戳由您收到数据包的时间决定,而不是由您处理数据的时间决定。您可以使用任何您关心的方法来确定要发送给客户端的更新,但是对于您想要确定作弊的阈值方法,上述不会受到延迟的影响。

在第 1 秒接收数据包 1:位置 1 的字符

在第 100 秒接收数据包 2:位置 3000 的字符

行驶距离 = 2999

时间 = 99

率 = 30

没有发生作弊。

在第 101 秒接收数据包 3:位置 3301 处的字符

行驶距离 = 301

时间 = 1

率 = 301

检测到作弊。

您所说的“延迟峰值”实际上是数据包传递的高延迟。但这并不重要,因为您不会在处理数据时经过,而是在收到每个数据包时经过。如果您保持时间计算独立于您的游戏滴答处理(因为它们应该与“滴答”期间发生的事情一样)高和低延迟只会影响服务器对角色位置的确定程度,您可以使用插值+外推来解决.

如果客户端不同步到他们没有收到对其位置的任何更正并且与服务器严重不同步的情况下,则会出现严重的数据包丢失和高延迟,您的作弊检查将无法解释。您需要通过处理实际网络通信在较低层考虑这一点。

对于任何游戏数据,理想的方法是让除服务器之外的所有系统都落后 100-200ms。假设您每 50 毫秒进行一次预期更新。客户端收到第一个和第二个。客户端在收到第二次更新之前没有任何数据要显示。在接下来的 50 毫秒内,它显示了已经发生的变化进程(即,它处于非常轻微的延迟播放状态)。客户端将其按钮状态发送到服务器。本地客户端还根据这些按钮按下预测移动、效果等,但只向服务器发送“按钮状态”(由于按钮数量有限,表示每个状态所需的比特数量有限,这允许更紧凑的数据包格式)。

服务器是权威模拟,决定实际结果。服务器每隔 50 毫秒向客户端发送一次更新。服务器不是在两个已知帧之间进行插值,而是为任何丢失的数据推断位置等。服务器知道最后的真实位置是什么。当它接收到更新时,发送给每个客户端的下一个数据包包括更新的信息。然后,客户端应该在到达该时间点之前收到此信息,并且玩家会在它发生时做出反应,不会看到任何奇怪的跳跃,因为它从未显示不正确的位置。

可以让客户端对某些事情具有权威性,或者让客户端充当权威服务器。关键是确定对客户的信任有多大影响。


客户端应该定期发送更新,比如每 50 毫秒。这意味着 500 毫秒的“延迟峰值”(数据包接收延迟),在延迟期内发送的所有数据包都将延迟相似的数量,或者数据包将被无序接收。底层网络应该优雅地处理这些延迟(通过丢弃具有过大延迟的数据包,强制按顺序发送数据包等)。最终结果是,通过适当的数据包处理,预期的问题不会发生。此外,不从客户端接收显式字符位置,而是让服务器显式更正客户端并仅从客户端接收控制状态将防止此问题。

于 2009-11-06T04:06:13.423 回答