1

我正在构建一个 iOS 游戏。每个游戏室由两个用户构成。两个用户匹配后,我会在两台设备上显示“等待响应”计时器:他们需要在 8 秒内单击“我准备好了”按钮,否则他们都会被踢出房间 + 从 Firebase 中删除.

用户的正确状态(在他们每个人点击“我准备好按钮”之前):

Parent
      Matches
             User 1
                   opponent : User2
                   state : "NotReady"
             User 2
                   opponent : User1
                   state : "NotReady"

重要说明 - 两个设备上的计时器时间差为 +-2 秒,换句话说 - 一旦设备计时器将在另一个设备之前结束

当用户按下“我准备好了”按钮时,我正在更新state : "userReady",并检查其他用户是否也准备好了(观察其state值)。

如果两个用户都是userReady - 游戏开始。

问题

所以我们已经清楚,在 100% 的情况下,两个设备之间的时间差很小。但是,例如,

User1单击I'm Ready的按钮。所以现在User2有一个ChildUpdate事件,据他所知 -User2完全准备好比赛了。

User1 timer首先结束(事实),所以当他的计时器结束时,User2 timer将保持1秒数。 现在,准时User1刚到零,所以他被踢出房间,并removeValue在每个用户节点上发送一个事件。当这种情况发生时,在这个非常小的“间隙”,(在结​​束的零时间User1 timerUser2时钟显示的 1 秒(事实)之间 - 他按下准备按钮。比他认为User1准备好播放,他也是 -比游戏开始玩。

最终结果 -

两名玩家都已从 Firebase 中删除

用户 1 不在房间内

User2开始游戏(他认为他是对手)

我该如何解决这种最终情况?,我已经尝试过startGame仅在“UpdateChild state”完成时调用函数,但它仍然进入,可能是因为 updateChild 和 removeValue 的顺序?

有什么建议么?非常感谢 Firebase 团队一直以来的努力!!!

4

1 回答 1

3

用户 1 接受然后过期是没有意义的。如果达到时间限制并且他尚未接受,则用户 2 应该是过期的。

为了防止这种情况,您正在寻找transactions。当您要使用户“过期”时,请使用事务来执行此操作,以免发生数据冲突。

var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/");

ref.child('Parent/Matches').child(user1).transaction(function(currentValue) {
    if( currentValue && currentValue.state === 'NotReady' ) {
       return null; // delete the user
    }
    else {
       return undefined; // abort the transaction; status changed while we were attempting to remove it
    }
});

可能迅速正确:

var ref = Firebase(url: "https://<YOUR-FIREBASE-APP>.firebaseio.com/Parent/Matches/<user id>")

upvotesRef.runTransactionBlock({
    (currentData:FMutableData!) in
    if currentData && currentData.value["state"] != "NotReady" {
        return FTransactionResult.successWithValue(NSNull)
    }
    return FTransactionResult.abort();
});

此外,您可以简化事情并减少混乱的机会,方法是在两个用户都达到他们的接受时间限制之前不删除/过期记录,然后在事务中同时执行这两项操作。

于 2016-01-13T23:52:05.827 回答