You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于AWS API Gateway Websockets的双人游戏回合结束判定方案问询

解决AWS Websocket双人对战游戏的回合结束检测问题

针对你遇到的并发提交时无法检测双方操作完成的问题,核心根源是非原子的读-检查-写流程导致竞态条件,以下是几个实用的解决方案,适配你的DynamoDB数据模型:

方案1:新增游戏状态记录(推荐)

在现有两条玩家记录的基础上,新增一条以游戏为维度的状态记录,用原子更新操作避免竞态:

  • 数据结构:
    • primaryKey: 1$(同游戏ID)
    • secondaryKey: STATUS
    • 属性:user1_submitted(布尔,标记玩家1当前回合是否提交)、user2_submitted(布尔)、current_round(整数)
  • 操作流程:
    1. 玩家提交操作时,先更新自己的moves数组(保持现有逻辑)
    2. 调用DynamoDB的UpdateItem原子更新状态记录:设置当前玩家的_submitted为true,同时利用返回的最新状态检查是否双方都已提交
    3. 若确认双方都提交,立刻通过Websocket通知回合结束,然后重置状态记录的提交标记,准备下一回合

这种方式把状态集中管理,原子更新彻底避免了并发问题,还能方便扩展回合数、超时检测等功能。

方案2:基于现有模型的事务+条件检查

如果不想新增状态记录,可以用DynamoDB事务结合条件表达式实现:

  • 给moves数组中的每个操作添加回合标识(比如{"round": 3, "move": "attack"}),方便查询当前回合操作
  • 玩家提交操作时,执行一个事务:
    1. UpdateItem:追加自己的回合操作到moves数组
    2. ConditionCheck:检查对方的moves数组中是否存在当前回合的操作
  • 若条件检查通过,说明对方已提交,直接触发回合结束;若不通过,仅完成自己的更新,等对方提交时再触发检查

这个方案改动小,但事务的读写消耗略高,且需要维护回合标识的一致性。

方案3:优化现有DynamoDB Streams方案

你觉得Streams只适合日志,但实际生产中用它做业务触发是完全可行的,关键是处理幂等性:

  • 在Lambda中处理Stream事件时,用强一致性读查询游戏的两条记录,检查双方当前回合的操作是否都已提交
  • 新增一个round_processed字段到状态记录(或玩家记录),当确认双方都提交后,先检查该字段是否为false,再触发Websocket通知,最后原子性地将其设为true
  • 这样即使Stream事件重复推送,也不会重复发送通知

这种方案不用大幅改动现有逻辑,适合快速迭代,Streams的延迟通常在毫秒级,不会影响游戏体验。

方案1的代码示例(Python + boto3)

import boto3
from your_websocket_utils import send_round_end_notification  # 假设你有发送Socket通知的工具函数

dynamodb = boto3.resource('dynamodb')
game_table = dynamodb.Table('your_game_table_name')

def handle_player_move(game_id, player_id, move):
    # 更新玩家自己的操作记录
    game_table.update_item(
        Key={
            'primaryKey': f"{game_id}$",
            'secondaryKey': player_id
        },
        UpdateExpression="SET moves = list_append(moves, :new_move)",
        ExpressionAttributeValues={":new_move": [{"round": 1, "action": move}]}  # 假设当前是第1回合
    )

    # 原子更新并检查游戏状态
    update_response = game_table.update_item(
        Key={
            'primaryKey': f"{game_id}$",
            'secondaryKey': "STATUS"
        },
        UpdateExpression=f"SET {player_id}_submitted = :true",
        ConditionExpression=f"attribute_exists({player_id}_submitted) = false",  # 防止重复提交
        ExpressionAttributeValues={":true": True},
        ReturnValues="ALL_NEW"
    )

    current_status = update_response['Attributes']
    # 检查双方是否都已提交
    if current_status.get("user1_submitted") and current_status.get("user2_submitted"):
        # 通知双方回合结束
        send_round_end_notification(game_id, ["user1", "user2"])
        # 重置状态,准备下一回合
        game_table.update_item(
            Key={
                'primaryKey': f"{game_id}$",
                'secondaryKey': "STATUS"
            },
            UpdateExpression="SET user1_submitted = :false, user2_submitted = :false",
            ExpressionAttributeValues={":false": False}
        )

内容的提问来源于stack exchange,提问作者Kob3Bryant

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 04:15:41