Browse Source

refactor server io.js with immutable data

romanmatiasko 9 years ago
parent
commit
5d4a3a0cbe
1 changed files with 129 additions and 157 deletions
  1. 129 157
      io.js

+ 129 - 157
io.js

@@ -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);
 
     // token is valid for 5 minutes
-    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)];
     }
 
     // join room
     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]);
   }
 }