DEV Community

loading...

A simple multi-player online game using node.js - Part I

valyouw profile image Yuval Originally published at thecodingnotebook.com ・4 min read

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.

Intro

Once upon a time I got a job interview task of coding the "snake" game, the result was a nice one (github, codepen).

Then I thought, what would it be to convert it into an online multi-player game, using node.js and WebSockets? let's see...

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, 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 she has to wait for the server's response before it will happen, this could lead to a very laggy game...

After reading about the concepts of "client-side predictions" and "server reconciliation" (here) I decided to start with a "simple" game, meaning a game that would work perfectly over LAN, and will be OKish over WAN, 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 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":

  1. 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
  2. Cell/Box - A fixed-sized rectangle, each element on the board must fit exactly into a cell
  3. 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.
  4. Pellet - This is what the snake needs to eat in order to grow, it also has the size of a board cell.
  5. 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 messages between the client and server will look like, my first thought was to simply use json, 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Performance wise, 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, in part 2 we will dive into the code...

Discussion (0)

pic
Editor guide