Advertisement
Guest User

Pre-Compiled

a guest
Jan 14th, 2013
338
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  2.  
  3.  
  4. // INLINED: server.js
  5. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  6.  
  7.  
  8. // CORE MODULE: http
  9. // CORE MODULE: socket.io
  10. // INLINED: config.js
  11. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  12. /*globals XSS*/
  13.  
  14.  
  15. var config =  {
  16.     SERVER_PORT        : 80,
  17.     SERVER_ENDPOINT    : 'http://localhost:80',
  18.     SOCKET_IO_JS       : 'http://localhost:80/socket.io/socket.io.js',
  19.  
  20.     GAME_TICK          : 50,
  21.  
  22.     ROOM_CAPACITY      : 4,
  23.  
  24.     TIME_GLOAT         : 5,
  25.     TIME_COUNTDOWN_FROM: 3,
  26.     TIME_RESPAWN_APPLE : 30,
  27.     TIME_SPAWN_POWERUP : [5, 30],
  28.  
  29.     SNAKE_SPEED        : 150,
  30.     SNAKE_SIZE         : 4
  31. };
  32.  
  33. if (typeof XSS !== 'undefined') {
  34.    
  35. }
  36.  
  37. // INLINED: event_handler.js
  38. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  39.  
  40.  
  41. // INLINED: events.js
  42. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  43. /*globals XSS*/
  44.  
  45.  
  46. // Event keys are hard-coded and unique.
  47. var events =  {
  48.     CLIENT_CONNECT       : 'CON',
  49.     CLIENT_PING          : 'PING',
  50.  
  51.     CLIENT_APPLE_HIT     : 'CA1',
  52.     CLIENT_APPLE_SPAWN   : 'CA2',
  53.  
  54.     CLIENT_CHAT_NOTICE   : 'CC1',
  55.     CLIENT_CHAT_MESSAGE  : 'CC2',
  56.  
  57.     CLIENT_GAME_COUNTDOWN: 'CG1',
  58.     CLIENT_GAME_START    : 'CG2',
  59.     CLIENT_GAME_SNAKES   : 'CG3',
  60.     CLIENT_GAME_SPAWNS   : 'CG4',
  61.  
  62.     CLIENT_POWERUP_HIT   : 'CP1',
  63.     CLIENT_POWERUP_SPAWN : 'CP2',
  64.  
  65.     CLIENT_ROOM_JOIN     : 'CR1',
  66.     CLIENT_ROOM_INDEX    : 'CR2',
  67.     CLIENT_ROOM_SCORE    : 'CR3',
  68.  
  69.     CLIENT_SNAKE_ACTION  : 'CS1',
  70.     CLIENT_SNAKE_CRASH   : 'CS2',
  71.     CLIENT_SNAKE_SPEED   : 'CS3',
  72.     CLIENT_SNAKE_UPDATE  : 'CS4',
  73.  
  74.     SERVER_PONG          : 'SP1',
  75.     SERVER_ROOM_MATCH    : 'SR1',
  76.     SERVER_CHAT_MESSAGE  : 'SC1',
  77.     SERVER_SNAKE_UPDATE  : 'SS1',
  78.     SERVER_GAME_STATE    : 'SG1'
  79. };
  80.  
  81. if (typeof XSS !== 'undefined') {
  82.    
  83. }
  84.  
  85.  
  86. /**
  87.  * @param {Object} server
  88.  * @param {Client} client
  89.  * @param {Object} socket
  90.  * @constructor
  91.  */
  92. function EventHandler(server, client, socket) {
  93.     this.server = server;
  94.     this.client = client;
  95.     this.socket = socket;
  96.  
  97.     this._pingInterval = setInterval(this._ping.bind(this), 5000);
  98.  
  99.     client.emit(events.CLIENT_CONNECT, client.id);
  100.  
  101.     socket.on('disconnect', this._disconnect.bind(this));
  102.     socket.on(events.SERVER_ROOM_MATCH, this._matchRoom.bind(this));
  103.     socket.on(events.SERVER_CHAT_MESSAGE, this._chat.bind(this));
  104.     socket.on(events.SERVER_SNAKE_UPDATE, this._snakeUpdate.bind(this));
  105.     socket.on(events.SERVER_GAME_STATE, this._gameState.bind(this));
  106.     socket.on(events.SERVER_PONG, this._pong.bind(this));
  107. }
  108.  
  109. // module.exports = EventHandler;
  110.  
  111. EventHandler.prototype = {
  112.  
  113.     destruct: function() {
  114.         // Other event listeners will remove themselves.
  115.         clearInterval(this._pingInterval);
  116.         this.server = null;
  117.         this.client = null;
  118.         this.socket = null;
  119.     },
  120.  
  121.     /**
  122.      * @private
  123.      */
  124.     _ping: function() {
  125.         this.client.emit(events.CLIENT_PING, +new Date());
  126.     },
  127.  
  128.     /**
  129.      * @param {number} sendTime
  130.      * @private
  131.      */
  132.     _pong: function(sendTime) {
  133.         var roundtrip = (+new Date()) - sendTime;
  134.         this.client.latency = Math.round(roundtrip / 2);
  135.     },
  136.  
  137.     /**
  138.      * @private
  139.      */
  140.     _disconnect: function() {
  141.         var room, client = this.client;
  142.         room = this.server.roomManager.rooms[client.roomid];
  143.         if (room) {
  144.             // If client is in a room, we cannot clean up immediately
  145.             // because we need data to remove the client from the room
  146.             // gracefully.
  147.             room.disconnect(client);
  148.         } else {
  149.             this.server.removeClient(client);
  150.         }
  151.     },
  152.  
  153.     /**
  154.      * @param {Object} data Object with keys name, pub, friendly
  155.      * @private
  156.      */
  157.     _matchRoom: function(data) {
  158.         var room, client = this.client, server = this.server;
  159.         client.name = data.name;
  160.         room = server.roomManager.getPreferredRoom(data);
  161.         room.join(client);
  162.     },
  163.  
  164.     /**
  165.      * @param {string} message
  166.      * @private
  167.      */
  168.     _chat: function(message) {
  169.         var room, data, index;
  170.         room = this._clientRoom(this.client);
  171.         if (room) {
  172.             index = room.clients.indexOf(this.client);
  173.             data = [index, message.substr(0, 30)];
  174.             room.broadcast(events.CLIENT_CHAT_MESSAGE, data, this.client);
  175.             room.emit(events.CLIENT_SNAKE_ACTION, [index, 'Blah']);
  176.         }
  177.     },
  178.  
  179.     /**
  180.      * @param data [<Array>,<number>] 0: parts, 1: direction
  181.      */
  182.     _snakeUpdate: function(data) {
  183.         var game = this._clientGame(this.client);
  184.         if (game && game.room.inProgress) {
  185.             game.updateSnake(this.client, data[0], data[1]);
  186.         }
  187.     },
  188.  
  189.     /**
  190.      * @private
  191.      */
  192.     _gameState: function() {
  193.         var game = this._clientGame(this.client);
  194.         if (game && game.room.inProgress) {
  195.             game.emitState(this.client);
  196.         }
  197.     },
  198.  
  199.     /**
  200.      * @param {Client} client
  201.      * @return {Room}
  202.      * @private
  203.      */
  204.     _clientRoom: function(client) {
  205.         return this.server.roomManager.room(client.roomid);
  206.     },
  207.  
  208.     /**
  209.      * @param {Client} client
  210.      * @return {Game}
  211.      * @private
  212.      */
  213.     _clientGame: function(client) {
  214.         return (client.roomid) ? this._clientRoom(client).game : null;
  215.     }
  216.  
  217. };
  218.  
  219. // INLINED: room_manager.js
  220. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  221.  
  222.  
  223. // INLINED: room.js
  224. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  225.  
  226.  
  227. // INLINED: game.js
  228. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  229.  
  230.  
  231. // ALREADY INLINED: util
  232. // INLINED: spawner.js
  233. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  234.  
  235.  
  236. // ALREADY INLINED: util.js
  237. // ALREADY INLINED: events.js
  238.  
  239. /**
  240.  * Spawnable
  241.  * @param {Game} game
  242.  * @constructor
  243.  */
  244. function Spawner(game) {
  245.     this.game = game;
  246.     this.spawns = [];
  247.     this.locations = []; // Keep separate list for speed
  248. }
  249.  
  250. // module.exports = Spawner;
  251.  
  252. Spawner.prototype = {
  253.  
  254.     APPLE  : 0,
  255.     POWERUP: 1,
  256.  
  257.     EVENTS: [
  258.         events.CLIENT_APPLE_SPAWN,
  259.         events.CLIENT_POWERUP_SPAWN
  260.     ],
  261.  
  262.     destruct: function() {
  263.         for (var i = 0, m = this.spawns.length; i < m; i++) {
  264.             this._destructSpawn(i);
  265.         }
  266.     },
  267.  
  268.     /**
  269.      * @param {number} type
  270.      * @param {number|null=} index
  271.      * @param {boolean=} respawn
  272.      * @param {number=} respawnAfter
  273.      * @return {Object}
  274.      */
  275.     spawn: function(type, index, respawn, respawnAfter) {
  276.         var spawn = {
  277.             location    : this.game.getEmptyLocation(),
  278.             type        : type,
  279.             respawn     : !!respawn,
  280.             respawnAfter: respawnAfter
  281.         };
  282.  
  283.         index = (typeof index === 'number') ? index : this.spawns.length;
  284.  
  285.         if (respawnAfter) {
  286.             spawn.timer = setTimeout(function() {
  287.                 this._destructSpawn(index);
  288.                 this.spawn(type, index, respawn, respawnAfter);
  289.             }.bind(this), respawnAfter);
  290.         }
  291.  
  292.         this.spawns[index] = spawn;
  293.         this.locations[index] = spawn.location;
  294.  
  295.         this.game.room.emit(this.EVENTS[type], [index, spawn.location]);
  296.  
  297.         return spawn;
  298.     },
  299.  
  300.     /**
  301.      * @param {Client} client
  302.      * @param {number} index
  303.      */
  304.     hit: function(client, index) {
  305.         var spawn = this.spawns[index];
  306.  
  307.         switch (spawn.type) {
  308.             case this.APPLE:
  309.                 this.game.hitApple(client, index);
  310.                 break;
  311.             case this.POWERUP:
  312.                 this.game.hitPowerup(client, index);
  313.                 break;
  314.         }
  315.  
  316.         if (spawn.respawn) {
  317.             this._destructSpawn(index);
  318.             this.spawn(spawn.type, index, true, spawn.respawnAfter);
  319.         } else {
  320.             this._destructSpawn(index);
  321.         }
  322.     },
  323.  
  324.     /**
  325.      * @param {Client} client
  326.      * @param {Array.<number>} location
  327.      * @return {Array}
  328.      */
  329.     handleHits: function(client, location) {
  330.         var hits = [];
  331.         for (var i = 0, m = this.spawns.length; i < m; i++) {
  332.             if (null !== this.spawns[i] && Util.eq(this.spawns[i].location, location)) {
  333.                 hits.push(i);
  334.                 this.hit(client, i);
  335.             }
  336.         }
  337.         return hits;
  338.     },
  339.  
  340.     /**
  341.      * @param {number} index
  342.      * @private
  343.      */
  344.     _destructSpawn: function(index) {
  345.         var spawn = this.spawns[index];
  346.         if (spawn && spawn.timer) {
  347.             clearTimeout(spawn.timer);
  348.         }
  349.         this.spawns[index] = null;
  350.         this.locations[index] = null;
  351.     }
  352.  
  353. };
  354.  
  355. // INLINED: levels.js
  356. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  357. /*globals XSS*/
  358.  
  359.  
  360. // Generated on Wed, 10 Oct 2012 17:11:27 GMT
  361. // Generate file: `node build/levels.js`
  362. // Template file: source/templates/levels.js.tpl
  363.  
  364. var levels =  [
  365.     {width: 63, height: 33, spawns: [192, 1886, 248, 1830], directions: [2, 0, 0, 2], walls: []},
  366.     {width: 63, height: 33, spawns: [192, 1886, 248, 1830], directions: [2, 0, 0, 2], walls: [94, 157, 220, 283, 346, 409, 472, 535, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1480, 1543, 1606, 1669, 1732, 1795, 1858, 1921, 1984]},
  367.     {width: 63, height: 33, spawns: [192, 1886, 248, 1830], directions: [2, 0, 0, 2], walls: [7, 31, 55, 70, 94, 118, 133, 157, 181, 220, 259, 283, 307, 322, 346, 370, 385, 409, 433, 441, 442, 443, 444, 445, 446, 447, 448, 472, 496, 497, 498, 499, 500, 501, 502, 503, 525, 526, 527, 528, 529, 530, 531, 532, 533, 534, 535, 536, 537, 538, 539, 540, 541, 542, 543, 544, 545, 777, 778, 779, 780, 781, 782, 783, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 794, 795, 796, 797, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296, 1297, 1298, 1299, 1300, 1301, 1533, 1534, 1535, 1536, 1537, 1538, 1539, 1540, 1541, 1542, 1543, 1544, 1545, 1546, 1547, 1548, 1549, 1550, 1551, 1552, 1553, 1575, 1576, 1577, 1578, 1579, 1580, 1581, 1582, 1606, 1630, 1631, 1632, 1633, 1634, 1635, 1636, 1637, 1645, 1669, 1693, 1708, 1732, 1756, 1771, 1795, 1819, 1858, 1897, 1921, 1945, 1960, 1984, 2008, 2023, 2047, 2071]}
  368. ];
  369.  
  370. if (typeof XSS !== 'undefined') {
  371.    
  372. }
  373.  
  374. // ALREADY INLINED: config.js
  375. // ALREADY INLINED: events.js
  376. // INLINED: util.js
  377. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  378.  
  379.  
  380. var Util = {
  381.  
  382.     /**
  383.      * @param {*} destination
  384.      * @param {*} source
  385.      * @return {*}
  386.      */
  387.     extend: function(destination, source) {
  388.         for (var property in source) {
  389.             if (source.hasOwnProperty(property)) {
  390.                 destination[property] = source[property];
  391.             }
  392.         }
  393.         return destination;
  394.     },
  395.  
  396.     /**
  397.      * @param {number} min
  398.      * @param {number} max
  399.      * @return {number}
  400.      */
  401.     randomBetween: function(min, max) {
  402.         return min + Math.floor(Math.random() * (max - min + 1));
  403.     },
  404.  
  405.     /**
  406.      * @param {Array} arr
  407.      * @return {*}
  408.      */
  409.     randomItem: function(arr) {
  410.         return arr[Util.randomBetween(0, arr.length - 1)];
  411.     },
  412.  
  413.     /**
  414.      * @return {string}
  415.      */
  416.     randomStr: function() {
  417.         return Math.random().toString(36).substring(2, 5);
  418.     },
  419.  
  420.     /**
  421.      * @param {Array.<number>} a
  422.      * @param {Array.<number>} b
  423.      * @return {number}
  424.      */
  425.     delta: function(a, b) {
  426.         return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
  427.     },
  428.  
  429.     /**
  430.      * @param {Array.<number>} a
  431.      * @param {Array.<number>} b
  432.      * @return {boolean}
  433.      */
  434.     eq: function(a, b) {
  435.         return a[0] === b[0] && a[1] === b[1];
  436.     },
  437.  
  438.     /**
  439.      * @param {*} obj
  440.      * @param {*} val
  441.      * @return {?string}
  442.      */
  443.     getKey: function(obj, val) {
  444.         for (var k in obj) {
  445.             if (obj.hasOwnProperty(k) && val === obj[k]) {
  446.                 return k;
  447.             }
  448.         }
  449.         return null;
  450.     }
  451.  
  452. };
  453.  
  454. // module.exports = Util;
  455.  
  456. // INLINED: level.js
  457. /*jshint globalstrict:true, es5:true, node:true */
  458.  
  459.  
  460. /**
  461.  * Collisions and levels
  462.  * @constructor
  463.  * @param {number} levelID
  464.  * @param {Object} levelData
  465.  */
  466. function Level(levelID, levelData) {
  467.     this.level = levelData[levelID];
  468. }
  469.  
  470. // module.exports = Level;
  471.  
  472. Level.prototype = {
  473.  
  474.     /**
  475.      * @param {number} x
  476.      * @param {number} y
  477.      * @return {boolean}
  478.      */
  479.     isWall: function(x, y) {
  480.         if (this.outOfBounds(x, y)) {
  481.             return true;
  482.         } else if (this.innerWall(x, y)) {
  483.             return true;
  484.         }
  485.         return false;
  486.     },
  487.  
  488.     /**
  489.      * @param {number} playerID
  490.      * @return {Array.<number>}
  491.      */
  492.     getSpawn: function(playerID) {
  493.         var pos = this.level.spawns[playerID];
  494.         return this.seqToXY(pos);
  495.     },
  496.  
  497.     /**
  498.      * @param {number} playerID
  499.      * @return {number}
  500.      */
  501.     getSpawnDirection: function(playerID) {
  502.         return this.level.directions[playerID];
  503.     },
  504.  
  505.     /**
  506.      * @param {Array.<Array>} locations
  507.      * @return {Array.<number>}
  508.      */
  509.     getEmptyLocation: function(locations) {
  510.         var m = this.level.width * this.level.height;
  511.         while (true) {
  512.             var location = this.seqToXY(Math.floor(Math.random() * m));
  513.             if (this.isEmpty(locations, location)) {
  514.                 return location;
  515.             }
  516.         }
  517.     },
  518.  
  519.     /**
  520.      * @param {Array.<Array>} locations
  521.      * @param {Array.<number>} location
  522.      * @return {boolean}
  523.      */
  524.     isEmpty: function(locations, location) {
  525.         if (this.isWall(location[0], location[1])) {
  526.             return false;
  527.         }
  528.         for (var i = 0, m = locations.length; i < m; i++) {
  529.             var iloc = locations[i];
  530.             if (iloc && iloc[0] === location[0] && iloc[1] === location[1]) {
  531.                 return false;
  532.             }
  533.         }
  534.         return true;
  535.     },
  536.  
  537.     /**
  538.      * @param {number} x
  539.      * @param {number} y
  540.      * @return {boolean}
  541.      */
  542.     outOfBounds: function(x, y) {
  543.         if (x < 0 || y < 0) {
  544.             return true;
  545.         } else {
  546.             return x >= this.level.width || y >= this.level.height;
  547.         }
  548.     },
  549.  
  550.     /**
  551.      * @param {number} x
  552.      * @param {number} y
  553.      * @return {boolean}
  554.      */
  555.     innerWall: function(x, y) {
  556.         var seq = this.xyToSeq(x, y);
  557.         return this.innerWallSeq(seq);
  558.     },
  559.  
  560.     /**
  561.      * @param {number} seq
  562.      * @return {boolean}
  563.      */
  564.     innerWallSeq: function(seq) {
  565.         var wall = this.level.walls;
  566.         for (var i = 0, m = wall.length; i < m; i++) {
  567.             if (seq === wall[i]) {
  568.                 return true;
  569.             }
  570.         }
  571.         return false;
  572.     },
  573.  
  574.     /**
  575.      * @param {number} x
  576.      * @param {number} y
  577.      * @return {number}
  578.      */
  579.     xyToSeq: function(x, y) {
  580.         return x + this.level.width * y;
  581.     },
  582.  
  583.     /**
  584.      * @param {number} seq
  585.      * @return {Array.<number>}
  586.      */
  587.     seqToXY: function(seq) {
  588.         return [
  589.             seq % this.level.width,
  590.             Math.floor(seq / this.level.width)
  591.         ];
  592.     }
  593.  
  594. };
  595.  
  596. // INLINED: snake.js
  597. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  598.  
  599.  
  600. /**
  601.  * Snake
  602.  * @param {Array.<number>} location
  603.  * @param {number} direction
  604.  * @param {number} size
  605.  * @param {number} speed
  606.  * @constructor
  607.  */
  608. function Snake(location, direction, size, speed) {
  609.     this.parts = [location];
  610.     this.direction = direction;
  611.     this.size = size;
  612.     this.speed = speed;
  613.     this.crashed = false;
  614. }
  615.  
  616. // module.exports = Snake;
  617.  
  618. Snake.prototype = {
  619.  
  620.     /**
  621.      * @param {Array.<number>} position
  622.      */
  623.     move: function(position) {
  624.         this.parts.push(position);
  625.         this.trim();
  626.     },
  627.  
  628.     /**
  629.      * @return {Array.<number>}
  630.      */
  631.     head: function() {
  632.         return this.parts[this.parts.length - 1];
  633.     },
  634.  
  635.     /**
  636.      * @param {Array.<number>} part
  637.      * @return {boolean}
  638.      */
  639.     hasPartPredict: function(part) {
  640.         var treshold = this.crashed ? -1 : 0;
  641.         return (this.partIndex(part) > treshold);
  642.     },
  643.  
  644.     /**
  645.      * @return {Array.<number>}
  646.      */
  647.     getNextPosition: function() {
  648.         var shift, head = this.head();
  649.         shift = this.directionToShift(this.direction);
  650.         return [head[0] + shift[0], head[1] + shift[1], 'x'];
  651.     },
  652.  
  653.     trim: function() {
  654.         while (this.parts.length > this.size) {
  655.             this.parts.shift();
  656.         }
  657.     },
  658.  
  659.     /**
  660.      * @param {Array.<number>} part
  661.      * @return {boolean}
  662.      */
  663.     hasPart: function(part) {
  664.         return (-1 !== this.partIndex(part));
  665.     },
  666.  
  667.     /**
  668.      * @param {Array.<number>} part
  669.      * @return {number}
  670.      */
  671.     partIndex: function(part) {
  672.         var parts = this.parts;
  673.         for (var i = 0, m = parts.length; i < m; i++) {
  674.             if (parts[i][0] === part[0] && parts[i][1] === part[1]) {
  675.                 return i;
  676.             }
  677.         }
  678.         return -1;
  679.     },
  680.  
  681.     /**
  682.      * @param {number} direction
  683.      * @return {Array.<number>}
  684.      */
  685.     directionToShift: function(direction) {
  686.         return [[-1, 0], [0, -1], [1, 0], [0, 1]][direction];
  687.     }
  688.  
  689. };
  690.  
  691. // INLINED: powerup.js
  692. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  693.  
  694.  
  695. // ALREADY INLINED: events.js
  696. // ALREADY INLINED: util.js
  697.  
  698. /**
  699.  * Powerup
  700.  * @param {Client} client
  701.  * @param {Game} game
  702.  * @constructor
  703.  */
  704. function Powerup(game, client) {
  705.     var powerup = this._getPowerUp().bind(this);
  706.     powerup(client, game);
  707. }
  708.  
  709. // module.exports = Powerup;
  710.  
  711. /** @const */ Powerup.APPLY_SELF = 0;
  712. /** @const */ Powerup.APPLY_OTHERS = 1;
  713. /** @const */ Powerup.APPLY_EITHER = 2;
  714. /** @const */ Powerup.APPLY_ALL = 3;
  715.  
  716. Powerup.prototype = {
  717.  
  718.     /**
  719.      * @return {Array}
  720.      * @private
  721.      */
  722.     _getPowerups: function() {
  723.         return [
  724.             // [Weight, Powerup]
  725.             [1, this._speed],
  726.             [1, this._apples],
  727.             [1, this._powerups],
  728.             [1, this._reverse]
  729.         ];
  730.     },
  731.  
  732.     /**
  733.      * @return {Function}
  734.      * @private
  735.      */
  736.     _getPowerUp: function() {
  737.         var i, m, random, powerups, cumulative = 0;
  738.  
  739.         powerups = this._getPowerups();
  740.         for (i = 0, m = powerups.length; i < m; i++) {
  741.             cumulative += powerups[i][0];
  742.             powerups[i][0] = cumulative;
  743.         }
  744.  
  745.         random = cumulative * Math.random();
  746.  
  747.         for (i = 0, m = powerups.length; i < m; i++) {
  748.             if (powerups[i][0] > random) {
  749.                 return powerups[i][1];
  750.             }
  751.         }
  752.         return powerups[0][1];
  753.     },
  754.  
  755.     /**
  756.      * Change snake speed
  757.      * @param {Client} client
  758.      * @param {Game} game
  759.      * @private
  760.      */
  761.     _speed: function(client, game) {
  762.         var index = game.room.clients.indexOf(client);
  763.         client.snake.speed -= 5;
  764.         game.room.emit(events.CLIENT_SNAKE_SPEED, [index, client.snake.speed]);
  765.         game.room.emit(events.CLIENT_SNAKE_ACTION, [index, 'Speed+']);
  766.     },
  767.  
  768.     /**
  769.      * Spawn multiple apples
  770.      * @param {Client} client
  771.      * @param {Game} game
  772.      * @private
  773.      */
  774.     _apples: function(client, game) {
  775.         var r = Util.randomBetween(2, 6);
  776.         this._spawn(client, game, game.spawner.APPLE, r, 'Apples+');
  777.     },
  778.  
  779.     /**
  780.      * Spawn multiple powerups
  781.      * @param {Client} client
  782.      * @param {Game} game
  783.      * @private
  784.      */
  785.     _powerups: function(client, game) {
  786.         var r = Util.randomBetween(2, 4);
  787.         this._spawn(client, game, game.spawner.POWERUP, r, 'Power-ups+');
  788.     },
  789.  
  790.     /**
  791.      * @param {Client} client
  792.      * @param {Game} game
  793.      * @param {number} type
  794.      * @param {number} amount
  795.      * @param {string} message
  796.      * @private
  797.      */
  798.     _spawn: function(client, game, type, amount, message) {
  799.         var index, spawn;
  800.  
  801.         index = game.room.clients.indexOf(client);
  802.         spawn = function() {
  803.             game.spawner.spawn(type);
  804.         };
  805.  
  806.         game.room.emit(events.CLIENT_SNAKE_ACTION, [index, message]);
  807.  
  808.         for (var i = 0; i < amount; i++) {
  809.             setTimeout(spawn, i * 100);
  810.         }
  811.     },
  812.  
  813.     /**
  814.      * Reverse Snake direction
  815.      * @param {Client} client
  816.      * @param {Game} game
  817.      * @private
  818.      */
  819.     _reverse: function(client, game) {
  820.         var snakes = game.snakes, index = game.room.clients.indexOf(client);
  821.         for (var i = 0, m = snakes.length; i < m; i++) {
  822.             if (i !== index) {
  823.                 game.room.emit(events.CLIENT_SNAKE_ACTION, [i, 'Reverse']);
  824.                 game.reverseSnake(i);
  825.             }
  826.         }
  827.     }
  828.  
  829. };
  830.  
  831.  
  832.  
  833. /**
  834.  * @param {Room} room
  835.  * @param {number} level
  836.  * @constructor
  837.  */
  838. function Game(room, level) {
  839.     this.room = room;
  840.     this.server = room.server;
  841.  
  842.     this.level = new Level(level, levels);
  843.     this.spawner = new Spawner(this);
  844.  
  845.     this.snakes = [];
  846.  
  847.     this._roundEnded = false;
  848.     this._tickBound = this._tick.bind(this);
  849. }
  850.  
  851. // module.exports = Game;
  852.  
  853. Game.prototype = {
  854.  
  855.     // Max allowed client-server delta
  856.     MAX_DELTA_ALLOWED: 4,
  857.  
  858.     CRASH_OBJECTS: {
  859.         WALL    : 0,
  860.         SELF    : 1,
  861.         OPPONENT: 2
  862.     },
  863.  
  864.     countdown: function() {
  865.         var delay = config.TIME_COUNTDOWN_FROM * 1000;
  866.         this._gameStartTimer = setTimeout(this.start.bind(this), delay);
  867.         this.room.emit(events.CLIENT_GAME_COUNTDOWN, null);
  868.         this._setupClients();
  869.     },
  870.  
  871.     start: function() {
  872.         console.log('___ NEW ROUND IN ROOM ' + this.room.id + ' ___');
  873.         this.room.emit(events.CLIENT_GAME_START, []);
  874.  
  875.         this.room.inProgress = true;
  876.         this.server.ticker.addListener('tick', this._tickBound);
  877.  
  878.         var respawnAfter = config.TIME_RESPAWN_APPLE * 1000;
  879.         this.spawner.spawn(this.spawner.APPLE, null, true, respawnAfter);
  880.         this._delaySpawnPowerup();
  881.     },
  882.  
  883.     destruct: function() {
  884.         var ticker = this.server.ticker;
  885.  
  886.         if (ticker.listeners('tick')) {
  887.             ticker.removeListener('tick', this._tickBound);
  888.         }
  889.  
  890.         clearTimeout(this._gameStartTimer);
  891.         clearTimeout(this._powerUpTimer);
  892.  
  893.         if (this.spawner) {
  894.             this.spawner.destruct();
  895.             this.spawner = null;
  896.         }
  897.  
  898.         this.snakes = null;
  899.         this.level = null;
  900.     },
  901.  
  902.     /**
  903.      * @param {Client} client
  904.      * @param {Array.<Array>} parts
  905.      * @param {number} direction
  906.      */
  907.     updateSnake: function(client, parts, direction) {
  908.         var head = parts[parts.length - 1],
  909.             allowedDelta = this._getAllowedDelta(client);
  910.  
  911.         client.snake.direction = direction;
  912.  
  913.         // Check if server-client delta is similar enough,
  914.         // We tolerate a small difference because of lag.
  915.         if (Util.delta(head, client.snake.head()) <= allowedDelta) {
  916.             client.snake.parts = parts;
  917.             this._broadCastSnake(client);
  918.         } else {
  919.             head = client.snake.head();
  920.             parts = client.snake.parts;
  921.             this._sendServerSnakeState(client);
  922.         }
  923.  
  924.         if (this._isCrash(client, parts)) {
  925.             this._setSnakeCrashed(client, parts);
  926.         } else {
  927.             client.snake.limbo = false;
  928.         }
  929.  
  930.         this.spawner.handleHits(client, head);
  931.     },
  932.  
  933.     /**
  934.      * Reverse Snake (powerup)
  935.      * @param {number} i
  936.      */
  937.     reverseSnake: function(i) {
  938.         var data, dx, dy, snake = this.snakes[i];
  939.  
  940.         dx = snake.parts[0][0] - snake.parts[1][0];
  941.         dy = snake.parts[0][1] - snake.parts[1][1];
  942.  
  943.         if (dx !== 0) {
  944.             snake.direction = (dx === -1) ? 0 : 2;
  945.         } else if (dy !== 0) {
  946.             snake.direction = (dy === -1) ? 1 : 3;
  947.         }
  948.  
  949.         snake.parts.reverse();
  950.         data = [i, snake.parts, snake.direction];
  951.         this.room.emit(events.CLIENT_SNAKE_UPDATE, data);
  952.     },
  953.  
  954.     /**
  955.      * @param client
  956.      */
  957.     clientDisconnect: function(client) {
  958.         this._setSnakeCrashed(client, client.snake.parts);
  959.     },
  960.  
  961.     /**
  962.      * @param client
  963.      */
  964.     emitState: function(client) {
  965.         this.emitSnakes(client);
  966.         this.emitSpawns(client);
  967.     },
  968.  
  969.     /**
  970.      * @param client
  971.      */
  972.     emitSnakes: function(client) {
  973.         var data = [];
  974.         for (var i = 0, m = this.snakes.length; i < m; i++) {
  975.             data.push([i, this.snakes[i].parts, this.snakes[i].direction]);
  976.         }
  977.         client.emit(events.CLIENT_GAME_SNAKES, data);
  978.     },
  979.  
  980.     /**
  981.      * Spawns a sent as separate messages.
  982.      * @param client
  983.      */
  984.     emitSpawns: function(client) {
  985.         var spawner = this.spawner,
  986.             spawns = spawner.spawns,
  987.             data = [];
  988.         for (var i = 0, m = spawns.length; i < m; i++) {
  989.             var spawn = spawns[i];
  990.             if (null !== spawn) {
  991.                 data.push([spawner.EVENTS[spawn.type], [i, spawn.location]]);
  992.             }
  993.         }
  994.         client.emit(events.CLIENT_GAME_SPAWNS, data);
  995.     },
  996.  
  997.     /**
  998.      * @param {Client} client
  999.      * @param {number} index
  1000.      */
  1001.     hitApple: function(client, index) {
  1002.         var clientIndex = this.room.clients.indexOf(client),
  1003.             size = client.snake.size += 3,
  1004.             score = ++this.room.points[clientIndex];
  1005.         this.room.emit(events.CLIENT_APPLE_HIT, [clientIndex, size, index]);
  1006.         this.room.emit(events.CLIENT_SNAKE_ACTION, [clientIndex, 'Nom']);
  1007.         this.room.emit(events.CLIENT_ROOM_SCORE, [clientIndex, score]);
  1008.     },
  1009.  
  1010.     /**
  1011.      * @param {Client} client
  1012.      * @param {number} index
  1013.      */
  1014.     hitPowerup: function(client, index) {
  1015.         var clientIndex = this.room.clients.indexOf(client);
  1016.         this.room.emit(events.CLIENT_POWERUP_HIT, [clientIndex, index]);
  1017.         return new Powerup(this, client);
  1018.     },
  1019.  
  1020.     /**
  1021.      * @return {Array.<number>}
  1022.      */
  1023.     getEmptyLocation: function() {
  1024.         var locations = this.spawner.locations.slice();
  1025.         for (var i = 0, m = this.snakes.length; i < m; i++) {
  1026.             var parts = this.snakes[i].parts;
  1027.             for (var ii = 0, mm = parts.length; ii < mm; ii++) {
  1028.                 locations.push(parts[ii]);
  1029.             }
  1030.         }
  1031.         return this.level.getEmptyLocation(locations);
  1032.     },
  1033.  
  1034.     /**
  1035.      * @param {Client} client
  1036.      * @return {number}
  1037.      * @private
  1038.      */
  1039.     _getAllowedDelta: function(client) {
  1040.         var allowedDelta = Math.ceil(client.latency / client.snake.speed) + 1;
  1041.         return Math.min(allowedDelta, this.MAX_DELTA_ALLOWED);
  1042.     },
  1043.  
  1044.     /**
  1045.      * @private
  1046.      */
  1047.     _delaySpawnPowerup: function() {
  1048.         var i = config.TIME_SPAWN_POWERUP;
  1049.         clearTimeout(this._powerUpTimer);
  1050.         this._powerUpTimer = setTimeout(function() {
  1051.             this.spawner.spawn(this.spawner.POWERUP);
  1052.             this._delaySpawnPowerup();
  1053.         }.bind(this), Util.randomBetween(i[0] * 1000, i[1]* 1000));
  1054.     },
  1055.  
  1056.     /**
  1057.      * @param {Client} client
  1058.      * @private
  1059.      */
  1060.     _sendServerSnakeState: function(client) {
  1061.         var data = [
  1062.             this.room.clients.indexOf(client),
  1063.             client.snake.parts,
  1064.             client.snake.direction
  1065.         ];
  1066.         this.room.emit(events.CLIENT_SNAKE_UPDATE, data);
  1067.     },
  1068.  
  1069.     /**
  1070.      * @param {Client} client
  1071.      * @private
  1072.      */
  1073.     _broadCastSnake: function(client) {
  1074.         var send = [
  1075.             this.room.clients.indexOf(client),
  1076.             client.snake.parts,
  1077.             client.snake.direction
  1078.         ];
  1079.         this.room.broadcast(events.CLIENT_SNAKE_UPDATE, send, client);
  1080.     },
  1081.  
  1082.     /**
  1083.      * @param {Client} client
  1084.      * @param {Array.<Array>} parts
  1085.      * @return {Array.<number>}
  1086.      * @private
  1087.      */
  1088.     _isCrash: function(client, parts) {
  1089.         var eq = Util.eq,
  1090.             limbo = client.snake.limbo,
  1091.             clients = this.room.clients,
  1092.             level = this.level;
  1093.  
  1094.         for (var i = 0, m = parts.length; i < m; i++) {
  1095.             var part = parts[i];
  1096.  
  1097.             // Wall
  1098.             if (level.isWall(part[0], part[1])) {
  1099.                 return [this.CRASH_OBJECTS.WALL, clients.indexOf(client)];
  1100.             }
  1101.  
  1102.             // Self
  1103.             if (m >= 5 && m - 1 !== i && eq(part, parts[m - 1])) {
  1104.                 return [this.CRASH_OBJECTS.SELF, clients.indexOf(client)];
  1105.             }
  1106.  
  1107.             // Self (limbo)
  1108.             else if (limbo && m >= 5 && m - 2 !== i && eq(part, parts[m - 2])) {
  1109.                 return [this.CRASH_OBJECTS.SELF, clients.indexOf(client)];
  1110.             }
  1111.  
  1112.             // Opponent
  1113.             for (var ii = 0, mm = clients.length; ii < mm; ii++) {
  1114.                 if (client !== clients[ii]) {
  1115.                     if (clients[ii].snake.hasPart(part)) {
  1116.                         return [
  1117.                             this.CRASH_OBJECTS.OPPONENT,
  1118.                             clients.indexOf(client),
  1119.                             ii
  1120.                         ];
  1121.                     }
  1122.                 }
  1123.             }
  1124.         }
  1125.  
  1126.         return null;
  1127.     },
  1128.  
  1129.     /**
  1130.      * @param {Client} client
  1131.      * @param {Array.<Array>} parts
  1132.      * @private
  1133.      */
  1134.     _setSnakeCrashed: function(client, parts) {
  1135.         client.snake.crashed = true;
  1136.         var clientIndex = this.room.clients.indexOf(client);
  1137.         this.room.emit(events.CLIENT_SNAKE_CRASH, [clientIndex, parts]);
  1138.         this._checkRoundEnded();
  1139.     },
  1140.  
  1141.     /**
  1142.      * @private
  1143.      */
  1144.     _checkRoundEnded: function() {
  1145.         var clients, numcrashed, alive;
  1146.  
  1147.         clients = this.room.clients;
  1148.         numcrashed = 0;
  1149.  
  1150.         for (var i = 0, m = clients.length; i < m; i++) {
  1151.             if (clients[i].snake.crashed) {
  1152.                 numcrashed++;
  1153.             } else {
  1154.                 alive = clients[i];
  1155.  
  1156.                 // Knockout system points
  1157.                 this.room.emit(
  1158.                     events.CLIENT_ROOM_SCORE,
  1159.                     [i, this.room.points[i] += 2]
  1160.                 );
  1161.             }
  1162.         }
  1163.  
  1164.         if (numcrashed >= clients.length -1 && !this._roundEnded) {
  1165.             this._endRound();
  1166.         }
  1167.     },
  1168.  
  1169.     /**
  1170.      * @private
  1171.      */
  1172.     _endRound: function() {
  1173.         this._roundEnded = true;
  1174.         this.room.emit(
  1175.             events.CLIENT_CHAT_NOTICE,
  1176.             'New round starting in ' + config.TIME_GLOAT + ' seconds'
  1177.         );
  1178.         setTimeout(this._startNewRound.bind(this), config.TIME_GLOAT * 1000);
  1179.     },
  1180.  
  1181.     /**
  1182.      * @private
  1183.      */
  1184.     _startNewRound: function() {
  1185.         this.server.ticker.removeListener('tick', this._tickBound);
  1186.         this.room.newRound();
  1187.     },
  1188.  
  1189.     /**
  1190.      * @param {number} delta
  1191.      * @private
  1192.      */
  1193.     _tick: function(delta) {
  1194.         var clients = this.room.clients;
  1195.         for (var i = 0, m = clients.length; i < m; i++) {
  1196.             this._tickClient(clients[i], delta);
  1197.         }
  1198.     },
  1199.  
  1200.     /**
  1201.      * @param {Client} client
  1202.      * @param {number} delta
  1203.      * @private
  1204.      */
  1205.     _tickClient: function(client, delta) {
  1206.         var snake = client.snake;
  1207.         if (!snake.crashed) {
  1208.             if (snake.elapsed >= snake.speed) {
  1209.                 snake.elapsed -= snake.speed;
  1210.                 this._applyPredictedPosition(client);
  1211.                 snake.trim();
  1212.             }
  1213.             snake.elapsed += delta;
  1214.         }
  1215.     },
  1216.  
  1217.     /**
  1218.      * @param {Snake} snake
  1219.      * @return {Array.<number>}
  1220.      * @private
  1221.      */
  1222.     _getPredictPosition: function(snake) {
  1223.         var head, shift;
  1224.         head = snake.head();
  1225.         shift = [[-1, 0], [0, -1], [1, 0], [0, 1]][snake.direction];
  1226.         return [head[0] + shift[0], head[1] + shift[1]];
  1227.     },
  1228.  
  1229.     /**
  1230.      * @param {Client} client
  1231.      * @private
  1232.      */
  1233.     _applyPredictedPosition: function(client) {
  1234.         var predict, predictParts, snake, crash;
  1235.  
  1236.         snake = client.snake;
  1237.         predict = this._getPredictPosition(snake);
  1238.  
  1239.         predictParts = snake.parts.slice(1);
  1240.         predictParts.push(predict);
  1241.         crash = this._isCrash(client, predictParts);
  1242.  
  1243.         if (crash) {
  1244.             // A snake is in limbo when the server predicts that a snake has
  1245.             // crashed. The prediction is wrong when the client made a turn
  1246.             // just in time but that message was received too late by the server
  1247.             // because of network delay. When the turn message is received by
  1248.             // the server, and it seems like the server made a wrong prediction,
  1249.             // the snake returns from limbo.
  1250.             if (snake.limbo) {
  1251.                 this._emitCrashMessage(crash);
  1252.                 this._setSnakeCrashed(client, snake.limbo);
  1253.             } else {
  1254.                 snake.limbo = snake.parts.slice();
  1255.             }
  1256.         }
  1257.  
  1258.         // Apply move
  1259.         if (!snake.crashed) {
  1260.             snake.move(predict);
  1261.         }
  1262.  
  1263.         // Cannot hit apples and powerups when in limbo;
  1264.         // Snake is either dead or made a turn to prevent death.
  1265.         if (!snake.limbo) {
  1266.             this.spawner.handleHits(client, predict);
  1267.         }
  1268.     },
  1269.  
  1270.     /**
  1271.      * @param {Array.<number>} crash
  1272.      * @private
  1273.      */
  1274.     _emitCrashMessage: function(crash) {
  1275.         var message, object = crash[0];
  1276.         if (object === this.CRASH_OBJECTS.WALL) {
  1277.             message = util.format('{%d} crashed into a wall', crash[1]);
  1278.         } else if (object === this.CRASH_OBJECTS.SELF) {
  1279.             message = util.format('{%d} crashed into own tail', crash[1]);
  1280.         } else if (object === this.CRASH_OBJECTS.OPPONENT) {
  1281.             message = util.format('{%d} crashed into {%d}', crash[1], crash[2]);
  1282.         }
  1283.         this.room.emit(events.CLIENT_CHAT_NOTICE, message);
  1284.     },
  1285.  
  1286.     /**
  1287.      * @private
  1288.      */
  1289.     _setupClients: function() {
  1290.         var clients = this.room.clients;
  1291.         for (var i = 0, m = clients.length; i < m; i++) {
  1292.             var snake = this._spawnSnake(i);
  1293.             this.snakes[i] = snake;
  1294.             clients[i].snake = snake;
  1295.         }
  1296.     },
  1297.  
  1298.     /**
  1299.      * @param {number} index
  1300.      * @return {Snake}
  1301.      * @private
  1302.      */
  1303.     _spawnSnake: function(index) {
  1304.         var spawn, direction, snake, size, speed;
  1305.  
  1306.         spawn = this.level.getSpawn(index);
  1307.         direction = this.level.getSpawnDirection(index);
  1308.         size = config.SNAKE_SIZE;
  1309.         speed = config.SNAKE_SPEED;
  1310.  
  1311.         snake = new Snake(spawn, direction, size, speed);
  1312.         snake.elapsed = 0;
  1313.  
  1314.         return snake;
  1315.     }
  1316.  
  1317. };
  1318.  
  1319. // ALREADY INLINED: events.js
  1320. // ALREADY INLINED: config.js
  1321.  
  1322. /**
  1323.  * @param {Server} server
  1324.  * @param {number} id
  1325.  * @param {Object} filter
  1326.  * @constructor
  1327.  */
  1328. function Room(server, id, filter) {
  1329.     this.server = server;
  1330.  
  1331.     this.id = id;
  1332.     this.clients = [];
  1333.     this.points = [];
  1334.     this.inProgress = false;
  1335.  
  1336.     this.pub      = !!filter['public'];
  1337.     this.friendly = !!filter['friendly'];
  1338.     this.capacity = config.ROOM_CAPACITY;
  1339.  
  1340.     this.level = 0;
  1341.     this.game = new Game(this, this.level);
  1342.  
  1343.     this._disconnected = [];
  1344. }
  1345.  
  1346. // module.exports = Room;
  1347.  
  1348. Room.prototype = {
  1349.  
  1350.     destruct: function() {
  1351.         this.game.destruct();
  1352.         this.game = null;
  1353.         this.clients = null;
  1354.     },
  1355.  
  1356.     emitState: function() {
  1357.         var names = this.names();
  1358.         for (var i = 0, m = this.clients.length; i < m; i++) {
  1359.             var data = [i, this.level, names, this.points];
  1360.             this.clients[i].emit(events.CLIENT_ROOM_INDEX, data);
  1361.         }
  1362.     },
  1363.  
  1364.     /**
  1365.      * @param {Client} client
  1366.      * @return {Room}
  1367.      */
  1368.     join: function(client) {
  1369.         var index = this.clients.push(client) - 1;
  1370.         client.socket.join(this.id);
  1371.         client.roomid = this.id;
  1372.         this.points[index] = 0;
  1373.  
  1374.         this.emitState();
  1375.         this.broadcast(events.CLIENT_CHAT_NOTICE, '{' + index + '} joined', client);
  1376.  
  1377.         if (this.isFull()) {
  1378.             this.game.countdown();
  1379.         }
  1380.  
  1381.         return this;
  1382.     },
  1383.  
  1384.     /**
  1385.      * @param {Client} client
  1386.      */
  1387.     disconnect: function(client) {
  1388.         var index = this.clients.indexOf(client);
  1389.  
  1390.         // Leave during game, clean up after round ends
  1391.         if (this.inProgress) {
  1392.             this.game.clientDisconnect(client);
  1393.             this._disconnected.push(client);
  1394.         }
  1395.  
  1396.         // Leave before game, clean up immediately
  1397.         else {
  1398.             this.clients.splice(index, 1);
  1399.             this.emitState();
  1400.             if (!this.clients.length) {
  1401.                 this.server.roomManager.remove(this);
  1402.             }
  1403.         }
  1404.  
  1405.         this.emit(events.CLIENT_CHAT_NOTICE, '{' + index + '} left');
  1406.     },
  1407.  
  1408.     /**
  1409.      * @return {Game}
  1410.      */
  1411.     newRound: function() {
  1412.         // Before round starts
  1413.         this.game.destruct();
  1414.         this._removeDisconnectedClients(this._disconnected);
  1415.  
  1416.         // Check if Room was destructed
  1417.         if (!this.clients.length) {
  1418.             this.server.roomManager.remove(this);
  1419.             return null;
  1420.         }
  1421.  
  1422.         // Round start
  1423.         this.game = new Game(this, this.level);
  1424.         this.emitState();
  1425.         this.game.countdown();
  1426.         return this.game;
  1427.     },
  1428.  
  1429.     /**
  1430.      * @return {boolean}
  1431.      */
  1432.     isFull: function() {
  1433.         return (this.clients.length === this.capacity);
  1434.     },
  1435.  
  1436.     /**
  1437.      * @return {Array.<string>}
  1438.      */
  1439.     names: function() {
  1440.         var names = [];
  1441.         for (var i = 0, m = this.clients.length; i < m; i++) {
  1442.             names.push(this.clients[i].name);
  1443.         }
  1444.         return names;
  1445.     },
  1446.  
  1447.     /**
  1448.      * Send data to everyone in the room.
  1449.      * @param {string} name
  1450.      * @param {*} data
  1451.      */
  1452.     emit: function(name, data) {
  1453.         this.server.io.sockets.in(this.id).emit(name, data);
  1454.     },
  1455.  
  1456.     /**
  1457.      * Send data to everyone else in the room.
  1458.      * @param {string} name
  1459.      * @param {*} data
  1460.      * @param {Client} exclude
  1461.      */
  1462.     broadcast: function(name, data, exclude) {
  1463.         exclude.socket.broadcast.to(this.id).emit(name, data);
  1464.     },
  1465.  
  1466.     /**
  1467.      * @param {Array.<Client>} clients
  1468.      * @private
  1469.      */
  1470.     _removeDisconnectedClients: function(clients) {
  1471.         if (clients) {
  1472.             for (var i = 0, m = clients.length; i < m; i++) {
  1473.                 this.clients.splice(this.clients.indexOf(clients[i]), 1);
  1474.                 this.server.removeClient(clients[i]);
  1475.             }
  1476.         }
  1477.         this._disconnected = [];
  1478.     }
  1479.  
  1480. };
  1481.  
  1482.  
  1483. /**
  1484.  * @constructor
  1485.  */
  1486. function RoomManager(server) {
  1487.     this.server = server;
  1488.     this.inc = 0;
  1489.     this.rooms = {};
  1490. }
  1491.  
  1492. // module.exports = RoomManager;
  1493.  
  1494. RoomManager.prototype = {
  1495.  
  1496.     /**
  1497.      * @param {number} id
  1498.      * @return {Room}
  1499.      */
  1500.     room: function(id) {
  1501.         return this.rooms[id];
  1502.     },
  1503.  
  1504.     /**
  1505.      * @param {Room} room
  1506.      */
  1507.     remove: function(room) {
  1508.         delete this.rooms[room.id];
  1509.         room.destruct();
  1510.     },
  1511.  
  1512.     /**
  1513.      * @param {Object.<string, boolean>} filter
  1514.      * @return {Room}
  1515.      */
  1516.     getPreferredRoom: function(filter) {
  1517.         var room = this._findRoom(filter);
  1518.         if (!room) {
  1519.             room = this.createRoom(filter);
  1520.         }
  1521.         return room;
  1522.     },
  1523.  
  1524.     /**
  1525.      * @param {Object.<string, boolean>} filter
  1526.      * @return {Room}
  1527.      */
  1528.     createRoom: function(filter) {
  1529.         var id, room;
  1530.         id = ++this.inc;
  1531.         room = new Room(this.server, id, filter);
  1532.         this.rooms[room.id] = room;
  1533.         return room;
  1534.     },
  1535.  
  1536.     /**
  1537.      * @param {Object.<string, boolean>} filter
  1538.      * @return {?Room}
  1539.      * @private
  1540.      */
  1541.     _findRoom: function(filter) {
  1542.         for (var k in this.rooms) {
  1543.             if (this.rooms.hasOwnProperty(k)) {
  1544.                 var room = this.rooms[k];
  1545.                 if (this._isFilterMatch(filter, room)) {
  1546.                     return room;
  1547.                 }
  1548.             }
  1549.         }
  1550.         return null;
  1551.     },
  1552.  
  1553.     /**
  1554.      * @param {Object.<string, boolean>} filter
  1555.      * @param {Room} room
  1556.      * @return {boolean}
  1557.      * @private
  1558.      */
  1559.     _isFilterMatch: function(filter, room) {
  1560.         var eqFriendly = room.friendly === filter.friendly;
  1561.         return (room.pub && eqFriendly && !room.isFull() && !room.inProgress);
  1562.     }
  1563.  
  1564. };
  1565.  
  1566. // INLINED: client.js
  1567. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  1568.  
  1569.  
  1570. /**
  1571.  * @param {number} id
  1572.  * @param {Server} server
  1573.  * @param {EventEmitter} socket
  1574.  * @constructor
  1575.  */
  1576. function Client(id, server, socket) {
  1577.     this.server = server;
  1578.     this.id = id;
  1579.     this.socket = socket;
  1580.     this.latency = 0;
  1581.  
  1582.     /** @type {?string} */
  1583.     this.name = null;
  1584.     /** @type {Snake} */
  1585.     this.snake = null;
  1586.     /** @type {?number} */
  1587.     this.roomid = null;
  1588.     /** @type {EventHandler} */
  1589.     this.eventHandler = null;
  1590.     /** @type {boolean} */
  1591.     this.limbo = false;
  1592. }
  1593.  
  1594. // module.exports = Client;
  1595.  
  1596. Client.prototype = {
  1597.  
  1598.     /**
  1599.      * Send data to client
  1600.      * @param {string} name
  1601.      * @param {*} data
  1602.      */
  1603.     emit: function(name, data) {
  1604.         this.socket.emit(name, data);
  1605.     },
  1606.  
  1607.     destruct: function() {
  1608.         this.eventHandler.destruct();
  1609.         this.eventHandler = null;
  1610.         this.snake = null;
  1611.         this.socket = null;
  1612.     }
  1613.  
  1614. };
  1615.  
  1616. // INLINED: ticker.js
  1617. /*jshint globalstrict:true, es5:true, node:true, sub:true*/
  1618.  
  1619.  
  1620. // CORE MODULE: util
  1621.     // Prevent name collision with shared/events.js
  1622. // CORE MODULE: events
  1623.  
  1624. /**
  1625.  * @param {number} tick
  1626.  * @extends {EventEmitter}
  1627.  * @constructor
  1628.  */
  1629. function Ticker(tick) {
  1630.     this._time = +new Date();
  1631.     setInterval(this.tick.bind(this), tick);
  1632. }
  1633.  
  1634. util.inherits(Ticker, nodeEvents.EventEmitter);
  1635. // module.exports = Ticker;
  1636.  
  1637. Ticker.prototype.tick = function() {
  1638.     var now = +new Date(),
  1639.         elapsed = now - this._time;
  1640.     this.emit('tick', elapsed);
  1641.     this._time = new Date();
  1642. };
  1643.  
  1644.  
  1645. /**
  1646.  * @constructor
  1647.  */
  1648. function Server() {
  1649.     /** @typedef {number} */
  1650.     this.inc = 0;
  1651.     /** @typedef {Object.<number, {Client}>} */
  1652.     this.clients = {};
  1653.  
  1654.     this.ticker = new Ticker(50);
  1655.     this.ticker.setMaxListeners(0);
  1656.     this.roomManager = new RoomManager(this);
  1657.     this.listen(config.SERVER_PORT);
  1658. }
  1659.  
  1660. // module.exports = Server;
  1661.  
  1662. Server.prototype = {
  1663.  
  1664.     /**
  1665.      * @param {number} port
  1666.      */
  1667.     listen: function(port) {
  1668.         var server = http.createServer();
  1669.  
  1670.         /** @type {Manager} */
  1671.         this.io = socketio.listen(server, {log: false});
  1672.  
  1673.         this.io.set('browser client etag', true);
  1674.         this.io.set('browser client gzip', true);
  1675.         this.io.set('browser client minification', true);
  1676.         this.io.set('transports', ['websocket']);
  1677.         this.io.set('close timeout', 10);
  1678.         this.io.set('heartbeat timeout ', 10);
  1679.  
  1680.         this.io.sockets.on('connection', this.addClient.bind(this));
  1681.  
  1682.         server.listen(port);
  1683.     },
  1684.  
  1685.     /**
  1686.      * @param {EventEmitter} socket
  1687.      */
  1688.     addClient: function(socket) {
  1689.         var client, id = ++this.inc;
  1690.         client = new Client(id, this, socket);
  1691.         client.eventHandler = new EventHandler(this, client, socket);
  1692.         this.clients[id] = client;
  1693.     },
  1694.  
  1695.     /**
  1696.      * @param {Client} client
  1697.      */
  1698.     removeClient: function(client) {
  1699.         client.destruct();
  1700.         delete this.clients[client.id];
  1701.     }
  1702.  
  1703. };
  1704.  
  1705. var server = new Server();
  1706.  
  1707. console.log('XSSNAKE server is running...');
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement