因此,我正在编写此在线游戏,并且在处理我应该多久接受一次移动数据包方面有些困难。问题是如果不信任客户端某些数据,我将无法使其正常工作。基本上我的想法是:
客户
/**** [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个移动数据包,则服务器会在播放器移动速度过快时认为第一个数据包的行进时间为100ms
,第二个数据包的行进时间为99ms
。那不是问题,问题是旅行时间有所不同,并且服务器保存player.lastMove
的时间有点晚,在这种情况下,错误余地听起来也不太好。
有什么想法吗?
编辑:为示例起见,唯一要发送到服务器的数据是玩家想要移动的方向,它恰好是相同的变量名。服务器具有自己的player.lastMove
变量
解决方案 Thanks to @Lev M.
我还没有实现,但是我已经考虑过退出了,但是我找不到任何弱点,如果您愿意,请随时发表评论!
我可以肯定地说,客户端唯一可以做的就是更改时间戳,他也可以更改时钟,但这将产生相同的效果,因此,我仅在假设他更改了数据包时间戳的情况下考虑了这一点< / p>
添加额外的&& packet.timeStamp < server.clock
到
boolean isTimeStampValid = packet.timeStamp >= player.lastMove + 1000 && packet.timeStamp < server.clock
不会出错,它将攻击#1 isTimeStampValid
标记为错误
答案 0 :(得分:1)
我认为这是一个易于实现的想法:
不要使用Date.now()
计算两次移动之间的时间。
保留某种游戏时钟。像Unix timestamps一样,可能是整个游戏开始以来的毫秒数,或者对于每个玩家来说,可能是唯一的毫秒数(自他们登录后的毫秒数)。
在服务器和客户端上独立保持此状态。 您可以偶尔使用SNTP之类的功能来同步它们,尽管这对于您而言可能是过大的选择。
每当玩家移动时,都让客户端根据此时钟在数据包中插入时间戳以及移动数据。
然后,让服务器上的逻辑检查这些时间戳的一致性。 这样,两次移动之间的时间就忽略了数据包的传输时间。
如果客户想要作弊,则不能发送时间戳小于1000毫秒的两个动作。 如果是这样,您的服务器将知道忽略此举。
您还可以实现一些逻辑,如果该逻辑检测到数据包一起到达或关闭(100-200ms),但时间戳间隔为1000ms或更长,则该逻辑将停止游戏。 这将是捕捉作弊者的简便方法。
如果您想要快节奏的游戏,则不免将状态保留在客户端中,让玩家无需等待服务器每次击键响应即可看到其移动结果。
实际上,根据游戏的速度,您可能希望分批向服务器报告移动,而不是单独移动,以节省数据(如果此游戏用于移动设备),并且这样做会更快。
您的服务器然后可以一起验证整个批次的一致性。
答案 1 :(得分:0)
您的架构错误。
游戏的客户端应该具有大约零逻辑处理用户输入...用户输入应尽可能原始地发送到服务器,而客户端保持无状态
服务器端保留游戏的整个状态,从客户端接收用户输入,处理新的游戏状态(验证是否为有效动作),然后将新的游戏状态返回给客户端
客户端应在屏幕上简单地打印新状态。 保持尽可能少的本地数据
您对客户“ 知道”的想法只有在有某种“ 撤消”最后一步的情况下才有意义
第二点是,如果您使用http
在客户端-服务器之间传输数据,则服务器将无法在move1之前接收move2 ... http是tcp协议,并且tcp保证交付顺序,因此您对导致服务器端顺序错误的不同延迟的假设是错误的
答案 2 :(得分:-1)
我认为客户端无需保存日期并将其发送到服务器。您可以做的就是简单地自行发送数据包,并在每次从播放器收到数据包时更新服务器上的.lastMove字段。这样,您就不必依赖客户端来获取信息。
如果您对客户端预测不满意,可以采用多种方法来实现,而不必让客户端发送任何可能导致问题的真实信息。
这里有一篇很棒的文章,描述了制作这类游戏的技术:
http://www.gabrielgambetta.com/client-server-game-architecture.html