8 min read

In this blog post we will create a small game and explore how to create a simple server that will allow real-time communication between players.

The game we are going to create is an interpretation of the playground game Tag. One player is it and needs to tag other players by moving around her circle and touching other players circle.

We will use Node.js to create the server. Node.js is a platform that brings JavaScript to the server by leveraging Chrome’s JavaScript Runtime and provides API’s to easily create applications. Don not worry if you are unfamiliar with Node. There are detailed instructions to follow.

Node consist of a small core and is meant to be enhanced by modules. One of the modules we are going to use is SocketIO. SocketIO harnesses WebSockets in a ease to use way. WebSockets are a recent addition to the protocols over TCP and allows for bi-directional communication, ideal for real-time games.

Follow along

If you want to follow along with the code examples, download Tag-follow-along.zip and unzip it in a suitable location.

It uses Node.js, so you need to install that before we can start. Once it is installed we need to retrieve the dependencies for the project. Open a console, enter the Tag-follow-along directory and execute the following command

npm install

This will install the dependencies over the network. You can start the game by running

npm run game

If you now open http://localhost:3000 in your browser you should be greeted with Tag although there is not a lot going on yet.

Tag

We will create a game Tag. One player is it and needs to tag other client and server. Below you see a screen-shot of the game.

Real-time Communication with SocketIO

A Game of Tag

The big circle is it and needs to tag other circles. Two smaller circles are trying to flee, but they seem to be cornered. Four tiny circles can be seen as well. These are already tagged and unable to move.

Game Overview

Because this blog post is about real-time communication we are not going into the game mechanics in detail. If you want to learn more about HTML games take a look at HTML5 Games Development by Example.

The important files in the project are listed below.

├── lib
│   ├── extend.js
│   └── game.js
├── package.json
├── public
│   ├── css
│   │   └── tag.css
│   ├── index.html
│   ├── js
│   │   ├── application.js
│   │   └── tag.js
│   └── options.json
└── server.js

We will give a short description of the role each file plays.

  • package.json is a description of the project. It is mainly used to manage the dependencies of the project.
  • server.js is the source for the server. It has two responsibilities: serving static files from the public directory and running the game.
  • game.js is the source for the actual game of Tag. It keeps track of players, their position and if they are tagged.
  • tag.js the counter-part of game.js in the browser.
  • application.js is responsible for setting up the game in the browser.

Communication

Real-time Communication with SocketIO

Communication between server and clients

The diagram above shows how the server and clients communicate with each other. The clients know about the position of the players. Each time a player moves the client should inform the server.

The server in turn keeps track of all the positions that the clients send and updates the game state accordingly. It also needs to inform the clients of the updated game state.

This is contrary to traditional pull-based communication between clients. I.e. a client makes a request and a server responds. From the diagram it is clear that the server can push information to clients without the clients make a request for it. This is made possible by WebSockets

a protocol providing full-duplex communication channels over a single TCP connection.

We will use the SocketIO library to tap into the potential of WebSockets.

Setting up Communication

In order to let the client and the server communicate with each other we need to add some code. Open server.js and require SocketIO.

var express = require('express');
var http = require('http');
var socketio = require('socket.io');
var Game = require('./lib/game');
var options = require('./public/options.json');

This allows us to use the library in our code. Next we need to get a handle on io by adding it to the server. Find the line var server = http.Server(app); and change it to

var server = http.Server(app);
var io = socketio(server);

This sets up the server for two-way communication with clients. In order to make an actual connection open public/js/application.js and add the following line

var socket = io();

Head over to server.js and we will make sure we get some feedback when a client connects. At the end of the server.js file add the following lines

io.on('connection', function(socket){
    console.log('%s connected', socket.id);

    socket.on('disconnect', function(){
        console.log('%s disconnected', socket.id);
    });
});

This will write a log message each time a client connects and disconnects. Restart the server, use Ctrl-C to stop it and run npm run game to start it. If you now refresh your browser a few times you should see something like

Listening on http://:::3000
jK0r5Eqhzn16wySjAAAA connected
jK0r5Eqhzn16wySjAAAA disconnected
52cTgdk_p3IwcrMXAAAB connected
52cTgdk_p3IwcrMXAAAB disconnected
X89V2p12k9Jsw2UUAAAC connected
X89V2p12k9Jsw2UUAAAC disconnected
hw-7y6phGkvRz-TmAAAD connected

Register Players

The game is responsible for keeping track of players. So when a client connects we need to inform the game of a new player. When a client disconnects we need to remove the player from the game.

Update the server.js to reflect this

io.on('connection', function(socket){
    console.log('%s connected', socket.id);
    game.addPlayer(socket.id);

    socket.on('disconnect', function(){
        console.log('%s disconnected', socket.id);
        game.removePlayer(socket.id);
    });
});

Keeping Track of Player Position

Each client should inform the server of the position the player wants to be in. We can use the socket that we created to emit this information to the server.

Open the development tools of the browser and select the Console tab. If you now move your mouse around inside the square you will see some log messages occur.

Object { x: 28, y: 417 }
Object { x: 123, y: 403 }
Object { x: 210, y: 401 }
Object { x: 276, y: 401 }
Object { x: 397, y: 401 }
Object { x: 480, y: 419 }
Object { x: 519, y: 435 }
Object { x: 570, y: 471 }

The position is already logged in the mouseMoveHandler in public/js/application.js. Change it to

function mouseMoveHandler(event){
        console.log({
            'x': event.pageX - this.offsetLeft,
            'y': event.pageY - this.offsetTop
        });
        socket.emit('position', {
            'x': event.pageX - this.offsetLeft,
            'y': event.pageY - this.offsetTop
        });
    }

The socket.emit call will send the position to the server. Lets make sure the server handles when it receives the updated position. In server.js head over to the socket registration code and add the following snippet.

socket.on('position', function(data){
        console.log('%s is at (%s, %s)', socket.id, data.x, data.y);
    });

This registers an event handler for position events that the client sends. If you now restart the server, refresh your browser and move your mouse around in the Tag square, you should also see log messages in the server output.

kZyZkVjycq0_ZIvcAAAC is at (628, 133)
kZyZkVjycq0_ZIvcAAAC is at (610, 136)
kZyZkVjycq0_ZIvcAAAC is at (588, 139)
kZyZkVjycq0_ZIvcAAAC is at (561, 145)
kZyZkVjycq0_ZIvcAAAC is at (540, 148)
kZyZkVjycq0_ZIvcAAAC is at (520, 148)
kZyZkVjycq0_ZIvcAAAC is at (506, 148)
kZyZkVjycq0_ZIvcAAAC is at (489, 148)
kZyZkVjycq0_ZIvcAAAC is at (477, 148)
kZyZkVjycq0_ZIvcAAAC is at (469, 148)

Broadcasting Game State

Now that the server receives the position of the client when it changes, we need to close the communication loop by sending the updated game state back.

In server.js, change the position event handler into

socket.on('position', function(data){
        console.log('%s is at (%s, %s)', socket.id, data.x, data.y);
        game.update(socket.id, data);
    });

This updates the position of the player in the game. In the emitGameState function the game state is updated by calling game’s tick method. This method is called 60 times a second. Change it to also broadcast the game state to all connected clients.

function emitGameState() {
    game.tick();
    io.emit('state', game.state());
};

In the client we need to respond when the server sends us a state event. We can do this be registering a state event handler in public/js/application.js

socket.on('state', function(state){
        game.updateState(state);
    });

If you now restart the server and reconnect your browser, you should see a big circle trying to follow your mouse around.

Multiple Clients

If you now open multiple browsers and let them all connect with http://localhost:3000, you could see the following picture

Real-time Communication with SocketIO

Multiple Clients

If you move your mouse in the Tag square in one of the browser, you would see the corresponding circle move in all of the browser.

Conclusion

We have seen that SocketIO is a very nice library that harnesses the power of WebSockets to enable real-time communication between client and server. Now start having fun with it!

About the author

Daan van Berkel is an enthusiastic software craftsman with a knack for presenting technical details in a clear and concise manner. Driven by the desire for understanding complex matters, Daan is always on the lookout for innovative uses of software.

LEAVE A REPLY

Please enter your comment!
Please enter your name here