]> git.example.dev Git - binbsis50.git/commitdiff
refactored the rooms module
authorLuigi Pinca <luigipinca@gmail.com>
Sat, 10 May 2014 14:45:05 +0000 (16:45 +0200)
committerLuigi Pinca <luigipinca@gmail.com>
Sat, 10 May 2014 14:45:05 +0000 (16:45 +0200)
lib/rooms.js
lib/sparks.js
lib/stats.js
package.json
public/js/app.js
routes/site.js

index a7ab5078f45e6299c6a394ee489438f50e323ce2..fcde1f75c2939ea53589e7f0540d1cbead59f92b 100644 (file)
@@ -26,8 +26,7 @@ module.exports = function(options) {
   primus = refs.primus;
   sparks = refs.sparks;
   config.rooms.forEach(function(room) {
-    room = rooms[room] = new Room(room);
-    room.start();
+    rooms[room] = new Room(room);
   });
 };
 
@@ -38,464 +37,562 @@ module.exports.rooms = rooms;
  */
 
 function Room(roomname) {
+  this.artist = null;       // Artists in lowercase
+  this.artistName = null;   // Artists of the track
+  this.artworkUrl = null;   // The URL of the album cover
+  this.feat = null;         // Featured artists
+  this.finishline = 1;      // A counter to handle the 3 fastest answers
+  this.playedtracks = [];   // The list of already played songs
+  this.previewUrl = null;   // The URL for the preview of the track
+  this.roomname = roomname;
+  this.songcounter = 0;     // A counter for the track of the current game
+  this.songtimeleft = 0;    // Remaining time for the current playing track
+  this.status = 3;          // The room status
+  this.title = null;        // Title in lowercase
+  this.trackName = null;    // Title of the track
+  this.trackViewUrl = null; // The iTunes URL of the track
+  this.trackscount = 0;     // The number of available tracks in the room
+  this.totusers = 0;        // The number of players in the room
+  this.usersData = Object.create(null);
+
+  this.initialize();
+}
 
-  var artist // Artists in lowercase
-    , artistName
-    , artworkUrl
-    , feat // Featured artists
-    , finishline = 1
-    , playedtracks = [] // The list of already played songs
-    , previewUrl
-    , songcounter = 0
-    , songtimeleft // Milliseconds
-    , status
-    , title // Title in lowercase
-    , trackName
-    , trackViewUrl
-    , trackscount // Number of tracks in the room
-    , totusers = 0
-    , usersData = Object.create(null);
-
-  // User points and statistics
-  var addPointsAndStats = function(nickname, allinone) {
-    usersData[nickname].guesstime = 30000 - songtimeleft;
-    var stats = {};
-    switch (finishline) {
-      case 1:
-        finishline++;
-        usersData[nickname].roundpoints = 6;
-        if (allinone) {
-          usersData[nickname].points += 6;
-          stats.points = 6;
-        }
-        else {
-          usersData[nickname].points += 5;
-          stats.points = 5;
-        }
-        usersData[nickname].golds++;
-        stats.gold = true;
-        break;
-      case 2:
-        finishline++;
-        usersData[nickname].roundpoints = 5;
-        if (allinone) {
-          usersData[nickname].points += 5;
-          stats.points = 5;
-        }
-        else {
-          usersData[nickname].points += 4;
-          stats.points = 4;
-        }
-        usersData[nickname].silvers++;
-        stats.silver = true;
-        break;
-      case 3:
-        finishline++;
-        usersData[nickname].roundpoints = 4;
-        if (allinone) {
-          usersData[nickname].points += 4;
-          stats.points = 4;
-        }
-        else {
-          usersData[nickname].points += 3;
-          stats.points = 3;
-        }
-        usersData[nickname].bronzes++;
-        stats.bronze = true;
-        break;
-      default:
-        usersData[nickname].roundpoints = 3;
-        if (allinone) {
-          usersData[nickname].points += 3;
-          stats.points = 3;
-        }
-        else {
-          usersData[nickname].points += 2;
-          stats.points = 2;
-        }
+/**
+ * Room states.
+ */
+
+Room.PLAYING = 0;   // A track is playing
+Room.LOADING = 1;   // A track is loading
+Room.ENDING = 2;    // The game is over
+Room.STARTING = 3;  // A new game is about to start
+
+/**
+ * Add points and collect players' statistics.
+ */
+
+Room.prototype.addPointsAndStats = function(nickname, allinone) {
+  var stats = {}
+    , userData = this.usersData[nickname];
+
+  switch (this.finishline) {
+    case 1:
+      this.finishline++;
+      if (allinone) {
+        stats.points = 6;
+        userData.points += 6;
+      } else {
+        stats.points = 5;
+        userData.points += 5;
+      }
+      stats.gold = true;
+      userData.golds++;
+      userData.roundpoints = 6;
+      break;
+    case 2:
+      this.finishline++;
+      if (allinone) {
+        stats.points = 5;
+        userData.points += 5;
+      } else {
+        stats.points = 4;
+        userData.points += 4;
+      }
+      stats.silver = true;
+      userData.silvers++;
+      userData.roundpoints = 5;
+      break;
+    case 3:
+      this.finishline++;
+      if (allinone) {
+        stats.points = 4;
+        userData.points += 4;
+      } else {
+        stats.points = 3;
+        userData.points += 3;
+      }
+      stats.bronze = true;
+      userData.bronzes++;
+      userData.roundpoints = 4;
+      break;
+    default:
+      if (allinone) {
+        stats.points = 3;
+        userData.points += 3;
+      } else {
+        stats.points = 2;
+        userData.points += 2;
+      }
+      userData.roundpoints = 3;
+  }
+
+  userData.guessed++;
+  userData.guesstime = 30000 - this.songtimeleft;
+  userData.matched = 'both';
+  userData.totguesstime += userData.guesstime;
+
+  if (userData.registered) {
+    stats.guesstime = userData.guesstime;
+    stats.userscore = userData.points;
+    updateStats(nickname, stats);
+  }
+};
+
+/**
+ * Add a new player in the room.
+ */
+
+Room.prototype.addUser = function(spark, loggedin) {
+  var nickname = spark.nickname
+    , usersData = this.usersData;
+
+  sparks[nickname] = spark;
+
+  usersData[nickname] = {
+    bronzes: 0,
+    golds: 0,
+    guessed: 0,
+    guesstime: null,
+    matched: null,
+    nickname: nickname,
+    points: 0,
+    registered: loggedin,
+    roundpoints: 0,
+    silvers: 0,
+    totguesstime: 0
+  };
+
+  this.totusers++;
+
+  // Broadcast new user event
+  primus.send('updateoverview', this.roomname, this.totusers);
+  spark.send('ready', usersData, this.trackscount, loggedin);
+  primus.room(this.roomname).except(spark.id).send('newuser', nickname, usersData);
+};
+
+/**
+ * Build the podium and start a new game.
+ */
+
+Room.prototype.gameOver = function() {
+  var podium = []
+    , usersData = this.usersData;
+
+  // Build podium
+  for (var key in usersData) {
+    podium.push(usersData[key]);
+  }
+  podium.sort(function(a, b) {
+    return b.points - a.points;
+  });
+  podium.splice(3);
+
+  primus.room(this.roomname).send('gameover', podium);
+
+  // Collect podium stats
+  podium.forEach(function(user, index) {
+    if (user.registered) {
+      updateStats(user.nickname, { podiumplace: index + 1 });
     }
-    usersData[nickname].matched = 'both';
-    usersData[nickname].guessed++;
-    usersData[nickname].totguesstime += usersData[nickname].guesstime;
-
-    if (usersData[nickname].registered) {
-      stats.userscore = usersData[nickname].points;
-      stats.guesstime = usersData[nickname].guesstime;
-      updateStats(nickname, stats);
+  });
+
+  this.resetPoints(false);
+  this.songcounter = 0;
+
+  // Check if FIFO is full
+  if (this.playedtracks.length === fifolength) {
+    this.playedtracks.splice(0, config.songsinarun);
+  }
+
+  // Start a new game
+  this.status = Room.STARTING;
+  setTimeout(this.sendLoadTrack.bind(this), 5000);
+};
+
+/**
+ * Initialize the room.
+ */
+
+Room.prototype.initialize = function() {
+  var room = this;
+
+  songsdb.zcard([this.roomname], function(err, card) {
+    if (err) {
+      console.error(err.message);
+      process.exit(1);
     }
-  };
 
-  // Add a new user in the room
-  var addUser = function(spark, loggedin) {
-    sparks[spark.nickname] = spark;
-    usersData[spark.nickname] = {
-      nickname: spark.nickname,
-      registered: loggedin,
-      points: 0,
-      roundpoints: 0,
-      matched: null,
-      guessed: 0,
-      guesstime: null,
-      totguesstime: 0,
-      golds: 0,
-      silvers: 0,
-      bronzes: 0
-    };
-    totusers++;
-    // Broadcast new user event
-    primus.send('updateoverview', roomname, totusers);
-    spark.send('ready', usersData, trackscount, loggedin);
-    primus.room(roomname).except(spark.id).send('newuser', spark.nickname, usersData);
-  };
+    room.trackscount = card;
+    room.sendLoadTrack();
+  });
+};
 
-  var gameOver = function() {
-    status = 3; // Game over
+/**
+ * Send a chat message.
+ */
 
-    // Build podium
-    var users = [];
-    for (var key in usersData) {
-      users.push(usersData[key]);
+Room.prototype.onChatMessage = function(msg, spark, to) {
+  var from = spark.nickname;
+
+  if (isString(to)) {
+    // Check if the recipient is in the room
+    if (this.usersData[to]) {
+      spark.send('chatmsg', msg, from, to);
+      sparks[to].send('chatmsg', msg, from, to);
     }
-    users.sort(function(a, b) {return b.points - a.points;});
-    var podium = users.slice(0,3);
-    primus.room(roomname).send('gameover', podium);
+    return;
+  }
+
+  // Censor answers from chat
+  var feat = this.feat
+    , msglcase = msg.toLowerCase();
+
+  if (this.status === Room.PLAYING && (amatch(this.artist, msglcase, true) ||
+      (feat && amatch(feat, msglcase, true)) || amatch(this.title, msglcase))) {
+    var notice = 'You are probably right, but you have to use the box above.';
+    spark.send('chatmsg', notice, 'binb', from);
+    return;
+  }
+
+  primus.room(this.roomname).send('chatmsg', msg, from);
+};
+
+/**
+ * Handle players' guesses.
+ */
+
+Room.prototype.onGuess = function(spark, guess) {
+  if (this.status !== Room.PLAYING) {
+    return;
+  }
 
-    // Collect podium stats
-    if (podium[0] && podium[0].registered) {
-      updateStats(podium[0].nickname, {firstplace:true});
+  var artist = this.artist
+    , feat = this.feat
+    , title = this.title
+    , userData = this.usersData[spark.nickname];
+
+  // The user hasn't guessed anything
+  if (!userData.matched) {
+    if ((artist === title) && amatch(title, guess, true)) {
+      return this.onPair(spark, true);
     }
-    if (podium[1] && podium[1].registered) {
-      updateStats(podium[1].nickname, {secondplace:true});
+    if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
+      return this.onMatch(spark, 'artist');
     }
-    if (podium[2] && podium[2].registered) {
-      updateStats(podium[2].nickname, {thirdplace:true});
+    if (amatch(title, guess)) {
+       return this.onMatch(spark, 'title');
     }
-
-    resetPoints(false);
-    songcounter = 0;
-    // Check if FIFO is full
-    if (playedtracks.length === fifolength) {
-      playedtracks.splice(0, config.songsinarun);
+    return spark.send('nomatch');
+  }
+
+  // The user has guessed the track or the artist
+  if (userData.matched !== 'both') {
+    if (userData.matched === 'artist') {
+      if (amatch(title, guess)) {
+        return this.onPair(spark);
+      }
+      return spark.send('nomatch');
     }
+    if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
+      return this.onPair(spark);
+    }
+    return spark.send('nomatch');
+  }
 
-    // Start a new game
-    setTimeout(sendLoadTrack, 5000);
-  };
+  // The user has guessed both track and artist
+  return spark.send('stoptrying');
+};
 
-  // Return the number of users in the room
-  this.getPopulation = function() {
-    return totusers;
-  };
+/**
+ * Inform a player that he/she is being ignored.
+ */
 
-  // A user is sending a guess
-  this.guess = function(spark, guess) {
-    if (status === 0) {
-      if (!usersData[spark.nickname].matched) { // No track no artist
-        if ((artist === title) && amatch(title, guess, true)) {
-          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[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};
-            updateStats(spark.nickname, stats);
-          }
-        }
-        else if (amatch(title, guess)) {
-          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};
-            updateStats(spark.nickname, stats);
-          }
-        }
-        else {
-          spark.send('nomatch');
-        }
-      }
-      else if (usersData[spark.nickname].matched !== 'both') { // Track or artist
-        if (usersData[spark.nickname].matched === 'artist') {
-          if (amatch(title, guess)) {
-            addPointsAndStats(spark.nickname, false);
-            spark.send('bothmatched');
-            primus.room(roomname).send('updateusers', usersData);
-          }
-          else {
-            spark.send('nomatch');
-          }
-        }
-        else {
-          if (amatch(artist, guess, true) || (feat && amatch(feat, guess, true))) {
-            addPointsAndStats(spark.nickname, false);
-            spark.send('bothmatched');
-            primus.room(roomname).send('updateusers', usersData);
-          }
-          else {
-            spark.send('nomatch');
-          }
-        }
-      }
-      else { // The user has guessed both track and artist
-        spark.send('stoptrying');
-      }
+Room.prototype.onIgnore = function(who, executor, callback) {
+  // Check if the player to be ignored is in the room
+  if (this.usersData[who]) {
+    sparks[who].send('chatmsg', executor + ' is ignoring you.', 'binb', who);
+    return callback(true, who);
+  }
+  callback(false);
+};
+
+/**
+ * Kick a player.
+ */
+
+Room.prototype.onKick = function(who, why, executor, callback) {
+  var room = this;
+
+  usersdb.hget(['user:' + executor, 'role'], function(err, role) {
+    if (err) {
+      console.error(err.message);
+      return callback(true);
     }
-  };
 
-  this.ignore = function(who, executor, callback) {
-    // 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 = sparks[who];
-      recipient.send('chatmsg', executor + ' is ignoring you.', 'binb', who);
-      return callback(true, who);
+    // Check role
+    if (role > 0) {
+      if (room.usersData[who]) {
+        if (why) {
+          why = ' (' + why + ')';
+        }
+        var notice = 'you have been kicked by ' + executor + why + '.'
+          , recipient = sparks[who];
+        recipient.send('chatmsg', notice, 'binb', who);
+        recipient.end();
+      }
+      return callback(true);
     }
     callback(false);
-  };
+  });
+};
 
-  this.joinRoom = function(spark) {
-    spark.join(roomname);
-    addUser(spark, true);
-  };
+/**
+ * Handle cases where the player has guessed title or artist.
+ */
 
-  // Kick a user
-  this.kick = function(who, why, executor, callback) {
-    usersdb.hget(['user:' + executor, 'role'], function(err, role) {
-      if (err) {
-        console.error(err.message);
-        return callback(true);
-      }
-      if (role > 0) { // Check role
-        if (usersData[who]) {
-          if (why) {
-            why = ' (' + why + ')';
-          }
-          var notice = 'you have been kicked by ' + executor + why + '.';
-          var recipient = sparks[who];
-          recipient.send('chatmsg', notice, 'binb', who);
-          recipient.end();
-        }
-        return callback(true);
-      }
-      callback(false);
+Room.prototype.onMatch = function(spark, what) {
+  var nickname = spark.nickname
+    , usersData = this.usersData
+    , userData = usersData[nickname];
+
+  userData.matched = what;
+  userData.points++;
+  userData.roundpoints++;
+  spark.send(what + 'matched');
+  primus.room(this.roomname).send('updateusers', usersData);
+
+  if (userData.registered) {
+    updateStats(nickname, {
+      points: 1,
+      userscore: userData.points
     });
-  };
+  }
+};
 
-  // A user has left (DCed, etc.)
-  this.removeUser = function(nickname) {
-    // Delete the references
-    delete sparks[nickname];
-    delete usersData[nickname];
-    totusers--;
-    // Broadcast the event
-    primus.send('updateoverview', roomname, totusers);
-    primus.room(roomname).send('userleft', nickname, usersData);
-  };
+/**
+ * Handle cases where the player has guessed both title and artist.
+ */
 
-  var resetPoints = function(roundonly) {
-    for (var key in usersData) {
-      if (!roundonly) {
-        usersData[key].points = 0;
-        usersData[key].guessed = 0;
-        usersData[key].totguesstime = 0;
-        usersData[key].golds = 0;
-        usersData[key].silvers = 0;
-        usersData[key].bronzes = 0;
-      }
-      usersData[key].roundpoints = 0;
-      usersData[key].matched = null;
-      usersData[key].guesstime = null;
-    }
-  };
+Room.prototype.onPair = function(spark, allinone) {
+  this.addPointsAndStats(spark.nickname, allinone);
+  spark.send('bothmatched');
+  primus.room(this.roomname).send('updateusers', this.usersData);
+};
 
-  // A user is sending a chat message
-  this.sendChatMessage = function(msg, spark, to) {
-    if (isString(to)) {
-      // Check if the recipient is in the room
-      if (usersData[to]) {
-        spark.send('chatmsg', msg, spark.nickname, to);
-        var recipient = sparks[to];
-        recipient.send('chatmsg', msg, spark.nickname, to);
-      }
-      return;
+/**
+ * Add an unauthenticated player in the room after checking that his/her
+ * nickname is valid.
+ */
+
+Room.prototype.onUnauthenticatedJoin = function(spark, nickname) {
+  var feedback
+    , room = this;
+
+  if (nickname === 'binb') {
+    feedback = 'That name is reserved.';
+  }
+  else if (!isUsername(nickname)) {
+    feedback = 'Name must contain only alphanumeric characters.';
+  }
+  else if (sparks[nickname]) {
+    feedback = 'Name already taken.';
+  }
+
+  if (feedback) {
+    return spark.send('invalidnickname', feedback);
+  }
+
+  // Check if requested nickname belongs to a registered user
+  usersdb.exists(['user:' + nickname], function(err, exists) {
+    if (err) {
+      console.error(err.message);
+      feedback = 'Could not check name availability.';
+      return spark.send('invalidnickname', feedback);
     }
-    // Censor answers from chat
-    var msglcase = msg.toLowerCase();
-    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.';
-      spark.send('chatmsg', notice, 'binb', spark.nickname);
-      return;
+
+    if (exists) {
+      feedback = 'That name belongs to a registered user.';
+      return spark.send('invalidnickname', feedback);
     }
-    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);
-    songsdb.zrange([roomname, index, index], function(err, res) {
-      if (err) {
-        console.error(err.message);
-        process.exit(1);
-      }
-      var id = res[0];
-      // Check if extracted track is in the list of already played tracks
-      if (~playedtracks.indexOf(id)) {
-        return sendLoadTrack();
-      }
-      playedtracks.push(id);
-      var args = [
-        'song:' + id
-        , 'artistName'
-        , 'trackName'
-        , 'previewUrl'
-        , 'artworkUrl60'
-        , 'trackViewUrl'
-      ];
-      songsdb.hmget(args, function(err, replies) {
-        if (err) {
-          console.error(err.message);
-          process.exit(1);
-        }
-        artistName = replies[0];
-        artist = artistName.toLowerCase();
-        trackName = replies[1];
-        title = trackName.toLowerCase();
-        feat = /feat\. (.+?)[)\]]/.test(title) ? RegExp.$1 : null;
-        previewUrl = replies[2];
-        artworkUrl = replies[3];
-        trackViewUrl = replies[4];
-        primus.room(roomname).send('loadtrack', previewUrl);
-        setTimeout(sendPlayTrack, 5000);
-      });
-    });
-    status = 1; // Loading next song
-  };
+    spark.nickname = nickname;
+    spark.join(room.roomname);
+    room.addUser(spark, false);
+  });
+};
 
-  var sendPlayTrack = function() {
-    songcounter++;
-    status = 0; // Playing track
-    var data = {
-      counter: songcounter,
-      tot: config.songsinarun,
-      users: usersData
-    };
-    primus.room(roomname).send('playtrack', data);
-    songTimeLeft(Date.now() + 30000, 50);
-    setTimeout(sendTrackInfo, 30000);
-  };
+/**
+ * Inform a player that he/she is no longer ignored.
+ */
 
-  // Send the room status
-  this.sendStatus = function(callback) {
-    var data = {
-      status: status,
-      timeleft: songtimeleft,
-      previewUrl: previewUrl
-    };
-    callback(data);
-  };
+Room.prototype.onUnignore = function(who, executor) {
+  if (this.usersData[who]) {
+    var notice = executor + ' has stopped ignoring you.';
+    sparks[who].send('chatmsg', notice, 'binb', who);
+  }
+};
 
-  var sendTrackInfo = function() {
-    var trackinfo = {
-      artworkUrl: artworkUrl,
-      artistName: artistName,
-      trackName: trackName,
-      trackViewUrl: trackViewUrl,
-    };
-    primus.room(roomname).send('trackinfo', trackinfo);
-    finishline = 1;
-
-    if (songcounter < config.songsinarun) {
-      resetPoints(true);
-      sendLoadTrack();
-      return;
-    }
+/**
+ * Remove a player from the room.
+ */
 
-    status = 2; // Sending last track info
-    setTimeout(gameOver, 5000);
-  };
+Room.prototype.removeUser = function(nickname) {
+  var usersData = this.usersData;
 
-  // A user is submitting a name
-  this.setNickName = function(spark, nickname) {
-    var feedback;
+  // Delete the references
+  delete sparks[nickname];
+  delete usersData[nickname];
 
-    if (nickname === 'binb') {
-      feedback = '<span class="label label-important">That name ' +
-        'is reserved.</span>';
-    }
-    else if (!isUsername(nickname)) {
-      feedback = '<span class="label label-important">Name must ' +
-        'contain only alphanumeric characters.</span>';
-    }
-    else if (sparks[nickname]) {
-      feedback = '<span class="label label-important">Name ' +
-        'already taken.</span>';
-    }
+  this.totusers--;
 
-    if (feedback) {
-      return spark.send('invalidnickname', feedback);
+  // Broadcast the event
+  primus.send('updateoverview', this.roomname, this.totusers);
+  primus.room(this.roomname).send('userleft', nickname, usersData);
+};
+
+/**
+ * Clean up users' data.
+ */
+
+Room.prototype.resetPoints = function(roundonly) {
+  var usersData = this.usersData
+    , userData;
+
+  for (var key in usersData) {
+    userData = usersData[key];
+    if (!roundonly) {
+      userData.points = 0;
+      userData.guessed = 0;
+      userData.totguesstime = 0;
+      userData.golds = 0;
+      userData.silvers = 0;
+      userData.bronzes = 0;
     }
+    userData.roundpoints = 0;
+    userData.matched = null;
+    userData.guesstime = null;
+  }
+};
 
-    // Check if requested nickname belongs to a registered user
-    var key = 'user:' + nickname;
-    usersdb.exists([key], function(err, exists) {
-      if (err) {
-        console.error(err.message);
-        feedback = '<span class="label label-important">Could not ' +
-          'check name availability.</span>';
-        return spark.send('invalidnickname', feedback);
-      }
-      if (exists) {
-        feedback = '<span class="label label-important">That name ' +
-          'belongs to a registered user.</span>';
-        return spark.send('invalidnickname', feedback);
-      }
-      spark.nickname = nickname;
-      spark.join(roomname);
-      addUser(spark, false);
-    });
-  };
+/**
+ * Extract a random track and send the load event to all connected clients.
+ */
+
+Room.prototype.sendLoadTrack = function() {
+  this.status = Room.LOADING;
+
+  var index = randInt(this.trackscount)
+    , room = this;
 
-  // Timer for the playing song
-  var songTimeLeft = function(end, delay) {
-    songtimeleft = end - Date.now();
-    if (songtimeleft < delay) {
-      return;
+  songsdb.zrange([this.roomname, index, index], function(err, res) {
+    if (err) {
+      console.error(err.message);
+      process.exit(1);
+    }
+
+    var id = res[0];
+    // Check if extracted track is in the list of already played tracks
+    if (~room.playedtracks.indexOf(id)) {
+      return room.sendLoadTrack();
     }
-    setTimeout(songTimeLeft, delay, end, delay);
-  };
 
-  // Start the room
-  this.start = function() {
-    songsdb.zcard([roomname], function(err, card) {
+    room.playedtracks.push(id);
+
+    songsdb.hmget([
+      'song:' + id
+      , 'artistName'
+      , 'trackName'
+      , 'previewUrl'
+      , 'artworkUrl60'
+      , 'trackViewUrl'
+    ], function(err, replies) {
       if (err) {
         console.error(err.message);
         process.exit(1);
       }
-      trackscount = card;
-      sendLoadTrack();
+
+      room.artistName = replies[0];
+      room.artist = room.artistName.toLowerCase();
+      room.trackName = replies[1];
+      room.title = room.trackName.toLowerCase();
+      room.feat = /feat\. (.+?)[)\]]/.test(room.title) ? RegExp.$1 : null;
+      room.previewUrl = replies[2];
+      room.artworkUrl = replies[3];
+      room.trackViewUrl = replies[4];
+      primus.room(room.roomname).send('loadtrack', room.previewUrl);
+
+      setTimeout(room.sendPlayTrack.bind(room), 5000);
     });
-  };
+  });
+};
 
-  // Return the number of tracks in the room
-  this.tracksCount = function() {
-    return trackscount;
-  };
+/**
+ * Send the play event to all connected clients.
+ */
+
+Room.prototype.sendPlayTrack = function() {
+  this.status = Room.PLAYING;
+  this.songcounter++;
+
+  primus.room(this.roomname).send('playtrack', {
+    counter: this.songcounter,
+    tot: config.songsinarun,
+    users: this.usersData
+  });
+
+  this.startTimer(Date.now() + 30000, 50);
+  setTimeout(this.sendTrackInfo.bind(this), 30000);
+};
+
+/**
+ * Send the room status to the client that asked for it.
+ */
+
+Room.prototype.sendStatus = function(callback) {
+  callback({
+    status: this.status,
+    timeleft: this.songtimeleft,
+    previewUrl: this.previewUrl
+  });
+};
+
+/**
+ * Send the track info to all connected clients.
+ */
+
+Room.prototype.sendTrackInfo = function() {
+  primus.room(this.roomname).send('trackinfo', {
+    artworkUrl: this.artworkUrl,
+    artistName: this.artistName,
+    trackName: this.trackName,
+    trackViewUrl: this.trackViewUrl,
+  });
 
-  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 = sparks[who];
-      recipient.send('chatmsg', notice, 'binb', who);
+  this.finishline = 1;
+
+  if (this.songcounter < config.songsinarun) {
+    this.resetPoints(true);
+    return this.sendLoadTrack();
+  }
+
+  this.status = Room.ENDING;
+  setTimeout(this.gameOver.bind(this), 5000);
+};
+
+/**
+ * Start a timer to periodically update the remaining time of the playing song.
+ */
+
+Room.prototype.startTimer = function(end, delay) {
+  var interval
+    , room = this;
+
+  room.songtimeleft = end - Date.now();
+
+  interval = setInterval(function() {
+    room.songtimeleft = end - Date.now();
+    if (room.songtimeleft < delay) {
+      clearInterval(interval);
     }
-  };
-}
+  }, delay);
+};
index b838508f18d205a61d40336ba8d2130b644cbc12..005b67ec773f1e89f742391de5948b59f50b3c59 100644 (file)
@@ -96,22 +96,23 @@ var connection = function(spark) {
     }
     var data = Object.create(null);
     for (var room in rooms) {
-      data[room] = rooms[room].getPopulation();
+      data[room] = rooms[room].totusers;
     }
     callback(data);
   });
-  spark.on('joinanonymous', function(nickname, room) {
-    if (!spark.nickname && isString(nickname) && ~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.join(room);
+      rooms[room].addUser(spark, true);
+    }
+  });
+  spark.on('joinunauthenticated', function(nickname, room) {
+    if (!spark.nickname && isString(nickname) && ~config.rooms.indexOf(room)) {
+      rooms[room].onUnauthenticatedJoin(spark, nickname);
     }
   });
   spark.on('loggedin', function(callback) {
@@ -127,34 +128,35 @@ var connection = function(spark) {
  */
 
 var joinRoom = function(room, spark) {
+  room = rooms[room];
+  spark.on('chatmsg', function(msg, to) {
+    if (isString(msg)) {
+      room.onChatMessage(msg, spark, to);
+    }
+  });
   spark.on('getstatus', function(callback) {
     if (isFunction(callback)) {
-      rooms[room].sendStatus(callback);
+      room.sendStatus(callback);
     }
   });
   spark.on('guess', function(guess) {
     if (isString(guess)) {
-      rooms[room].guess(spark, guess);
+      room.onGuess(spark, guess);
     }
   });
   spark.on('ignore', function(who, callback) {
     if (isString(who) && isFunction(callback)) {
-      rooms[room].ignore(who, spark.nickname, callback);
+      room.onIgnore(who, spark.nickname, callback);
     }
   });
   spark.on('kick', function(who, why, callback) {
     if (isString(who) && isString(why) && isFunction(callback)) {
-      rooms[room].kick(who, why, spark.nickname, callback);
-    }
-  });
-  spark.on('sendchatmsg', function(msg, to) {
-    if (isString(msg)) {
-      rooms[room].sendChatMessage(msg, spark, to);
+      room.onKick(who, why, spark.nickname, callback);
     }
   });
   spark.on('unignore', function(who) {
     if (isString(who)) {
-      rooms[room].unignore(who, spark.nickname);
+      room.onUnignore(who, spark.nickname);
     }
   });
 };
index d4fa7e3ebdfb1336293543d1d10918b3be0578b1..8c17418d208dd64cb6672e878ad4bf3deacbc064 100644 (file)
@@ -25,15 +25,17 @@ var updateStats = function(key, multi, username, stats) {
   if (stats.bronze) {
     multi.hincrby(key, 'bronzes', 1);
   }
-  if (stats.firstplace) {
-    // Update the number of first places
-    multi.hincrby(key, 'victories', 1);
-  }
-  if (stats.secondplace) {
-    multi.hincrby(key, 'secondplaces', 1);
-  }
-  if (stats.thirdplace) {
-    multi.hincrby(key, 'thirdplaces', 1);
+  if (stats.podiumplace) {
+    switch (stats.podiumplace) {
+      case 1:
+        multi.hincrby(key, 'victories', 1);
+        break;
+      case 2:
+        multi.hincrby(key, 'secondplaces', 1);
+        break;
+      case 3:
+        multi.hincrby(key, 'thirdplaces', 1);
+    }
   }
   multi.exec(function(err, replies) {
     if (err) {
index 0dbec93e64913349a54b1c88bbae33868e705a51..663177711aa894e41411010fb78a7ef43f525304 100644 (file)
@@ -34,5 +34,5 @@
     "start": "node app.js"
   },
   "subdomain": "binb",
-  "version": "0.4.7-3"
+  "version": "0.4.8-1"
 }
index f26635bb5a26b305642f613fd38f804763e7d381..e685325ee4c5ecccca62078dc7e461bc5bd93808 100644 (file)
 
   // Submitted name was invalid
   var invalidNickName = function(feedback) {
-    joinAnonymous(feedback+'<br/>Try with another one:');
+    feedback = '<span class="label label-important">' + feedback + '</span>';
+    joinUnauthenticated(feedback + '<br/>Try with another one:');
   };
 
   // Prompt for name and send it
-  var joinAnonymous = function(msg) {
+  var joinUnauthenticated = function(msg) {
     if (/nickname\s*\=/.test(document.cookie) && !msg) {
       nickname = document.cookie.replace(/.*nickname\s*\=\s*([^;]*);?.*/, '$1');
-      return primus.send('joinanonymous', nickname, roomname);
+      return primus.send('joinunauthenticated', nickname, roomname);
     }
 
     if (DOM.modal.hasClass('in')) {
     button.click(function() {
       if ($.trim(login.val()) !== '') {
         nickname = login.val();
-        primus.send('joinanonymous', nickname, roomname);
+        primus.send('joinunauthenticated', nickname, roomname);
       }
       else {
-        var txt = 'Nickname can\'t be empty.';
-        invalidNickName('<span class="label label-important">'+txt+'</span>');
+        invalidNickName('Nickname can\'t be empty.');
       }
       login.val('');
     });
         subscriber = true;
         return primus.send('joinauthenticated', roomname);
       }
-      joinAnonymous();
+      joinUnauthenticated();
     });
     if (!$.jPlayer.platform.mobile && !$.jPlayer.platform.tablet) {
       return addVolumeControl();
         var val = $.trim(DOM.messagebox.val());
         if (val !== '') {
           if (pvtmsgto) {
-            primus.send('sendchatmsg', val, pvtmsgto);
+            primus.send('chatmsg', val, pvtmsgto);
           }
           else if (/^\/[^ ]/.test(val)) {
             slashCommandHandler(val);
           }
           else {
-            primus.send('sendchatmsg', val);
+            primus.send('chatmsg', val);
           }
         }
         DOM.messagebox.val('');
index fe43ed425cad656e709099aaf0ca639451fc73e8..90eee840c5c4203d2490b9c80bd205b0072bfe32 100644 (file)
@@ -16,7 +16,7 @@ var async = require('async')
 
 var subTask = function(genre) {
   return function(callback) {
-    var index = randInt(rooms[genre].tracksCount());
+    var index = randInt(rooms[genre].trackscount);
     db.zrange([genre, index, index], function(err, res) {
       if (err) {
         return callback(err);