server.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. var express = require('express')
  2. , path = require('path')
  3. , crypto = require('crypto')
  4. , http = require('http')
  5. , winston = require('winston')
  6. , fs = require('fs');
  7. var app = express();
  8. app.configure(function() {
  9. app.set('ipaddress', process.env.OPENSHIFT_NODEJS_IP || '127.0.0.1');
  10. app.set('port', process.env.OPENSHIFT_NODEJS_PORT || 3000);
  11. app.set('views', __dirname + '/views');
  12. app.set('view engine', 'jade');
  13. app.use(express.favicon());
  14. app.use(express.logger('dev'));
  15. app.use(express.bodyParser());
  16. app.use(express.methodOverride());
  17. app.use(express.cookieParser('45710b553b5b7293753d03bd3601f70a'));
  18. app.use(express.session());
  19. app.use(app.router);
  20. app.use(express.static(path.join(__dirname, 'public')));
  21. });
  22. app.configure('development', function() {
  23. app.use(express.errorHandler());
  24. });
  25. app.get('/', function(req, res) {
  26. res.render('index');
  27. });
  28. app.get('/about', function(req, res) {
  29. res.render('about');
  30. });
  31. app.get('/play/:token/:time/:increment', function(req, res) {
  32. res.render('play', {
  33. 'token': req.params.token,
  34. 'time': req.params.time,
  35. 'increment': req.params.increment
  36. });
  37. });
  38. app.get('/logs', function(req, res) {
  39. fs.readFile(__dirname + '/logs/games.log', function (err, data) {
  40. if (err) {
  41. res.redirect('/');
  42. }
  43. res.set('Content-Type', 'text/plain');
  44. res.send(data);
  45. });
  46. });
  47. var server = http.createServer(app).listen(app.get('port'), app.get('ipaddress'), function() {
  48. console.log("Express server listening on port " + app.get('port'));
  49. });
  50. var games = {};
  51. var timer;
  52. /**
  53. * Winston logger
  54. */
  55. winston.add(winston.transports.File, {
  56. filename: __dirname + '/logs/games.log',
  57. handleExceptions: true,
  58. exitOnError: false,
  59. json: false
  60. });
  61. winston.remove(winston.transports.Console);
  62. winston.handleExceptions(new winston.transports.Console());
  63. winston.exitOnError = false;
  64. /**
  65. * Sockets
  66. */
  67. var io = require('socket.io').listen(server, {log: false});
  68. io.configure(function(){
  69. io.set('transports', ['websocket']);
  70. });
  71. io.sockets.on('connection', function (socket) {
  72. socket.on('start', function (data) {
  73. var token;
  74. var b = new Buffer(Math.random() + new Date().getTime() + socket.id);
  75. token = b.toString('base64').slice(12, 32);
  76. //token is valid for 5 minutes
  77. var timeout = setTimeout(function () {
  78. if (games[token].players.length === 0) {
  79. delete games[token];
  80. socket.emit('token-expired');
  81. }
  82. }, 4000);
  83. games[token] = {
  84. 'creator': socket,
  85. 'players': [],
  86. 'interval': null,
  87. 'timeout': timeout
  88. };
  89. socket.emit('created', {
  90. 'token': token
  91. });
  92. });
  93. socket.on('join', function (data) {
  94. var game, color, time = data.time;
  95. if (!(data.token in games)) {
  96. socket.emit('token-invalid');
  97. return;
  98. }
  99. clearTimeout(games[data.token].timeout);
  100. game = games[data.token];
  101. if (game.players.length >= 2) {
  102. socket.emit('full');
  103. return;
  104. } else if (game.players.length === 1) {
  105. if (game.players[0].color === 'black') {
  106. color = 'white';
  107. } else {
  108. color = 'black';
  109. }
  110. winston.log('info', 'Number of currently running games', { '#': Object.keys(games).length });
  111. } else {
  112. var colors = ['black', 'white'];
  113. color = colors[Math.floor(Math.random() * 2)];
  114. }
  115. //join room
  116. socket.join(data.token);
  117. games[data.token].players.push({
  118. 'id': socket.id,
  119. 'socket': socket,
  120. 'color': color,
  121. 'time': data.time - data.increment + 1,
  122. 'increment': data.increment
  123. });
  124. game.creator.emit('ready', {});
  125. socket.emit('joined', {
  126. 'color': color
  127. });
  128. });
  129. socket.on('timer-white', function (data) {
  130. runTimer('white', data.token, socket);
  131. });
  132. socket.on('timer-black', function (data) {
  133. runTimer('black', data.token, socket);
  134. });
  135. socket.on('timer-clear-interval', function (data) {
  136. if (data.token in games) {
  137. clearInterval(games[data.token].interval);
  138. }
  139. });
  140. socket.on('new-move', function (data) {
  141. var opponent;
  142. if (data.token in games) {
  143. opponent = getOpponent(data.token, socket);
  144. if (opponent) {
  145. opponent.socket.emit('move', {
  146. 'move': data.move
  147. });
  148. }
  149. }
  150. });
  151. socket.on('resign', function (data) {
  152. if (data.token in games) {
  153. clearInterval(games[data.token].interval);
  154. io.sockets.in(data.token).emit('player-resigned', {
  155. 'color': data.color
  156. });
  157. }
  158. });
  159. socket.on('rematch-offer', function (data) {
  160. var opponent;
  161. if (data.token in games) {
  162. opponent = getOpponent(data.token, socket);
  163. if (opponent) {
  164. opponent.socket.emit('rematch-offered');
  165. }
  166. }
  167. });
  168. socket.on('rematch-decline', function (data) {
  169. var opponent;
  170. if (data.token in games) {
  171. opponent = getOpponent(data.token, socket);
  172. if (opponent) {
  173. opponent.socket.emit('rematch-declined');
  174. }
  175. }
  176. });
  177. socket.on('rematch-confirm', function (data) {
  178. var opponent;
  179. if (data.token in games) {
  180. for(var j in games[data.token].players) {
  181. games[data.token].players[j].time = data.time - data.increment + 1;
  182. games[data.token].players[j].increment = data.increment;
  183. games[data.token].players[j].color = games[data.token].players[j].color === 'black' ? 'white' : 'black';
  184. }
  185. opponent = getOpponent(data.token, socket);
  186. if (opponent) {
  187. io.sockets.in(data.token).emit('rematch-confirmed');
  188. }
  189. }
  190. })
  191. socket.on('disconnect', function (data) {
  192. var player, opponent, game;
  193. for (var token in games) {
  194. game = games[token];
  195. for (var j in game.players) {
  196. player = game.players[j];
  197. if (player.socket === socket) {
  198. opponent = game.players[Math.abs(j - 1)];
  199. if (opponent) {
  200. opponent.socket.emit('opponent-disconnected');
  201. }
  202. clearInterval(games[token].interval);
  203. delete games[token];
  204. }
  205. }
  206. }
  207. });
  208. socket.on('send-message', function (data) {
  209. if (data.token in games) {
  210. var opponent = getOpponent(data.token, socket);
  211. if (opponent) {
  212. opponent.socket.emit('receive-message', data);
  213. }
  214. }
  215. });
  216. });
  217. function runTimer(color, token, socket) {
  218. var player, time_left, game = games[token];
  219. if (!game) return;
  220. for (var i in game.players) {
  221. player = game.players[i];
  222. if (player.socket === socket && player.color === color) {
  223. clearInterval(games[token].interval);
  224. games[token].players[i].time += games[token].players[i].increment;
  225. return games[token].interval = setInterval(function() {
  226. games[token].players[i].time -= 1;
  227. time_left = games[token].players[i].time;
  228. if (time_left >= 0) {
  229. io.sockets.in(token).emit('countdown', {
  230. 'time': time_left,
  231. 'color': color
  232. });
  233. } else {
  234. io.sockets.in(token).emit('countdown-gameover', {
  235. 'color': color
  236. });
  237. clearInterval(games[token].interval);
  238. }
  239. }, 1000);
  240. }
  241. }
  242. }
  243. function getOpponent(token, socket) {
  244. var player, game = games[token];
  245. for (var j in game.players) {
  246. player = game.players[j];
  247. if (player.socket === socket) {
  248. var opponent = game.players[Math.abs(j - 1)];
  249. return opponent;
  250. }
  251. }
  252. }