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

如何用Firestore和Swift构建双人游戏数据结构并限制单游戏仅两人?

实现Firestore双人对战房间的玩家数量限制(Swift)

嘿,刚接触Firestore的时候碰到这类并发控制问题太正常了,完全不用不好意思!我之前做类似的双人游戏时也踩过坑,给你分享下最可靠的实现方式——用Firestore事务来确保原子性操作,避免多个玩家同时加入导致超过2人上限。

第一步:定义游戏房间的数据结构

首先,我们需要一个清晰的游戏房间文档结构,用来记录玩家列表和核心状态:

import FirebaseFirestoreSwift

struct GameRoom: Codable {
    @DocumentID var id: String? // 自动映射Firestore文档ID
    var players: [String] = [] // 存储玩家的UID
    let maxPlayers: Int = 2 // 固定双人对战上限
    var status: String = "waiting" // 标记房间状态(等待中/游戏中)
    // 可扩展其他游戏字段:当前回合、玩家分数等
}

第二步:用事务实现加入房间的核心逻辑

Firestore的事务能保证读取-修改-写入的操作是原子性的——也就是说,在你的事务执行期间,不会有其他客户端修改这个房间文档。如果有并发修改,事务会自动重试,完美解决多人同时抢房间的问题。

下面是完整的Swift代码示例:

import FirebaseFirestore

func joinGameRoom(roomId: String, currentUserId: String, completion: @escaping (Bool, Error?) -> Void) {
    let db = Firestore.firestore()
    let roomRef = db.collection("gameRooms").document(roomId)
    
    // 启动Firestore事务
    db.runTransaction({ (transaction, errorPointer) -> Any? in
        // 1. 读取目标房间的当前数据
        let roomDoc: DocumentSnapshot
        do {
            try roomDoc = transaction.getDocument(roomRef)
        } catch let fetchError as NSError {
            errorPointer?.pointee = fetchError
            return nil
        }
        
        // 2. 解析房间数据到自定义模型
        guard var gameRoom = try? roomDoc.data(as: GameRoom.self) else {
            let error = NSError(domain: "GameRoomError", code: -1, userInfo: [NSLocalizedDescriptionKey: "无法解析房间数据"])
            errorPointer?.pointee = error
            return nil
        }
        
        // 3. 检查加入条件:房间未开始+玩家不在房间内+房间有空位
        guard gameRoom.status == "waiting",
              !gameRoom.players.contains(currentUserId),
              gameRoom.players.count < gameRoom.maxPlayers else {
            let errorMessage: String
            switch true {
            case gameRoom.status != "waiting":
                errorMessage = "游戏已开始,无法加入"
            case gameRoom.players.contains(currentUserId):
                errorMessage = "你已经在这个房间里啦"
            default:
                errorMessage = "房间已满"
            }
            let error = NSError(domain: "GameRoomError", code: -2, userInfo: [NSLocalizedDescriptionKey: errorMessage])
            errorPointer?.pointee = error
            return nil
        }
        
        // 4. 添加当前玩家到房间,满员后自动切换状态
        gameRoom.players.append(currentUserId)
        if gameRoom.players.count == gameRoom.maxPlayers {
            gameRoom.status = "playing"
        }
        
        // 5. 提交修改到Firestore
        do {
            try transaction.setData(from: gameRoom, forDocument: roomRef)
        } catch let setError as NSError {
            errorPointer?.pointee = setError
            return nil
        }
        
        return gameRoom
    }) { (result, error) in
        // 回调返回操作结果
        if let error = error {
            print("加入房间失败:\(error.localizedDescription)")
            completion(false, error)
        } else {
            print("成功加入房间!")
            completion(true, nil)
        }
    }
}

额外优化建议

  • 玩家退出处理:如果玩家断开连接或主动退出,可通过Firestore的onDisconnect方法配合事务,从房间的players数组中移除对应UID,同时将房间状态切回等待中。
  • 自动匹配房间:若玩家需要自动找房,可先查询status: waiting且players.count < 2的房间,找到第一个符合条件的房间调用上述方法;若无空房则创建新房间。
  • 安全规则加固:在Firestore安全规则中添加验证,比如限制players数组长度不得超过2,只有房间内玩家才能修改房间数据,防止客户端恶意篡改。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 12:27:23