Matching very close events in Firebase

I am creating an iOS game. Each game room is built of two users. After reconciling the two users, I show the timer “waiting for a response” on both devices: they need to click the “I'm ready” button within 8 seconds, or both of them will be removed from the room + removed from Firebase.

The correct state of users (before each of them clicked the "I'm ready" button):

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

Critical note. The timer on the time difference between the two devices is + -2 seconds. In other words - as soon as the device’s timer expires before another

When the user clicks the "I'm ready" button, I update state : "userReady" and check if another user is ready (observing its state value).

If both users are userReady - the game is on.

PROBLEM

therefore, we have already cleared that in 100% of cases I have a slight time difference between both devices. BUT if, for example,

User1 click I'm Ready . So, now User2 received the ChildUpdate event, and as far as he knows - User2 completely ready for playback.

User1 timer ends first (fact), so when its timer ends, User2 timer will remain 1 second. NOW, in User1 time just reached zero, so they kicked him out of the room and sent a removeValue event to each of the Users nodes. While this happens, in this very small "gap" (between the zero end time of User1 timer , User2 clock shows 1 second (fact) - and he presses the ready button, than he thinks User1 ready to play, and he also - what the game starts play.

Final Results -

Both players have been removed from Firebase.

User1 is outside the room

User2 launches the game (and he considers himself an adversary)

How can I solve this final scenario? , I already tried to call the startGame function only when the "UpdateChild" state is complete, but it still gets there, possibly because the order is for updateChild and removeValue "?

Any suggestions? And BIG thanks to the Firebase team for having achieved how all the time !!!

+1
source share
1 answer

It does not make sense that User 1 will accept and then expire. User 2 must expire if the deadline has been reached and has not yet been accepted.

To prevent this, you are looking for transactions . When you expire a user, use a transaction to do this so that there are no data conflicts.

 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 } }); 

Perhaps corrected correctly:

 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(); }); 

In addition, you could simplify things and reduce the likelihood of chaos by not deleting / not ending records until both users reach the deadline for receipt and then do both at the same time in the transaction.

+2
source

All Articles