I have a question about a situation I am in currently which I have a solution for but am not quite sure if it 100% solves the issue at hand as I do not have tests written that could validate my solution.
I would love your oppinion on the matter and maybe a suggestion of a more elegant solution or possibly even a way to avoid the issue completely.
Here it is:
I am making a game where you may create or join open rooms/games.
There is a gamelist in the UI and when you click a game you attempt to join that game.
Each game has a bet (amount of credit that you win or lose) that the creator set which anyone joining must match.
On the serverside, before I let the player actually join the room, I must validate that his credit balance is sufficient to match the bet of the game he is joining. This will be via an API call.
Now, if two players join the game at once, lets say the validation of the first player joining takes 3 seconds but the validation of the second only 1 second.
Since rooms are 1 vs 1 I must not let a player join if someone else already did.
I can do this simply by checking if theres a player in the game already:
// game already full
if (game.p2) {
return socket.emit("join_game_reply", {
err: "Someone else already joined."
})
}
But, the issue at hand is, after that check, I must validate the balance.
So we get something like this:
socket.on("join_game", data => {
const game = openGames[data.gameId}
// game already full
if (game.p2) {
return socket.emit("join_game_reply", {
err: "Someone else already joined."
})
}
// check if users balance is sufficient to match bet of room creator
verifyUserBalance(socket.player, game.bet)
.then(sufficient => {
if(sufficient){
// join game
game.p2 = socket.player
}
})
})
The issue here:
What if at the time playerX clicks join the game is open, validation starts but while validating playerY joins and finishes validation before playerX and therefore is set as game.p2. Validation of playerX finished shortly after and the server then continues to set game.p2 to playerX, leaving playerY with a UI state of ingame even though on the server he is not anymore.
The solution I have is to literally just do the check again after validation:
socket.on("join_game", data => {
const game = openGames[data.gameId}
// game already full
if (game.p2) {
return socket.emit("join_game_reply", {
err: "Someone else already joined."
})
}
// check if users balance is sufficient to match bet of room creator
verifyUserBalance(socket.player, game.bet)
.then(sufficient => {
if(sufficient){
// join game
if (game.p2) {
return socket.emit("join_game_reply", {
err: "Someone else already joined."
})
game.p2 = socket.player
}
}
})
})
The reason I think this works is because nodeJS is single threaded and I can therefore make sure that after validating I only let players join if no one else joined in the meantime.
After writing this up I actually feel pretty confident that it will work so please let me in on my mistakes if you see any! Thanks a lot for taking the time!
elsebeforegame.p2 = socket.player