Skip to content

Realtime Multiplayer Game

netzzwerg edited this page Mar 2, 2013 · 29 revisions

Preparation

node --version

0.8.21

npm --version

1.2.11

[sudo] npm install socket.io
[sudo] npm install express
[sudo] npm install node-uuid
cd node_modules
ls
node server.js

http://localhost:1337/?debug

Example 01 Basic Express Setup

  var serverPort    = process.env.PORT || 1337,
      express       = require('express'),
      app           = express(),
      http          = require('http'),
      server        = http.createServer(app);

  /* ------  ------  ------ Express ------  ------  ------ */

  server.listen(serverPort);
  console.log('\t :: Express :: Listening on port ' + serverPort );

  app.get('/', function(req, res){
    res.send('Hello World');
  });

File Respond

  app.get( '/', function( req, res ){
    res.sendfile( __dirname + '/index.html' );
  });

  app.get( '/*' , function( req, res, next ) {
    var file = req.params[0];
    if(verbose) console.log('\t :: Express :: file requested : ' + file);
    res.sendfile( __dirname + '/' + file );
  });

Example 02 Local Player Movement

Example 03 Basic SocketIO Setup

<script src="/socket.io/socket.io.js"></script>
var sio = require('socket.io').listen(server);

 sio.configure(function (){
    sio.set('log level', 0);
    sio.set('authorization', function (handshakeData, callback) {
     callback(null, true); // error first callback style
    });
 });

Send Message

sio.sockets.on('connection', function (socket) {
  socket.emit('news', { hello: 'world' });
  socket.on('my other event', function (data) {
   console.log(data);
  });
});
var socket = io.connect('http://localhost:1337');
socket.on('news', function (data) {
  console.log(data);
  socket.emit('my other event', { my: 'data' });
});

Broadcast message

  • Send to the current socket:
socket.emit('message', data);
  • Send to all sockets
sio.sockets.emit('message', data);
  • Send to all sockets except the current one:
socket.broadcast.emit('message', data);

Example 04 Multiplayer Setup

client.js

socket.on('connected', function (data) {
  localUID = data.uid;
  for (var prop in data.clients) {
    if( data.clients.hasOwnProperty(prop) ) {
      var client = data.clients[prop];
      var actor = new Actor(client.data.uid);
      actor.targetX = client.data.x;
      actor.targetY = client.data.y;
      actors.push(actor);
    }
  }
});
socket.on('clientConnect', function (data) {
  var actor = new Actor(data.uid);
  if(data.uid === localUID){
    actor.c = '#FF00FF';
  }
  actors.push(actor);
});
socket.on('clientDisconnect', function (data) {
  for (var i = 0; i < actors.length; i++) {
    if(actors[i].uid === data.uid) {
      actors.splice(i, 1);
    }
  }
});
socket.on('clientMessage', function (data) {
  for (var i = 0; i < actors.length; i++) {
    if(actors[i].uid === data.uid) {
      actors[i].setTarget(data.x, data.y);
    }
  }
});
socket.on('connect', function () {
  console.log('server connected');
});
socket.on('disconnect', function (data) {
  actors = [];
});

server.js

sio.sockets.on('connection', function (socket) {
  newClient(socket);
  socket.on('clientMessage', onClientMessage);
  socket.on('disconnect', onDisconnect);
});
function onClientMessage (data) {
  clients[data.uid].data = data;
  sio.sockets.emit('clientMessage', data);
  console.log(' client\t - '.blue, data);
}
function onDisconnect () {
  var uid = this.id;
  sio.sockets.emit('clientDisconnect', {uid:uid});
  delete clients[uid];
  console.log(' client\t - '.red + uid + ' disconnected');
}
function newClient(socket) {
  var clientUID = socket.id;
  clients[clientUID] = {'data' : {
    'x' : 0,
    'y' : 0,
    'uid': clientUID
  }};

  // tell current connection that it is connected
  socket.emit('connected', {
    'uid' : clientUID,
    'clients' : clients
  });

  // tell other sockets that there is a new client
  sio.sockets.emit('clientConnect', {
    'uid' : clientUID
  });

  console.log(' client\t - '.green + clientUID + ' connected');
}

Example 05 Advanced Networking

Problems with the simple approach:

  • Frame rate dependence
  • Server is dumb (cheat protection, ...)
  • At client movement the server only knows the end position
  • Update position (using client prediction)
  • Move the other clients based on the server position (interpolation)
Client Rendering max. 60FPS (17ms) Server Update 22FPS (45ms)
Client Physics 66FPS (15ms) Server Physics 66FPS (15ms)

Example 06 RequireJS Refactoring

Example 07 Grunt Build

Clone this wiki locally