Monday, September 21, 2015

Making a Real Time Multiplayer Online Game in NodeJS (Part 1)

So, I have this bad habit of starting too many side projects.  This is one of those projects.  Having never built an online multiplayer game, I wanted to make a very simple, real time game using what I use every day at work:  NodeJS.  And so I did.  I built a game called:  QuadPong.  The source code is available on GitHub and is free to use under the ISC Open Source License.

This series of posts is a guide to the open source code, showing what I did, how and why I did it, and how you can take it and make something better.  We're going to take a look at the architecture, the back end and the front end and step through exactly how it all works together.  So first, let's talk about the idea, the overall architecture, and what I was trying to accomplish.

The Idea

I wanted to learn more about network coding and synchronization, and create a real time game where players could play against each other with the game run on the server, and displayed in the browser.  For this project I chose to create my own engine, libraries, and structure; excepting a few small libraries I used on the front end.

QuadPong is a four player Pong Game, where each player takes control of a paddle on one side of a four sided arena and must prevent the ball from hitting that wall.  Simple, yet slightly more complex than a standard pong game.

As the players lose points, their paddle gets smaller until they are removed from the game and the ball will simply bounce against their wall. Thus, players are removed one by one until one remains: the winner.

For the network side of things, the player simply passes back state to the server, updating the server of what actions it's doing currently, and receives updates from the server with all of the Game Object positions and bounding boxes to render in the HTML5 Canvas.  It made synchronization fairly easy, as I didn't have a multitude of objects to render, so I could simply pass back the entire state of the game.

The Architecture

I wanted to keep the game simple, with high IO and transferable libraries across the client and server, so I went with a full JavaScript stack, something I'm familiar with at work.

QuadPong is built in NodeJS, using Express and Socket.IO to manage the web socket connections.  In addition I use a little bit of JQuery on the Client, as well as a library called KeyPress.js, which handles input and keystrokes and a snippet of code from Stack Overflow to create a HiDPI Canvas.  The hosted game also sits behind NGinx as a reverse proxy server, which points to the application.

The general application architecture is structured something like the image below.  First in the center we have our web and socket server.  The Socket server handles all requests for the static files (index.html) as well as maintaining the web socket connections of the client.  It spins up new socket rooms and games on creation, as well as handling all communication to and from various clients.

Next we have the client side, where we have our canvas where the game is rendered as well as our client side socket connections.  The Client handles all of the key strokes and actions of a player, passing in state data of the player ID to the server.

The Client side notifies the Socket Server of all events, information, key presses, game creation and join events, and the Socket Server will emit back the corresponding response tag.  For instance, when the client emits a "join_game" event.  The Socket Server will correctly find a game for the player, add then to that game, and emit a matching "joined_game" event back at the Client as well as all other players listening in on that game room.

When a player creates a new game The Socket Server spins up a brand new Game Loop instance and stores a reference to that game in memory.  It also creates a new Socket.IO Room for the player who created the game, as well as any joining players, to join and listen in on relevant events for the game.

The Game Loop instances are the final part.  Each one is a separate game, playing out on the server with which the Clients simply notify the game of their states, and receive updates from the Game Loops.  Game Loops make use of a variety of custom built libraries, including a Class Library of game objects for the game, as well as a Physics Library, with a Vector2 Class I built for the game, and a BoundingBox class.  We'll look into these more in Part 2, when I go into more depth about the Server Side code.

The Game Loops are designed to run off of a minimum delta time between each loop.  This lets us tweak the speeds of the games, and increase the minimum time between loops if the server starts to get overloaded.  Once an event loop has completed on the server, the Game Loop will queue up another function call on the main NodeJS Event Loop some time later (typically 15 ms).  This allows us to create the optimal frame rate we desire, while allowing the server to run as many games as possible.

My Goal

The goal of this project, and series of blog posts is to provide a base project to build real time online games for the web.  These posts are targeted toward intermediate web and game developers looking to start making real time games.  Mostly, I wanted to practice socket connections, and see exactly how much work an online multiplayer game would take at a very base level, including building the engine.  The answer is that it really didn't take much time, but the game is still a ways from being a full fledged game.

I still need to add security features, fallback features, the ability to scale the project, memory clean up, etc.  But at a base level, it works.  You can log in and play with other people if they connect to your game, and play through to a winner.

So I've got the basic build of the code up, and you can see it, download the code, fork the repository and build on it.  Make your own real time games!  And I'll be polishing up the code as well since it's a portfolio piece for myself.

The next post will be about the back end logic: how did I architect it, what does it do, and how it could be made better.  We'll go through the code step by step, so you can understand what I was trying to do and how to replicate and modify it.  After that we'll take a look at the front end, how to render our server side data, handling key inputs and more.

So stay tuned, and feel free to follow me on Twitter, as well as check out my GitHub account and pull the code down!




2 comments: