Discrepancy Between Data in Callback Passed to save() and Actual Server Data

19 views
Skip to first unread message

Ryan Emberling

unread,
Jul 6, 2015, 1:05:58 PM7/6/15
to sai...@googlegroups.com
Hey Guys,

I'm having some trouble using nested save calls. I am making a multiplayer card game. I have 3 Models: Card, Player, and Game. Card has an 'attachments' attribute, which is a collection of Cards. Player has a 'points' attribute and a 'runes' attribute, both of which are collections are cards. The Game has a 'players' attribute, which is a collection of two players, as well as 'deck' and 'scrap' attributes, which are both collections of cards. I am making an action where a player plays a jack card from their hand, adds that jack to the attachments of a card in their opponent's point collection, and then moves the card that was in their opponent's points (which we'll call the 'target' card) into their own points collection. The jack is thus played on 'top' of an opponent's point card to steal it. 

In order to facilitate this, we find the relevant game, find both of its players and find the target card. After the add() and remove() calls are made, we save the game, save the target card, save the first player, then save the second player, all in a nested fashion to ensure that by the time publishUpdate() is called, all of the saves have happened. The problem is that when the first player to be saved is the one playing the jack, the point card being added to his point collection isn't there on the server. What is really weird is that we log both players inside the callbacks passed to their save() methods, and both players are always logged correctly. Checking the server at localhost:1337/player immediately after executing the action reveals that the server has not actually added the card to this player's points. What I really don't understand is, how can the data inside a cb inside a save method be different from what the server actually stores? Isn't the second parameter of the function passed to the save() method always an exact copy of what the server has stored? There are no other save() calls after the logs inside the action. Here is the action, itself:

req.body has 'gameId': the id of the game, 'pNum', the number determining if the player playing the jack is player0, or player1 (used to index the playerSort array), 'thiefId': the id of the player who is playing the jack card from their hand, 'victimId': the id of the player whose point card is being removed from his points collection and added to the thief player's point collection, 'jackId': the id of the card that is the jack being played from the thief's hand collection, and 'targetId': the id of the card in the victim player's point collection that will be moved to the thief player's points. 

req.body.pNum = 0 if the playerSort[0] is the player playing the jack and req.body.pNum = 1 if playerSort[1] is the player playing the jack ie playerSort[req.body.pNum] is the thief player and playerSort[(req.body.pNum + 1) % 2] is the victim player.

jack: function(req, res) {
if (req.isSocket && req.body.hasOwnProperty('gameId') && req.body.hasOwnProperty('pNum') && req.body.hasOwnProperty('thiefId') && req.body.hasOwnProperty('victimId') && req.body.hasOwnProperty('jackId') && req.body.hasOwnProperty('targetId')) {

Game.findOne(req.body.gameId).populate('players').populate('deck').populate('scrap').exec(function(error, game) {
if (error || !game) {
console.log("Game " + req.body.gameId + " not found for jack");
res.send(404);
} else {
Player.find([req.body.thiefId, req.body.victimId]).populate('hand').populate('points').populate('runes').exec(function(erro, players) {
if (erro || !players[0] || !players[1]) {
console.log("Can't find players for jack");
res.send(404);
} else {
                                                        //Sort players returns an array of the two players sorted according to their pNum attribute
                                                        //This is used to determine who is player0 and who is player1
var playerSort = sortPlayers(players);

Card.findOne(req.body.targetId).populate('attachments').exec(function(err, target) {
if (err || !target) {
console.log("Card " + req.body.targetId + " not found for jack");
res.send(404);
} else {
var yourTurn = req.body.pNum === game.turn % 2;
if (yourTurn) {
                                                                               //Player models have a frozenId attribute corresponding to one card that they are not allowed to play on a given turn
var cardIsFrozen = playerSort[req.body.pNum].frozenId === req.body.jackId;

if (!cardIsFrozen) {

playerSort[(req.body.pNum + 1) % 2].points.remove(target.id);
playerSort[req.body.pNum].points.add(target.id);
playerSort[req.body.pNum].hand.remove(req.body.jackId);

target.attachments.add(req.body.jackId);

playerSort[req.body.pNum].frozenId = null;
game.turn++;
game.save(function(er, savedGame) {
target.save(function(e, savedTarget) {
                                                                                                        //This save is the weird one. When the first player to be saved is the one playing
                                                                                                        //the jack from their hand, the log is correct, but the server is wrong
playerSort[0].save(function(e6, savedP0) {
console.log("\nsavedP0: ");

                                                                                                                        //If player0 played the jack, this log
                                                                                                                        //properly shows the target card was added to 
                                                                                                                        //player0's points collection
                                                                                                                        //In that case, this log is INCONSISTENT
                                                                                                                        //WITH THE SERVER, which doesn't have
                                                                                                                        //the new card added to the points collection
console.log(savedP0);

playerSort[0].save(function(e7, savedP1) {
console.log("\nsavedP1: ");
                                                                                                                        //This log is also always right, but the server
                                                                                                                        //actually agrees with what is logged for this player
console.log(savedP1);
                                                                                                                        //publishUpdate always has the right data, since savedP0 and savedP1
                                                                                                                        //are always right, even though the server is wrong if req.body.pNum === 0
Game.publishUpdate(game.id, {
change: 'jack',
game: savedGame,
players: [savedP0, savedP1],
thief: req.body.pNum,
targetCard: savedTarget
});

});
});
});
});
}

}
res.send({
jack: true,
yourTurn: yourTurn,
frozen: cardIsFrozen
});
}
});
}
});
}
});
}
},

Interestingly enough, the remove() call does work successfully for player0, if player1 plays the jack. This means the target point card is successfully taken out of player0's points collection and this is properly saved in the case where playerSort[1] is the thief. It is only in the case where playerSort[0] is the thief player that there is a problem and the problem is always just that this player's points collection does not get the target card added (though it is logged as being added inside the save() cb for playerSort[0] and thus is properly passed to the clients in publishUpdate).

I have never encountered this problem before and I'm quite at a loss. Has anyone else seen a save cb function properly log the desired data, but had the server fail to reflect this change?

I apologize for the ugly formatting for such a large block of code! Any help would be enormously appreciated!

Ryan Emberling

unread,
Jul 14, 2015, 11:43:21 AM7/14/15
to sai...@googlegroups.com
I understand that my formatting here is terrible and that there is far too much code to focus on the actual problem. I've made stack stackoverflow post with a more concise example. I believe that the problem centers around my attempt to nest several save() calls inside of each other, but I don't know how to avoid this issue and also gain access to updated copies of all of the records affected by add() and remove() in order to send my publishUpdate with the new data. The records are not actually modified until save() is called, and the local copies remain effectively unchanged by add() and remove(). Do I need to make local temp copies of the data and change them in the way that the database is expected to change, in order to avoid needing to force the save() calls to be sequential by nesting them?

Any help would be much appreciated =)
Reply all
Reply to author
Forward
0 new messages