]> git.example.dev Git - binbsis50.git/commitdiff
abandoned socket.io in favour of Primus
authorLuigi Pinca <luigipinca@gmail.com>
Thu, 28 Nov 2013 20:13:27 +0000 (21:13 +0100)
committerLuigi Pinca <luigipinca@gmail.com>
Thu, 28 Nov 2013 20:13:27 +0000 (21:13 +0100)
13 files changed:
.gitignore
app.js
lib/redis-clients.js
lib/rooms.js [moved from lib/room.js with 69% similarity]
lib/sparks.js [new file with mode: 0644]
lib/utils.js
package.json
public/js/app.js
public/js/home.js
routes/site.js
routes/user.js
views/home.jade
views/room.jade

index 846f0deeefe37070b24ebf28b1888eeb7f9a4956..a196d93c565ac0ee984fe1b297d8a87d93a8460c 100644 (file)
@@ -4,3 +4,4 @@ node_modules/
 public/js/app.min.js
 public/js/home.min.js
 public/js/leaderboards.min.js
+public/js/primus.min.js
diff --git a/app.js b/app.js
index b722d593c1b7ba12cf1d2d98ee5ba131666753aa..074f9efdecbe77a837fd9d315dc4e63421a11d17 100644 (file)
--- a/app.js
+++ b/app.js
@@ -2,11 +2,9 @@
  * Module dependencies.
  */
 
-var config = require('./config')
-  , express = require('express')
+var express = require('express')
   , http = require('http')
-  , parseCookie = require('express/node_modules/cookie').parse
-  , parseSignedCookies = require('express/node_modules/connect').utils.parseSignedCookies
+  , port = require('./config').port
   , redisstore = require('connect-redis')(express)
   , secret = process.env.SITE_SECRET || 'shhhh, very secret'
   , site = require('./routes/site')
@@ -19,7 +17,8 @@ var config = require('./config')
 
 var app = express()
   , pub = __dirname + '/public' // Path to public directory
-  , sessionstore = new redisstore({client: usersdb});
+  , sessionstore = new redisstore({client: usersdb})
+  , server = http.createServer(app); // HTTP server object
 
 // Configuration
 app.set('view engine', 'jade');
@@ -52,137 +51,17 @@ app.post('/signup', user.validateSignUp, user.userExists, user.emailExists, user
 app.get('/:room', site.room);
 app.get('/user/:username', user.profile);
 
-// HTTP server object
-var server = http.createServer(app);
-
-/**
- * Setting up Socket.IO.
- */
-
-var io = require('socket.io').listen(server)
-  , sockets = Object.create(null); // Sockets of all rooms
-
-// Configuration
-io.enable('browser client minification');
-io.enable('browser client etag');
-io.enable('browser client gzip');
-io.set('log level', 1);
-io.set('transports', [
-  'websocket'
-  , 'htmlfile'
-  , 'xhr-polling'
-  , 'jsonp-polling'
-]);
-
-// Authorization
-io.set('authorization', function(data, accept) {
-  if(!data.headers.cookie) {
-    return accept('no cookie transmitted', false);
-  }
-  var signedcookie = parseCookie(data.headers.cookie);
-  var cookie = parseSignedCookies(signedcookie, secret);
-  sessionstore.get(cookie['connect.sid'], function(err, session) {
-    if (err) {
-      return accept(err.message, false);
-    }
-    else if (!session) {
-      var debuginfos = {
-        address: data.headers['x-forwarded-for'],
-        ua: data.headers['user-agent'],
-        cookie: data.headers.cookie
-      };
-      console.log(debuginfos);
-      return accept('session not found', false);
-    }
-    data.session = session;
-    accept(null, true);
-  });
-});
-
-io.sockets.on('connection', function(socket) {
-  var session = socket.handshake.session;
-  socket.on('disconnect', function() {
-    if (socket.roomname) {
-      rooms[socket.roomname].removeUser(socket.nickname);
-    }
-  });
-  socket.on('getoverview', function(callback) {
-    if (typeof callback !== 'function') {
-      return;
-    }
-    var data = Object.create(null);
-    for (var prop in rooms) {
-      data[prop] = rooms[prop].getPopulation();
-    }
-    callback(data);
-  });
-  socket.on('getstatus', function(callback) {
-    if (socket.roomname && typeof callback === 'function') {
-      rooms[socket.roomname].sendStatus(callback);
-    }
-  });
-  socket.on('guess', function(guess) {
-    if (socket.roomname && typeof guess === 'string') {
-      rooms[socket.roomname].guess(socket, guess);
-    }
-  });
-  socket.on('ignore', function(who, callback) {
-    if (socket.roomname && typeof who === 'string' && typeof callback === 'function') {
-      rooms[socket.roomname].ignore(who, socket.nickname, callback);
-    }
-  });
-  socket.on('joinanonymously', function(nickname, roomname) {
-    if (!socket.nickname && typeof nickname === 'string' && ~config.rooms.indexOf(roomname)) {
-      rooms[roomname].setNickName(socket, nickname);
-    }
-  });
-  socket.on('joinroom', function(room) {
-    if (session.user && ~config.rooms.indexOf(room)) {
-      if (sockets[session.user]) { // User already in a room
-        socket.emit('alreadyinaroom');
-        return;
-      }
-      socket.nickname = session.user;
-      rooms[room].joinRoom(socket);
-    }
-  });
-  socket.on('kick', function(who, why, callback) {
-    if (socket.roomname && typeof who === 'string' && typeof why === 'string' &&
-      typeof callback === 'function') {
-      rooms[socket.roomname].kick(who, why, socket.nickname, callback);
-    }
-  });
-  socket.on('loggedin', function(callback) {
-    if (typeof callback !== 'function') {
-      return;
-    }
-    return (session.user) ? callback(session.user) : callback(false);
-  });
-  socket.on('sendchatmsg', function(msg, to) {
-    if (socket.roomname && typeof msg === 'string') {
-      rooms[socket.roomname].sendChatMessage(msg, socket, to);
-    }
-  });
-  socket.on('unignore', function(who) {
-    if (socket.roomname && typeof who === 'string') {
-      rooms[socket.roomname].unignore(who, socket.nickname);
-    }
-  });
-});
-
 /**
  * Setting up the rooms.
  */
 
-var Room = require('./lib/room')({io: io, sockets: sockets})
-  , rooms = Object.create(null); // The Object that contains all the room instances
-
-config.rooms.forEach(function(room) {
-  room = rooms[room] = new Room(room);
-  room.start();
+require('./lib/rooms')({
+  secret: secret,
+  server: server,
+  sessionstore: sessionstore
 });
 
 // Begin accepting connections
-server.listen(config.port, function() {
-  console.log('binb server listening on port ' + config.port);
+server.listen(port, function() {
+  console.info('binb server listening on port ' + port);
 });
index 8f4838e7484ea111ed2d95dd0897a40ead8ebc06..28bb76ef4d0c3d132927140d6c9e685913227061 100644 (file)
@@ -17,11 +17,11 @@ if (process.env.NODE_ENV === 'production') {
 }
 
 songsclient.on('error', function(err) {
-  console.log(err.message);
+  console.error(err);
 });
 
 usersclient.on('error', function(err) {
-  console.log(err.message);
+  console.error(err);
 });
 
 /**
similarity index 69%
rename from lib/room.js
rename to lib/rooms.js
index 2521c31cd51df3712e5b61b69c348fa0578a9316..9a5cb2c1282e82acf0dbb0499d776e2562648069 100644 (file)
@@ -7,25 +7,30 @@ var amatch = require('./match')
   , collectStats = require('./stats')
   , config = require('../config')
   , fifolength = config.songsinarun * config.gameswithnorepeats
-  , io
+  , isUsername = require('./utils').isUsername
+  , primus
   , randInt = require('./prng').randInt
-  , sockets
+  , rooms = {} // The Object that contains all the room instances
   , songsdb = clients.songs
-  , usersdb = clients.users
-  , utils = require('./utils')
-  , isUsername = utils.isUsername
-  , trackscount = utils.trackscount;
+  , sparks
+  , usersdb = clients.users;
 
 /**
- * Expose the constructor.
+ * Expose a function to set up the rooms.
  */
 
 module.exports = function(options) {
-  io = options.io;
-  sockets = options.sockets;
-  return Room;
+  var refs = require('./sparks')(options);
+  primus = refs.primus;
+  sparks = refs.sparks;
+  config.rooms.forEach(function(room) {
+    room = rooms[room] = new Room(room);
+    room.start();
+  });
 };
 
+module.exports.rooms = rooms;
+
 /**
  * Room constructor.
  */
@@ -45,6 +50,7 @@ function Room(roomname) {
     , title // Title in lowercase
     , trackName
     , trackViewUrl
+    , trackscount // Number of tracks in the room
     , totusers = 0
     , usersData = Object.create(null);
 
@@ -118,10 +124,10 @@ function Room(roomname) {
   };
 
   // Add a new user in the room
-  var addUser = function(socket, loggedin) {
-    sockets[socket.nickname] = socket;
-    usersData[socket.nickname] = {
-      nickname: socket.nickname,
+  var addUser = function(spark, loggedin) {
+    sparks[spark.nickname] = spark;
+    usersData[spark.nickname] = {
+      nickname: spark.nickname,
       registered: loggedin,
       points: 0,
       roundpoints: 0,
@@ -135,9 +141,9 @@ function Room(roomname) {
     };
     totusers++;
     // Broadcast new user event
-    io.sockets.emit('updateoverview', roomname, totusers);
-    socket.emit('ready', usersData, trackscount[roomname], loggedin);
-    socket.broadcast.to(roomname).emit('newuser', socket.nickname, usersData);
+    primus.send('updateoverview', roomname, totusers);
+    spark.send('ready', usersData, trackscount, loggedin);
+    spark.room(roomname).send('newuser', spark.nickname, usersData);
   };
 
   var gameOver = function() {
@@ -150,7 +156,7 @@ function Room(roomname) {
     }
     users.sort(function(a, b) {return b.points - a.points;});
     var podium = users.slice(0,3);
-    io.sockets.in(roomname).emit('gameover', podium);
+    primus.room(roomname).send('gameover', podium);
 
     // Collect podium stats
     if (podium[0] && podium[0].registered) {
@@ -180,64 +186,64 @@ function Room(roomname) {
   };
 
   // A user is sending a guess
-  this.guess = function(socket, guess) {
+  this.guess = function(spark, guess) {
     if (status === 0) {
-      if (!usersData[socket.nickname].matched) { // No track no artist
+      if (!usersData[spark.nickname].matched) { // No track no artist
         if ((artist === title) && amatch(title, guess, true)) {
-          addPointsAndStats(socket.nickname, true);
-          socket.emit('bothmatched');
-          io.sockets.in(roomname).emit('updateusers', usersData);
+          addPointsAndStats(spark.nickname, true);
+          spark.send('bothmatched');
+          primus.room(roomname).send('updateusers', usersData);
         }
         else if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
-          usersData[socket.nickname].roundpoints++;
-          usersData[socket.nickname].points++;
-          usersData[socket.nickname].matched = 'artist';
-          socket.emit('artistmatched');
-          io.sockets.in(roomname).emit('updateusers', usersData);
-          if (usersData[socket.nickname].registered) {
-            var stats = {points:1,userscore:usersData[socket.nickname].points};
-            collectStats(socket.nickname, stats);
+          usersData[spark.nickname].roundpoints++;
+          usersData[spark.nickname].points++;
+          usersData[spark.nickname].matched = 'artist';
+          spark.send('artistmatched');
+          primus.room(roomname).send('updateusers', usersData);
+          if (usersData[spark.nickname].registered) {
+            var stats = {points:1,userscore:usersData[spark.nickname].points};
+            collectStats(spark.nickname, stats);
           }
         }
         else if (amatch(title, guess)) {
-          usersData[socket.nickname].roundpoints++;
-          usersData[socket.nickname].points++;
-          usersData[socket.nickname].matched = 'title';
-          socket.emit('titlematched');
-          io.sockets.in(roomname).emit('updateusers', usersData);
-          if (usersData[socket.nickname].registered) {
-            var stats = {points:1,userscore:usersData[socket.nickname].points};
-            collectStats(socket.nickname, stats);
+          usersData[spark.nickname].roundpoints++;
+          usersData[spark.nickname].points++;
+          usersData[spark.nickname].matched = 'title';
+          spark.send('titlematched');
+          primus.room(roomname).send('updateusers', usersData);
+          if (usersData[spark.nickname].registered) {
+            var stats = {points:1,userscore:usersData[spark.nickname].points};
+            collectStats(spark.nickname, stats);
           }
         }
         else {
-          socket.emit('nomatch');
+          spark.send('nomatch');
         }
       }
-      else if (usersData[socket.nickname].matched !== 'both') { // Track or artist
-        if (usersData[socket.nickname].matched === 'artist') {
+      else if (usersData[spark.nickname].matched !== 'both') { // Track or artist
+        if (usersData[spark.nickname].matched === 'artist') {
           if (amatch(title, guess)) {
-            addPointsAndStats(socket.nickname, false);
-            socket.emit('bothmatched');
-            io.sockets.in(roomname).emit('updateusers', usersData);
+            addPointsAndStats(spark.nickname, false);
+            spark.send('bothmatched');
+            primus.room(roomname).send('updateusers', usersData);
           }
           else {
-            socket.emit('nomatch');
+            spark.send('nomatch');
           }
         }
         else {
           if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
-            addPointsAndStats(socket.nickname, false);
-            socket.emit('bothmatched');
-            io.sockets.in(roomname).emit('updateusers', usersData);
+            addPointsAndStats(spark.nickname, false);
+            spark.send('bothmatched');
+            primus.room(roomname).send('updateusers', usersData);
           }
           else {
-            socket.emit('nomatch');
+            spark.send('nomatch');
           }
         }
       }
       else { // The user has guessed both track and artist
-        socket.emit('stoptrying');
+        spark.send('stoptrying');
       }
     }
   };
@@ -246,17 +252,16 @@ function Room(roomname) {
     // Check if the player to be ignored is in the room
     if (usersData[who]) {
       // Inform the bad player that he/she is being ignored
-      var recipient = sockets[who];
-      recipient.emit('chatmsg', executor+' is ignoring you.', 'binb', who);
-      return callback(who);
+      var recipient = sparks[who];
+      recipient.send('chatmsg', executor+' is ignoring you.', 'binb', who);
+      return callback(true, who);
     }
     callback(false);
   };
 
-  this.joinRoom = function(socket) {
-    socket.roomname = roomname;
-    socket.join(roomname);
-    addUser(socket, true);
+  this.joinRoom = function(spark) {
+    spark.join(roomname);
+    addUser(spark, true);
   };
 
   // Kick a user
@@ -268,25 +273,25 @@ function Room(roomname) {
             why = ' ('+why+')';
           }
           var notice = 'you have been kicked by '+executor+why+'.';
-          var recipient = sockets[who];
-          recipient.emit('chatmsg', notice, 'binb', who);
-          recipient.disconnect();
+          var recipient = sparks[who];
+          recipient.send('chatmsg', notice, 'binb', who);
+          recipient.end();
         }
-        return;
+        return callback(true);
       }
-      callback();
+      callback(false);
     });
   };
 
   // A user has left (DCed, etc.)
   this.removeUser = function(nickname) {
     // Delete the references
-    delete sockets[nickname];
+    delete sparks[nickname];
     delete usersData[nickname];
     totusers--;
     // Broadcast the event
-    io.sockets.emit('updateoverview', roomname, totusers);
-    io.sockets.in(roomname).emit('userleft', nickname, usersData);
+    primus.send('updateoverview', roomname, totusers);
+    primus.room(roomname).send('userleft', nickname, usersData);
   };
 
   var resetPoints = function(roundonly) {
@@ -306,13 +311,13 @@ function Room(roomname) {
   };
 
   // A user is sending a chat message
-  this.sendChatMessage = function(msg, socket, to) {
+  this.sendChatMessage = function(msg, spark, to) {
     if (typeof to === 'string') {
       // Check if the recipient is in the room
       if (usersData[to]) {
-        socket.emit('chatmsg', msg, socket.nickname, to);
-        var recipient = sockets[to];
-        recipient.emit('chatmsg', msg, socket.nickname, to);
+        spark.send('chatmsg', msg, spark.nickname, to);
+        var recipient = sparks[to];
+        recipient.send('chatmsg', msg, spark.nickname, to);
       }
       return;
     }
@@ -321,15 +326,15 @@ function Room(roomname) {
     if (status === 0 && (amatch(artist, msglcase, true) ||
         (feat && amatch(feat, msglcase, true)) || amatch(title, msglcase))) {
       var notice = 'You are probably right, but you have to use the box above.';
-      socket.emit('chatmsg', notice, 'binb', socket.nickname);
+      spark.send('chatmsg', notice, 'binb', spark.nickname);
       return;
     }
-    io.sockets.in(roomname).emit('chatmsg', msg, socket.nickname);
+    primus.room(roomname).send('chatmsg', msg, spark.nickname);
   };
 
   // Extract a random track from the database and send the load event
   var sendLoadTrack = function() {
-    var index = randInt(trackscount[roomname]);
+    var index = randInt(trackscount);
     songsdb.zrange(roomname, index, index, function(err, res) {
       var id = res[0];
       // Check if extracted track is in the list of already played tracks
@@ -354,7 +359,7 @@ function Room(roomname) {
         previewUrl = replies[2];
         artworkUrl = replies[3];
         trackViewUrl = replies[4];
-        io.sockets.in(roomname).emit('loadtrack', previewUrl);
+        primus.room(roomname).send('loadtrack', previewUrl);
         setTimeout(sendPlayTrack, 5000);
       });
     });
@@ -369,7 +374,7 @@ function Room(roomname) {
       tot: config.songsinarun,
       users: usersData
     };
-    io.sockets.in(roomname).emit('playtrack', data);
+    primus.room(roomname).send('playtrack', data);
     songTimeLeft(Date.now() + 30000, 50);
     setTimeout(sendTrackInfo, 30000);
   };
@@ -391,7 +396,7 @@ function Room(roomname) {
       trackName: trackName,
       trackViewUrl: trackViewUrl,
     };
-    io.sockets.in(roomname).emit('trackinfo', trackinfo);
+    primus.room(roomname).send('trackinfo', trackinfo);
     finishline = 1;
 
     if (songcounter < config.songsinarun) {
@@ -405,7 +410,7 @@ function Room(roomname) {
   };
 
   // A user is submitting a name
-  this.setNickName = function(socket, nickname) {
+  this.setNickName = function(spark, nickname) {
     var feedback = null;
 
     if (nickname === 'binb') {
@@ -415,12 +420,12 @@ function Room(roomname) {
       feedback = '<span class="label label-important">Name must contain only ';
       feedback += 'alphanumeric characters.</span>';
     }
-    else if (sockets[nickname]) {
+    else if (sparks[nickname]) {
       feedback = '<span class="label label-important">Name already taken.</span>';
     }
 
     if (feedback) {
-      return socket.emit('invalidnickname', feedback);
+      return spark.send('invalidnickname', feedback);
     }
 
     // Check if requested nickname belong to a registered user
@@ -429,12 +434,11 @@ function Room(roomname) {
       if (resp === 1) {
         feedback = '<span class="label label-important">That name belongs ';
         feedback += 'to a registered user.</span>';
-        return socket.emit('invalidnickname', feedback);
+        return spark.send('invalidnickname', feedback);
       }
-      socket.nickname = nickname;
-      socket.roomname = roomname;
-      socket.join(roomname);
-      addUser(socket, false);
+      spark.nickname = nickname;
+      spark.join(roomname);
+      addUser(spark, false);
     });
   };
 
@@ -450,17 +454,22 @@ function Room(roomname) {
   // Start the room
   this.start = function() {
     songsdb.zcard(roomname, function(err, card) {
-      trackscount[roomname] = card;
+      trackscount = card;
       sendLoadTrack();
     });
   };
 
+  // Return the number of tracks in the room
+  this.tracksCount = function() {
+    return trackscount;
+  };
+
   this.unignore = function(who, executor) {
     if (usersData[who]) {
       // Inform the bad player that he/she is no longer ignored
       var notice = executor+' has stopped ignoring you.';
-      var recipient = sockets[who];
-      recipient.emit('chatmsg', notice, 'binb', who);
+      var recipient = sparks[who];
+      recipient.send('chatmsg', notice, 'binb', who);
     }
   };
 }
diff --git a/lib/sparks.js b/lib/sparks.js
new file mode 100644 (file)
index 0000000..587ad21
--- /dev/null
@@ -0,0 +1,151 @@
+/**
+ * Module dependencies.
+ */
+
+var config = require('../config')
+  , fs = require('fs')
+  , minify = require('uglify-js').minify
+  , parseCookie = require('express/node_modules/cookie').parse
+  , parseSignedCookies = require('express/node_modules/connect').utils.parseSignedCookies
+  , Primus = require('primus')
+  , primus
+  , primusemitter = require('primus-emitter')
+  , primusrooms = require('primus-rooms')
+  , rooms = require('./rooms').rooms
+  , secret
+  , sessionstore
+  , sparks = Object.create(null); // Sparks of all rooms
+
+/**
+ * Expose a function to set up Primus.
+ */
+
+module.exports = function(options) {
+  secret = options.secret;
+  sessionstore = options.sessionstore;
+
+  // Create Primus instance
+  primus = new Primus(options.server, {
+    authorization: authorize,
+    plugin: {
+      emitter: primusemitter,
+      rooms: primusrooms
+    },
+    transformer: 'websockets'
+  });
+  
+  // Minify and store the client-side library in the public directory
+  var library = minify(primus.library(), {fromString: true});
+  fs.writeFileSync(__dirname + '/../public/js/primus.min.js', library.code);
+
+  primus.on('connection', connection);
+  primus.on('log', function(type, message, context) {
+    if (type === 'error') {
+      console.error(context);
+    }
+  });
+  return {primus: primus, sparks: sparks};
+};
+
+/**
+ * Authorization handler.
+ */
+
+var authorize = function(req, authorized) {
+  var cookie = req.headers.cookie;
+  if(!cookie) {
+    var err = new Error('no cookie transmitted');
+    console.error(err);
+    return authorized(err);
+  }
+  cookie = parseCookie(cookie);
+  cookie = parseSignedCookies(cookie, secret);
+  sessionstore.get(cookie['connect.sid'], function(err, session) {
+    if (err || !session) {
+      err = err || new Error('session not found');
+      console.error(err);
+      return authorized(err);
+    }
+    req.headers.session = session;
+    authorized();
+  });
+};
+
+/**
+ * Handle `connection` event.
+ */
+
+var connection = function(spark) {
+  var room
+    , user = spark.headers.session.user;
+  delete spark.headers.session;
+  spark.on('end', function() {
+    if (room) {
+      rooms[room].removeUser(spark.nickname);
+    }
+  });
+  spark.on('getoverview', function(callback) {
+    if (typeof callback !== 'function') {
+      return;
+    }
+    var data = Object.create(null);
+    for (var room in rooms) {
+      data[room] = rooms[room].getPopulation();
+    }
+    callback(data);
+  });
+  spark.on('getstatus', function(callback) {
+    if (room && typeof callback === 'function') {
+      rooms[room].sendStatus(callback);
+    }
+  });
+  spark.on('guess', function(guess) {
+    if (room && typeof guess === 'string') {
+      rooms[room].guess(spark, guess);
+    }
+  });
+  spark.on('ignore', function(who, callback) {
+    if (room && typeof who === 'string' && typeof callback === 'function') {
+      rooms[room].ignore(who, spark.nickname, callback);
+    }
+  });
+  spark.on('joinanonymous', function(nickname, room) {
+    if (!spark.nickname && typeof nickname === 'string' && ~config.rooms.indexOf(room)) {
+      rooms[room].setNickName(spark, nickname);
+    }
+  });
+  spark.on('joinauthenticated', function(room) {
+    if (user && ~config.rooms.indexOf(room)) {
+      if (sparks[user]) { // User already in a room
+        return spark.send('alreadyinaroom');
+      }
+      spark.nickname = user;
+      rooms[room].joinRoom(spark);
+    }
+  });
+  spark.on('joinroom', function(roomname) {
+    room = roomname;
+  });
+  spark.on('kick', function(who, why, callback) {
+    if (room && typeof who === 'string' && typeof why === 'string' &&
+      typeof callback === 'function') {
+      rooms[room].kick(who, why, spark.nickname, callback);
+    }
+  });
+  spark.on('loggedin', function(callback) {
+    if (typeof callback !== 'function') {
+      return;
+    }
+    return user ? callback(true, user) : callback(false);
+  });
+  spark.on('sendchatmsg', function(msg, to) {
+    if (room && typeof msg === 'string') {
+      rooms[room].sendChatMessage(msg, spark, to);
+    }
+  });
+  spark.on('unignore', function(who) {
+    if (room && typeof who === 'string') {
+      rooms[room].unignore(who, spark.nickname);
+    }
+  });
+};
index d806a4fc06c6d634fed8b004a583ae4dd39bd771..367e60115b993233c387010dc56e7ca5a8c2d314 100644 (file)
@@ -39,12 +39,6 @@ exports.britishFormat = function(date) {
   return day+'/'+month+'/'+year;
 };
 
-/**
- * Helper object for faster and easier access to rooms' cardinalities (number of tracks).
- */
-
-exports.trackscount = {};
-
 /**
  * Check whether a given string is a valid email address.
  */
index 10f71e95e451944a6911ac25abb30b5d76ca96a6..fec757775c420e732434252bfb0a9862f037bde7 100644 (file)
@@ -7,8 +7,12 @@
     "express": "3.4.x",
     "jade": "0.35.x",
     "nodemailer": "0.5.x",
+    "primus": "1.4.x",
+    "primus-emitter": "2.0.x",
+    "primus-rooms": "2.1.x",
     "redis": "0.9.x",
-    "socket.io": "0.9.x"
+    "uglify-js": "2.4.x",
+    "ws": "0.4.x"
   },
   "devDependencies": {
     "JSONStream": "0.7.x"
@@ -26,5 +30,5 @@
     "start": "node app.js"
   },
   "subdomain": "binb",
-  "version": "0.3.6-13"
+  "version": "0.3.6-14"
 }
index ccfa571416d13fb7a3a6354be240b6a3435498f7..66da7966b26a6010e1f55e0bbb6525b7ff2fba38 100644 (file)
     , subscriber = false
     , roundpoints = 0
     , roomname = window.location.pathname.replace(/\//g, '')
-    , socket
+    , primus
     , stopanimation = false
     , touchplay
     , urlregex = /(https?:\/\/[\-A-Za-z0-9+&@#\/%?=~_()|!:,.;]*[\-A-Za-z0-9+&@#\/%=~_()|])/
-    , uri = window.location.protocol+'//'+window.location.host; // Socket.IO server URI
+    , uri = window.location.protocol+'//'+window.location.host; // Primus server URI
 
   var amstrings = [
     'Yes, that\'s the artist. What about the title?'
       outcome.text('(From binb): '+args[0]+' is already ignored.');
       return addChatEntry(outcome);
     }
-    socket.emit('ignore', args[0], function(player) {
-      if (player) {
+    primus.send('ignore', args[0], function(ignored, player) {
+      if (ignored) {
         ignoredplayers[player] = true;
         outcome.text('(From binb): '+player+' is now ignored.');
         return addChatEntry(outcome);
 
   // Submitted name was invalid
   var invalidNickName = function(feedback) {
-    joinAnonymously(feedback+'<br/>Try with another one:');
+    joinAnonymous(feedback+'<br/>Try with another one:');
   };
 
   // Prompt for name and send it
-  var joinAnonymously = function(msg) {
+  var joinAnonymous = function(msg) {
     if (/nickname\s*\=/.test(document.cookie) && !msg) {
       nickname = document.cookie.replace(/.*nickname\s*\=\s*([^;]*);?.*/, '$1');
-      return socket.emit('joinanonymously', nickname, roomname);
+      return primus.send('joinanonymous', nickname, roomname);
     }
 
     if (DOM.modal.hasClass('in')) {
     button.click(function() {
       if ($.trim(login.val()) !== '') {
         nickname = login.val();
-        socket.emit('joinanonymously', nickname, roomname);
+        primus.send('joinanonymous', nickname, roomname);
       }
       else {
         var txt = 'Nickname can\'t be empty.';
   };
 
   var jplayerReady = function() {
-    socket.emit('loggedin', function(data) {
-      if (data) {
-        nickname = data;
+    primus.send('loggedin', function(isloggedin, loggedinas) {
+      if (isloggedin) {
+        nickname = loggedinas;
         subscriber = true;
-        return socket.emit('joinroom', roomname);
+        return primus.send('joinauthenticated', roomname);
       }
-      joinAnonymously();
+      joinAnonymous();
     });
     if (!$.jPlayer.platform.mobile && !$.jPlayer.platform.tablet) {
       return addVolumeControl();
       return addChatEntry(outcome);
     }
     var why = args[1] || '';
-    socket.emit('kick', args[0], why, function() {
-      addChatEntry(outcome);
+    primus.send('kick', args[0], why, function(success) {
+      if (!success) {
+        addChatEntry(outcome);
+      }
     });
   };
 
     addFeedback('What is this song?');
   };
 
+  // Return a function that will add a random text from the given set, with the given style
+  var randomFeedback = function(set, style) {
+    var card = set.length;
+    return function() {
+      var index =  Math.floor(Math.random() * card)
+        , text = set[index];
+      addFeedback(text, style);
+    };
+  };
+
   // Successfully joined the room
   var ready = function(usersData, trackscount, loggedin) {
     if (!loggedin && !/nickname\s*\=/.test(document.cookie)) {
         var val = $.trim(DOM.messagebox.val());
         if (val !== '') {
           if (pvtmsgto) {
-            socket.emit('sendchatmsg', val, pvtmsgto);
+            primus.send('sendchatmsg', val, pvtmsgto);
           }
           else if (/^\/[^ ]/.test(val)) {
             slashCommandHandler(val);
           }
           else {
-            socket.emit('sendchatmsg', val);
+            primus.send('sendchatmsg', val);
           }
         }
         DOM.messagebox.val('');
           var guess = $.trim(DOM.guessbox.val());
           if (guess !== '') {
             if (isplaying) {
-              socket.emit('guess', guess.toLowerCase());
+              primus.send('guess', guess.toLowerCase());
             }
             else {
               addFeedback('You have to wait the next song...');
 
     DOM.guessbox.focus();
 
-    socket.on('artistmatched', function() {
-      addFeedback(amstrings[Math.floor(Math.random()*amstrings.length)], 'correct');
-    });
-    socket.on('bothmatched', function() {
-      addFeedback(bmstrings[Math.floor(Math.random()*bmstrings.length)], 'correct');
-    });
-    socket.on('chatmsg', getChatMessage);
-    socket.on('gameover', gameOver);
-    socket.on('loadtrack', loadTrack);
-    socket.on('newuser', userJoin);
-    socket.on('nomatch', function() {
-      addFeedback(nmstrings[Math.floor(Math.random()*nmstrings.length)], 'wrong');
-    });
-    socket.on('playtrack', playTrack);
-    socket.on('stoptrying', function() {
+    primus.on('artistmatched', randomFeedback(amstrings, 'correct'));
+    primus.on('bothmatched', randomFeedback(bmstrings, 'correct'));
+    primus.on('chatmsg', getChatMessage);
+    primus.on('gameover', gameOver);
+    primus.on('loadtrack', loadTrack);
+    primus.on('newuser', userJoin);
+    primus.on('nomatch', randomFeedback(nmstrings, 'wrong'));
+    primus.on('playtrack', playTrack);
+    primus.on('stoptrying', function() {
       addFeedback('You guessed both artist and title. Please wait...');
     });
-    socket.on('titlematched', function() {
-      addFeedback(tmstrings[Math.floor(Math.random()*tmstrings.length)], 'correct');
-    });
-    socket.on('trackinfo', addTrackInfo);
-    socket.on('updateusers', updateUsers);
-    socket.on('userleft', userLeft);
-    socket.emit('getstatus', setStatus);
+    primus.on('titlematched', randomFeedback(tmstrings, 'correct'));
+    primus.on('trackinfo', addTrackInfo);
+    primus.on('updateusers', updateUsers);
+    primus.on('userleft', userLeft);
+    primus.send('getstatus', setStatus);
   };
 
   // Show the number of players inside each room
       return addChatEntry(outcome);
     }
     delete ignoredplayers[args[0]];
-    socket.emit('unignore', args[0]);
+    primus.send('unignore', args[0]);
     outcome.text('(From binb): '+args[0]+' is no longer ignored.');
     addChatEntry(outcome);
   };
       e.preventDefault();
     }
   });
-  socket = io.connect(uri, {
-    'force new connection': true,
-    'reconnect': false
-  });
-  socket.on('connect', function() {
+  primus = Primus.connect(uri, {'strategy': 'none'});
+  primus.on('open', function() {
     jplayer = $('#player').jPlayer({
       ready: jplayerReady,
       swfPath: '/static/swf/',
       preload: 'auto',
       volume: 1
     });
-    socket.on('alreadyinaroom', alreadyInARoom);
-    socket.on('disconnect', disconnect);
-    socket.on('invalidnickname', invalidNickName);
-    socket.on('ready', ready);
-    socket.on('updateoverview', updateRoomsOverview);
-    socket.emit('getoverview', roomsOverview);
+    primus.on('alreadyinaroom', alreadyInARoom);
+    primus.on('invalidnickname', invalidNickName);
+    primus.on('ready', ready);
+    primus.on('updateoverview', updateRoomsOverview);
+    primus.send('getoverview', roomsOverview);
   });
+  primus.on('close', disconnect);
 
 })();
index 238bdb5db5bd98ebb434ad0c0b63dcfae080cf4d..3a94ad397b17bf30457d1e099e176e7c591d5ef6 100644 (file)
     });
   });
   var uri = window.location.protocol+'//'+window.location.host;
-  var socket = io.connect(uri, {'reconnect':false});
-  socket.on('connect', function() {
-    socket.emit('getoverview', function(data) {
+  var primus = Primus.connect(uri, {'strategy': 'none'});
+  primus.on('open', function() {
+    primus.send('getoverview', function(data) {
       for (var prop in data) {
         $('#'+prop).text(data[prop]);
       }
     });
-    socket.on('updateoverview', function(room, players) {
+    primus.on('updateoverview', function(room, players) {
       $('#'+room).text(players);
     });
   });
index b702608f920453840a348c930af0071f95c0a927..d559f55ebf495a41c643be429d559a36cb8df741 100644 (file)
@@ -4,12 +4,11 @@
 
 var async = require('async')
   , Captcha = require('../lib/captcha')
+  , config = require('../config')
   , db = require('../lib/redis-clients').songs
   , randInt = require('../lib/prng').randInt
-  , rooms = require('../config').rooms
-  , utils = require('../lib/utils')
-  , randomSlogan = utils.randomSlogan
-  , trackscount = utils.trackscount;
+  , randomSlogan = require('../lib/utils').randomSlogan
+  , rooms = require('../lib/rooms').rooms;
 
 /**
  * Generate a sub-task.
@@ -17,7 +16,7 @@ var async = require('async')
 
 var subTask = function(genre) {
   return function(callback) {
-    var index = randInt(trackscount[genre]);
+    var index = randInt(rooms[genre].tracksCount());
     db.zrange(genre, index, index, function(err, res) {
       db.hget('song:'+res[0], 'artworkUrl100', callback);
     });
@@ -30,7 +29,7 @@ var subTask = function(genre) {
 
 exports.artworks = function(req, res) {
   var tasks = {};
-  rooms.forEach(function(room) {
+  config.rooms.forEach(function(room) {
     tasks[room] = function(callback) {
       var subtasks = [];
       for (var i = 0; i < 6; i++) {
@@ -58,7 +57,7 @@ exports.changePasswd = function(req, res) {
 exports.home = function(req, res) {
   res.render('home', {
     loggedin: req.session.user,
-    rooms: rooms,
+    rooms: config.rooms,
     slogan: randomSlogan()
   });
 };
@@ -88,11 +87,11 @@ exports.resetPasswd = function(req, res) {
 };
 
 exports.room = function(req, res) {
-  if (~rooms.indexOf(req.params.room)) {
+  if (~config.rooms.indexOf(req.params.room)) {
     return res.render('room', {
       loggedin: req.session.user,
       roomname: req.params.room,
-      rooms: rooms,
+      rooms: config.rooms,
       slogan: randomSlogan()
     });
   }
index 7a6955e07a0d0b5f0c2f2e96b05d50f5827651f8..edea3c7c79397fc8b43d00245ba9afb142f6e992 100644 (file)
@@ -313,7 +313,7 @@ exports.sendEmail = function(req, res) {
       db.setex('token:'+token, 14400, data, function(err, reply) {
         mailer.sendEmail(req.body.email, token, function(err, response) {
           if (err) {
-            console.log('error sending email: '+err.message);
+            console.error(err);
           }
         });
       });
index 37fe556407a883cef9605fb083b29a1604528958..9d77d6fa1a9023f4ca76aa88bc9087ce70c3c88b 100644 (file)
@@ -56,5 +56,5 @@ block sections
                   |  Players
 
 append scripts    
-  script(src="/socket.io/socket.io.js")
+  script(src="/static/js/primus.min.js")
   script(src="/static/js/home.min.js")
index d8bf2885f940b1759809567efaaf6dfff3064cc5..7622e357970beec6b4511ebba4f7d23119e18c19 100644 (file)
@@ -97,5 +97,5 @@ block media
 
 append scripts
   script(src="/static/js/jquery.jplayer.min.js")
-  script(src="/socket.io/socket.io.js")
+  script(src="/static/js/primus.min.js")
   script(src="/static/js/app.min.js")