Controller

Real Time HTML5 Canvas & Node.js: Now with Sockets!

A year ago I posted an article on how to get started with real time interaction by using Node.js, HTML5 and some powerful frameworks built on those technologies. If you haven’t had a chance to read it, follow the links below and get familiar with the concepts and components before reading on.

In part two, I mentioned that the type of communication demonstrated in the recipe might be better served by using WebSockets. In this article and sample code I'll introduce socket.io to our real time experience and highlight some key changes.

You can grab the code here under the sockets branch: https://bitbucket.org/liquidint/blogmultiplayer/branches/

UnRESTful

One of the biggest changes to our multiplayer architecture is switching from the request/response to a notification and event pattern. Socket.io takes full advantage of WebSockets by providing a simple event driven API that works seamlessly between client and server.

The core concept is that when your browser visits the application, socket.io takes care of initiating socket communication. If successful, events are fired on both the server and client. You can listen for those events to take necessary actions and/or emit your own events between the browser and server, or even allow the server to broadcast a message to all connected clients.

Normally we expect the server to be always listening and waiting for requests. Sockets allow the browser to listen almost as effectively for messages from the server.

For example, in server.js, we have the server taking care of notifying the browser that it has successfully joined a game. This happens when the page first loads without the need for a specific request from the browser.

Server

socket.emit('enteredGame', {GUID: playerGuid, id: playerID, playerList: players})

This sends a message to the browser with the name ‘enteredGame’ and a JSON object containing important information about the game state. Since there is a matching event listener in main.js, the message is received and the browser processes the results:

Browser/Client

playerSocket.on('enteredGame', function(playerRef){

    console.log('You Entered The Game');

    // Set globals.

    playerGuid = playerRef.GUID;

    playerID = playerRef.id;

    playerData = playerRef.playerList;

    // Update game state.

    updateRemotePlayers();

    // start sending player location

    sendPlayerLocation();

});

Broadcasting

The above is an example of simple communication between the server and browser. By itself that may look like a minor improvement over RESTful communication. For this app, the magic lies in the way messages can be sent to all of the connected clients.

As you may recall, for each player, the browser had to regularly make a GET request for data on all of the connected players at once. This had to be done at a fast rate to make sure all of the player states were represented without interruption. Much of the data retrieved was useless, especially if the other players weren’t doing much.

With socket.io, the server can listen to an event from a single client and then notify all connected clients about whatever impact that event had. For example, when a player changes position, the browser fires an event and passes the new X,Y coordinates. The server then emits a message to all connected clients letting them know of that player’s new position.

Client

playerSocket.emit('playerUpdate', { x: playerLocationX, y: playerLocationY });

Server

socket.on('playerUpdate', function(updatedPlayer){

    app.models.Player.setlocation(clients[socket.id].GUID, updatedPlayer.x, updatedPlayer.y, function(nulVal, players) {

      app.models.Player.findOne(

      {

        where: { GUID: clients[socket.id].GUID }

      }, function (err, player) {

        if (player != null) {

          app.io.emit('playerChanged', player);

        }

      });

    }); 

  });

The line app.io.emit(‘playerChanged’, player) does all the work of telling each connected client about the player’s change.

What’s next?

So, now that all of the restful code has been replaced with sockets, how does it perform? Get the code and use ‘slc run’ to find out. In the near future I’ll profile the performance against last year’s RESTful approach and post the results here.

This guide and sample code are not meant for use in a production real-time multiplayer game. Much work is needed to make something truly scalable and secure.

Other Posts in This Series

Related Links

Let us put our HTML5 skills to work for you! Contact us to find out how Liquid can help.