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 <60FPS (<17ms) Client Physics 66FPS (15ms)

Server Update 22FPS (45ms) Server Physics 66FPS (15ms)

Example 06 RequireJS Refactoring

Example 07 Grunt Build

Clone this wiki locally