server.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  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. if (process.env.OPENSHIFT_NODEJS_IP) {
  69. io.configure(function(){
  70. io.set('transports', ['websocket']);
  71. });
  72. }
  73. io.sockets.on('connection', function (socket) {
  74. socket.on('start', function (data) {
  75. var token;
  76. var b = new Buffer(Math.random() + new Date().getTime() + socket.id);
  77. token = b.toString('base64').slice(12, 32);
  78. //token is valid for 5 minutes
  79. var timeout = setTimeout(function () {
  80. if (games[token].players.length === 0) {
  81. delete games[token];
  82. socket.emit('token-expired');
  83. }
  84. }, 5 * 60 * 1000);
  85. games[token] = {
  86. 'creator': socket,
  87. 'players': [],
  88. 'interval': null,
  89. 'timeout': timeout
  90. };
  91. socket.emit('created', {
  92. 'token': token
  93. });
  94. });
  95. socket.on('join', function (data) {
  96. var game, color, time = data.time;
  97. if (!(data.token in games)) {
  98. socket.emit('token-invalid');
  99. return;
  100. }
  101. clearTimeout(games[data.token].timeout);
  102. game = games[data.token];
  103. if (game.players.length >= 2) {
  104. socket.emit('full');
  105. return;
  106. } else if (game.players.length === 1) {
  107. if (game.players[0].color === 'black') {
  108. color = 'white';
  109. } else {
  110. color = 'black';
  111. }
  112. winston.log('info', 'Number of currently running games', { '#': Object.keys(games).length });
  113. } else {
  114. var colors = ['black', 'white'];
  115. color = colors[Math.floor(Math.random() * 2)];
  116. }
  117. //join room
  118. socket.join(data.token);
  119. games[data.token].players.push({
  120. 'id': socket.id,
  121. 'socket': socket,
  122. 'color': color,
  123. 'time': data.time - data.increment + 1,
  124. 'increment': data.increment
  125. });
  126. game.creator.emit('ready', {});
  127. socket.emit('joined', {
  128. 'color': color
  129. });
  130. });
  131. socket.on('timer-white', function (data) {
  132. runTimer('white', data.token, socket);
  133. });
  134. socket.on('timer-black', function (data) {
  135. runTimer('black', data.token, socket);
  136. });
  137. socket.on('timer-clear-interval', function (data) {
  138. if (data.token in games) {
  139. clearInterval(games[data.token].interval);
  140. }
  141. });
  142. socket.on('new-move', function (data) {
  143. var opponent;
  144. if (data.token in games) {
  145. opponent = getOpponent(data.token, socket);
  146. if (opponent) {
  147. opponent.socket.emit('move', {
  148. 'move': data.move
  149. });
  150. }
  151. }
  152. });
  153. socket.on('resign', function (data) {
  154. if (data.token in games) {
  155. clearInterval(games[data.token].interval);
  156. io.sockets.in(data.token).emit('player-resigned', {
  157. 'color': data.color
  158. });
  159. }
  160. });
  161. socket.on('rematch-offer', function (data) {
  162. var opponent;
  163. if (data.token in games) {
  164. opponent = getOpponent(data.token, socket);
  165. if (opponent) {
  166. opponent.socket.emit('rematch-offered');
  167. }
  168. }
  169. });
  170. socket.on('rematch-decline', function (data) {
  171. var opponent;
  172. if (data.token in games) {
  173. opponent = getOpponent(data.token, socket);
  174. if (opponent) {
  175. opponent.socket.emit('rematch-declined');
  176. }
  177. }
  178. });
  179. socket.on('rematch-confirm', function (data) {
  180. var opponent;
  181. if (data.token in games) {
  182. for(var j in games[data.token].players) {
  183. games[data.token].players[j].time = data.time - data.increment + 1;
  184. games[data.token].players[j].increment = data.increment;
  185. games[data.token].players[j].color = games[data.token].players[j].color === 'black' ? 'white' : 'black';
  186. }
  187. opponent = getOpponent(data.token, socket);
  188. if (opponent) {
  189. io.sockets.in(data.token).emit('rematch-confirmed');
  190. }
  191. }
  192. })
  193. socket.on('disconnect', function (data) {
  194. var player, opponent, game;
  195. for (var token in games) {
  196. game = games[token];
  197. for (var j in game.players) {
  198. player = game.players[j];
  199. if (player.socket === socket) {
  200. opponent = game.players[Math.abs(j - 1)];
  201. if (opponent) {
  202. opponent.socket.emit('opponent-disconnected');
  203. }
  204. clearInterval(games[token].interval);
  205. delete games[token];
  206. }
  207. }
  208. }
  209. });
  210. socket.on('send-message', function (data) {
  211. if (data.token in games) {
  212. var opponent = getOpponent(data.token, socket);
  213. if (opponent) {
  214. opponent.socket.emit('receive-message', data);
  215. }
  216. }
  217. });
  218. });
  219. function runTimer(color, token, socket) {
  220. var player, time_left, game = games[token];
  221. if (!game) return;
  222. for (var i in game.players) {
  223. player = game.players[i];
  224. if (player.socket === socket && player.color === color) {
  225. clearInterval(games[token].interval);
  226. games[token].players[i].time += games[token].players[i].increment;
  227. return games[token].interval = setInterval(function() {
  228. games[token].players[i].time -= 1;
  229. time_left = games[token].players[i].time;
  230. if (time_left >= 0) {
  231. io.sockets.in(token).emit('countdown', {
  232. 'time': time_left,
  233. 'color': color
  234. });
  235. } else {
  236. io.sockets.in(token).emit('countdown-gameover', {
  237. 'color': color
  238. });
  239. clearInterval(games[token].interval);
  240. }
  241. }, 1000);
  242. }
  243. }
  244. }
  245. function getOpponent(token, socket) {
  246. var player, game = games[token];
  247. for (var j in game.players) {
  248. player = game.players[j];
  249. if (player.socket === socket) {
  250. var opponent = game.players[Math.abs(j - 1)];
  251. return opponent;
  252. }
  253. }
  254. }