如何解决1v1卡牌游戏War并发修改游戏状态导致的数据不一致问题?
我正在开发一款1v1卡牌游戏(类似经典卡牌游戏War),规则为:玩家1从卡组翻一张牌到桌面,玩家2完成翻牌后,系统对比两张牌的大小,牌面等级更高的玩家将两张牌收入自己的卡组。
玩家点击翻牌按钮时,客户端向服务器发送PATCH请求,告知服务器执行翻牌操作的玩家身份(player1或player2)。服务器查询数据库获取当前游戏状态,待异步查询完成后,更新游戏状态以记录新翻出的牌,再将更新后的状态同步给两个客户端。
当两名玩家同时点击“翻牌”按钮时会出现数据冲突:玩家1的翻牌请求处理完成后,服务器向双方返回更新后的状态,但紧接着玩家2的同时请求生成的新状态会覆盖该结果,新状态中玩家1的翻牌操作并未生效,导致玩家1需要重新点击翻牌按钮。
核心原因是:客户端发起请求时,无法获知另一个客户端同时发起请求时的游戏状态,导致服务器处理两个请求时基于的是同一初始状态,后处理的请求会直接覆盖先处理的请求结果。
- React
- Node.js
- Express.js
- Socket.IO
我调整了服务器的处理逻辑,没有让客户端的翻牌请求立即返回更新后的状态,而是添加了一个守卫逻辑(参考Stack Overflow社区用户dmikester1的建议):服务器会先检查两名玩家是否都完成了翻牌操作,只有当双方都完成翻牌后,才会向两个客户端返回最终更新的游戏状态。
但这带来了新的体验问题:玩家需要等到对方也完成翻牌后,才能看到对方的牌面。针对这个问题,我借鉴了**客户端预测(Client-side prediction)**的思路(参考Stack Overflow社区用户evilmandarine的提示),让客户端在发送翻牌请求后,先临时更新本地游戏状态展示翻牌结果,等服务器返回真实的同步状态后再做校正。目前测试下来这个方案可行,但不确定是否为最优解。
内容的提问来源于stack exchange,提问作者Anthony

