From: Luigi Pinca Date: Tue, 1 May 2012 15:30:22 +0000 (+0200) Subject: splitted server code into multiple files to improve readability X-Git-Url: https://git.saalbach.dev/?a=commitdiff_plain;h=4838fb0ef2915b0186bbae98de8388a4d7879323;p=binbsis50.git splitted server code into multiple files to improve readability --- diff --git a/lib/captcha.js b/lib/captcha.js new file mode 100644 index 0000000..6f4e605 --- /dev/null +++ b/lib/captcha.js @@ -0,0 +1,29 @@ +var canvas = require("canvas"); + +module.exports = function(characters) { + + function Captcha() { + var code = ""; + while (code.length < 4) { + code += characters[Math.floor(Math.random() * characters.length)]; + } + var _canvas = new canvas(64, 26); + var ctx = _canvas.getContext('2d'); + ctx.fillStyle = "#DDDDDD"; + ctx.fillRect(0, 0, 64, 26); + ctx.font = "bold 20px Helvetica"; + ctx.lineWidth = 1; + ctx.textAlign = "center"; + ctx.strokeStyle = "#080"; + ctx.strokeText(code, 31, 20); + ctx.save(); + this.getCode = function() { + return code; + }; + this.toDataURL = function() { + return _canvas.toDataURL(); + }; + } + + return Captcha; +}; diff --git a/lib/match.js b/lib/match.js new file mode 100644 index 0000000..24a3b08 --- /dev/null +++ b/lib/match.js @@ -0,0 +1,120 @@ +module.exports = function(threshold) { + + /* + Check if the edit distance between two strings is smaller than a threshold k. + We dont need to trace back the optimal alignment, so we can run the Levenshtein distance algorithm + in better than O(n*m). + We use only a diagonal stripe of width 2k+1 in the matrix. + See Algorithms on strings, trees, and sequences: computer science and computational biology. + Cambridge, UK: Cambridge University Press. pp 263-264. ISBN 0-521-58519-8. + */ + var checkDistance = function(s1, s2, k) { + if (k === 0) { + return s1 === s2; + } + if (Math.abs(s1.length - s2.length) > k) { + return false; + } + var d = []; + for (var i=0; i <= s1.length; i++) { + d[i] = []; // Now d is a matrix with s1.length + 1 rows + d[i][0] = i; + } + for (var j=0; j <= s2.length; j++) { + d[0][j] = j; + } + for (i=1; i <= s1.length; i++) { + var l = ((i-k) < 1) ? 1 : i-k; + var m = ((i+k) > s2.length) ? s2.length : i+k; + for (j=l; j<=m; j++) { + if (s1.charAt(i-1) === s2.charAt(j-1)) { + d[i][j] = d[i-1][j-1]; + } + else { + if ((j === l) && (d[i][j-1] === undefined)) { + d[i][j] = Math.min(d[i-1][j-1]+1, d[i-1][j]+1); + } + else if ((j === m) && (d[i-1][j] === undefined)) { + d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1); + } + else { + d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1, d[i-1][j]+1); + } + } + } + } + return d[s1.length][s2.length] <= k; + }; + + var amatch = function(subject, guess, enableartistrules) { + if (checkDistance(subject, guess, threshold)) { + return true; + } + var splitted, trimmed; + if (subject.match(/\./) && + checkDistance(subject.replace(/\./g, ""), guess, threshold)) { + return true; + } + if (subject.match(/\-/) && + checkDistance(subject.replace(/\-/g, ""), guess, threshold)) { + return true; + } + if (subject.match(/\+/) && + checkDistance(subject.replace(/\+/, "and"), guess, threshold)) { + return true; + } + if (enableartistrules) { + if (subject.match(/^the /)) { + var nothe = subject.replace(/^the /, ""); + if (checkDistance(nothe, guess, threshold)) { + return true; + } + if (nothe.match(/jimi hendrix experience/) && + checkDistance(nothe.replace(/ experience/, ""), guess, threshold)) { + return true; + } + } + splitted = subject.split("&"); + if (splitted.length !== 1) { + for (var i=0; i 15) { + feedback = 'That name is too long.'; + } + else if (data.nickname === "binb") { + feedback = 'That name is reserved.'; + } + else if (sockets[data.nickname]) { + feedback = 'Name already taken.'; + } + if (feedback) { + return invalidNickName(socket, feedback); + } + + var key = "user:"+data.nickname; + usersdb.exists(key, function(err, resp) { + if (resp === 1) { // User already exists + feedback = 'That name belongs '; + feedback += 'to a registered user.'; + return invalidNickName(socket, feedback); + } + else { + socket.nickname = data.nickname; + socket.roomname = roomname; + socket.join(roomname); + // Add user to the list of active users + addUser(socket, false); + } + }); + }; + + // A user has left (DCed, etc.) + this.userLeft = function(nickname) { + removeUser(nickname); + io.sockets.in(roomname).emit('userleft', {nickname:nickname,users:usersData}); + }; + + this.sendChatMessage = function (socket, data) { + if (typeof data === "string") { + var datalcase = data.toLowerCase(); + if (allowedguess && (amatch(artistlcase, datalcase, true) || + amatch(tracklcase, datalcase))) { + var msg = "You are probably right, but you have to use the box above."; + socket.emit('chatmsg', {from:"binb",to:socket.nickname,chatmsg:msg}); + return; + } + io.sockets.in(roomname).emit('chatmsg', {from:socket.nickname,chatmsg:data}); + } + else if (typeof data === "object" && typeof data.to === "string" && + userExists(data.to) && typeof data.chatmsg === "string") { + // Private message + socket.emit('chatmsg', {from:socket.nickname,to:data.to,chatmsg:data.chatmsg}); + var recipient = sockets[data.to]; + recipient.emit('chatmsg', {from:socket.nickname,to:data.to,chatmsg:data.chatmsg}); + } + }; + + var addPoints = 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; + } + } + usersData[nickname].matched = 'both'; + usersData[nickname].guessed++; + if (usersData[nickname].guesstime < usersData[nickname].bestguesstime) { + usersData[nickname].bestguesstime = usersData[nickname].guesstime; + } + if (usersData[nickname].registered) { + stats.userscore = usersData[nickname].points; + stats.guesstime = usersData[nickname].guesstime; + collectStats(nickname, stats); + } + }; + + this.guess = function(socket, guess) { + if (allowedguess) { + if (!usersData[socket.nickname].matched) { // No track no artist + if ((artistlcase === tracklcase) && amatch(tracklcase, guess, true)) { + addPoints(socket.nickname, true); + socket.emit('bothmatched'); + io.sockets.in(roomname).emit('updateusers', {users:usersData}); + } + else if (amatch(artistlcase, guess, true)) { + usersData[socket.nickname].roundpoints++; + usersData[socket.nickname].points++; + usersData[socket.nickname].matched = 'artist'; + socket.emit('artistmatched'); + io.sockets.in(roomname).emit('updateusers', {users:usersData}); + if (usersData[socket.nickname].registered) { + var stats = {points:1,userscore:usersData[socket.nickname].points}; + collectStats(socket.nickname, stats); + } + } + else if (amatch(tracklcase, guess)) { + usersData[socket.nickname].roundpoints++; + usersData[socket.nickname].points++; + usersData[socket.nickname].matched = 'title'; + socket.emit('titlematched'); + io.sockets.in(roomname).emit('updateusers', {users:usersData}); + if (usersData[socket.nickname].registered) { + var stats = {points:1,userscore:usersData[socket.nickname].points}; + collectStats(socket.nickname, stats); + } + } + else { + socket.emit('nomatch'); + } + } + else if (usersData[socket.nickname].matched !== 'both') { // Track or artist + if (usersData[socket.nickname].matched === 'artist') { + if (amatch(tracklcase, guess)) { + addPoints(socket.nickname, false); + socket.emit('bothmatched'); + io.sockets.in(roomname).emit('updateusers', {users:usersData}); + } + else { + socket.emit('nomatch'); + } + } + else { + if (amatch(artistlcase, guess, true)) { + addPoints(socket.nickname, false); + socket.emit('bothmatched'); + io.sockets.in(roomname).emit('updateusers', {users:usersData}); + } + else { + socket.emit('nomatch'); + } + } + } + else { // The user has guessed both track and artist + socket.emit('stoptrying'); + } + } + else { + socket.emit('noguesstime'); + } + }; + + var resetPoints = function(roundonly) { + for (var key in usersData) { + if (!roundonly) { + usersData[key].points = 0; + usersData[key].guessed = 0; + usersData[key].bestguesstime = 30000; + usersData[key].golds = 0; + usersData[key].silvers = 0; + usersData[key].bronzes = 0; + } + usersData[key].roundpoints = 0; + usersData[key].matched = null; + usersData[key].guesstime = null; + } + }; + + var sendLoadTrack = function() { + songsdb.srandmember(roomname, function(err, res) { + songsdb.hmget(res, "artistName", "trackName", "collectionName", "previewUrl", + "artworkUrl60", "trackViewUrl", function(e, replies) { + if (playedtracks.indexOf(res) !== -1) { + return sendLoadTrack(); + } + playedtracks.push(res); + artistName = replies[0]; + artistlcase = artistName.toLowerCase(); + trackName = replies[1]; + tracklcase = trackName.toLowerCase(); + collectionName = replies[2]; + previewUrl = replies[3]; + artworkUrl = replies[4]; + trackViewUrl = replies[5]; + io.sockets.in(roomname).emit('loadtrack', {previewUrl:previewUrl}); + setTimeout(sendPlayTrack, 5000); + }); + }); + status = 1; // Loading next song + }; + + var sendPlayTrack = function() { + songcounter = songcounter + 1; + status = 0; // Playing track + io.sockets.in(roomname).emit('playtrack', {counter:songcounter,tot:songsinarun, + users:usersData}); + songTimeLeft(Date.now()+30000, 50); + allowedguess = true; + setTimeout(sendTrackInfo, 30000); + }; + + var songTimeLeft = function(end, delay) { + songtimeleft = end - Date.now(); + if (songtimeleft < delay) { + return; + } + setTimeout(songTimeLeft, delay, end, delay); + }; + + var sendTrackInfo = function() { + io.sockets.in(roomname).emit('trackinfo', {artworkUrl:artworkUrl,artistName:artistName, + trackName:trackName,collectionName:collectionName, + trackViewUrl:trackViewUrl}); + finishline = 1; + allowedguess = false; + if (songcounter < songsinarun) { + resetPoints(true); + sendLoadTrack(); + } + else { + status = 2; // Sending last track info + setTimeout(gameOver, 5000); + } + }; + + var gameOver = function() { + status = 3; // Game over + var users = []; + for (var key in usersData) { + users.push(usersData[key]); + } + users.sort(function(a, b) {return b.points - a.points;}); + var podium = users.slice(0,3); + io.sockets.in(roomname).emit('gameover', {users:podium}); + if (podium[0] && podium[0].registered) { + collectStats(podium[0].nickname, {firstplace:true}); + } + if (podium[1] && podium[1].registered) { + collectStats(podium[1].nickname, {secondplace:true}); + } + if (podium[2] && podium[2].registered) { + collectStats(podium[2].nickname, {thirdplace:true}); + } + resetPoints(false); + setTimeout(reset, 5000); + }; + + this.sendStatus = function(socket) { + socket.emit('status', {status:status,timeleft:songtimeleft,previewUrl:previewUrl}); + }; + + var reset = function() { + songcounter = 0; + if (playedtracks.length === fifolength) { + playedtracks.splice(0, songsinarun); + } + sendLoadTrack(); + }; + + // Start the room + this.start = function() { + songsdb.scard(roomname, function(err, res) { + trackscount = res; + }); + sendLoadTrack(); + }; + } + + return Room; +}; diff --git a/lib/stats.js b/lib/stats.js new file mode 100644 index 0000000..3f89aa7 --- /dev/null +++ b/lib/stats.js @@ -0,0 +1,51 @@ +module.exports = function(db) { + + var collectStats = function(username, stats) { + var key = "user:"+username; + if (stats.points) { + db.hincrby(key, "totpoints", stats.points); + } + if (stats.userscore) { + // Set personal best + db.hget(key, "bestscore", function(err, res) { + if (res < stats.userscore) { + db.hset(key, "bestscore", stats.userscore); + } + }); + } + if (stats.gold) { + db.hincrby(key, "golds", 1); + } + if (stats.silver) { + db.hincrby(key, "silvers", 1); + } + if (stats.bronze) { + db.hincrby(key, "bronzes", 1); + } + if (stats.guesstime) { + db.hincrby(key, "guessed", 1); + db.hincrby(key, "totguesstime", stats.guesstime); + db.hget(key, "bestguesstime", function(err, res) { + if (stats.guesstime < res) { + db.hset(key, "bestguesstime", stats.guesstime); + } + }); + db.hget(key, "worstguesstime", function(err, res) { + if (stats.guesstime > res) { + db.hset(key, "worstguesstime", stats.guesstime); + } + }); + } + if (stats.firstplace) { + db.hincrby(key, "victories", 1); + } + if (stats.secondplace) { + db.hincrby(key, "secondplaces", 1); + } + if (stats.thirdplace) { + db.hincrby(key, "thirdplaces", 1); + } + }; + + return collectStats; +}; diff --git a/package.json b/package.json index f6eb75c..a140912 100644 --- a/package.json +++ b/package.json @@ -18,5 +18,5 @@ "engines": { "node": "0.6.x" }, - "version": "0.3.0-8" + "version": "0.3.0-9" } \ No newline at end of file diff --git a/public/static/css/style.css b/public/static/css/style.css index ed69b9b..2bf9180 100644 --- a/public/static/css/style.css +++ b/public/static/css/style.css @@ -537,7 +537,7 @@ input { background: -ms-linear-gradient(center top , #FBFBFB, #F5F5F5); background: linear-gradient(center top , #FBFBFB, #F5F5F5); } -.registered, #users .name, #users .points, .round-rank, .round-points, #users .guess-time, #tracks img.artwork, #tracks .info, #copy, #facebook-button, #twitter-button, #github-button { +.registered, #users .name, #users .points, .round-rank, .round-points, #users .guess-time, #tracks img.artwork, #tracks .info, #copy, .fb-like, #twitter-button, #github-button { float:left; } #tracks img.artwork { @@ -590,7 +590,7 @@ footer { #copy { margin: 0 20px 0 20px; } -#facebook-button { +.fb-like { width:90px; height:21px } diff --git a/server.js b/server.js index 553e105..e008524 100644 --- a/server.js +++ b/server.js @@ -1,11 +1,14 @@ -var async = require("async"); -var crypto = require("crypto"); -var canvas = require("canvas"); -var express = require("express"); -var form = require("express-form"); +var async = require('async'); +var characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; +var Captcha = require('./lib/captcha.js')(characters); +var config = require('./config.js').configure(); +var crypto = require('crypto'); +var express = require('express'); +var form = require('express-form'); var parseCookie = require('connect').utils.parseCookie; var redisstore = require('connect-redis')(express); -var config = require("./config.js").configure(); +var rooms = Object.create(null); // The Object that contains all the room instances +var sockets = Object.create(null); // Sockets of all rooms // Setting up Redis var songsdb = require("redis-url").createClient(config.songsdburl); @@ -39,32 +42,6 @@ http.get("/", function(req, res) { res.render("index", {rooms:config.rooms}); }); -// Captcha generator -const CHARACTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; - -var Captcha = function() { - var code = ""; - while (code.length < 4) { - code += CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)]; - } - var _canvas = new canvas(64, 26); - var ctx = _canvas.getContext('2d'); - ctx.fillStyle = "#DDDDDD"; - ctx.fillRect(0, 0, 64, 26); - ctx.font = "bold 20px Helvetica"; - ctx.lineWidth = 1; - ctx.textAlign = "center"; - ctx.strokeStyle = "#080"; - ctx.strokeText(code, 31, 20); - ctx.save(); - this.getCode = function() { - return code; - }; - this.toDataURL = function() { - return _canvas.toDataURL(); - }; -}; - http.get("/signup", function(req, res) { var captcha = new Captcha(); req.session.captchacode = captcha.getCode(); @@ -132,7 +109,7 @@ http.post("/signup", var mailkey = "email:"+req.form.email; var salt = ""; while (salt.length < 8) { - salt += CHARACTERS[Math.floor(Math.random() * CHARACTERS.length)]; + salt += characters[Math.floor(Math.random() * characters.length)]; } var hash = crypto.createHash('sha256').update(salt+req.form.password).digest('hex'); var date = new Date(); @@ -310,8 +287,8 @@ io.sockets.on('connection', function(socket) { var session = socket.handshake.session; socket.on('getoverview', function() { var data = Object.create(null); - for (var prop in Rooms) { - data[prop] = Rooms[prop].getPopulation(); + for (var prop in rooms) { + data[prop] = rooms[prop].getPopulation(); } socket.join('home'); socket.emit('overview', data); @@ -321,604 +298,59 @@ io.sockets.on('connection', function(socket) { }); socket.on('joinroom', function(data) { if (session.user && typeof data === "string" && config.rooms.indexOf(data) !== -1) { - if (getUserSocket(session.user)) { // User already in a room + if (sockets[session.user]) { // User already in a room socket.emit('alreadyinaroom'); return; } socket.nickname = session.user; - Rooms[data].joinRoom(socket); + rooms[data].joinRoom(socket); } - }); + }); socket.on('joinanonymously', function(data) { if (!socket.nickname && typeof data === "object" && typeof data.nickname === "string" && data.nickname !== "" && typeof data.roomname === "string" && config.rooms.indexOf(data.roomname) !== -1) { - Rooms[data.roomname].setNickName(socket, data); + rooms[data.roomname].setNickName(socket, data); } }); socket.on('getstatus', function() { if (socket.roomname) { - Rooms[socket.roomname].sendStatus(socket); + rooms[socket.roomname].sendStatus(socket); } }); socket.on('sendchatmsg', function(data) { if (socket.roomname) { - Rooms[socket.roomname].sendChatMessage(socket, data); + rooms[socket.roomname].sendChatMessage(socket, data); } }); socket.on('guess', function(data) { if (socket.roomname && typeof data === "string") { - Rooms[socket.roomname].guess(socket, data); + rooms[socket.roomname].guess(socket, data); } }); socket.on("disconnect", function() { - if (socket.roomname && socket.nickname) { - Rooms[socket.roomname].userLeft(socket); + if (socket.roomname) { + rooms[socket.roomname].userLeft(socket.nickname); } }); }); -// Sockets of all rooms -var sockets = Object.create(null); - -// Get the socket of a player -var getUserSocket = function(nickname) { - return sockets[nickname]; -}; - -/* -Check if the edit distance between two strings is smaller than a threshold k. -We dont need to trace back the optimal alignment, so we can run the Levenshtein distance -algorithm in better than O(n*m). -We use only a diagonal stripe of width 2k+1 in the matrix. -See Algorithms on strings, trees, and sequences: computer science and computational biology. -Cambridge, UK: Cambridge University Press. pp 263-264. ISBN 0-521-58519-8. -*/ -var checkDistance = function(s1, s2, k) { - if (k === 0) { - return s1 === s2; - } - if (Math.abs(s1.length - s2.length) > k) { - return false; - } - var d = []; - for (var i=0; i <= s1.length; i++) { - d[i] = []; // Now d is a matrix with s1.length + 1 rows - d[i][0] = i; - } - for (var j=0; j <= s2.length; j++) { - d[0][j] = j; - } - for (i=1; i <= s1.length; i++) { - var l = ((i-k) < 1) ? 1 : i-k; - var m = ((i+k) > s2.length) ? s2.length : i+k; - for (j=l; j<=m; j++) { - if (s1.charAt(i-1) === s2.charAt(j-1)) { - d[i][j] = d[i-1][j-1]; - } - else { - if ((j === l) && (d[i][j-1] === undefined)) { - d[i][j] = Math.min(d[i-1][j-1]+1, d[i-1][j]+1); - } - else if ((j === m) && (d[i-1][j] === undefined)) { - d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1); - } - else { - d[i][j] = Math.min(d[i][j-1]+1, d[i-1][j-1]+1, d[i-1][j]+1); - } - } - } - } - return d[s1.length][s2.length] <= k; -}; - -var amatch = function(subject, guess, enableartistrules) { - if (checkDistance(subject, guess, config.threshold)) { - return true; - } - var splitted, trimmed; - if (subject.match(/\./) && - checkDistance(subject.replace(/\./g, ""), guess, config.threshold)) { - return true; - } - if (subject.match(/\-/) && - checkDistance(subject.replace(/\-/g, ""), guess, config.threshold)) { - return true; - } - if (subject.match(/\+/) && - checkDistance(subject.replace(/\+/, "and"), guess, config.threshold)) { - return true; - } - if (enableartistrules) { - if (subject.match(/^the /)) { - var nothe = subject.replace(/^the /, ""); - if (checkDistance(nothe, guess, config.threshold)) { - return true; - } - if (nothe.match(/jimi hendrix experience/) && - checkDistance(nothe.replace(/ experience/, ""), guess, config.threshold)) { - return true; - } - } - splitted = subject.split("&"); - if (splitted.length !== 1) { - for (var i=0; i res) { - usersdb.hset(key, "worstguesstime", stats.guesstime); - } - }); - } - if (stats.firstplace) { - usersdb.hincrby(key, "victories", 1); - } - if (stats.secondplace) { - usersdb.hincrby(key, "secondplaces", 1); - } - if (stats.thirdplace) { - usersdb.hincrby(key, "thirdplaces", 1); - } +// Setting up the rooms +var roomoptions = { + songsdb: songsdb, + usersdb: usersdb, + io: io, + sockets: sockets, + songsinarun: config.songsinarun, + fifolength: config.fifolength, + threshold: config.threshold }; -function Room(name) { - - var roomname = name; - var totusers = 0; - - var usersData = Object.create(null); - var playedtracks = []; // The list of already played songs - - var artistName = null; - var artistlcase = null; - var trackName = null; - var tracklcase = null; - var collectionName = null; - var previewUrl = null; - var artworkUrl = null; - var trackViewUrl = null; - var finishline = 1; - var allowedguess = false; - var status = null; - var songtimeleft = null; // Milliseconds - var songcounter = 0; - var trackscount = 0; - - this.getPopulation = function() { - return totusers; - }; - - var addUser = function(socket, loggedin) { - sockets[socket.nickname] = socket; - usersData[socket.nickname] = { - nickname: socket.nickname, - registered: loggedin, - points: 0, - roundpoints: 0, - matched: null, - guessed: 0, - guesstime: null, - bestguesstime: 30000, - golds: 0, - silvers: 0, - bronzes: 0 - }; - totusers = totusers + 1; - io.sockets.in('home').emit('update', {room:roomname,players:totusers}); - // Broadcast new user event - socket.emit('ready', {users:usersData,trackscount:trackscount,loggedin:loggedin}); - socket.broadcast.to(roomname).emit('newuser', {nickname:socket.nickname,users:usersData}); - }; - - var removeUser = function(socket) { - // Delete the references - delete sockets[socket.nickname]; - delete usersData[socket.nickname]; - totusers = totusers - 1; - io.sockets.in('home').emit('update', {room:roomname,players:totusers}); - }; - - var userExists = function(nickname) { - var user = usersData[nickname]; - if (user) { - return true; - } - return false; - }; - - this.joinRoom = function(socket) { - socket.roomname = roomname; - socket.join(roomname); - addUser(socket, true); - }; - - // A user requested an invalid name - var invalidNickName = function(socket, feedback) { - socket.emit('invalidnickname', feedback); - }; - - // A user is submitting a name - this.setNickName = function(socket, data) { - var feedback = null; - if (data.nickname.length > 15) { - feedback = 'That name is too long.'; - } - else if (data.nickname === "binb") { - feedback = 'That name is reserved.'; - } - else if (getUserSocket(data.nickname)) { - feedback = 'Name already taken.'; - } - if (feedback) { - return invalidNickName(socket, feedback); - } - - var key = "user:"+data.nickname; - usersdb.exists(key, function(err, resp) { - if (resp === 1) { // User already exists - feedback = 'That name belongs '; - feedback += 'to a registered user.'; - return invalidNickName(socket, feedback); - } - else { - socket.nickname = data.nickname; - socket.roomname = roomname; - socket.join(roomname); - // Add user to the list of active users - addUser(socket, false); - } - }); - }; - - // A user has left (DCed, etc.) - this.userLeft = function(socket) { - var leftname = socket.nickname; - removeUser(socket); - io.sockets.in(roomname).emit('userleft', {nickname:leftname,users:usersData}); - }; - - this.sendChatMessage = function (socket, data) { - if (typeof data === "string") { - var datalcase = data.toLowerCase(); - if (allowedguess && (amatch(artistlcase, datalcase, true) || - amatch(tracklcase, datalcase))) { - var msg = "You are probably right, but you have to use the box above."; - socket.emit('chatmsg', {from:"binb",to:socket.nickname,chatmsg:msg}); - return; - } - io.sockets.in(roomname).emit('chatmsg', {from:socket.nickname,chatmsg:data}); - } - else if (typeof data === "object" && typeof data.to === "string" && - userExists(data.to) && typeof data.chatmsg === "string") { - // Private message - socket.emit('chatmsg', {from:socket.nickname,to:data.to,chatmsg:data.chatmsg}); - var recipient = getUserSocket(data.to); - recipient.emit('chatmsg', {from:socket.nickname,to:data.to,chatmsg:data.chatmsg}); - } - }; - - var addPoints = function(socket, allinone) { - usersData[socket.nickname].guesstime = 30000 - songtimeleft; - var stats = {}; - switch (finishline) { - case 1: - finishline++; - usersData[socket.nickname].roundpoints = 6; - if (allinone) { - usersData[socket.nickname].points += 6; - stats.points = 6; - } - else { - usersData[socket.nickname].points += 5; - stats.points = 5; - } - usersData[socket.nickname].golds++; - stats.gold = true; - break; - case 2: - finishline++; - usersData[socket.nickname].roundpoints = 5; - if (allinone) { - usersData[socket.nickname].points += 5; - stats.points = 5; - } - else { - usersData[socket.nickname].points += 4; - stats.points = 4; - } - usersData[socket.nickname].silvers++; - stats.silver = true; - break; - case 3: - finishline++; - usersData[socket.nickname].roundpoints = 4; - if (allinone) { - usersData[socket.nickname].points += 4; - stats.points = 4; - } - else { - usersData[socket.nickname].points += 3; - stats.points = 3; - } - usersData[socket.nickname].bronzes++; - stats.bronze = true; - break; - default: - usersData[socket.nickname].roundpoints = 3; - if (allinone) { - usersData[socket.nickname].points += 3; - stats.points = 3; - } - else { - usersData[socket.nickname].points += 2; - stats.points = 2; - } - } - usersData[socket.nickname].matched = 'both'; - usersData[socket.nickname].guessed++; - if (usersData[socket.nickname].guesstime < usersData[socket.nickname].bestguesstime) { - usersData[socket.nickname].bestguesstime = usersData[socket.nickname].guesstime; - } - if (usersData[socket.nickname].registered) { - stats.userscore = usersData[socket.nickname].points; - stats.guesstime = usersData[socket.nickname].guesstime; - collectStats(socket.nickname, stats); - } - }; - - this.guess = function(socket, guess) { - if (allowedguess) { - if (!usersData[socket.nickname].matched) { // No track no artist - if ((artistlcase === tracklcase) && amatch(tracklcase, guess, true)) { - addPoints(socket, true); - socket.emit('bothmatched'); - io.sockets.in(roomname).emit('updateusers', {users:usersData}); - } - else if (amatch(artistlcase, guess, true)) { - usersData[socket.nickname].roundpoints++; - usersData[socket.nickname].points++; - usersData[socket.nickname].matched = 'artist'; - socket.emit('artistmatched'); - io.sockets.in(roomname).emit('updateusers', {users:usersData}); - if (usersData[socket.nickname].registered) { - var stats = {points:1,userscore:usersData[socket.nickname].points}; - collectStats(socket.nickname, stats); - } - } - else if (amatch(tracklcase, guess)) { - usersData[socket.nickname].roundpoints++; - usersData[socket.nickname].points++; - usersData[socket.nickname].matched = 'title'; - socket.emit('titlematched'); - io.sockets.in(roomname).emit('updateusers', {users:usersData}); - if (usersData[socket.nickname].registered) { - var stats = {points:1,userscore:usersData[socket.nickname].points}; - collectStats(socket.nickname, stats); - } - } - else { - socket.emit('nomatch'); - } - } - else if (usersData[socket.nickname].matched !== 'both') { // Track or artist - if (usersData[socket.nickname].matched === 'artist') { - if (amatch(tracklcase, guess)) { - addPoints(socket, false); - socket.emit('bothmatched'); - io.sockets.in(roomname).emit('updateusers', {users:usersData}); - } - else { - socket.emit('nomatch'); - } - } - else { - if (amatch(artistlcase, guess, true)) { - addPoints(socket, false); - socket.emit('bothmatched'); - io.sockets.in(roomname).emit('updateusers', {users:usersData}); - } - else { - socket.emit('nomatch'); - } - } - } - else { // The user has guessed both track and artist - socket.emit('stoptrying'); - } - } - else { - socket.emit('noguesstime'); - } - }; - - var resetPoints = function(roundonly) { - for (var key in usersData) { - if (!roundonly) { - usersData[key].points = 0; - usersData[key].guessed = 0; - usersData[key].bestguesstime = 30000; - usersData[key].golds = 0; - usersData[key].silvers = 0; - usersData[key].bronzes = 0; - } - usersData[key].roundpoints = 0; - usersData[key].matched = null; - usersData[key].guesstime = null; - } - }; - - var sendLoadTrack = function() { - songsdb.srandmember(roomname, function(err, res) { - songsdb.hmget(res, "artistName", "trackName", "collectionName", "previewUrl", - "artworkUrl60", "trackViewUrl", function(e, replies) { - if (playedtracks.indexOf(res) !== -1) { - return sendLoadTrack(); - } - playedtracks.push(res); - artistName = replies[0]; - artistlcase = artistName.toLowerCase(); - trackName = replies[1]; - tracklcase = trackName.toLowerCase(); - collectionName = replies[2]; - previewUrl = replies[3]; - artworkUrl = replies[4]; - trackViewUrl = replies[5]; - io.sockets.in(roomname).emit('loadtrack', {previewUrl:previewUrl}); - setTimeout(sendPlayTrack, 5000); - }); - }); - status = 1; // Loading next song - }; - - var sendPlayTrack = function() { - songcounter = songcounter + 1; - status = 0; // Playing track - io.sockets.in(roomname).emit('playtrack', {counter:songcounter,tot:config.songsinarun, - users:usersData}); - songTimeLeft(Date.now()+30000, 50); - allowedguess = true; - setTimeout(sendTrackInfo, 30000); - }; - - var songTimeLeft = function(end, delay) { - songtimeleft = end - Date.now(); - if (songtimeleft < delay) { - return; - } - setTimeout(songTimeLeft, delay, end, delay); - }; - - var sendTrackInfo = function() { - io.sockets.in(roomname).emit('trackinfo', {artworkUrl:artworkUrl,artistName:artistName, - trackName:trackName,collectionName:collectionName, - trackViewUrl:trackViewUrl}); - finishline = 1; - allowedguess = false; - if (songcounter < config.songsinarun) { - resetPoints(true); - sendLoadTrack(); - } - else { - status = 2; // Sending last track info - setTimeout(gameOver, 5000); - } - }; - - var gameOver = function() { - status = 3; // Game over - var users = []; - for (var key in usersData) { - users.push(usersData[key]); - } - users.sort(function(a, b) {return b.points - a.points;}); - var podium = users.slice(0,3); - io.sockets.in(roomname).emit('gameover', {users:podium}); - if (podium[0] && podium[0].registered) { - collectStats(podium[0].nickname, {firstplace:true}); - } - if (podium[1] && podium[1].registered) { - collectStats(podium[1].nickname, {secondplace:true}); - } - if (podium[2] && podium[2].registered) { - collectStats(podium[2].nickname, {thirdplace:true}); - } - resetPoints(false); - setTimeout(reset, 5000); - }; - - this.sendStatus = function(socket) { - socket.emit('status', {status:status,timeleft:songtimeleft,previewUrl:previewUrl}); - }; - - var reset = function() { - songcounter = 0; - if (playedtracks.length === config.fifolength) { - playedtracks.splice(0, config.songsinarun); - } - sendLoadTrack(); - }; - - // Start the room - this.start = function() { - songsdb.scard(roomname, function(err, res) { - trackscount = res; - }); - sendLoadTrack(); - }; -} +var Room = require('./lib/room.js')(roomoptions); -var Rooms = Object.create(null); for (var i=0; i