Web application state, à la Dogfight (1983)

Adding real-time notifications to your Node.js app

If you have a Node.js application which powers a website and you want changing data broadcasted to the web clients as change happens, then you need a “real time” connection between the client and server. Usually web requests are initiated by the web browser; the browser decides which assets it needs. If the server wants to send data to the browser — to “push” data from the server — then there has to be an open connection between client and server to transmit the data.

Pushing data to the client’s browser keeps a websites’ data fresh and can make the user experience more compelling.

As an example, let’s take a multi-player game. By the end of the post we shall have turned a vintage video game from the 1980s into a web app that allows two players to duel in separate web browsers:

Dogfight (1983)

Before we write some code, I should describe the game. When I was a kid, I had a BBC Micro computer. One of my favourite games was “Dogfight” by Opus Software, which allowed two players to control two aeroplanes and shoot each other.

The BBC Micro had limited resources. Its 32k of RAM had to contain the operating system, the running program and the video memory. In 320x256 4-colour mode, 20k of your memory was gone, leaving 12k for your code and the operating system.

I couldn’t find Dogfight in a playable condition for the purposes of this article. I only had a single original screenshot and my memory of the game as inspiration to recreate it.

Recreating Dogfight in a web browser

There are many game frameworks to help you build in-browser JavaScript games. I didn’t use any of them — they seemed to be overkill for my simple game. To simplify things, here’s what I used:

  • JavaScript objects to represent the location, direction and projectiles of each plane
  • an HTML Canvas control to allow the scene to be drawn
  • transparent PNG images to render the planes, cloud and sun. Note that the cloud and the sun are green, as in the original, for reasons unknown
  • CSS transformations to rotate the plane images in response to user controls
  • HTML Audio tags to play sounds
  • a JavaScript setInterval timer to control the recalculation redrawing of the scene

What data needs syncing between browsers?

I started by building the app as if two players were sharing the keyboard: one player uses the A & D keys to control one plane, and the other player users J & L to control theirs. In this case, the state of the application is automatically shared because it resides on the same machine.

When we separate the players and give them their own browser, there is no shared state. We are then forced to think about what state needs to be transferred between players so that gameplay is identical on each screen.

For this game, each plane needs to transfer the following state information:

  • x coordinate
  • y coordinate
  • direction of travel (degrees)
  • array of objects for each bullet (x, y, direction of missile)
  • number of rounds fired
  • number of hits on the opponent

e.g.,

The JSON above represents a plane flying at 6 degrees from horizontal with three bullets in flight:

Every 50 milliseconds, when the JavaScript setInterval method fires, the app recalculates the plane's position based on its direction and performs the same calculation for each of its bullets.

Adding the Simple Notification Service to your app

The Simple Notification Service (SNS) can be baked into your web app to add real-time notifications. It handles the “last mile” between the browser and the server and uses RethinkDB to handle the storage of data and streaming of changes.

The SNS module is published as an npm module. Building SNS into your own app is easy:

  • create your own Express ‘routes’
  • create your webpage assets and place them in a ‘public’ folder in your project
  • add SNS to your app using npm install --save simple-notification-service
  • pass your routes and public directory to the SNS module at startup

Here’s the code:

SNS creates an Express app for you with:

  • your public directory served out and the SNS demo code switched off
  • your custom routes
  • the SNS client library /sns-client.js

SNS also creates an API key at startup, to allow your client-side app to authenticate at run time (SNS API keys are a pair consisting of hostname and API key).

Sending real-time data from your client-side app

The client-side code needs to include the SNS client library:

<script src="/sns-client.js"></script>

Then your code makes a connection to SNS at startup that defines the identity of the user connecting:

The code above connects to the server-side SNS instance using the API key that was created at startup. It identifies this user as being involved in a ‘game’ as the ‘white’ colour. It also subscribes to any connection/disconnection events from the game, so we know when other players arrive and leave.

To send data to the server, simply call sns.send:

The first parameter of sns.send is who you are sending the data to (the red player), and the second parameter is the JavaScript object to send (the white player's latest position information).

With this simple code, each player’s data is transmitted to the other — the white player sends the red player their status and vice versa!

In your code you can pick up incoming notifications as they arrive:

Try it yourself

You can try Dogfight (2017) yourself at dogfight.mybluemix.net, which is running with RethinkDB from Compose.com or simply clone the code to run it with your local RethinkDB database.

If you liked this example, or just like Dogfight in general, please consider clicking the ♡ so other Medium readers will feel the love.