Building a 3D MMO using Websockets

Published October 29, 2014 by Nick Janssen, posted by Nikke
Do you see issues with this article? Let us know.
Advertisement
Author's note: This article does not aim to build huge MMO's like World of Warcraft or similar. These games have millions of players and gigantic budgets, and employ many skilled engineers to keep servers from falling apart. Do not start one of these on your own. The techniques described in this article however allow you to build moderately-sized MMO's that can potentially hold hundreds of players on a single server, depending on your hardware and packet size. Hi there! My name is Nick Janssen, creator of Ironbane, a 3D MMO that uses WebGL and WebSockets. With this article I would like to give you a better insight in MMO's and make you less afraid of the complexities involved in building one. From my experience I have found that people consider them very hard, while they are in fact quite easy to make using the web technologies of today.

An MMO? You can't do that!

MMO's are cool. Yet they are considered one of the hardest things to make when it comes to developing software. I believe MMO's are mainly intimidating to people because of historical reasons. In the old days, network programming used to be very hard. Complex socket calls were everywhere, multithreading was necessary and JSON was still unknown. A lot has changed since then with the coming of Node.js, its event loop and easy to use socket libraries. In addition, writing a 3D game was a challenge on its own. You had to include the right libraries, install dependencies on the client, and write complicated engine calls to do trivial things such as creating a texture. Getting a triangle to show on the screen was already quite an accomplishment. Creating a texture with DirectX10 D3DX10_IMAGE_LOAD_INFO loadInfo; ZeroMemory( &loadInfo, sizeof(D3DX10_IMAGE_LOAD_INFO) ); loadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE; ID3D10Resource *pTexture = NULL; D3DX10CreateTextureFromFile( pDevice, "crate.gif", &loadInfo, NULL, &pTexture, NULL ); Creating a texture with Three.JS var texture = THREE.ImageUtils.loadTexture('crate.gif'),

The beginnings

For our MMO Ironbane I took things one at a time, and it worked out very well. Remember, Rome wasn't built in a day. But with today's technology you can achieve things at a much faster pace than what was ever possible before. I started from a three.js terrain demo and modified it step by step. Within a few days, I had a plane running around with a texture that looked like the pixelated back of a guy. is1.png The next step was to make the player connect to a centralized server. Using Socket.IO I set up a very simple Node.js backend that responds to player connections, and puts them in a global unitList managed by a service called worldHandler: io.sockets.on("connection", function (socket) { socket.unit = null; socket.on("connectServer", function (data, reply) { var unit = new IB.Player(data); worldHandler.addUnit(unit); } });

Telling players about other players nearby

To let players know which other players are nearby, the server has to know at any time which players can see other players. To do so, every player instance on the server makes use of an otherUnits array. This array is simply filled with instances of other entities which are currently in the vicinity. When a new player is added to the worldHandler, their otherUnits list gets updated depending on where they are in the world. Later, when they move around, this list is evaluated again, and any changes to this list are sent to the client in the form of addUnit and removeUnit socket events. Now, I would like to point out that the first letter of MMO stands for Massive. For massive games, every player should not know about every other player because it will melt your server.

Spatial partioning

To remedy this, you need spatial partioning. In a nutshell, this means that you divide your world into a grid. To visualize it, think of it as the server making use of a Snap To Grid option, to "snap" the position of the players to an imaginary grid. The positions of the players are not altered, rather the server just calculates what the new snapped position of the player would be. 8WyeWxR.png Image courtesy of Gameprogrammingpatterns.com With many players spanning over many different positions, some will have the same "snapped" position. A player then, should only know about all players that are snapped in the same position and all players that are just one cell away from them. You can easily convert between grid and world positions using these functions: function worldToGridCoordinates(x, y, gridsize) { if ( gridsize % 2 != 0 ) console.error("gridsize not dividable by 2!"); var gridHalf = gridsize / 2; x = Math.floor((x + gridHalf)/gridsize); y = Math.floor((y + gridHalf)/gridsize); return { x: x, y: y }; } function gridToWorldCoordinates(x, y, gridsize) { if ( gridsize % 2 != 0 ) console.error("gridsize not dividable by 2!"); x = (x * gridsize); y = (y * gridsize); return { x: x, y: y }; } When a new player is created on the server, they automatically add themselves to a multidimensional array of units on the worldHandler, using the grid position. In Ironbane we even use an additional zone index, since most MMO's have multiple areas where players can reside. worldHandler.world[this.zone][this.cellX][this.cellY].units.push(this);

Updating the list of players nearby

Once they are added to the list of units on the server, the next step is calculate which other players are nearby. // We have two lists // There is a list of units we currently have, and a list that we will have once we recalculate // If an item is in the first list, but no longer in the second list, do removeOtherUnit // If an item is in the first & second list, don't do anything // If an item is only in the last list, do addOtherUnit var firstList = this.otherUnits; var secondList = []; // Check for all players that are nearby and add them to secondList var gridPosition = worldToGridPosition(this.x, this.y, 50) var cx = gridPosition.x; var cy = gridPosition.y; for (var x = cx - 1; x <= cx + 1; x++) { for (var y = cy - 1; y <= cy + 1; y++) { _.each(worldHandler.units[this.zone][x][y], function(unit) { if (unit !== this) { secondList.push(unit); } }, this); } } for (var i = 0; i < firstList.length; i++) { if (secondList.indexOf(firstList) === -1) { // Not found in the second list, so remove it this.removeOtherUnit(firstList); } } for (var i = 0; i < secondList.length; i++) { if (firstList.indexOf(secondList) === -1) { // Not found in the first list, so add it this.addOtherUnit(secondList); } } Here, addOtherUnit() adds that player to their otherUnits array, and sends a packet to the client informing a new player has entered in their vicinity. This packet will contain the initial position, velocity, name and other metadata which only needs to be sent once. removeOtherUnit() simply removes the player from their array, and tells the client to destroy that player. var packet = { id: id, position: unit.position, name: unit.name, isGameMaster: true }; this.socket.emit("addUnit", packet);

Sending packets to the players

Now, we have the beating heart of an MMO. The final step is to inform the players on a regular basis the positions of the other players in their vicinity. We do this step only twice per second, because we do not want to overload the server. _.each(this.world, function(zone) { _.each(zone, function(cellX) { _.each(cellX, function(cellY) { _.each(cellY.units, function(unit) { var snapshot = []; _.each(unit.otherUnits, function(otherUnit) { var packet = { id:otherUnit.id, x:otherUnit.x, y:otherUnit.y }; snapshot.push(packet); )); if ( snapshot.length ) { unit.socket.emit("snapshot", snapshot); } )); }); }); });

Conclusion

That's really all there is to building an MMO. The only things left to do now are building the features that are unique to your game, fine-tuning and security. I hope I have given you fresh insights into MMO programming, and above all the courage to start working on them. At Ironbane we are surely looking for collaborators! You can find the full source code of Ironbane straight on GitHub, and you should be able to install it on your machine with ease.
Cancel Save
0 Likes 18 Comments

Comments

nevS

Nice game :)

I'm also experimenting with three.js and it's just awesome.

October 16, 2013 05:10 PM
jbadams

I expect you'll probably cop a bit of flack about whether the term "MMO" really applies to a game with the scale you're talking about here -- and technically, it's a fair point; what you've created is impressive, but nothing like a "true" MMO like World of Warcraft, RuneScape or Ultima Online -- but I think this is still a good example of how to make a smaller online game, which is very valuable to a lot of people and is really all many people are actually aiming for when they say they want to create an MMO.

You might consider noting this discrepancy in terminology so as not to mislead anyone who is thinking they might make the next WoW, but otherwise I think this is a good example.

(EDIT: The article author has since made changes to the article to better address this concern. smile.png)

October 17, 2013 06:03 AM
Krohm

I appreciate a lot your work, but I have to second jbadams, this is not "the beating heart of an MMO", it's just a basic multiplayer game.

When it comes to me, I appreciate people warning me of difficulties. At the same time I don't appreciate people claiming everything to be fast and easy. This is because, as a learner, I often spent much more time than required on things that were not worth it, pursuing goals that were far beyond my capabilities. Think about it.

October 17, 2013 01:16 PM
Nikke

@jbadams I would argue that it all boils down to your perception of the word 'MMO'. For me, MMO means an architecture that can potentially hold thousands of players. Entities and players could be represented by triangles for what its worth, but the network size and load are comparable to real-world MMO's. If your requirement for a 'true' MMO holds high quality music, art, 3d models, thousands of quests and other game content, I obviously cannot live up to your expectations since it is a hobby project which runs at a lower pace.

@Krohm However, these techniques are the beating heart of an MMO. They are very efficient in delivering packets and there is much less overhead and complexity compared to others because the server always runs in a single thread. In addition you can develop faster with Javascript with its unique expressiveness, sharing code between client and server and taking advantage of the fastest evolving platform ever: the web. Everything else depends on your hardware.

The real problem is that people are trying to do huge projects causing them to fail. This has nothing to do with MMO's.

October 17, 2013 10:49 PM
jbadams

To be clear, I am definitely not commenting on asset quality or the amount of content provided, and I agree that this is just about the definition of "MMO". The quality of assets has absolutely nothing to do with it, and while it is typical for an MMO to have a large amount of content I don't think it's necessary for a game to qualify.

Instead, I'm talking about scale. The first M in MMO is for massive, and at least personally I would suggest that term implies hundreds of thousands if not millions of concurrently connected players rather than simply a few thousand. I understand that the term doesn't mean that to everyone, but it does mean that to a very significant number of people.

As I said, I think you've provided a fantastic example of how to create a simple but reasonably robust multi-player online game, but a lot of people will expect those larger numbers when they see the term MMO, and it simply isn't proven that your code could scale to handle that sort of load. I absolutely agree that aiming for those sort of numbers is unrealistic for indie or hobbyist developers, but the sad truth is that a lot of them will have that goal in mind, and I just think it's important to clarify that what you're offering is something smaller -- though still very impressive, and much more realistically approachable -- which has not been proven and may not hold up under that sort of load. There's a reason AAA MMOs use much more complex architectures than this, and while it should be obvious to more experienced developers I think it can be valuable to beginners to point out that this simpler architecture is not "all there is" to bigger commercial games like World of Warcraft or RuneScape.

I would absolutely encourage any beginner to aim for a more achievable project on the scale of yours, and I think you've provided an absolutely fantastic example for that -- I just think you should also be clear (or at least mention in passing) that this is not the same thing as a AAA MMO.

For the record, I voted in favour of peer review status for your article because I think it's potentially very helpful and valuable, but I think this one addition would serve to increase that value, and I think that leaving it out may attract votes against peer review; unfortunately enough of those would result in the article being hidden from the site unless changes are made and re-submitted, which I think would be a real shame for all the people that stand to benefit.

I'm not trying to be argumentative here, but rather trying to play devil's advocate a bit to help improve on a single point, because I think you've made a great contribution here and I would hate to see it removed over something as trivial as a common disagreement about terminology.

October 18, 2013 05:58 AM
Nikke

@jbadams Thank you for your detailed response. I understand your point better now, and I have included a small author's note which will hopefully be enough to stop those trying to build the next WoW.

October 18, 2013 07:25 AM
Datamancer

Nice article. I can tell that you put a lot of work into it. Like a few other people though, I will recommend that you change the "MMO" tag.

October 18, 2013 04:03 PM
LJ_1102

Not so fast, sorry for beeing the nit picker here... but

Your comparison of DirectX and ThreeJS is like comparing cooking with using a microwave.

ThreeJS is a library and does a lot of work in the back, when comparing native WebGL to DirectX the complexity is almost the same.

Also from watching your js code it looks like there is still room for enhancement, did you know javascript has no block scope, using strict mode your code will fail since you're redeclaring variables in your loops:

for (var x = cx - 1; x <= cx + 1; x++) {
for (var y = cy - 1; y <= cy + 1; y++) {

...
Also while underscore.js (the syntactic sugar used in the examples[you should mention that for the non informed reader]) is quite nice, you would not want to write a high performance server with it since the scoping of all these foreach loops you got there slows things unnecessarily down.
While speaking of performance, every time you use curly brackets a new object is constructed(slow),
sending these objects over a WebSocket will actually stringify the objects using JSON.stringify and send them as a string, while you could send binary data using array buffers, saving a lot of bandwidth.
Oh and it would be nice if you would document the scope in your code examples since in most cases its not really clear what "this" refers to.
Anyway thumbs up for showing the potential of webgl powered browsergames.
October 22, 2013 12:41 AM
Erebel

Love the article :)

October 23, 2013 08:49 PM
Laszlo Fuleki

Great! I love to see that I`m not the only maniac wanting to do this. :D

November 12, 2013 12:58 PM
JoelPerez

@LJ_1102 is right

This is coming from the absolute beginner.

I say this with a nice tone and only from my way of thinking and understanding.

To me it read very:

To drive the car you must first....Manual transmission cars have a third pedal.....Hit the brake and you just finished driving the car. Great now switched sides with me and you try it!

Again I am not being mean. Nor am I trying to upset anyone. Some maybe most of you actually do understand everything he said.

But to me I would of felt more comfortable having each code segment explained in details.

Very specific since your are publishing it here. Maybe you intended for only experience people to read this. But again the title or description was not specific to your target level.

Also for me it would of been better too, if you had more links and references to help look up more on each tool/s you used or recommend.

Well that said thank you very much for the article. I hope that my criticism did not offend you or anyone else. I only wish you the best and also I tried out your game and it was very cool to try out. It looks like lots of time went into making it. Oh and for some reason I fell through the wall as I went up the first platform that moves up and down as a guest. I fell into water and then I could see the building to my left. I was see through and I swam or walked not sure, over to it and went right through the invisible wall. Maybe it was intentional on your part. Im not sure but thought I should mention it.

Joel

August 09, 2014 08:28 AM
Vilem Otte

Good article ... but ... the link http://rampantgames.com/blog/2004/10/black-triangle.html does not work. (I get 404 in there)

October 29, 2014 01:58 PM
TheeCrowe

Good article ... but ... the link http://rampantgames.com/blog/2004/10/black-triangle.html does not work. (I get 404 in there)

Looks like the blog had a disk failure since this article was written. It's new location

http://rampantgames.com/blog/?p=7745

October 29, 2014 08:03 PM
Gaiiden

thanks, I fixed the link

October 29, 2014 08:05 PM
turkert

From performance perspective, Can we really use Node.js for multiplayer games for 100 online player?

October 30, 2014 10:14 AM
Vilem Otte

From the performance perspective, we shouldn't (but yes, we can - although we will hardly fail when there are more players - thousands, tens of thousands). There are better ways to do so (but not necessarily easier).

Also note, that it is important to have fast server for any online game (that runs through server). Basic VPS is NEVER good enough for that.

October 30, 2014 06:09 PM
tnovelli

Sure, you can run a 100 player game on Node.js if your physics are simple enough and you're not on oversubscribed shared or VPS hosting. But definitely plan on porting to C++ if you get anywhere near 100 simultaneous players!

VPSes have gotten better. I don't see any significant performance difference between my desktop and VPS servers. This is with KVM virtualization, Linux host & guest, at an honest provider.

I would want to be sure I have a game architecture than can scale out to 100 little 512MB VPSes before I even think about dedicated hardware. Sure, a dedicated 16-core Xeon can handle maybe 100x more players than a little VPS, but when you max it out, you've hit a wall until you rearchitect your game. But those are all nice problems to have for the typical solo developer who will probably never get beyond experimenting with a Node.js server. (Yep, I've done that.. not psyched about running an MMO!)

November 01, 2014 03:53 AM
RockJoe

Good article! I think it addresses the basics of an MMO network and how to partition the space in memory.

I think a good follow up article would be how to coordinate state within the MMO. Suppose you have to scale to two servers to handle the load. How to synchronize events between the two servers?

April 13, 2016 09:43 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

With this article I would like to give you a better insight in MMO

Advertisement

Other Tutorials by Nikke

Nikke has not posted any other tutorials. Encourage them to write more!
Advertisement