Using MongoDB for development of massively multiplayer online (MMO) games
Tuesday, January 11, 2011 » database, MMO, Mongo, MongoDB, NoSQL
<!--break-->We selected MongoDB, a document-centric NoSQL database. MongoDB stores documents instead of rows and columns. Documents are stored in "collections" instead of tables. Documents are objects (think XML or JSON representations). This meant that the various objects used in our game, such as player objects, level objects, and item objects could be stored in the database without having to map them to SQL tables. In essense, our schema is defined by our code. A powerful attribute of MongoDB's documents is that they can contain complex data structures, such as arrays, maps, and even other documents. So, for example, a player object can contain arrays of the item objects that they possess. Player objects can be retrieved all with all their related objects in a single step without performing any join operations on the database. This all makes storage and retrieval of these objects ridiculously simple. We performed tests with MongoDB using collections up to 600GB with both a single machine and using three machines configured as a replica set, and MongoDB appears to be wicked fast for our application. Simulations of thousands of users yielded average record retrieval times under 100 milliseconds - roughly 4-6 times faster than a MySQL database we set up for comparison (we tried several different schemas in MySQL to try to optimize retrievals). Another design decision we made was to use MongoEngine (an ORM - Object Relational Mapper) to map our objects to MongoDB documents. Use of an ORM allows the programmers to focus more on the logic of the game by relieving them of writing database code (most of it, anyways). Using MongoEngine, objects are declared, instantiated, assigned, and used. For example (NOTE: these examples are not from our game, but contrived for this article): from mongoengine import * class ItemObject(EmbeddedDocument): name = StringField(required = True) itemType = StringField(required = True) itemStrength = IntField(required = True) class PlayerObject(Document): name = StringField(required = True, unique=True) raceName = StringField(required = True, default = "Human") strength= IntField(required = True, default = 10) dexterity = IntField(required = True, default = 10) constitution = IntField(required = True, default = 10) intelligence = IntField(required = True, default = 10) wisdom = IntField(required = True, default = 10) dexterity = IntField(required = True, default = 10) hitPoints = IntField(required = True, default = 10) weapon = EmbeddedDocumentField(ItemObject) defense = EmbeddedDocumentField(ItemObject) bag = ListField(EmbeddedDocumentField(ItemObject), default = []) meta = { 'indexes' : ['name'] } connect('ddgame', username = 'dddbuser', password = 'secretpassword', host = dbserver, port = 27017) newPlayer = PlayerObject(name = "Gandolf") newPlayer.defense = ItemObject(name = "shield", itemType = "defense", itemStrength = 10) newPlayer.weapon = ItemObject(name = "sword", itemType = "offense", itemStrength = 10) newItem1 = ItemObject(name = "magic rock", itemType = "magical", itemStrength = 10) newItem2 = ItemObject(name = "gemstone", itemType = "valuable", itemStrength = 10) newPlayer.bag=[newItem1,newItem2] newPlayer.save() for players in PlayerObject.objects: print players.name existingPlayer = PlayerObject.objects(name = "Gandolf").first() print existingPlayer.name+" is carrying a "+existingPlayer.defense.name </span> +" and a "+existingPlayer.weapon.name+"."Recently I participated in the implementation of a new MMO (Massively Multiplayer Online) game. This game is browser/text based, has no concept akin to "rooms," and no direct realtime communications between players although players can perform actions against other players. In the past, most of the games this team has built have been coded in Flash on the client, Java on the server, and have been implemented atop a socket server. In this case, due to the type of game, the game was to be implemented in Python directly over a database with no socket server in the mix. In selecting a database, our main criteria was speed and scalability. Along the way, we discovered how to gain more.