|
@@ -1,225 +1,197 @@
|
|
|
'use strict';
|
|
|
|
|
|
-
|
|
|
- * Socket.IO
|
|
|
- */
|
|
|
+const io = require('socket.io').listen();
|
|
|
+const winston = require('./winston');
|
|
|
+const Immutable = require('immutable');
|
|
|
+const {Map, List} = Immutable;
|
|
|
+var games = Map();
|
|
|
|
|
|
-var io = require('socket.io').listen();
|
|
|
-var winston = require('./winston');
|
|
|
-var games = {};
|
|
|
-var timer;
|
|
|
-
|
|
|
-io.sockets.on('connection', function (socket) {
|
|
|
+io.sockets.on('connection', socket => {
|
|
|
|
|
|
- socket.on('start', function (data) {
|
|
|
- var token;
|
|
|
- var b = new Buffer(Math.random() + new Date().getTime() + socket.id);
|
|
|
+ socket.on('start', data => {
|
|
|
+ let token;
|
|
|
+ const b = new Buffer(Math.random() + new Date().getTime() + socket.id);
|
|
|
token = b.toString('base64').slice(12, 32);
|
|
|
|
|
|
|
|
|
- var timeout = setTimeout(function () {
|
|
|
- if (games[token].players.length === 0) {
|
|
|
- delete games[token];
|
|
|
+ const timeout = setTimeout(() => {
|
|
|
+ if (games.getIn([token, 'players']).isEmpty()) {
|
|
|
+ games = games.delete(token);
|
|
|
socket.emit('token-expired');
|
|
|
}
|
|
|
}, 5 * 60 * 1000);
|
|
|
|
|
|
- games[token] = {
|
|
|
- 'creator': socket,
|
|
|
- 'players': [],
|
|
|
- 'interval': null,
|
|
|
- 'timeout': timeout
|
|
|
- };
|
|
|
+ games = games.set(token, Map({
|
|
|
+ creator: socket,
|
|
|
+ players: List(),
|
|
|
+ interval: null,
|
|
|
+ timeout: timeout
|
|
|
+ }));
|
|
|
|
|
|
- socket.emit('created', {
|
|
|
- 'token': token
|
|
|
- });
|
|
|
+ socket.emit('created', {token: token});
|
|
|
});
|
|
|
|
|
|
- socket.on('join', function (data) {
|
|
|
- var game, color, time = data.time;
|
|
|
+ socket.on('join', data => {
|
|
|
+ const game = games.get(data.token);
|
|
|
+ const colors = ['black', 'white'];
|
|
|
+ let color;
|
|
|
|
|
|
- if (!(data.token in games)) {
|
|
|
+ if (!game) {
|
|
|
socket.emit('token-invalid');
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- clearTimeout(games[data.token].timeout);
|
|
|
- game = games[data.token];
|
|
|
+ clearTimeout(game.get('timeout'));
|
|
|
|
|
|
- if (game.players.length >= 2) {
|
|
|
+ if (game.get('players').size >= 2) {
|
|
|
socket.emit('full');
|
|
|
return;
|
|
|
- } else if (game.players.length === 1) {
|
|
|
- if (game.players[0].color === 'black') {
|
|
|
+ } else if (game.get('players').size === 1) {
|
|
|
+ if (game.getIn(['players', 0, 'color']) === 'black')
|
|
|
color = 'white';
|
|
|
- } else {
|
|
|
+ else
|
|
|
color = 'black';
|
|
|
- }
|
|
|
- winston.log('info', 'Number of currently running games', { '#': Object.keys(games).length });
|
|
|
- } else {
|
|
|
- var colors = ['black', 'white'];
|
|
|
|
|
|
+ winston.log('info', 'Number of currently running games', {
|
|
|
+ '#': games.size
|
|
|
+ });
|
|
|
+ } else {
|
|
|
color = colors[Math.floor(Math.random() * 2)];
|
|
|
}
|
|
|
|
|
|
|
|
|
socket.join(data.token);
|
|
|
|
|
|
- games[data.token].players.push({
|
|
|
- 'id': socket.id,
|
|
|
- 'socket': socket,
|
|
|
- 'color': color,
|
|
|
- 'time': data.time - data.inc + 1,
|
|
|
- 'inc': data.inc
|
|
|
- });
|
|
|
-
|
|
|
- game.creator.emit('ready', {});
|
|
|
-
|
|
|
- socket.emit('joined', {
|
|
|
- 'color': color
|
|
|
- });
|
|
|
+ games = games.updateIn([data.token, 'players'], players =>
|
|
|
+ players.push(Map({
|
|
|
+ id: socket.id,
|
|
|
+ socket: socket,
|
|
|
+ color: color,
|
|
|
+ time: data.time - data.inc + 1,
|
|
|
+ inc: data.inc
|
|
|
+ })));
|
|
|
+
|
|
|
+ game.get('creator').emit('ready');
|
|
|
+ socket.emit('joined', {color: color});
|
|
|
});
|
|
|
|
|
|
- socket.on('clock-run', function (data) {
|
|
|
- runClock(data.color, data.token, socket);
|
|
|
- });
|
|
|
+ socket.on('clock-run', data => runClock(data.color, data.token, socket));
|
|
|
|
|
|
- socket.on('new-move', function (data) {
|
|
|
- var opponent;
|
|
|
-
|
|
|
- if (data.token in games) {
|
|
|
- opponent = getOpponent(data.token, socket);
|
|
|
- if (opponent) {
|
|
|
- opponent.socket.emit('move', data.move);
|
|
|
- }
|
|
|
- if (data.move.gameOver) {
|
|
|
- clearInterval(games[data.token].interval);
|
|
|
- }
|
|
|
+ socket.on('new-move', data => {
|
|
|
+ maybeEmit('move', data.move, data.token, socket);
|
|
|
+ if (data.move.gameOver) {
|
|
|
+ clearInterval(games.getIn([data.token, 'interval']));
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- socket.on('resign', function (data) {
|
|
|
- if (data.token in games) {
|
|
|
- clearInterval(games[data.token].interval);
|
|
|
- io.sockets.in(data.token).emit('player-resigned', {
|
|
|
- 'color': data.color
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
+ socket.on('resign', data => {
|
|
|
+ if (!games.has(data.token)) return;
|
|
|
+ clearInterval(games.getIn([data.token, 'interval']));
|
|
|
|
|
|
- socket.on('rematch-offer', function (data) {
|
|
|
- var opponent;
|
|
|
-
|
|
|
- if (data.token in games) {
|
|
|
- opponent = getOpponent(data.token, socket);
|
|
|
- if (opponent) {
|
|
|
- opponent.socket.emit('rematch-offered');
|
|
|
- }
|
|
|
- }
|
|
|
+ io.sockets.in(data.token).emit('player-resigned', {
|
|
|
+ color: data.color
|
|
|
+ });
|
|
|
});
|
|
|
|
|
|
- socket.on('rematch-decline', function (data) {
|
|
|
- var opponent;
|
|
|
+ socket.on('rematch-offer', data =>
|
|
|
+ maybeEmit('rematch-offered', {}, data.token, socket));
|
|
|
|
|
|
- if (data.token in games) {
|
|
|
- opponent = getOpponent(data.token, socket);
|
|
|
- if (opponent) {
|
|
|
- opponent.socket.emit('rematch-declined');
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
+ socket.on('rematch-decline', data =>
|
|
|
+ maybeEmit('rematch-declined', {}, data.token, socket));
|
|
|
|
|
|
- socket.on('rematch-confirm', function (data) {
|
|
|
- var opponent;
|
|
|
+ socket.on('rematch-confirm', data => {
|
|
|
+ if (!games.has(data.token)) return;
|
|
|
|
|
|
- if (data.token in games) {
|
|
|
+ games = games.updateIn([data.token, 'players'], players =>
|
|
|
+ players.map(player => player
|
|
|
+ .set('time', data.time - data.inc + 1)
|
|
|
+ .set('inc', data.inc)
|
|
|
+ .update('color', color => color === 'black' ? 'white' : 'black')));
|
|
|
|
|
|
- for(var j in games[data.token].players) {
|
|
|
- games[data.token].players[j].time = data.time - data.inc + 1;
|
|
|
- games[data.token].players[j].inc = data.inc;
|
|
|
- games[data.token].players[j].color = games[data.token].players[j].color === 'black' ? 'white' : 'black';
|
|
|
- }
|
|
|
-
|
|
|
- opponent = getOpponent(data.token, socket);
|
|
|
- if (opponent) {
|
|
|
- io.sockets.in(data.token).emit('rematch-confirmed');
|
|
|
- }
|
|
|
- }
|
|
|
+ io.sockets.in(data.token).emit('rematch-confirmed');
|
|
|
});
|
|
|
|
|
|
- socket.on('disconnect', function (data) {
|
|
|
- var player, opponent, game;
|
|
|
- for (var token in games) {
|
|
|
- game = games[token];
|
|
|
+ socket.on('disconnect', data => {
|
|
|
+ let tokenToDelete;
|
|
|
|
|
|
- for (var j in game.players) {
|
|
|
- player = game.players[j];
|
|
|
+ games.forEach((game, token) => {
|
|
|
+ const opponent = getOpponent(token, socket);
|
|
|
|
|
|
- if (player.socket === socket) {
|
|
|
- opponent = game.players[Math.abs(j - 1)];
|
|
|
- if (opponent) {
|
|
|
- opponent.socket.emit('opponent-disconnected');
|
|
|
- }
|
|
|
- clearInterval(games[token].interval);
|
|
|
- delete games[token];
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- socket.on('send-message', function (data) {
|
|
|
- if (data.token in games) {
|
|
|
- var opponent = getOpponent(data.token, socket);
|
|
|
if (opponent) {
|
|
|
- opponent.socket.emit('receive-message', data);
|
|
|
+ opponent.get('socket').emit('opponent-disconnected');
|
|
|
+ clearInterval(game.get('interval'));
|
|
|
+ tokenToDelete = token;
|
|
|
+
|
|
|
+ return false;
|
|
|
}
|
|
|
+ });
|
|
|
+
|
|
|
+ if (tokenToDelete) {
|
|
|
+ games = games.delete(tokenToDelete);
|
|
|
}
|
|
|
});
|
|
|
+
|
|
|
+ socket.on('send-message', data =>
|
|
|
+ maybeEmit('receive-message', data, data.token, socket));
|
|
|
});
|
|
|
|
|
|
-function runClock(color, token, socket) {
|
|
|
- var player, time_left, game = games[token];
|
|
|
+function maybeEmit(event, data, token, socket) {
|
|
|
+ if (!games.has(token)) return;
|
|
|
|
|
|
- if (!game) return;
|
|
|
+ const opponent = getOpponent(token, socket);
|
|
|
+ if (opponent) {
|
|
|
+ opponent.get('socket').emit(event, data);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
- for (var i in game.players) {
|
|
|
- player = game.players[i];
|
|
|
- if (player.socket === socket && player.color === color) {
|
|
|
- clearInterval(games[token].interval);
|
|
|
- games[token].players[i].time += games[token].players[i].inc;
|
|
|
+function runClock(color, token, socket) {
|
|
|
+ if (!games.has(token)) return;
|
|
|
+
|
|
|
+ games.getIn([token, 'players']).forEach((player, idx) => {
|
|
|
+ if (player.get('socket') === socket && player.get('color') === color) {
|
|
|
+ clearInterval(games.getIn([token, 'interval']));
|
|
|
+
|
|
|
+ games = games
|
|
|
+ .updateIn([token, 'players', idx, 'time'], time =>
|
|
|
+ time += player.get('inc'))
|
|
|
+ .setIn([token, 'interval'], setInterval(() => {
|
|
|
+ let timeLeft = 0;
|
|
|
+ games = games.updateIn([token, 'players', idx, 'time'], time => {
|
|
|
+ timeLeft = time - 1;
|
|
|
+ return time - 1;
|
|
|
+ });
|
|
|
|
|
|
- return games[token].interval = setInterval(function() {
|
|
|
- games[token].players[i].time -= 1;
|
|
|
- time_left = games[token].players[i].time;
|
|
|
+ if (timeLeft >= 0) {
|
|
|
+ io.sockets.in(token).emit('countdown', {
|
|
|
+ time: timeLeft,
|
|
|
+ color: color
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ io.sockets.in(token).emit('countdown-gameover', {
|
|
|
+ color: color
|
|
|
+ });
|
|
|
+ clearInterval(games.getIn([token, 'interval']));
|
|
|
+ }
|
|
|
+ }, 1000));
|
|
|
|
|
|
- if (time_left >= 0) {
|
|
|
- io.sockets.in(token).emit('countdown', {
|
|
|
- 'time': time_left,
|
|
|
- 'color': color
|
|
|
- });
|
|
|
- } else {
|
|
|
- io.sockets.in(token).emit('countdown-gameover', {
|
|
|
- 'color': color
|
|
|
- });
|
|
|
- clearInterval(games[token].interval);
|
|
|
- }
|
|
|
- }, 1000);
|
|
|
+ return false;
|
|
|
}
|
|
|
- }
|
|
|
+ });
|
|
|
}
|
|
|
|
|
|
function getOpponent(token, socket) {
|
|
|
- var player, game = games[token];
|
|
|
+ let index = null;
|
|
|
|
|
|
- for (var j in game.players) {
|
|
|
- player = game.players[j];
|
|
|
+ games.getIn([token, 'players']).forEach((player, idx) => {
|
|
|
+ if (player.get('socket') === socket) {
|
|
|
+ index = Math.abs(idx - 1);
|
|
|
|
|
|
- if (player.socket === socket) {
|
|
|
- var opponent = game.players[Math.abs(j - 1)];
|
|
|
-
|
|
|
- return opponent;
|
|
|
+ return false;
|
|
|
}
|
|
|
+ });
|
|
|
+
|
|
|
+ if (index !== null) {
|
|
|
+ return games.getIn([token, 'players', index]);
|
|
|
}
|
|
|
}
|
|
|
|