Created
January 10, 2013 16:57
-
-
Save seanhess/4503752 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// creating global parameters and start | |
// listening to 'port', we are creating an express | |
// server and then we are binding it with socket.io | |
var express = require('express'), | |
app = express(), | |
util = require('util'), | |
server = require('http').createServer(app), | |
io = require('socket.io').listen(server), | |
port = 7890, | |
routes = require('./routes'), | |
testing = require('./testing'), | |
auth = require('./auth'), | |
Models = require('./models'), | |
Config = require('./config'), | |
Game = Models.Game, | |
Room = Models.Room, | |
Comment = Models.Comment, | |
// hash object to save clients data, | |
// { socketid: { clientid, nickname }, socketid: { ... } } | |
chatClients = new Object(), | |
Manager = new Object({Clients : {}, Games:{}, Rooms:{}}), | |
ROOMMAX = Config.MaxRoomUsers, | |
RECENTCHATS = Config.RecentChatCount; | |
server.listen(port); | |
/* COMMAND LINE INTERFACE */ | |
var arg1 = process.argv[2]; | |
console.log('CLI ARG = '+arg1); | |
if(arg1 === 'rebuild'){ | |
models.synchronize(function(success){ | |
if(success){ | |
testing.createFixtures(); | |
}else{ | |
console.log('database error not synched'); | |
} | |
}); | |
} | |
/** EXPRESS CONFIG **/ | |
app.use(express.bodyParser()); | |
app.use("/styles", express.static(__dirname + '/public/styles')); | |
app.use("/scripts", express.static(__dirname + '/public/scripts')); | |
app.use("/images", express.static(__dirname + '/public/images')); | |
/** ROUTING */ | |
// authentication endpoints | |
app.get('/auth/register', auth.getRegister); | |
app.post('/auth/register', auth.postRegister); | |
app.get('/auth/login', auth.getLogin); | |
app.post('/auth/login', auth.postLogin); | |
// web pages | |
app.get('/', routes.homepage); | |
// accessing models | |
app.get('/api/games', routes.getGames); | |
app.post('/api/games', routes.postGames); | |
app.get('/api/teams', routes.getTeams); | |
app.post('/api/teams', routes.postTeams); | |
//test suite | |
app.get('/testing/debug', testing.getDebug); | |
/* IO Bindings Events */ | |
io.set('log level', 2); | |
io.set('transports', [ 'websocket', 'xhr-polling' ]); | |
io.sockets.on('connection', function(socket){ | |
socket.on( 'connect', function(data) { connect (socket, data); }); | |
socket.on( 'chatmessage', function(data) { chatmessage (socket, data); }); | |
socket.on( 'subscribe', function(data) { subscribe (socket, data); }); | |
socket.on( 'unsubscribe', function(data) { unsubscribe (socket, data); }); | |
socket.on( 'disconnect', function(data) { disconnect (socket); }); | |
}); | |
// create a client for the socket | |
function connect(socket, data){ | |
// data = { gameId, isHome, apiKey} | |
auth.validateToken(data, function(userData){ | |
if(userData){ | |
console.log('******* NEW AUTHENTICATED USER ********'); | |
socket.emit('log',{msg : 'User Authenticated'}); | |
// remove apiKey | |
delete data.apiKey; | |
data.username = userData.username; | |
data.userId = userData.id; | |
// Global list of all Clients | |
Manager.Clients[socket.id] = data; | |
subscribeNewUser(socket,data, function(roomGuid){ | |
if(roomGuid){ | |
socket.emit('log',{msg :'Game & Room Prepared'}); | |
getRecentChats(roomGuid, RECENTCHATS, function(chats){ | |
if(chats){ | |
socket.emit('ready',{ roomGuid : roomGuid, username : data.username, | |
isHome : data.isHome, recentChats : chats }); | |
}else{ | |
socket.emit('ready',{ roomGuid : roomGuid, username : data.username, | |
isHome : data.isHome, recentChats : []}); | |
} | |
}); | |
}else{ | |
console.log('not able to subscribeNewUser()'); | |
} | |
}); | |
}else{ | |
socket.emit('error',{code:1, msg:"invalid token unable to connect"}); | |
console.log("invalid username + token"); | |
} | |
}); | |
} | |
function subscribeNewUser(socket, user, fn){ | |
joinGame(data.gameId, function(roomId) { | |
subscribe(socket, data, roomId); | |
}) | |
} | |
function joinGame(gameId, fn) { | |
// GAMES | |
findOrCreateGame(data.gameId, function(success){ | |
if(success){ | |
// ROOMS | |
findOrCreateRoom(data.gameId, function(roomGuid){ | |
if(!roomGuid) return fn(false) | |
// SUBSCRIBE | |
console.log('**** SUBSCRIBE '+data.username+' to '+roomGuid+'****'); | |
fn(roomGuid); | |
}); | |
}else{ | |
console.log('Error : Game not created or found'); | |
fn(false); | |
} | |
}); | |
} | |
//------------------------ GAMES -------------------------------- | |
// should return that a game was created and is ready for users | |
function findOrCreateGame(gameId,fn){ | |
console.log('** GAME **'); | |
// FIND game exists in the manager | |
gameExists(gameId,function(exists){ | |
if(exists){ | |
console.log('-- FOUND GAME'); | |
fn(true); | |
// CREATE game does not exist in Manager.Games create it insert | |
}else{ | |
console.log('-- CREATE NEW GAME'); | |
createGame(gameId, fn); | |
} | |
}); | |
} | |
function createGame(gameId, fn){ | |
// pull the game out of the database | |
Game.find(gameId) | |
.success(function(game){ | |
if (!game) return fn(null, false); | |
// if the database finds one and returns an object | |
// init rooms object inside of Manager.Games.rooms | |
game.rooms = {}; | |
Manager.Games[game.id] = game; | |
fn(null, true); | |
}) | |
.error(fn) | |
; | |
} | |
function gameExists(gameId,fn){ | |
var game = Manager.Games[gameId]; | |
fn(game) | |
//if(game){ fn(true); } | |
//else{ fn(false); } | |
} | |
function doX(cb) { | |
doSomething(function(err, data) { | |
if (err) return cb (err) | |
// more code | |
}) | |
} | |
function doSomething(cb) { | |
cb(new Error("something bad happened") | |
cb(null, "some data") | |
} | |
// ------------------------ ROOMS -------------------------------- | |
// make sure Room is prepared and return the roomGUID | |
function findOrCreateRoom(gameId,fn){ | |
console.log('** ROOM **'); | |
roomExists(gameId,function(exists){ | |
if(exists){ | |
console.log('-- ROOM EXISTS'); | |
roomAvailable(gameId,function(available){ | |
if(available){ | |
console.log('---- FOUND ROOM'); | |
// already checked that their is room so find the last and jump in | |
findLastRoomForGame(gameId,function(roomGuid){ | |
if(roomGuid){ | |
fn(roomGuid); | |
}else{ | |
console.log('Error : Last room not found'); | |
fn(false); | |
} | |
}); | |
}else{ | |
console.log('---- BRAND NEW ROOM CREATE'); | |
createRoom(gameId,function(roomGuid){ | |
if(roomGuid){ | |
fn(roomGuid); | |
}else{ | |
console.log('failed to create room'); | |
fn(false); | |
} | |
}); | |
} | |
}); | |
}else{ | |
console.log('-- NO ROOM EXISTS'); | |
createRoom(gameId,function(roomGuid){ | |
if(roomGuid){ | |
console.log('---- CREATE ROOM'); | |
fn(roomGuid); | |
}else{ | |
console.log('failed to create room'); | |
fn(false); | |
} | |
}); | |
} | |
}); | |
} | |
function roomExists(gameId,fn){ | |
var rooms = Manager.Games[gameId].rooms; | |
var roomCount = Object.keys(rooms).length; | |
if(roomCount === 0 || roomCount === undefined){ | |
fn(false); | |
}else{ | |
fn(true); | |
} | |
} | |
function roomAvailable(gameId,fn){ | |
findLastRoomForGame(gameId,function(roomGuid){ | |
if(roomGuid){ | |
var count = Manager.Games[gameId].rooms[roomGuid].userCount; | |
if(count < ROOMMAX){ | |
console.log(count +' of '+ROOMMAX+' users in room'); | |
fn(true); | |
}else{ | |
console.log('---- NEW ROOM NEEDED!!!!!!!!'); | |
fn(false); | |
} | |
}else{ | |
console.log('Error : room Availability not found'); | |
fn(false); | |
} | |
}); | |
} | |
function createRoom(gameId,fn){ | |
var newRoom = { | |
roomId : 0, | |
userCount: 0, | |
awayCount : 0, | |
homeCount : 0, | |
totalUsage: 0, | |
totalComments: 0, | |
users:[] | |
}; | |
var roomGuid = generateRoomGuid(); | |
Room.build({roomGuid : roomGuid, gameId : gameId}) | |
.save() | |
.success(function(room){ | |
if(room){ | |
newRoom.roomId = room.id; | |
Manager.Games[gameId].rooms[roomGuid] = newRoom; | |
// store gameId | |
newRoom.gameId = gameId; | |
Manager.Rooms[roomGuid] = newRoom | |
fn(roomGuid); | |
}else{ | |
fn(false); | |
} | |
}); | |
} | |
function lastRoomForGame(gameId,fn){ | |
var rooms = Manager.Games[gameId].rooms; | |
var keys = Object.keys(rooms); | |
var roomCount = keys.length; | |
var lastRoomKey = keys[roomCount-1]; | |
//fn(lastRoomKey); | |
return lastRoomKey | |
} | |
// when a client disconnect, unsubscribe him from | |
// the rooms he subscribed to | |
function disconnect(socket){ | |
console.log('disconnect()'); | |
// Identify the user | |
var user = Manager.Clients[socket.id]; | |
console.log(user); | |
var roomUsers = Manager.Games[user.gameId].rooms[user.roomGuid].users; | |
console.log('roomUsers'); | |
console.log(roomUsers); | |
for (var i = 0; i < roomUsers.length; i++) { | |
var roomUser = roomUsers[i]; | |
// TODO make this a stronger comparison | |
if(roomUser.username == user.username && roomUser.userId == user.userId){ | |
console.log(roomUser); | |
Manager.Games[user.gameId].rooms[user.roomGuid].users.splice(i,1); | |
unsubscribe(socket,user); | |
} | |
}; | |
// unsubscribe from the rooms | |
// for(var room in rooms){ | |
// if(room && rooms[room]){ | |
// unsubscribe(socket, { room: room.replace('/','') }); | |
// } | |
// } | |
// client was unsubscribed from the rooms, | |
// now we can delete him from the hash object | |
delete Manager.Clients[socket.id]; | |
} | |
// receive chat message from a client and | |
// send it to the relevant room | |
function chatmessage(socket, data){ | |
//data = { msg,'Hello',room: '0764ec245e794adbfa81', username: 'nwalter', isHome: false } | |
// find the game id from the Manager.Rooms Hashtable | |
var room = Manager.Rooms[data.room]; | |
var gameId = room.gameId; | |
// increment the totalComments in the game | |
Manager.Games[gameId].rooms[data.room].totalComments++; | |
// saves the message but doesn't block the thread | |
findIdByUsername(data.username ,function(userId){ | |
data.userId = userId; | |
saveChatMessage(data,gameId); | |
}); | |
socket.emit('log',{msg:data.username+': '+data.msg}); | |
console.log( 'chatMessage = '+data.username +': '+ data.msg +" in "+ data.room+" is home "+data.isHome); | |
socket.broadcast.to(data.room).emit('chatmessage', { username: data.username, msg: data.msg, isHome: data.isHome}); | |
} | |
function findIdByUsername(username,fn){ | |
User.find({where : {username : username}}) | |
.success(function(user){ | |
fn(user.id); | |
}) | |
.error(function(user){ | |
}); | |
} | |
function saveChatMessage(data, gameId){ | |
console.log('saveChatMessage()'); | |
console.log(data) | |
var room = Manager.Rooms[data.room]; | |
var roomId = room.roomId; | |
var comment = { | |
msg : data.msg, | |
userId : data.userId, | |
roomId : roomId, | |
username : data.username, | |
isHome : data.isHome | |
}; | |
Comment.build(comment) | |
.save() | |
.success(function(savedComment){ | |
}) | |
.error(function(error){ | |
console.log('comment was not saved'); | |
console.log(error); | |
} | |
); | |
} | |
// Room already prepared | |
// subscribe a client to a room | |
function subscribe(socket, data){ | |
// increment the home or awayCount in the room | |
if(data.isHome === true){ | |
Manager.Games[data.gameId].rooms[data.roomGuid].homeCount++; | |
}else{ | |
Manager.Games[data.gameId].rooms[data.roomGuid].awayCount++; | |
} | |
// incrementing the number number of userCount in the room | |
Manager.Games[data.gameId].rooms[data.roomGuid].userCount++; | |
Manager.Games[data.gameId].rooms[data.roomGuid].totalUsage++; | |
// adding a new user to the specific game | |
Manager.Games[data.gameId].rooms[data.roomGuid] | |
.users.push( | |
{ | |
userId : data.userId, | |
username : data.username, | |
isHome : data.isHome | |
} | |
); | |
console.log(Manager.Games[data.gameId].rooms[data.roomGuid]); | |
// PULL OUT GAMEId NOW? | |
socket.join(data.roomGuid); | |
socket.emit('log',{msg:'subscribe() - '+data.username+' to room '+data.roomGuid}); | |
// update all other clients about the online presence | |
updatePresence(data.roomGuid, data, socket, 'online'); | |
// send to the client a list of all subscribed clients in this room | |
// socket.emit('roomclients', { room: data.roomGuid, clients: getClientsInRoom(socket.id, data.room) }); | |
} | |
// unsubscribe a client from a room, this can be | |
// occured when a client disconnected from the server | |
// or he subscribed to another room | |
function unsubscribe(socket, data){ | |
console.log('unsubscribe()'); | |
console.log(data); | |
// update all other clients about the offline | |
// presence | |
var room = Manager.Rooms[data.roomGuid]; | |
var gameId = room.gameId; | |
if(data.isHome === true){ | |
Manager.Games[gameId].rooms[data.roomGuid].homeCount--; | |
}else{ | |
Manager.Games[gameId].rooms[data.roomGuid].awayCount--; | |
} | |
Manager.Games[gameId].rooms[data.roomGuid].userCount--; | |
updatePresence(data.roomGuid, data, socket, 'offline'); | |
// remove the client from socket.io room | |
socket.leave(data.roomGuid); | |
// if this client was the only one in that room | |
// we are updating all clients about that the | |
// room is destroyed | |
// if(!countClientsInRoom(data.room)){ | |
// // with 'io.sockets' we can contact all the | |
// // clients that connected to the server | |
// io.sockets.emit('removeroom', { room: data.room }); | |
// } | |
} | |
// 'io.sockets.manager.rooms' is an object that holds | |
// the active room names as a key, returning array of | |
// room names | |
function getRooms(){ | |
return Object.keys(Manager.rooms); | |
} | |
// get array of clients in a room | |
function getClientsInRoom(socketId, room){ | |
// get array of socket ids in this room | |
var socketIds = io.sockets.manager.rooms['/' + room]; | |
var clients = []; | |
if(socketIds && socketIds.length > 0){ | |
socketsCount = socketIds.length; | |
// push every client to the result array | |
for(var i = 0, len = socketIds.length; i < len; i++){ | |
// check if the socket is not the requesting | |
// socket | |
if(socketIds[i] != socketId){ | |
clients.push(chatClients[socketIds[i]]); | |
} | |
} | |
} | |
return clients; | |
} | |
// get the amount of clients in aroom | |
function countClientsInRoom(room){ | |
// 'io.sockets.manager.rooms' is an object that holds | |
// the active room names as a key and an array of | |
// all subscribed client socket ids | |
if(io.sockets.manager.rooms['/' + room]){ | |
return io.sockets.manager.rooms['/' + room].length; | |
} | |
return 0; | |
} | |
// updating all other clients when a client goes | |
// online or offline. | |
function updatePresence(room, data, socket, state){ | |
console.log('updatePresence() room = '+room+' state = '+state); | |
// socket.io may add a trailing '/' to the | |
// room name so we are clearing it | |
// room = room.replace('/',''); | |
// by using 'socket.broadcast' we can send/emit | |
// a message/event to all other clients except | |
// the sender himself | |
socket.broadcast.to(room).emit('presence', | |
{ | |
username : data.username, | |
client: Manager.Clients[socket.id], | |
state: state, | |
room: data.roomGuid | |
}); | |
} | |
function getRecentChats(roomGuid, numChats ,fn){ | |
console.log('getRecentChats()'); | |
room = Manager.Rooms[roomGuid]; | |
Comment.findAll({where: {roomId : room.roomId}, limit: numChats, order : 'id DESC'}) | |
.success(function(comments){ | |
if(comments){ | |
var CommentList = []; | |
for (var i = 0; i < comments.length; i++) { | |
var comment = comments[i] | |
CommentList.push(comment); | |
} | |
CommentList.reverse(); | |
fn(CommentList); | |
}else{ | |
fn(false); | |
} | |
}); | |
} | |
// unique id generator | |
function generateRoomGuid(){ | |
var S4 = function () { | |
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); | |
}; | |
return (S4() + S4() + S4() + S4() + S4()); | |
} | |
// show a message in console | |
console.log('Chat server is running and listening to port %d...', port); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment