3

所以我写了这个在线游戏,我有点难以处理我应该多久接受一次移动包。问题是,如果不信任客户端一些数据,我就无法让它工作,我就无法让它工作。基本上我的想法是:

客户

/**** [game/client] movePlayer ***/


boolean playerCanMove = Date.now() - player.lastMove > 1000;

if(playerCanMove){

    player.lastMove = Date.now();
    player.move(RIGHT);
    client.send(new PacketMove(RIGHT));
}

服务器:

/**** [server] handle a move packet ****/
/** The only data received from the client is the direction(RIGHT) **/
/** the server has its own player.lastMove **/

let playerCanMove  = Date.now() - player.lastMove > 1000; 

if(playerCanMove){
    player.lastMove = Date.now();
    player.move(RIGHT);  
}
else{
   error = "Player is moving too fast"; 
}

这个问题是那个服务器player.lastMove由于数据包到达所需的时间,客户端/服务器中的服务器将不一样。

因此,如果客户端发送 2 个移动数据包,第一个移动时间为 0 100ms,第二个移动时间99ms为服务器保存player.lastMove有点晚,在这种情况下,误差幅度听起来也不太好。

在此处输入图像描述

有任何想法吗?

编辑:发送到服务器的唯一数据是为了示例而玩家想要移动的方向,它只是碰巧是相同的变量名。服务器有自己的player.lastMove变量

解决方案 感谢@Lev M。

我还没有实现这个,但是我已经考虑过这个了,我找不到任何弱点,如果你这样做,请随时发表评论!

我认为可以肯定地说,客户端唯一可以做的就是更改时间戳,他也可以更改时钟,但这会产生相同的效果,所以我只考虑这个假设他正在更改数据包时间戳

在此处输入图像描述

添加一个额外的&& packet.timeStamp < server.clock

boolean isTimeStampValid = packet.timeStamp >= player.lastMove + 1000 && packet.timeStamp < server.clock

不会出错,它会将攻击 #1 标记isTimeStampValid为错误

4

3 回答 3

2

这是一个我认为很容易实现的想法:不要Date.now()用来计算移动之间的时间。

保持某种游戏时钟。它可以是自整个游戏开始以来的毫秒数,例如Unix 时间戳,或者对于每个玩家来说都是唯一的,自他们登录以来的毫秒数。

将其独立地保存在服务器和客户端中。您可以不时使用SNTP之类的东西同步它们,尽管在您的情况下这可能是矫枉过正。

每当玩家移动时,让客户端根据该时钟在数据包中插入时间戳以及移动数据。

然后,让服务器逻辑检查这些时间戳的一致性。这样,移动之间的时间忽略了数据包的传输时间。

如果客户端想作弊,它不能发送两个时间戳小于 1000 毫秒的移动。如果是这样,您的服务器将知道忽略该移动。

您还可以实现一些逻辑,如果它检测到数据包一起到达或关闭(100-200 毫秒)但时间戳相隔 1000 毫秒或更长,则游戏将停止。这将是捕捉作弊者的简单方法。

如果你想要一个快节奏的游戏,你会忍不住在客户端保持状态,让玩家看到他们移动的结果,而无需等待服务器每次按键的响应。

事实上,根据您的游戏速度,您可能希望分批向服务器报告动作,而不是每一个动作都单独报告,这样既可以节省数据(如果此游戏是移动游戏),而且这样会更快。

然后,您的服务器可以一起验证整个批次的一致性。

于 2018-11-18T06:41:03.393 回答
0

你的架构是错误的。

游戏的客户端应该有大约零逻辑处理用户输入......用户输入应该尽可能原始地发送到服务器,并且客户端保持无状态

服务器端保持游戏的整个状态,接收来自客户端的用户输入,处理新的游戏状态(验证是否是有效的移动)然后 向客户端返回游戏的新状态

客户端应该简单地在屏幕上打印新状态。保持尽可能少的本地数据

您对客户“知道”最后一步的想法只有在有某种“撤消”最后一步时才有意义

第二点是,如果您http用于在客户端-服务器之间传输数据,则服务器无法在 move1 之前接收 move2 ... http 是 tcp 协议,tcp 保证交付顺序,因此您对不同延迟的假设导致错误服务器端的订单错误

于 2018-11-18T00:58:57.173 回答
-1

我认为客户端不需要保存日期并将其发送到服务器。您可以做的只是自己发送数据包,并在每次从播放器收到数据包时更新服务器上的 .lastMove 字段。这样你就不用依赖客户的信息了。

如果您对客户端预测一无所知,有多种方法可以实现这一目标,而无需让客户端发送任何可能导致问题的真实信息。

这里有一篇精彩的文章描述了用于制作这些类型游戏的技术:
http ://www.gabrielgambetta.com/client-server-game-architecture.html

于 2018-11-18T05:23:47.120 回答