A simple multi-player online game (HTML5 + node.js) - Part I

Thursday, July 16, 2015

This is the first part of a 4 part series story where I describe what it took me to build a simple multiplayer online game ([part 2](http://www.valyouw.com/2015/07/a-simple-multi-player-online-game-html5_55.html){_blank}, [part 3](http://www.valyouw.com/2015/07/a-simple-multi-player-online-game-html5.html){_blank}, [part 4](http://www.valyouw.com/2015/07/a-simple-multi-player-online-game-html5_16.html){_blank}).

## Intro
I took a nice Coursera class on Game Programming and decided to experiment with programming a game in the browser using the Canvas element, the result was a nice Snake game (github, codepen).
Then I thought, what would it be to convert it into an online multi-player game, using node.js and WebSockets? So let me tell ya...

## Why "simple"? Who controls the game?
I started to think about how I wanted to implement the game, and the first question was "who controls the game"? do we put all the game management and business logic in the client and use the server just as a hub for broadcasting messages? or do we let the server control the game and the client just draw what the server says.
Set "cheating" aside, implementing everything in the client was much more simple, as I already had a working snake game... but... that isn't really an online game, so I decided to let the server be the king.
But hey, if the server controls the game, it means the snake (on the client) can't move until the server told it so, and if the player wants to change the snake direction he has to wait for the server's response before it will happen, this could lead to a very laggy game, that's when I went online to read a little bit about realtime online games, obviously I'm not the first one... After reading about the concepts of "client-side predections" and "server reconciliation" (great article) I decided to start with a "simple" game, meaning a game that would work perfectly over the LAN, and will be OKish over the WAN, also I will limit it currently to a "dual" match, meaning 2 players only per match. I believe there is still a lot to learn by doing so, and adding advanced concepts could be done later.
So... you can play the game on Heroku, the lag is obviously apparent, but not that bad...

## Terminology
The SnakeMatch is a regular "snake" game where player1 competes with player2 for Pellets, each match is for 60 seconds and the player who collected/ate more pellets wins. Of course that the regular Snake rules apply (a snake can't collide with itslef and can't hit the wall).


The game is built from the following "game objects":
  • Board - This is where everything happens, we divide the board into a grid, this will help us to make sure that all elements on the board are aligned.
    We index the cells for simplicity, starting from zero, then we can convert each cell index to a canvas x/y and vice versa
  • Cell/Box - A fixed-sized rectangle, each element on the board must fit exactly into a cell
  • Snake - The snake is built from "parts", where the first part is called the "snake head", we will see later how it is different from the rest of the body. Each snake part has the size of a board cell.
  • Pellet - This is what the snake needs to eat in order to grow, it also has the size of a board cell.
  • Status Bar - Holds the scores and the time till the end of the game.
## High Level Design
As we said, this is a fairly simple game, the server is responsible for managing the game, and the client is responsible for rendering the game state and send commands to the server.
Below is a schema of a game flow:


And here is a diagram with the main classes in the client and server:


## The Protocol
The protocol determines how the messages between the client and server will look like, my first thought was to simply use json, it is easy to understand and we have built-in functions in javascript to encode/decode it (JSON.parse / JSON.stringify).
However, there are two issues with json that bothered me:
  1.  It is a wasteful protocol (compared to custom protocol)
  2. Although parse/stringify are fast, when the server is under load, a custom protocol could do better.
For example, let's take a look at the following update message (remember we have 10 of those every second):
var updMessage = { 
  type: 5,                     // Message type
  timeToEnd: 53,               // Time to game end
  directions: [ '6', '4' ],    // The directions each snake is heading
  sizes: [ 6, 6 ],             // The snake sizes
  pellets: [ 34, 21, 67, 54 ], // The cell indices where we have pellets
  score: [ 6, 5 ]              // The players score
};
var encoded = JSON.stringify(updMessage); // encoded.length = 100
On the other hand, using a custom protocol, we would get the following string:
var encoded = '5#53#6,4#6,6#34,21,67,54#6,5'; // encoded.length = 28
Performance wise, here is a simple jsperf that compares a custom decode function vs json strigify, unless I have an error in the test case (which I might :)), on my laptop, JSON.stringify is 83% slower, that is quite a difference, especially if we would want later to increase the update rate from 10/sec to something like 30/sec...


OK, enough talking, let's see some code ([part 2](http://www.valyouw.com/2015/07/a-simple-multi-player-online-game-html5_55.html))...

No comments :

Post a Comment