Creating Realtime Multiplayer Games using Node JS

A few months ago at work, we decided to build a realtime multiplayer game for the web, and we chose to go with Node.js for our multiplayer server.
It was a pretty strong success, and was up running for several months without a single crash or restart of the Node.js process.

We decided to write our game in Node.js because we had heard of this cool platform and had been itching to use it for a while. This turned out to be a blessing because it was very easy to get into, and many interesting Node.js libraries exist to help accomplish various task. A side benefit of using node is that javascript itself is very easy to use. This allowed us to focus on the problems all realtime network games face with less fussing with the constraints or compile times of less dynamic languages.

Node.js was also very lightweight on our servers even during peek usage. On our game the node.js process was using only 1 thread and ran at about 3-4% CPU usage running 8-10 simultaneous games each with their own collision engine instance.

Writing a realtime multiplayer game – the initial (naive) approach:

var info = {};
info.position = {x:10, y:15};
info.velocity = {x:0.5,y: 0.2};
info.currentWeapon = 1;
info.radius = 10;
this.netChannel.send( info );

Server re-broadcast that info to all other clients:

function broadcastInfo( sendingClient, messageInfo )
{
     for(var aClient in allClients) {
          if(aClient != sendingClient) {
               aClient.connection.send( messageInfo );
          }
     }
}

Continue reading Creating Realtime Multiplayer Games using Node JS

Simple Example: Using Boost Signals with Cinder

The Observer pattern is a good way of simplifying the parts that need to know about each other, and be tied together in a complex program. Here’s how a simple how to of using Boost::Signals in a Cinder project.

One thing to note, is that we have to use ‘Signals2.hpp’ not ‘Signals.hpp’, because ‘Signals.hpp’ requires it be compiled before it is used, and can lead to compilation errors for some reason in a Cinder project.

HelloEclipse.cpp

/**
 * Mario Gonzalez
 * http://onedayitwillmake
 */
#include "cinder/app/AppBasic.h"
#include "cinder/gl/gl.h"
#include "cinder/Vector.h"
#include "cinder/app/MouseEvent.h"
#include "cinder/Rand.h"
#include "gl.h"
 
#include "Button.h"
#include "boost/bind.hpp"
 
class HelloWorldApp : public ci::app::AppBasic {
public:
	void setup();
	void prepareSettings( ci::app::AppBasic::Settings *settings );
	void mouseDown( ci::app::MouseEvent event );
	void update();
	void draw();
 
	// An imaginary button which contains the Signal
	Button button;
 
	// This will be the callback
	void onButtonWasClicked( Button* buttonInstance );
};
 
void HelloWorldApp::prepareSettings( ci::app::AppBasic::Settings *settings ) {
	settings->setWindowSize( 800, 600 );
}
 
void HelloWorldApp::setup() {
	// Get the signal instance
	// Use boost::bind, to bind our class's function, to our (this) specific instnace
	// _1 is a typedef for boost::arg - So we're creating a spot for the first argument in the function
	button.getSignal()->connect(boost::bind(&HelloWorldApp::onButtonWasClicked, this, _1));
}
 
void HelloWorldApp::mouseDown( ci::app::MouseEvent event ) {
	// Test it out, this should fire the signal
	button.clicked();
}
 
void HelloWorldApp::onButtonWasClicked( Button* buttonInstance ) {
	// Outputs: Button instance 'Button1' was pressed!
	std::cout << "Button instance '" << buttonInstance->getId() << "' was pressed!" << std::endl;
}
 
void HelloWorldApp::update() { }
void HelloWorldApp::draw() {
	// clear out the window with black
	ci::gl::clear( ci::Color( 0, 0, 0 ) );
}
 
CINDER_APP_BASIC( HelloWorldApp, ci::app::RendererGl )

Button.h – which will dispatch the signal

#ifndef BUTTON_H_
#define BUTTON_H_
 
#include <boost/signals2.hpp>
#include <iostream>
 
class Button {
public:
	// this typedef creates a simple shorthand, so that ButtonSignal refers to boost::signals2::signal<void( Button* )>
	// If you wanted you could use the long name instead, but its easier to typo
	typedef boost::signals2::signal<void( Button* )> ButtonSignal;
 
	Button();
	virtual ~Button();
	void clicked();
 
	// ACCESSORS
	std::string getId() { return _id; };
	ButtonSignal* getSignal() { return &_signal; }; // Notice we return a pointer to the signal
private:
	std::string _id;
	ButtonSignal    _signal;
};
 
#endif /* BUTTON_H_ */

Button.cpp

#include "Button.h"
#include "boost/bind.hpp"
 
Button::Button() {
	_id = "Button1";
}
 
Button::~Button() {
}
 
 
/**
 * Dispatch the event to all listeners
 */
void Button::clicked() {
	_signal( this );
}

If you wanted no arguments passed, simply remove the _1 parameter and change your method signatures accordingly. As you can guess, you could extrapolate it to have say five arguments by using boost::bind(&HelloWorldApp::onButtonWasClicked, this, _1, _2, _3, _4, _5)

That’s about it – not too hard after you figure out the ‘Signals2.hpp’ requirement