Socket

Copyright(c) 2012 Stefano Balietti MIT Licensed

nodeGame component responsible for dispatching events and messages


(function (exports, node) {

  
exports.Socket = Socket;  
  

Global scope

  
var GameMsg = node.GameMsg,
  GameState = node.GameState,
  Player = node.Player,
  GameMsgGenerator = node.GameMsgGenerator,
  SocketFactory = node.SocketFactory;

var action = node.action;

var buffer,
  session;

function Socket(options) {
  

Private properties

Socket.buffer

Buffer of queued messages

API
private
  buffer = [];
  if (node.support.defineProperty) {
    Object.defineProperty(this, 'buffer', {
      value: buffer,
      enumerable: true
    });
  }
  else {
    this.buffer = buffer;
  }
  

Socket.session

The session id shared with the server

This property is initialized only when a game starts

  session = null;
  if (node.support.defineProperty) {
    Object.defineProperty(this, 'session', {
      value: session,
      enumerable: true
    });
  }
  else {
    this.session = session;
  }
  
  this.socket = null;
  
  this.url = null;
}


Socket.prototype.setup = function(options) {
  options = options || {};
  
  if (options.type) {
    this.setSocketType(options.type, options);
  }
  
};

Socket.prototype.setSocketType = function(type, options) {
  var socket =  SocketFactory.get(type, options);
  if (socket) {
    this.socket = socket;
    return true;
  }
  else {
    return false;
  }
};

Socket.prototype.connect = function(url, options) {
  
  if (!this.socket) {
    node.err('cannot connet to ' + url + ' . No open socket.');
    return false;
  }
  
  this.url = url;
  node.log('connecting to ' + url);
  
  this.socket.connect(url, options);
};

Socket.prototype.onDisconnect = function() {

Save the current state of the game

  node.session.store();
  node.log('closed');
};

Socket.prototype.onMessage = function(msg) {
  
  console.log('FIRST MESSAGE')
  
  msg = this.secureParse(msg);
  if (!msg) return;
  
  console.log('FIRST MESSAGE parsed correctly')
  var sessionObj;
  

Parsing successful

  if (msg.target === 'HI') {
    
    this.startSession(msg)
    
    sessionObj = node.store(msg.session);
    
    if (false) {

if (sessionObj) {

      node.session.restore(sessionObj);
      
      msg = node.msg.create({
        action: action.SAY,
        target: 'HI_AGAIN',
        data: node.player
      });
      
      this.send(msg);
      
    }
    else {
      node.store(msg.session, node.session.save());
      
      this.sendHI(node.player, 'ALL');
    }
    
    this.attachMsgListeners();

     } 
};

Socket.prototype.attachMsgListeners = function() {
  this.onMessage = this.onMessageFull;
  node.emit('NODEGAME_READY');
};

Socket.prototype.onMessageFull = function(msg) {
  msg = this.secureParse(msg);
  
  if (msg) { // Parsing successful

TODO: improve

    if (node.game.isReady && node.game.isReady()) {
      node.emit(msg.toInEvent(), msg);
    }
    else {
      console.log('BUFFERING')
      node.log('buffering: ' + msg, 'DEBUG');
      buffer.push(msg);
    }
  }
};


Socket.prototype.registerServer = function(msg) {

Setting global info

  this.servername = msg.from;

Keep serverid = msg.from for now

  this.serverid = msg.from;
};


Socket.prototype.secureParse = secureParse = function (msg) {
  
  var gameMsg;
  try {
    gameMsg = GameMsg.clone(JSON.parse(msg));
    node.info(gameMsg, 'R: ');
  }
  catch(e) {
    return logSecureParseError('malformed msg received',  e);
  }
  
  if (this.session && gameMsg.session !== this.session) {
    return logSecureParseError('local session id does not match incoming message session id');
  }
  
  return gameMsg;
};

Socket.clearBuffer

Emits and removes all the events in the message buffer

See
node.emit
Socket.prototype.clearBuffer = function () {
  var nelem = buffer.length, msg;
  for (var i=0; i < nelem; i++) {
    msg = this.buffer.shift();
    if (msg) {
      node.emit(msg.toInEvent(), msg);
      node.log('Debuffered ' + msg, 'DEBUG');
    }
  }
};

Socket.startSession

Initializes a nodeGame session

Creates a the player and saves it in node.player, and stores the session ids in the session object

Params
msg GameMsg A game-msg
Returns
boolean TRUE, if session was correctly initialized @see node.createPlayer
Socket.prototype.startSession = function (msg) {

Store server info

  this.registerServer(msg);
  
  var player = {
      id:   msg.data, 
      sid:  msg.data
  };
  node.createPlayer(player);
  this.session = msg.session;
  return true;
};

SEND methods

Socket.send

Pushes a message into the socket.

The msg is actually received by the client itself as well.

Params
The GameMsg game message to send @see GameMsg
Socket.prototype.send = function(msg) {
  if (!this.socket) {
    node.err('socket cannot send message. No open socket.');
    return false;
  }
  
  this.socket.send(msg);
  node.info('S: ' + msg);
  node.emit('LOG', 'S: ' + msg.toSMS());
  return true;
}

Socket.sendHI

Creates a HI message and pushes it into the socket

Params
from string Optional. The message sender. Defaults node.player
to string Optional. The recipient of the message. Defaults 'SERVER'
Socket.prototype.sendHI = function (from, to) {
  from = from || node.player;
  to = to || 'SERVER';
  var msg = node.msg.createHI(from, to);
  this.send(msg);
};

Socket.sendSTATE

Creates a STATE message and pushes it into the socket

Params
action string A nodeGame action (e.g. 'get' or 'set')
state GameState The GameState object to send
to string Optional. The recipient of the message.
Socket.prototype.sendSTATE = function (action, state, to) { 
  var msg = node.msg.createSTATE(action, state, to);
  this.send(msg);
};

Socket.sendTXT

Creates a TXT message and pushes it into the socket

Params
text string Text to send
to string Optional. The recipient of the message
Socket.prototype.sendTXT = function(text, to) { 
  var msg = node.msg.createTXT(text,to);
  this.send(msg);
};

Socket.sendDATA

Creates a DATA message and pushes it into the socket

Params
action string Optional. A nodeGame action (e.g. 'get' or 'set'). Defaults 'say'
data object An object to exchange
to string Optional. The recipient of the message. Defaults 'SERVER'
text string Optional. A descriptive text associated to the message.
Socket.prototype.sendDATA = function (action, data, to, text) {
  action = action || GameMsg.say;
  to = to || 'SERVER';
  text = text || 'DATA';
  var msg = node.msg.createDATA(action, data, to, text);
  this.send(msg);
};

helping methods

var logSecureParseError = function (text, e) {
  text = text || 'Generic error while parsing a game message';
  var error = (e) ? text + ": " + e : text;
  node.log(error, 'ERR');
  node.emit('LOG', 'E: ' + error);
  return false;
}





})(
  'undefined' != typeof node ? node : module.exports
  , 'undefined' != typeof node ? node : module.parent.exports
);