Documente Academic
Documente Profesional
Documente Cultură
multiplayer space shooter. Throughout this talk I plan to take a journey through some of the issues that plagued early development of the game, and cover the subsequent solutions that helped resolve them. Therell be some code, but Im more interested in highlighting the concepts and theories involved. This talk is denitely light-hearted but Ill be assuming some prior knowledge, so feel free to raise your hand throughout the talk if you want to explore an area further, or to simply ask a question.
For those who dont know much about about me Im a Technical Evangelist at Mozilla, which means that its my job to engage with developers about cool new technologies on the Web.
EXPERIMENTATION
Rawkets is a graduate from my lab
Rawkets is a project that originally came out of this experimentation, from a desire to learn more about WebSockets in regards to multiplayer gaming. Now, the game is much more mature and Id consider it as a separate entity aside from the experiments. Its something that I plan to develop and support way beyond the original scope of learning WebSockets.
WHAT IS RAWKETS?
Rawkes, WebSockets, and Rockets
Rawkets stands for Rawkes (my blog), WebSockets, and Rockets.
Rawkets is a multiplayer space game that allows you to shoot your friends in the face with HTML5 technologies. Still not really at a beta release level yet, hence the bugs you might notice in this video. http://rawkets.com
By now youve probably realised that the graphics at the beginning of this talk and on the posters arent the real game graphics. They are actually an awesome artists impression illustration that I commissioned a guy called Reid Southen to create, although maybe when WebGL gets better it will become a reality. Who knows.
It looks pretty awesome as a 6ft banner. So awesome in fact that my girlfriend actually asked me if I was going to put it up in our at our not. She seemed pretty down about me saying no (it smells of ink and solvents). This is a photo of me in front of the banner at my university end-of-year show. If you think it looks small then let me put it into perspective by telling you that its about 8ft away. Read into that what you will.
TECHNOLOGY
There are a few key technologies that are involved in the development of the game. All of them bar one are tightly related to HTML or JavaScript.
2D graphics platform
CANVAS
FLASH AUDIO
Flash audio for game sound effects and background music. Ill explain why I use Flash over HTML5 Audio further on in the talk.
WEBSOCKETS
Multiplayer communication
WebSockets is used for the communication between each player and the game server. For anyone not up to speed with WebSockets, its an upgraded HTTP connection that allows for fully bi-directional streaming communication.
NODE.JS
Node is used as the game server, controlling the logic and handling the WebSockets connections to the players. It will eventually be used for player authentication and the storage of data so gameplay can persist over multiple game sessions. This is all made relatively easy with great third-party modules, like Socket.IO for WebSockets, and others that handle Redis and MongoDB for storage, for example.
ISSUES
Its not all plain sailing when making a gaming using HTML5 and JavaScript. Im going to cover a few of the main issues that tripped me up during the development of Rawkets.
NETWORKING
Not as easy as I thought
Issues with networking have plagued development of the game right from the beginning. This probably stems from my lack of prior experience with socket connection and multiplayer gaming in general. In the original prototype of the game the network communication was woefully simple and everything was transmitted in a verbose format with no further thought. In hindsight its obvious why I was experiencing massive performance issues with the network communication. I was basically sending way too much data back and forth.
MESSAGE PROTOCOL
Structured and short communication
One of the ways that I solved these problems was by implementing a structured protocol for the messages that are being sent and received. This included assigning each message type a number and using enumeration to represent those types in the code.
ENUMERATION
Message protocol
PACKAGE
Message protocol
COMPRESSION
Data in WebSockets is transmitted as verbose plain text, so its important to cut down and compress it as much as possible. Some of the ways that Ive done this include rounding numerical values, reducing the length of words if theyre only used for reference, and generally removing any data that isnt necessary. There is also BISON, which is a binary representation of JSON that can cut down the amount of data sent back and forth even further. http://kaijaeger.com/articles/introducing-bison-binary-interchange-standard.html
RATE LIMITING
Aside from the message protocol, one of the biggest issues with networking has been dealing with the sheer number of messages being sent back and forth during the lifetime of a game.
MESSAGES IN
MESSAGES OUT
1
1 1
Having only one player in the game is easy, you have one message coming in to the server, saying the player has moved, for example, and one message coming back out, updating the player with details from the server.
MESSAGES IN
MESSAGES OUT
2
1 2 2 1
So say we now have two players, there is still only 1 message in from each player, but now each player receives 2 messages back from the server; one for them, and one for the other player. This isnt too bad, but notice how the server is having to send 4 messages 2 for each player.
MESSAGES IN
MESSAGES OUT
4
1 4 1 4 1 4 1 4
16
4 players now, look how the server is having to send 16 messages, yet it only receives 4. If you havent already noticed, the messages out from the server are the square of the number of players. But 16 messages out is alright, its hardly going to tax the server.
MESSAGES IN
MESSAGES OUT
30
1 30 1 30 1 30 1 30
900
So imagine if we now move into properly multiplayer territory. 30 players in the game would mean 30 messages coming in to the server, and 900 NINE HUNDRED messages going out, every update. Thats a silly amount of data for just 30 people. But lets go further still
MESSAGES IN
MESSAGES OUT
100
1 100 1 100 1 100 1 100
10000
Say we go massively multiplayer and have 100 players in the game at one time. Its not so bad for each individual player, they send one message in and get 100 back thats bearable. But check out the server, its getting 100 messages in and is having to send out 10,000 back, every update. Thats just a mentally stupid number thats going to cause a lot of grief.
INTELLIGENCE
Fortunately there is a way around this that cuts down the amount of messages sent; you just need to send data only for players visible to another player, in essence ltering out game data that doesn't affect the current player. Another trick I used is to only send updates when a player is active and moving. If they havent moved since the last frame and nothing else has changed then why bother sending an update and wasting bandwidth? These are such simple solutions, but ones that I never even considered at rst.
RESPECTING TCP
Something else that I discovered is important to be aware of when making a game with WebSockets is that youre using TCP. This is a problem as such, but it means that you need to play by a certain set of rules, and to expect a certain set of issues. By the way, I should point out that that you could argue that the icon that Ive used could represent WebSockets, but thats not why I used it. Its the US plug symbol and I just thought it was funny because it looks like a surprised face. The UK plug symbol is boring in comparison.
FORCED DELAY
Ive not attacked the TCP issues head on yet, but one possible way to approach them is to introduce some sort of delay into the game. What I mean by this is that you would give every player a 100ms buffer in communication, meaning that they are always 100ms behind the server in regards to what is going on. If everyone is experiencing this buffer then it shouldnt be too much of a problem, and 100ms is plenty of time for the game to catch up with resent packets and the like. As for how to implement this, Ive not looked into it yet.
USE SOCKET.IO
I use Socket.IO for all the WebSockets communication for Rawkets simply because its easy to use and it has Flash fallback for browsers that dont support the technology yet. The cool thing about the latest version of Socket.IO, 0.7, is that is has introduced the concept of volatile messages. These volatile messages are completely voluntary, and messages sent with them aren't actually sent if the client is having network problems, saving bandwidth for other more important messages. Its not an ideal solution, but itll certainly help.
CHEATERS
Theres no denying it, your code is going to be visible to anyone who wants to look at the source. I experienced this early on in the development of the game with players adding in their own features, like invincibility, epic speed, rapid-re, and even creating completely new weapons like cluster bombs! Now dont get me wrong, I actually appreciate the cheaters because they highlighted all the errors of my ways, for free. One of the benets of the open nature of JavaScript is that it can be looked at and poked very easily by others, which means that I can x bugs quicker than if I was testing on my own.
(function() { var rawkets = rawkets || {}, r = rawkets; rawkets.namespace = function(namespace_str) { var parts = namespace_str.split("."), parent = rawkets, i; if (parts[0] === "rawkets") parts = parts.slice(1); }; {
for (i = 0; i < parts.length; i++) { if (typeof parent[parts[i]] === "undefined") { parent[parts[i]] = {}; }; parent = parent[parts[i]]; }; return parent; }; window.rawkets = window.r = rawkets; })(window);
By adding my own rawkets namespace I was able to hide code away, and by deliberately utilising closures and private variables I was able to further frustrate efforts by cheaters to overwrite game functionality. Plus the new namespace makes code so much neater. Code manipulation isnt something that I can prevent entirely, but I can at least make it as difficult as possible.
CLIENT AUTHORITY
Power isnt always a good thing
Im not going to lie, the rst version of Rawkets was way too trusting. I used what is referred to as the authoritative client model, which basically means that the client, the player, made all the decisions regarding its position and then sent those positions to the server. The server than trusted those positions and transmitted them to all the other players, which is ne until the client edits their position and increments it by 100 pixel per frame, rather than 5. Bad times. This can be referred to as the Here I am approach.
SERVER AUTHORITY
Relinquish that power
The solution is to make the server authoritative, which means that you prevent manipulation of the clients code from doing any damage. All the movement logic is now performed on the server, meaning that when a client moves it simply lets the server know which direction it wants to move. From there the server calculates the new position and sends it back to the client. This can be referred to as the Where am I? approach, and if done right it can completely remove the ability to cheat.
INHERENT LATENCY
Server authority
Input +0
40ms
Move +40
40ms
Update +80
CLIENT PREDICTION
Server authority isnt enough
To solve the latency issues with the authoritative server you need to implement some element of prediction on the client. What I mean by prediction is an ability for the client to guess, quite accurately, where it should move the player before the message comes back from the server detailing the new position.
INSTANT MOVEMENT
Client prediction
Input +0
40ms
Move +40
40ms
Update +80
CORRECTION
Whereas the authoritative client model would be in control, with the authoritative server model and client prediction, the server is in control. The whole point of using the authoritative server is because the client cant be trusted. So it makes sense that prediction cant be trusted either. To get around this you use periodically check the client position against the server and perform a correction if necessary. This may sound simple in concept, but its one of the hardest aspect of multiplayer gaming to get right. Simply because its obvious when you get it wrong.
var correction = function(time, state, input, entity, rk4) { ... if (Math.abs(state.x - lastMove.state.x) > 2) { ... var currentTime = time, currentInput = input; entity.setState(state); // Rewind entity state var move, // Current move frameTime; // Time between correction and stored move for (m = 0; m < moveCount; m++) { move = moves[m]; frameTime = (move.time - currentTime) / 1000; // Update physics based on corrected time, input and state ... currentTime = move.time; currentInput = move.input; move.state = entity.getState(); }; }; };
This is the Gaffer on Games approach by Glen Fiedler. There are also other solutions, like Valves approach which is based on the old QuakeWorld theory. http://gafferongames.com/game-physics/networked-physics/ http://developer.valvesoftware.com/wiki/Latency_Compensating_Methods_in_Client/ Server_In-game_Protocol_Design_and_Optimization
STABILITY
Keeping the game running is massively important, especially while its in rapid development and is prone to crashing (through errors of my own I must add). I needed a way to automatically restart the game server if it crashed or something went horribly wrong.
To do that I went with Monit scripts that monitor the WebSockets port for activity. If the port is inactive for 3 separate checks in a row then the game is automatically restarted in Node. Ideally the game shouldn't crash, but this has proven to be a great solution that allows me to continue rapidly developing the game without worrying about it going down permanently if I'm not around. http://mmonit.com/monit/
MONIT SCRIPT
Keeping the game running
check host rawkets.com with address 127.0.0.1 start program = "/etc/init.d/rawkets start" stop program = "/etc/init.d/rawkets stop" if failed port 8000 type tcp for 2 times within 3 cycles then restart
Just as a quick example, here is the entire monit script for keeping the game up and running. Id say four lines of code was pretty impressive! This effectively checks port 8000 on rawkets.com for an active tcp connection every cycle (60 seconds), and will only restart the game if there is a problem for at least 2 cycles. The time between cycles can be changed in the monit conguration les.
INIT.D SCRIPT
Keeping the game running #!/bin/sh case "$1" in start) cd /rawkets /usr/local/bin/node rawkets.js 2>&1 >> /var/log/node.log & exit 1 ;; stop) /usr/bin/pkill -f 'node rawkets.js' exit 1 ;; esac exit 1
This is the init.d script that I use to start and stop the game server from the command line. All it does is binds a particular set of shell commands to start and stop keywords, which I use to start and stop the Node process for the game. Im not massively experienced with init.d scripts so Im sure that it can probably be optimised, but it works perfectly for my needs and is pretty damn cool.
LOADS MORE
Stuff that I couldnt t in Canvas optimisation. Using events to decouple game logic. Using the same code for client and server. setTimeout vs. requestAnimationFrame. Storage. Control system, like Sebs.
THE FUTURE
What Id like to see
There are a few things that Id like to see in the near future to help with game development. Need a way to benchmark browsers, connections and operating systems. - Like Google Analytics, but for game performance and feature detection. - Measuring FPS. - Measuring network performance. Better HTML5 audio. Hardware accelerated canvas on Mac and mobile devices. Better documentation.
ROB HAWKES
@robhawkes
Rawkets.com
HTML5 & WebSockets game. Twitter - @robhawkes Rawkes - http://rawkes.com
ExplicitWeb.co.uk
Web development podcast.
Out now Paperback and digital formats Become a canvas master Learn how to animate Make two cool space games
RAWKES.COM/FOUNDATIONCANVAS
DEV DERBY
Every month This month is HTML5 video Manipulate video with canvas Win prizes (like an Android) Next month is all about touch
DEVELOPER.MOZILLA.ORG/EN-US/DEMOS/DEVDERBY
Also, you should denitely take part in the Dev Derby, which is a monthly competition run by the Mozilla Developer Network to see what can be done with the latest Web technologies. This month the focus is on HTML5 video, which is pretty interesting considering that you can manipulate it using the canvas. The winners get cool prizes, like an Android phone. Its a great excuse to play around with these technologies. https://developer.mozilla.org/en-US/demos/devderby
Thank you. If you have any questions feel free to grab me on Twitter (@robhawkes), or email rob@rawkes.com