38
Hi, I’m Rob Hawkes and I’m here today to give an inside look at the development of Rawkets, my HTML5 and JavaScript multiplayer space shooter.

MelbJS - Inside Rawkets

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: MelbJS - Inside Rawkets

Hi, I’m Rob Hawkes and I’m here today to give an inside look at the development of Rawkets, my HTML5 and JavaScript multiplayer space shooter.

Page 2: MelbJS - Inside Rawkets

If you don’t already know, I work at Mozilla.

My official job title is Technical Evangelist, but I prefer Rawket Scientist, which is what it says on my business card.

Part of my job is to engage with developers like you and me about cool new technologies on the Web.

Page 3: MelbJS - Inside Rawkets

Throughout this talk I plan to take a quick journey through some of the issues that plagued early development of the game, and cover the subsequent solutions that helped resolve them.

Page 4: MelbJS - Inside Rawkets

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 I’d consider it as a separate entity aside from the experiments. It’s something that I plan to develop and support way beyond the original scope of learning WebSockets.

Page 5: MelbJS - Inside Rawkets

What is Rawkets?

Rawkes, WebSockets, and Rockets

Rawkets stands for Rawkes (a merging of my names), as well as WebSockets, and Rockets.

Page 6: MelbJS - Inside Rawkets

Rawkets is a multiplayer space game that allows you to shoot your friends in the face with HTML5 technologies.

It’s still not really at a beta release level yet, hence the bugs you might notice in this video.

http://rawkets.com

Page 7: MelbJS - Inside Rawkets

By now you’ve probably realised that the graphics at the beginning of this talk and on the posters aren’t the real game graphics.

They are actually an awesome “artists impression” illustration that I commissioned a guy called Reid Southen to create.

Perhaps when WebGL gets better it will become a reality. Who knows.

Page 8: MelbJS - Inside Rawkets

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 flat our not. She seemed pretty down about me saying no (it smells pretty horrible).

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 it’s about 8ft away.

Page 9: MelbJS - Inside Rawkets

Issues

Making games can be a challenge

It’s not all plain sailing when making a game using HTML5 and JavaScript.

I’m going to cover a few of the main issues that tripped me up during the development of Rawkets.

Page 10: MelbJS - Inside Rawkets

Tweaking animation

Putting the browser in

control

One of the simplest fixes is to stop using setTimeout or setInterval and to use requestAnimationFrame instead.

If you use setTimeout or setInterval and don’t manage it then you put a huge amount of stress on the CPU and continue that stress even if you switch tabs or minimise the browser.

By using requestAnimationFrame you give the browser control over when a new animation loop should occur, reducing load on the CPU and saving battery life on mobile devices.

requestAnimationFrame also automatically limits the number of updates if you switch to another tab or minimise the browser, again saving resources and keeping your players happy.

Right now you can’t easily set a specific framerate when using requestAnimationFrame but so long as you use time-based updates (not frame-based) in your game then you’ll be fine.

Page 11: MelbJS - Inside 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 it’s obvious why I was experiencing massive performance issues with the network communication. I was basically sending way too much data back and forth.

Page 12: MelbJS - Inside Rawkets

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.

Page 13: MelbJS - Inside Rawkets

types = {

PING: 1,

SYNC: 2,

SYNC_COMPLETED: 3,

NEW_PLAYER: 4,

UPDATE_PLAYER: 5,

UPDATE_INPUT: 6,

REMOVE_PLAYER: 7

};

Enumeration

By enumerating the messages types like this I was able to refer to them in a verbose format within the code, but benefit from only sending the one or two digit number when transmitting a message.

This is only possible if both the client and server follow the same protocol in regards to which number refers to which message type.

It’s a simple but effective solution and allowed me to cut a large number of characters from transmitted messages.

Page 14: MelbJS - Inside Rawkets

1 | 1234567890 | 1316763202872 | 5 | 34

Message package

MSG_ID PLAYER_ID TIMESTAMP X Y

Put together with the message types, a full message package is put together as a simple string representation of a JavaScript object.

All the other pieces of data are attached to the object with a key that is as short as possible.

The MSG_ID that you can see above is a reserved key that is used solely for the message type.

The other items in this example are the player id, timestamp, and the player position.

Page 15: MelbJS - Inside Rawkets

Compression

Reducing data as much as possible

Data in WebSockets is normally transmitted as verbose plain text, so it’s important to cut down and compress it as much as possible.

Some of the ways that I’ve done this include rounding numerical values, reducing the length of words if they’re only used for reference, and generally removing any data that isn’t necessary.

Page 16: MelbJS - Inside Rawkets

Binary messages

Even shorter, faster communication

I never got around to implementing this but there is now binary message support in WebSockets.

By switching to binary you can reduce the size of your messages by a noticeable amount while also increasing the amount of data that you can transmit at a single point in time.

http://hobbycoding.posterous.com/websockt-binary-data-transfer-benchmark-rsulthttp://hobbycoding.posterous.com/the-fastest-websocket-module-for-nodejs

Page 17: MelbJS - Inside Rawkets

Rate limitin

g

Cutting down on communication

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.

Page 18: MelbJS - Inside Rawkets

1 1

MESSAGES IN

1MESSAGES OUT

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.

Page 19: MelbJS - Inside Rawkets

1 2

2 1

MESSAGES IN

2MESSAGES OUT

4

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 isn’t too bad, but notice how the server is having to send 4 messages – 2 for each player.

Page 20: MelbJS - Inside Rawkets

1 4

4 1

1

4

4

1

MESSAGES IN

4MESSAGES OUT

16

4 players now, look how the server is having to send 16 messages, yet it only receives 4.

If you haven’t already noticed, the messages out from the server are the square of the number of players.

But 16 messages out is alright, it’s hardly going to tax the server.

Page 21: MelbJS - Inside Rawkets

MESSAGES IN

30MESSAGES OUT

9001 30

30 1

1

30

30

1

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. That’s a silly amount of data for just 30 people.

But let’s go further still…

Page 22: MelbJS - Inside Rawkets

MESSAGES IN

100MESSAGES OUT

100001 100

100 1

1

100

100

1

Say we go massively multiplayer and have 100 players in the game at one time.

It’s not so bad for each individual player, they send one message in and get 100 back – that’s bearable.

But check out the server, it’s getting 100 messages in and is having to send out 10,000 back, every update. That’s just a mentally stupid number that’s going to cause a lot of grief.

Page 23: MelbJS - Inside Rawkets

Intelligence

Letting the game prioritis

e messages

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 filtering 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 haven’t 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 first.

Page 24: MelbJS - Inside Rawkets

Respecting TCP

WebSockets uses TCP. Deal with it

Something else that I discovered is important to be aware of when making a game with WebSockets is that you’re 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 I’ve used could represent WebSockets, but that’s not why I used it. It’s 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.

Page 25: MelbJS - Inside Rawkets

Obey the order

You can’t do much about it

One issue with TCP is that packets will come through in order and get queued up if there are any significant connection issues.

This can be a problem with a real-time game as it can cause hang-ups in the transmission and subsequently a hang-up in the graphic display.

In short, the ordering issue can result in jumpy gameplay. Not fun.

With UDP this wouldn’t be so much of a problem, but we don’t have that luxury yet. Although similar protocols are in the pipeline and may make their way into our lives relatively soon, things like Media Streaming APIs and WebRTC.

Page 26: MelbJS - Inside Rawkets

Cheaters

A blessing and a curse

There’s 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-fire, and even creating completely new weapons like cluster bombs!

Now don’t get me wrong, I actually appreciate the cheaters because they highlighted all the errors of my ways, for free. One of the benefits of the open nature of JavaScript is that it can be looked at and poked very easily by others, which means that I can fix bugs quicker than if I was testing on my own.

Page 27: MelbJS - Inside Rawkets

Globals are bad

Don’t keep code wide open

There are two reasons why cheating was so prevalent and so easy to do.

The first is that by keeping all the game code in the global namespace and not using anything like closures I was practically inviting people to come in and edit the game code. It was too easy to do!

It was so easy in fact that after a few hours of releasing the first prototype, players were already sharing code snippets that others could paste into their browser console to get new features. Annoying, but actually pretty cool.

Page 28: MelbJS - Inside Rawkets

Client authority

Power isn’t always a good thing

I’m not going to lie, the first 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 fine 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.

Page 29: MelbJS - Inside Rawkets

Server authority

Relinquish that power

The solution is to make the server authoritative, which means that you prevent manipulation of the client’s 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.

Page 30: MelbJS - Inside Rawkets

Client Server Client+0 +40 +80

40ms 40ms

80ms total round-trip

Inherent latency

However, the problem with the authoritative server model is that there is some inherent latency within the system.

What I mean by this is that it obviously takes some time for a movement to be sent from the client to the server, then for the server to move the client, and then for the server to send the new position back again.

In the example here imagine that there is a 40ms latency between the client and server, which means that a message sent to the server will take a total of 80ms to make the round-trip.

The problem here is what happens during that 80ms period that you’re waiting for the updated position? If you do nothing then there’s going to be an 80ms delay between you pressing the up arrow and your rawket moving forward. Not good.

Page 31: MelbJS - Inside Rawkets

Client prediction

Server authority isn’t 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.

Page 32: MelbJS - Inside Rawkets

Prediction happens here

Client Server Client+0 +40 +80

40ms 40ms

Instant movement

The prediction happens as soon as the client performs some sort of movement (a key-press, etc), before the server has received the input.

All the prediction does is run the same physics as the server, based on the new input.

This is exactly as if were using the authoritative client model, apart from one important difference.

Page 33: MelbJS - Inside Rawkets

Correction

When prediction goes wrong

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 can’t be trusted. So it makes sense that prediction can’t 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 it’s one of the hardest aspect of multiplayer gaming to get right. Simply because it’s obvious when you get it wrong.

Page 34: MelbJS - Inside Rawkets

Stability

Keeping the game running

Keeping the game running is massively important, especially while it’s 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.

I also needed a way to scale the game and keep it running as fast as possible.

Page 35: MelbJS - Inside Rawkets

Forever

I use a little Node module called Forever. It’s amazing!

https://github.com/nodejitsu/forever

Page 36: MelbJS - Inside Rawkets

forever start game.js

Forever

All I have to do now is make sure the game process quits on a catastrophic error and Forever will automatically restart it for me.

Using Forever is as simple as installing the module with NPM and then starting your Node script using the Forever demon. The rest is taken care of for you.

Page 37: MelbJS - Inside Rawkets

Hook.io

Some of you may also be interested in hook.io, which can help create more stable Node applications.

The concept is to decouple your application logic by breaking it into individual processes so that if one process goes down the rest can continue to run and your entire game doesn’t crash.

You use hook.io through its event system that lets you communicate between these separate processes, regardless of whether they’re on the same server or not. It’s a pretty cool concept.

https://github.com/hookio/hook.io

Page 38: MelbJS - Inside Rawkets

Rob Hawkes

Rawkets.comHTML5 & WebSockets game

Twitter sentiment analysisDelving into your soul

RECENT PROJECTS

Rawkes.comPersonal website and blog

MORE COOL STUFF

Rawket ScientistTechnical Evangelist at Mozilla

@robhawkes

Slidesslideshare.net/robhawkes

Get in touch with me on Twitter: @robhawkes

Follow my blog (Rawkes) to keep up to date with stuff that I’m working on: http://rawkes.com

I’ve recently worked on a project that analyses sentiment on Twitter: http://rawkes.com/blog/2011/05/05/people-love-a-good-smooch-on-a-balcony

Rawkets is my multiplayer HTML5 and JavaScript game. Play it, it’s fun: http://rawkets.com

These slides are online at http://slideshare.net/robhawkes