I'm currently building a HTML5 game using Node.JS, MongoDB and Socket.IO. The purpose of this project is not really creating a finished, fully playable game but rather understanding and implementing some basic concepts of multiplayer game programming and getting used to MongoDB.
Here is the basic server architecture I went with. The server listens to clients through Socket.IO and everytime a message is received, it push it to a queue. A message is received whenever a player wants to move, or effectively alter the game in a way. I'm staying really vague because it does not really matter to show you in details what this game is. So the server receives messages from all the clients and keeps it in memory for a certain time.
Every 50ms the server processes sequentially all the messages in the queue and makes the game state advance, broadcast the changes to clients and then empties the queue and starts listening again to clients.
I'm having some difficulties building this game loop as I'm not really sure of what does MongoDB and if it does it in time, as all the calls are pure async. Let's say the following code is in my game loop, here is my concerns:
for (var i=0; i<queue.length; i++) {
if(queue[i].message.type === "move") {
//The server has first to ensure that the player can effectively move,
//thus making a query to MongoDB. Once the result has been retrieved,
//the server makes an update in MongoDB with the new player position
}
//At this point, due to the async nature of MongoDB,
//I cannot ensure that the queries have been executed nor that the message was effectively handled
}
//Here again, I cannot ensure that the game state gracefully advanced
//and then I cannot broadcast it.
I think the game loop has to be sequential, but I'm not sure if it's possible to do so using MongoDB and I'm not sure MongoDB is the right tool for that job.
I'm using the official Node.JS driver for MongoDB as I'm more interested in nested documents than in Object Data Modeling.
Do you have any clues on building a sequential game loop in this case ? Or am I using MongoDB in a case outside its purpose ?
Seems fairly straight forward.
The solution is not to use a for loop, as you only want to start the next message being processed after the previous one has completed. For this, it is probably easier to use a library like async
and the eachSeries
function.
https://github.com/caolan/async#each
async.eachSeries(queue, processMessage, allDone);
function processMessage(message, callback) {
// do stuff with message, don't forget to call callback when you have complete all async calls and are done processing the message!
// eg
if(message.type === "move") {
mongo.insert({player: message.player, x:message.x, y:message.y}, function (err, res) {
// error check etc
callback();
}
}
}
function allDone() {
// called when all messages have been proccessed!
// setTimeout(processNewQueue, 50);
}