├── .gitignore ├── LICENSE ├── README.md ├── Server.js ├── client ├── assets │ ├── btn-join-game.png │ └── red-fly.png ├── index.html └── js │ ├── Boot.js │ ├── Game.js │ ├── Main.js │ ├── Menu.js │ ├── Preload.js │ └── lib │ ├── phaser.js │ └── socket.io-1.7.3.min.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/windows,webstorm,jetbrains 3 | 4 | ### JetBrains ### 5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 7 | 8 | # User-specific stuff: 9 | .idea/**/workspace.xml 10 | .idea/**/tasks.xml 11 | .idea/dictionaries 12 | 13 | # Sensitive or high-churn files: 14 | .idea/**/dataSources/ 15 | .idea/**/dataSources.ids 16 | .idea/**/dataSources.xml 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | 22 | # Gradle: 23 | .idea/**/gradle.xml 24 | .idea/**/libraries 25 | 26 | # Mongo Explorer plugin: 27 | .idea/**/mongoSettings.xml 28 | 29 | ## File-based project format: 30 | *.iws 31 | 32 | ## Plugin-specific files: 33 | 34 | # IntelliJ 35 | /out/ 36 | 37 | # mpeltonen/sbt-idea plugin 38 | .idea_modules/ 39 | 40 | # JIRA plugin 41 | atlassian-ide-plugin.xml 42 | 43 | # Cursive Clojure plugin 44 | .idea/replstate.xml 45 | 46 | # Crashlytics plugin (for Android Studio and IntelliJ) 47 | com_crashlytics_export_strings.xml 48 | crashlytics.properties 49 | crashlytics-build.properties 50 | fabric.properties 51 | 52 | ### JetBrains Patch ### 53 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 54 | 55 | # *.iml 56 | # modules.xml 57 | # .idea/misc.xml 58 | # *.ipr 59 | 60 | # Sonarlint plugin 61 | .idea/sonarlint 62 | 63 | ### WebStorm ### 64 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 65 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 66 | 67 | # User-specific stuff: 68 | 69 | # Sensitive or high-churn files: 70 | 71 | # Gradle: 72 | 73 | # Mongo Explorer plugin: 74 | 75 | ## File-based project format: 76 | 77 | ## Plugin-specific files: 78 | 79 | # IntelliJ 80 | 81 | # mpeltonen/sbt-idea plugin 82 | 83 | # JIRA plugin 84 | 85 | # Cursive Clojure plugin 86 | 87 | # Crashlytics plugin (for Android Studio and IntelliJ) 88 | 89 | ### WebStorm Patch ### 90 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 91 | 92 | # *.iml 93 | # modules.xml 94 | # .idea/misc.xml 95 | # *.ipr 96 | 97 | # Sonarlint plugin 98 | 99 | ### Windows ### 100 | # Windows thumbnail cache files 101 | Thumbs.db 102 | ehthumbs.db 103 | ehthumbs_vista.db 104 | 105 | # Folder config file 106 | Desktop.ini 107 | 108 | # Recycle Bin used on file shares 109 | $RECYCLE.BIN/ 110 | 111 | # Windows Installer files 112 | *.cab 113 | *.msi 114 | *.msm 115 | *.msp 116 | 117 | # Windows shortcuts 118 | *.lnk 119 | 120 | # End of https://www.gitignore.io/api/windows,webstorm,jetbrains 121 | 122 | # Node 123 | node_modules 124 | package-lock.json -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # basic-networked-multiplayer-game 2 | A basic networked multiplayer game example/template that uses Socket.io and Phaser, with easy to understand, yet detailed explanations of pretty much everything. 3 | 4 | The Server.js and Boot.js files should contain all of the instructions and code needed to get a litte networked multiplayer game set up quickly and easily. 5 | 6 | If there is anything wrong, any misinformation or if something doesn't work, let me know. 7 | -------------------------------------------------------------------------------- /Server.js: -------------------------------------------------------------------------------- 1 | /** Simple, easy, networked multiplayer game template using Socket.io and Phaser.io, with lots of explanations. 2 | * By Johnathan Fisher http://www.waywardworlds.com @waywardworlds 3 | * 4 | * * * * * * * * * * * * * * * 5 | * * * * How to set up the project * * * * 6 | * * * * * * * * * * * * * * * 7 | * 8 | * First, you need to have NodeJS and NPM installed. That should be fairly easy to do. 9 | * Open a command-line user interface (CLI), and enter 'node -v', then 'npm -v', which 10 | * should show the versions of Node and NPM if they are set up correctly, which should 11 | * output something like 'v5.10.0' and '4.4.4', or whatever version you installed. 12 | * 13 | * This project uses the Express.js and Socket.io libraries. 14 | * Everything that is required should be included in the package.json file. Just navigate to 15 | * the parent directory of your game, i.e. 'C:\path\to\basic-multiplayer-template' in a CLI 16 | * and enter 'npm install'. 17 | * This should go through everything listed in the package.json file and install them from NPM. 18 | * 19 | * There should now be a folder in your game directory called 'node_modules'. 20 | * It will be huge and full of junk and looks like a mess, but that's just how NPM is... 21 | * 22 | * This file is Server.js, which is what you should run with Node. 23 | * Assuming you are still in the directory of your game in the command line, enter 24 | * 'node server'. That will run this javascript file and set everything up that is in here. 25 | * 26 | * Below is the code and how it works. 27 | * */ 28 | 29 | // Strict mode helps to prevent the use of weird things that you might do in javascript by accident. 30 | "use strict"; 31 | 32 | // Gathering dependencies. The require(...) bit imports the stuff that was installed through npm. 33 | var express = require('express'); 34 | // Create an Express app. Socket.io just sits on top of Express, but Express itself isn't 35 | // used much, unless you want to serve files with it, but that is not recommended. 36 | var app = express(); 37 | // Make a new HTTP server from Express. This doesn't get used itself either, unless you want to do stuff with it. 38 | var server = require('http').Server(app); 39 | // This is where Socket.io is set up. Socket takes in the HTTP server created above, and basically adds it's own 40 | // layer on top of it that is nice and easy to use. 'io' is the Socket.io server object. You could call it 41 | // 'socketIOServer' or something similar if you wish, but all of the documentation for Socket.io uses just 'io'. 42 | var io = require('socket.io')(server); 43 | 44 | // What port and IP address to bind the server to. The port number can be any valid public port number (Google it if not sure). 45 | // The port is used to direct network traffic arriving at your computer to a particular service, in this case 46 | // the HTTP server that was created, so anything arriving on port 3512 will go to the HTTP server. 47 | 48 | // There is a chance that the port you want to use is already being occupied by another 49 | // process, so something to watch out for if you can't start the server. 50 | 51 | // Defining route handlers 52 | app.get('/',function(req, res) { 53 | res.sendFile(__dirname + '/client/index.html'); 54 | }); 55 | app.use(express.static(__dirname + '/client')); 56 | 57 | // Your IP address is how other devices on a network find this one. The 127.0.0.1 is known as a loop-back address, or 58 | // otherwise known as 'localhost', which is basically a way for a device to send messages to itself. 59 | server.listen(3512, "127.0.0.1"); 60 | console.log("Server started on http://127.0.0.1:3512"); 61 | 62 | 63 | // Used to manage players in the game. See the slightly more advanced stuff 64 | // after you understand everything else and are done with the basics. 65 | var players = {}; 66 | 67 | /** * * * * * * * * * * * * * * * * * * 68 | * * * * Some useful stuff you can do with Socket.io * * * * 69 | * * * * * * * * * * * * * * * * * * * 70 | * 71 | * socket.on('event_name', function(optionalData){ ... ); - Adds an event listener to this socket. 72 | * io.emit('event_name', optionalData); - Sends an event to all sockets. 73 | * socket.emit('event_name', optionalData); - Sends an event to this socket. 74 | * socket.broadcast.emit('event_name', optionalData); - Sends an event to all sockets except this one. 75 | * io.in('room-name').emit('event_name', optionalData); - Sends an event to all sockets that are in the specified room. 76 | * socket.join('room-name'); - Adds a socket to a room. 77 | * socket.leave('room-name'); - Removes a socket from a room. 78 | */ 79 | 80 | // A 'socket' it just an object that manages a client's connection to the server. For each client that connects to 81 | // this server, they will get their own socket object. These socket objects are stored internally on the io object, 82 | // and can be accessed manually with 'io.sockets' to get a list of the connected sockets, but you shouldn't really need to. 83 | 84 | // The first time a connection is made with a new socket (a client), the 'connection' event is triggered 85 | // on io (the Socket.io server object), and it runs the 'connection' callback function (the 'function(socket){ ... }' part). 86 | // The socket object for the client that connected is sent to the callback, and this allows us to do stuff with that 87 | // socket, such as adding event listeners to that socket, which is what is done below. 88 | // v The socket object is passed in automatically by io when a connection is made. 89 | io.on('connection', function (socket) { 90 | console.log("* * * A new connection has been made."); 91 | // Each socket object (one for each connected client) has an 'id' property, 92 | // which can be used to uniquely identify each socket connection. 93 | // Check the command line that was used to start the server to see 94 | // the id of each socket that connects being printed. 95 | console.log("* ID of new socket object: " + socket.id); 96 | 97 | // Using the socket object that was passed in, events can be sent to the 98 | // client that socket belongs to using .emit(...) 99 | // The socket object on the client (see Boot.js in /client/js) should have event 100 | // listeners of the event name that you are sending to it, or it won't pick them up. 101 | 102 | // So if the server emits 'super_event', then the client must also be listening 103 | // for 'super_event', and vice versa for when the client sends events to the server. 104 | 105 | // In this case, an event called 'hello_client' is sent, and the (optional) second 106 | // parameter is any data you might want to send along with the event. 107 | socket.emit('hello_client', {crazyString: 'abc123', coolArray: [40, 'beep', true]}); 108 | // Or with no data, just an event. 109 | socket.emit('how_are_you'); 110 | // An event that the client isn't listening for, so will be ignored when the client receives it. 111 | socket.emit('anyone_there'); 112 | 113 | // You can add your own properties onto the socket object like any other object. 114 | // Useful if you want to store player data like a score, username, or a flag of 115 | // whether they are currently in a game. 116 | socket.username = 'DEFAULT NAME'; 117 | socket.score = 0; 118 | socket.isInGame = false; 119 | 120 | // Event listeners can be added to this socket. Every time the client sends 121 | // an event to this server, the server will look to see if the name of that event 122 | // matches any events that this socket is listening for. 123 | 124 | // In this case, an event listener is being added that will listen for an event 125 | // called 'change_username', and giving it a callback function to run whenever the 126 | // event is received. When the client sends this event, they can also pass along data. 127 | // The data that is sent is automatically passed in to the callback as the first argument. 128 | socket.on('change_username', function(data) { 129 | // Update the player's username with the data that they sent from their client. 130 | // The name of the property that you access on the data object must match how it 131 | // looks when the client sent it. 132 | socket.username = data.username; 133 | console.log("* Username changed to: " + data.username); 134 | }); 135 | 136 | socket.on('im_fine', function (/* No data was sent by the client for this event. You could put 'data' here but it would just be undefined. */) { 137 | socket.emit('good_to_hear'); 138 | }); 139 | 140 | socket.on('join_game', function () { 141 | // Check that the player is not already in a game before letting them join one. 142 | if(socket.isInGame === false){ 143 | // This player is now in a game. 144 | socket.isInGame = true; 145 | // Add a basic object that tracks player position to the list of players, using 146 | // the ID of this socket as the key for convenience, as each socket ID is unique. 147 | players[socket.id] = { 148 | x: 200, 149 | y: 150 150 | }; 151 | // Add this socket to the room for the game. A room is an easy way to group sockets, so you can send events to a bunch of sockets easily. 152 | // A socket can be in many rooms. 153 | socket.join('game-room'); 154 | 155 | // Tell the client that they successfully joined the game. 156 | socket.emit('join_game_success'); 157 | console.log("* " + socket.username + " joined a game."); 158 | } 159 | else { 160 | console.log("* " + socket.username + " is already in a game."); 161 | } 162 | }); 163 | 164 | socket.on('move_player', function (data) { 165 | // Access the object in the list of players that has the key of this socket ID. 166 | // 'data.axis' is the axis to move in, x or y. 167 | // 'data.force' is which direction on the given axis to move, 1 or -1. 168 | // So if the axis is 'y', and the force is -1, then the player would move up. 169 | // Change the * 2 multiplier to change the movement speed. 170 | players[socket.id][data.axis] += data.force * 2; 171 | }); 172 | 173 | // When a client socket disconnects (closes the page, refreshes, timeout etc.), 174 | // then this event will automatically be triggered. 175 | socket.on('disconnecting', function () { 176 | // Check if this player was in a game before they disconnected. 177 | if(socket.isInGame === true){ 178 | // Remove this player from the player list. 179 | delete players[socket.id]; 180 | // This player was in a game and has disconnected, but the other players still in the game don't know that. 181 | // We need to tell the other players to remove the sprite for this player from their clients. 182 | // All of the players still in the game are in the room called 'game-room', so emit an event called 'remove_player' 183 | // to that room, sending with it the key of the property to remove. 184 | io.in('game-room').emit('remove_player', socket.id); 185 | } 186 | }); 187 | 188 | }); 189 | 190 | // How often to send game updates. Faster paced games will require a lower value for emitRate, 191 | // so that updates are sent more often. Do some research and test what works for your game. 192 | var emitRate = 100; 193 | // This is what I call an 'emitter'. It is used to continuously send updates of the game world to all relevant clients. 194 | setInterval(function () { 195 | // Prepare the positions of the players, ready to send to all players. 196 | var dataToSend = preparePlayersDataToSend(); 197 | 198 | // Send the data to all clients in the room called 'game-room'. 199 | io.in('game-room').emit('state_update', dataToSend); 200 | }, emitRate); 201 | 202 | function preparePlayersDataToSend() { 203 | // Prepare the positions of the players, ready to send to all players. 204 | var dataToSend = []; 205 | // 'players' is an object, so get a list of the keys. 206 | var keys = Object.keys(players); 207 | // Loop though the list of players and get the position of each player. 208 | keys.forEach(function (key) { 209 | // Add the position (and ID, so the client knows who is where) to the data to send. 210 | dataToSend.push({id: key, x: players[key].x, y: players[key].y}); 211 | }); 212 | return dataToSend; 213 | } 214 | -------------------------------------------------------------------------------- /client/assets/btn-join-game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcanorum/basic-networked-multiplayer-game/2d9b0c9c34e09bbac4f101d79e1ba18293ee9f6a/client/assets/btn-join-game.png -------------------------------------------------------------------------------- /client/assets/red-fly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcanorum/basic-networked-multiplayer-game/2d9b0c9c34e09bbac4f101d79e1ba18293ee9f6a/client/assets/red-fly.png -------------------------------------------------------------------------------- /client/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Basic Networked Multiplayer Game Client 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /client/js/Boot.js: -------------------------------------------------------------------------------- 1 | 2 | // Global reference to the active Phaser game state. This makes doing things with 3 | // a state possible without actually being in the state file itself. 4 | var _this; 5 | 6 | // Connect to the Socket.io server that is running on the IP address 127.0.0.1 and at port number 3512. 7 | var socket = io("http://127.0.0.1:3512"); 8 | // This connects to 127.0.0.1 which is localhost (this computer), which is also where the server is running. 9 | // If the server was running somewhere else, like on a cloud service, then change the IP address to the 10 | // public IP address of that device. If on Windows, open a console and type 'ipconfig' to find the IPv4 11 | // address of a computer. 12 | 13 | // Add some event listeners to this socket object. 14 | // When the connection is made, the server can send events to this client, and vice versa. 15 | // Open your browser's console to see the output. Follow along with the instructions there. 16 | 17 | socket.on('hello_client', function (data) { 18 | console.log(""); 19 | console.log("* * * hello_client event received from server."); 20 | console.log("* Data was also sent:"); 21 | console.log(data); 22 | }); 23 | 24 | socket.on('how_are_you', function () { 25 | console.log(""); 26 | console.log("* * * how_are_you event received from server."); 27 | console.log("* Try sending an event back. You can use the browser console to send events manually."); 28 | console.log("* Type in `socket.emit('im_fine');` and hit enter."); 29 | }); 30 | 31 | socket.on('good_to_hear', function () { 32 | console.log(""); 33 | console.log("* * * good_to_hear event received from server."); 34 | console.log("* That's good to hear. :)"); 35 | console.log("* Now try sending an event with some data to the server."); 36 | console.log("* Let's change the username stored on the socket for this client on the server."); 37 | console.log("* Type in `socket.emit('change_username', {username: 'PUT A NEW USERNAME HERE'});` and hit enter."); 38 | console.log("* Now check the output in the command line console that the server is running in."); 39 | }); 40 | 41 | socket.on('join_game_success', function () { 42 | console.log(""); 43 | console.log("* * * join_game_success event received from server."); 44 | console.log("* Starting Game state."); 45 | // This player joined the game. Start the 'Game' state. 46 | _this.state.start("Game"); 47 | }); 48 | 49 | socket.on('remove_player', function (data) { 50 | console.log(""); 51 | console.log("* * * remove_player event received from server."); 52 | // Check that the 'playerSprites' object exists on whatever the context is for '_this'. 53 | if(_this.playerSprites !== undefined){ 54 | // Check that the player sprite to remove is actually in the list of player sprites. 55 | if(_this.playerSprites[data]){ 56 | // Destroy the player sprite for the player to remove. 57 | _this.playerSprites[data].destroy(); 58 | // Delete the property for that player. 59 | delete _this.playerSprites[data]; 60 | } 61 | } 62 | }); 63 | 64 | var updateCount = 0; 65 | socket.on('state_update', function (data) { 66 | 67 | // Uncomment the below code in an editor, save it and restart the client (refresh the page) to see the emitter output. 68 | /*updateCount += 1; 69 | console.log(""); 70 | console.log("* * * state_update event received from server. Update count: " + updateCount);*/ 71 | 72 | // The server sent the positions of each player with this event. Update the position of each player's sprite. 73 | // Check that the 'playerSprites' object exists on whatever the context is for '_this'. 74 | if(_this.playerSprites !== undefined){ 75 | // The 'playerSprites' object exists. 76 | for(var i= 0, len = data.length; i=31}function i(){var t=arguments,r=this.useColors;if(t[0]=(r?"%c":"")+this.namespace+(r?" %c":" ")+t[0]+(r?"%c ":" ")+"+"+e.humanize(this.diff),!r)return t;var n="color: "+this.color;t=[t[0],n,"color: inherit"].concat(Array.prototype.slice.call(t,1));var o=0,i=0;return t[0].replace(/%[a-z%]/g,function(t){"%%"!==t&&(o++,"%c"===t&&(i=o))}),t.splice(i,0,n),t}function s(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function a(t){try{null==t?e.storage.removeItem("debug"):e.storage.debug=t}catch(t){}}function c(){try{return e.storage.debug}catch(t){}if("undefined"!=typeof n&&"env"in n)return n.env.DEBUG}function u(){try{return window.localStorage}catch(t){}}e=t.exports=r(5),e.log=s,e.formatArgs=i,e.save=a,e.load=c,e.useColors=o,e.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:u(),e.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],e.formatters.j=function(t){try{return JSON.stringify(t)}catch(t){return"[UnexpectedJSONParseError]: "+t.message}},e.enable(c())}).call(e,r(4))},function(t,e){function r(){throw new Error("setTimeout has not been defined")}function n(){throw new Error("clearTimeout has not been defined")}function o(t){if(h===setTimeout)return setTimeout(t,0);if((h===r||!h)&&setTimeout)return h=setTimeout,setTimeout(t,0);try{return h(t,0)}catch(e){try{return h.call(null,t,0)}catch(e){return h.call(this,t,0)}}}function i(t){if(p===clearTimeout)return clearTimeout(t);if((p===n||!p)&&clearTimeout)return p=clearTimeout,clearTimeout(t);try{return p(t)}catch(e){try{return p.call(null,t)}catch(e){return p.call(this,t)}}}function s(){y&&l&&(y=!1,l.length?d=l.concat(d):g=-1,d.length&&a())}function a(){if(!y){var t=o(s);y=!0;for(var e=d.length;e;){for(l=d,d=[];++g1)for(var r=1;r1e4)){var e=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return r*h;case"days":case"day":case"d":return r*u;case"hours":case"hour":case"hrs":case"hr":case"h":return r*c;case"minutes":case"minute":case"mins":case"min":case"m":return r*a;case"seconds":case"second":case"secs":case"sec":case"s":return r*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}function n(t){return t>=u?Math.round(t/u)+"d":t>=c?Math.round(t/c)+"h":t>=a?Math.round(t/a)+"m":t>=s?Math.round(t/s)+"s":t+"ms"}function o(t){return i(t,u,"day")||i(t,c,"hour")||i(t,a,"minute")||i(t,s,"second")||t+" ms"}function i(t,e,r){if(!(t0)return r(t);if("number"===i&&isNaN(t)===!1)return e.long?o(t):n(t);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(t))}},function(t,e,r){function n(){}function o(t){var r="",n=!1;return r+=t.type,e.BINARY_EVENT!=t.type&&e.BINARY_ACK!=t.type||(r+=t.attachments,r+="-"),t.nsp&&"/"!=t.nsp&&(n=!0,r+=t.nsp),null!=t.id&&(n&&(r+=",",n=!1),r+=t.id),null!=t.data&&(n&&(r+=","),r+=f.stringify(t.data)),p("encoded %j as %s",t,r),r}function i(t,e){function r(t){var r=d.deconstructPacket(t),n=o(r.packet),i=r.buffers;i.unshift(n),e(i)}d.removeBlobs(t,r)}function s(){this.reconstructor=null}function a(t){var r={},n=0;if(r.type=Number(t.charAt(0)),null==e.types[r.type])return h();if(e.BINARY_EVENT==r.type||e.BINARY_ACK==r.type){for(var o="";"-"!=t.charAt(++n)&&(o+=t.charAt(n),n!=t.length););if(o!=Number(o)||"-"!=t.charAt(n))throw new Error("Illegal attachments");r.attachments=Number(o)}if("/"==t.charAt(n+1))for(r.nsp="";++n;){var i=t.charAt(n);if(","==i)break;if(r.nsp+=i,n==t.length)break}else r.nsp="/";var s=t.charAt(n+1);if(""!==s&&Number(s)==s){for(r.id="";++n;){var i=t.charAt(n);if(null==i||Number(i)!=i){--n;break}if(r.id+=t.charAt(n),n==t.length)break}r.id=Number(r.id)}return t.charAt(++n)&&(r=c(r,t.substr(n))),p("decoded %s as %j",t,r),r}function c(t,e){try{t.data=f.parse(e)}catch(t){return h()}return t}function u(t){this.reconPack=t,this.buffers=[]}function h(t){return{type:e.ERROR,data:"parser error"}}var p=r(8)("socket.io-parser"),f=r(11),l=r(13),d=r(14),y=r(16);e.protocol=4,e.types=["CONNECT","DISCONNECT","EVENT","ACK","ERROR","BINARY_EVENT","BINARY_ACK"],e.CONNECT=0,e.DISCONNECT=1,e.EVENT=2,e.ACK=3,e.ERROR=4,e.BINARY_EVENT=5,e.BINARY_ACK=6,e.Encoder=n,e.Decoder=s,n.prototype.encode=function(t,r){if(p("encoding packet %j",t),e.BINARY_EVENT==t.type||e.BINARY_ACK==t.type)i(t,r);else{var n=o(t);r([n])}},l(s.prototype),s.prototype.add=function(t){var r;if("string"==typeof t)r=a(t),e.BINARY_EVENT==r.type||e.BINARY_ACK==r.type?(this.reconstructor=new u(r),0===this.reconstructor.reconPack.attachments&&this.emit("decoded",r)):this.emit("decoded",r);else{if(!y(t)&&!t.base64)throw new Error("Unknown type: "+t);if(!this.reconstructor)throw new Error("got binary data when not reconstructing a packet");r=this.reconstructor.takeBinaryData(t),r&&(this.reconstructor=null,this.emit("decoded",r))}},s.prototype.destroy=function(){this.reconstructor&&this.reconstructor.finishedReconstruction()},u.prototype.takeBinaryData=function(t){if(this.buffers.push(t),this.buffers.length==this.reconPack.attachments){var e=d.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null},u.prototype.finishedReconstruction=function(){this.reconPack=null,this.buffers=[]}},function(t,e,r){function n(){return"WebkitAppearance"in document.documentElement.style||window.console&&(console.firebug||console.exception&&console.table)||navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31}function o(){var t=arguments,r=this.useColors;if(t[0]=(r?"%c":"")+this.namespace+(r?" %c":" ")+t[0]+(r?"%c ":" ")+"+"+e.humanize(this.diff),!r)return t;var n="color: "+this.color;t=[t[0],n,"color: inherit"].concat(Array.prototype.slice.call(t,1));var o=0,i=0;return t[0].replace(/%[a-z%]/g,function(t){"%%"!==t&&(o++,"%c"===t&&(i=o))}),t.splice(i,0,n),t}function i(){return"object"==typeof console&&console.log&&Function.prototype.apply.call(console.log,console,arguments)}function s(t){try{null==t?e.storage.removeItem("debug"):e.storage.debug=t}catch(t){}}function a(){var t;try{t=e.storage.debug}catch(t){}return t}function c(){try{return window.localStorage}catch(t){}}e=t.exports=r(9),e.log=i,e.formatArgs=o,e.save=s,e.load=a,e.useColors=n,e.storage="undefined"!=typeof chrome&&"undefined"!=typeof chrome.storage?chrome.storage.local:c(),e.colors=["lightseagreen","forestgreen","goldenrod","dodgerblue","darkorchid","crimson"],e.formatters.j=function(t){return JSON.stringify(t)},e.enable(a())},function(t,e,r){function n(){return e.colors[h++%e.colors.length]}function o(t){function r(){}function o(){var t=o,r=+new Date,i=r-(u||r);t.diff=i,t.prev=u,t.curr=r,u=r,null==t.useColors&&(t.useColors=e.useColors()),null==t.color&&t.useColors&&(t.color=n());var s=Array.prototype.slice.call(arguments);s[0]=e.coerce(s[0]),"string"!=typeof s[0]&&(s=["%o"].concat(s));var a=0;s[0]=s[0].replace(/%([a-z%])/g,function(r,n){if("%%"===r)return r;a++;var o=e.formatters[n];if("function"==typeof o){var i=s[a];r=o.call(t,i),s.splice(a,1),a--}return r}),"function"==typeof e.formatArgs&&(s=e.formatArgs.apply(t,s));var c=o.log||e.log||console.log.bind(console);c.apply(t,s)}r.enabled=!1,o.enabled=!0;var i=e.enabled(t)?o:r;return i.namespace=t,i}function i(t){e.save(t);for(var r=(t||"").split(/[\s,]+/),n=r.length,o=0;o1e4)){var e=/^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(t);if(e){var r=parseFloat(e[1]),n=(e[2]||"ms").toLowerCase();switch(n){case"years":case"year":case"yrs":case"yr":case"y":return r*h;case"days":case"day":case"d":return r*u;case"hours":case"hour":case"hrs":case"hr":case"h":return r*c;case"minutes":case"minute":case"mins":case"min":case"m":return r*a;case"seconds":case"second":case"secs":case"sec":case"s":return r*s;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r}}}}function n(t){return t>=u?Math.round(t/u)+"d":t>=c?Math.round(t/c)+"h":t>=a?Math.round(t/a)+"m":t>=s?Math.round(t/s)+"s":t+"ms"}function o(t){return i(t,u,"day")||i(t,c,"hour")||i(t,a,"minute")||i(t,s,"second")||t+" ms"}function i(t,e,r){if(!(t1)))/4)-T((t-1901+e)/100)+T((t-1601+e)/400)};if((d=m.hasOwnProperty)||(d=function(t){var e,r={};return(r.__proto__=null,r.__proto__={toString:1},r).toString!=v?d=function(t){var e=this.__proto__,r=t in(this.__proto__=null,this);return this.__proto__=e,r}:(e=r.constructor,d=function(t){var r=(this.constructor||e).prototype;return t in this&&!(t in r&&this[t]===r[t])}),r=null,d.call(this,t)}),y=function(t,e){var r,n,o,i=0;(r=function(){this.valueOf=0}).prototype.valueOf=0,n=new r;for(o in n)d.call(n,o)&&i++;return r=n=null,i?y=2==i?function(t,e){var r,n={},o=v.call(t)==w;for(r in t)o&&"prototype"==r||d.call(n,r)||!(n[r]=1)||!d.call(t,r)||e(r)}:function(t,e){var r,n,o=v.call(t)==w;for(r in t)o&&"prototype"==r||!d.call(t,r)||(n="constructor"===r)||e(r);(n||d.call(t,r="constructor"))&&e(r)}:(n=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"],y=function(t,e){var r,o,i=v.call(t)==w,a=!i&&"function"!=typeof t.constructor&&s[typeof t.hasOwnProperty]&&t.hasOwnProperty||d;for(r in t)i&&"prototype"==r||!a.call(t,r)||e(r);for(o=n.length;r=n[--o];a.call(t,r)&&e(r));}),y(t,e)},!r("json-stringify")){var N={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"},j="000000",O=function(t,e){return(j+(e||0)).slice(-t)},P="\\u00",R=function(t){for(var e='"',r=0,n=t.length,o=!S||n>10,i=o&&(S?t.split(""):t);r-1/0&&a<1/0){if(_){for(f=T(a/864e5),u=T(f/365.2425)+1970-1;_(u+1,0)<=f;u++);for(h=T((f-_(u,0))/30.42);_(u,h+1)<=f;h++);f=1+f-_(u,h),l=(a%864e5+864e5)%864e5,m=T(l/36e5)%24,b=T(l/6e4)%60,w=T(l/1e3)%60,S=l%1e3}else u=a.getUTCFullYear(),h=a.getUTCMonth(),f=a.getUTCDate(),m=a.getUTCHours(),b=a.getUTCMinutes(),w=a.getUTCSeconds(),S=a.getUTCMilliseconds();a=(u<=0||u>=1e4?(u<0?"-":"+")+O(6,u<0?-u:u):O(4,u))+"-"+O(2,h+1)+"-"+O(2,f)+"T"+O(2,m)+":"+O(2,b)+":"+O(2,w)+"."+O(3,S)+"Z"}else a=null;if(r&&(a=r.call(e,t,a)),null===a)return"null";if(c=v.call(a),c==B)return""+a;if(c==x)return a>-1/0&&a<1/0?""+a:"null";if(c==A)return R(""+a);if("object"==typeof a){for(P=s.length;P--;)if(s[P]===a)throw p();if(s.push(a),E=[],q=i,i+=o,c==C){for(j=0,P=a.length;j0)for(n="",r>10&&(r=10);n.length=48&&o<=57||o>=97&&o<=102||o>=65&&o<=70||I();t+=M("0x"+i.slice(e,q));break;default:I()}else{if(34==o)break;for(o=i.charCodeAt(q),e=q;o>=32&&92!=o&&34!=o;)o=i.charCodeAt(++q);t+=i.slice(e,q)}if(34==i.charCodeAt(q))return q++,t;I();default:if(e=q,45==o&&(n=!0,o=i.charCodeAt(++q)),o>=48&&o<=57){for(48==o&&(o=i.charCodeAt(q+1),o>=48&&o<=57)&&I(),n=!1;q=48&&o<=57);q++);if(46==i.charCodeAt(q)){for(r=++q;r=48&&o<=57);r++);r==q&&I(),q=r}if(o=i.charCodeAt(q),101==o||69==o){for(o=i.charCodeAt(++q),43!=o&&45!=o||q++,r=q;r=48&&o<=57);r++);r==q&&I(),q=r}return+i.slice(e,q)}if(n&&I(),"true"==i.slice(q,q+4))return q+=4,!0;if("false"==i.slice(q,q+5))return q+=5,!1;if("null"==i.slice(q,q+4))return q+=4,null;I()}return"$"},z=function(t){var e,r;if("$"==t&&I(),"string"==typeof t){if("@"==(S?t.charAt(0):t[0]))return t.slice(1);if("["==t){for(e=[];t=H(),"]"!=t;r||(r=!0))r&&(","==t?(t=H(),"]"==t&&I()):I()),","==t&&I(),e.push(z(t));return e}if("{"==t){for(e={};t=H(),"}"!=t;r||(r=!0))r&&(","==t?(t=H(),"}"==t&&I()):I()),","!=t&&"string"==typeof t&&"@"==(S?t.charAt(0):t[0])&&":"==H()||I(),e[t.slice(1)]=z(H());return e}I()}return t},J=function(t,e,r){var n=X(t,e,r);n===g?delete t[e]:t[e]=n},X=function(t,e,r){var n,o=t[e];if("object"==typeof o&&o)if(v.call(o)==C)for(n=o.length;n--;)J(o,n,r);else y(o,function(t){J(o,t,r)});return r.call(t,e,o)};e.parse=function(t,e){var r,n;return q=0,U=""+t,r=z(H()),"$"!=H()&&I(),q=U=null,e&&v.call(e)==w?X((n={},n[""]=r,n),"",e):r}}}return e.runInContext=o,e}var i="function"==typeof n&&n.amd,s={function:!0,object:!0},a=s[typeof e]&&e&&!e.nodeType&&e,c=s[typeof window]&&window||this,u=a&&s[typeof t]&&t&&!t.nodeType&&"object"==typeof r&&r;if(!u||u.global!==u&&u.window!==u&&u.self!==u||(c=u),a&&!i)o(c,a);else{var h=c.JSON,p=c.JSON3,f=!1,l=o(c,c.JSON3={noConflict:function(){return f||(f=!0,c.JSON=h,c.JSON3=p,h=p=null),l}});c.JSON={parse:l.parse,stringify:l.stringify}}i&&n(function(){return l})}).call(this)}).call(e,r(12)(t),function(){return this}())},function(t,e){t.exports=function(t){return t.webpackPolyfill||(t.deprecate=function(){},t.paths=[],t.children=[],t.webpackPolyfill=1),t}},function(t,e){function r(t){if(t)return n(t)}function n(t){for(var e in r.prototype)t[e]=r.prototype[e];return t}t.exports=r,r.prototype.on=r.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks[t]=this._callbacks[t]||[]).push(e),this},r.prototype.once=function(t,e){function r(){n.off(t,r),e.apply(this,arguments)}var n=this;return this._callbacks=this._callbacks||{},r.fn=e,this.on(t,r),this},r.prototype.off=r.prototype.removeListener=r.prototype.removeAllListeners=r.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var r=this._callbacks[t];if(!r)return this;if(1==arguments.length)return delete this._callbacks[t],this;for(var n,o=0;o0&&!this.encoding){var t=this.packetBuffer.shift();this.packet(t)}},n.prototype.cleanup=function(){p("cleanup");for(var t=this.subs.length,e=0;e=this._reconnectionAttempts)p("reconnect failed"),this.backoff.reset(),this.emitAll("reconnect_failed"),this.reconnecting=!1;else{var e=this.backoff.duration();p("will wait %dms before reconnect attempt",e),this.reconnecting=!0;var r=setTimeout(function(){t.skipReconnect||(p("attempting reconnect"),t.emitAll("reconnect_attempt",t.backoff.attempts),t.emitAll("reconnecting",t.backoff.attempts),t.skipReconnect||t.open(function(e){e?(p("reconnect attempt error"),t.reconnecting=!1,t.reconnect(),t.emitAll("reconnect_error",e.data)):(p("reconnect success"),t.onreconnect())}))},e);this.subs.push({destroy:function(){clearTimeout(r)}})}},n.prototype.onreconnect=function(){var t=this.backoff.attempts;this.reconnecting=!1,this.backoff.reset(),this.updateSocketIds(),this.emitAll("reconnect",t)}},function(t,e,r){t.exports=r(19)},function(t,e,r){t.exports=r(20),t.exports.parser=r(27)},function(t,e,r){(function(e){function n(t,r){if(!(this instanceof n))return new n(t,r);r=r||{},t&&"object"==typeof t&&(r=t,t=null),t?(t=h(t),r.hostname=t.host,r.secure="https"===t.protocol||"wss"===t.protocol,r.port=t.port,t.query&&(r.query=t.query)):r.host&&(r.hostname=h(r.host).host), 2 | this.secure=null!=r.secure?r.secure:e.location&&"https:"===location.protocol,r.hostname&&!r.port&&(r.port=this.secure?"443":"80"),this.agent=r.agent||!1,this.hostname=r.hostname||(e.location?location.hostname:"localhost"),this.port=r.port||(e.location&&location.port?location.port:this.secure?443:80),this.query=r.query||{},"string"==typeof this.query&&(this.query=f.decode(this.query)),this.upgrade=!1!==r.upgrade,this.path=(r.path||"/engine.io").replace(/\/$/,"")+"/",this.forceJSONP=!!r.forceJSONP,this.jsonp=!1!==r.jsonp,this.forceBase64=!!r.forceBase64,this.enablesXDR=!!r.enablesXDR,this.timestampParam=r.timestampParam||"t",this.timestampRequests=r.timestampRequests,this.transports=r.transports||["polling","websocket"],this.readyState="",this.writeBuffer=[],this.prevBufferLen=0,this.policyPort=r.policyPort||843,this.rememberUpgrade=r.rememberUpgrade||!1,this.binaryType=null,this.onlyBinaryUpgrades=r.onlyBinaryUpgrades,this.perMessageDeflate=!1!==r.perMessageDeflate&&(r.perMessageDeflate||{}),!0===this.perMessageDeflate&&(this.perMessageDeflate={}),this.perMessageDeflate&&null==this.perMessageDeflate.threshold&&(this.perMessageDeflate.threshold=1024),this.pfx=r.pfx||null,this.key=r.key||null,this.passphrase=r.passphrase||null,this.cert=r.cert||null,this.ca=r.ca||null,this.ciphers=r.ciphers||null,this.rejectUnauthorized=void 0===r.rejectUnauthorized?null:r.rejectUnauthorized,this.forceNode=!!r.forceNode;var o="object"==typeof e&&e;o.global===o&&(r.extraHeaders&&Object.keys(r.extraHeaders).length>0&&(this.extraHeaders=r.extraHeaders),r.localAddress&&(this.localAddress=r.localAddress)),this.id=null,this.upgrades=null,this.pingInterval=null,this.pingTimeout=null,this.pingIntervalTimer=null,this.pingTimeoutTimer=null,this.open()}function o(t){var e={};for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e}var i=r(21),s=r(35),a=r(3)("engine.io-client:socket"),c=r(42),u=r(27),h=r(2),p=r(43),f=r(36);t.exports=n,n.priorWebsocketSuccess=!1,s(n.prototype),n.protocol=u.protocol,n.Socket=n,n.Transport=r(26),n.transports=r(21),n.parser=r(27),n.prototype.createTransport=function(t){a('creating transport "%s"',t);var e=o(this.query);e.EIO=u.protocol,e.transport=t,this.id&&(e.sid=this.id);var r=new i[t]({agent:this.agent,hostname:this.hostname,port:this.port,secure:this.secure,path:this.path,query:e,forceJSONP:this.forceJSONP,jsonp:this.jsonp,forceBase64:this.forceBase64,enablesXDR:this.enablesXDR,timestampRequests:this.timestampRequests,timestampParam:this.timestampParam,policyPort:this.policyPort,socket:this,pfx:this.pfx,key:this.key,passphrase:this.passphrase,cert:this.cert,ca:this.ca,ciphers:this.ciphers,rejectUnauthorized:this.rejectUnauthorized,perMessageDeflate:this.perMessageDeflate,extraHeaders:this.extraHeaders,forceNode:this.forceNode,localAddress:this.localAddress});return r},n.prototype.open=function(){var t;if(this.rememberUpgrade&&n.priorWebsocketSuccess&&this.transports.indexOf("websocket")!==-1)t="websocket";else{if(0===this.transports.length){var e=this;return void setTimeout(function(){e.emit("error","No transports available")},0)}t=this.transports[0]}this.readyState="opening";try{t=this.createTransport(t)}catch(t){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t)},n.prototype.setTransport=function(t){a("setting transport %s",t.name);var e=this;this.transport&&(a("clearing existing transport %s",this.transport.name),this.transport.removeAllListeners()),this.transport=t,t.on("drain",function(){e.onDrain()}).on("packet",function(t){e.onPacket(t)}).on("error",function(t){e.onError(t)}).on("close",function(){e.onClose("transport close")})},n.prototype.probe=function(t){function e(){if(f.onlyBinaryUpgrades){var e=!this.supportsBinary&&f.transport.supportsBinary;p=p||e}p||(a('probe transport "%s" opened',t),h.send([{type:"ping",data:"probe"}]),h.once("packet",function(e){if(!p)if("pong"===e.type&&"probe"===e.data){if(a('probe transport "%s" pong',t),f.upgrading=!0,f.emit("upgrading",h),!h)return;n.priorWebsocketSuccess="websocket"===h.name,a('pausing current transport "%s"',f.transport.name),f.transport.pause(function(){p||"closed"!==f.readyState&&(a("changing transport and sending upgrade packet"),u(),f.setTransport(h),h.send([{type:"upgrade"}]),f.emit("upgrade",h),h=null,f.upgrading=!1,f.flush())})}else{a('probe transport "%s" failed',t);var r=new Error("probe error");r.transport=h.name,f.emit("upgradeError",r)}}))}function r(){p||(p=!0,u(),h.close(),h=null)}function o(e){var n=new Error("probe error: "+e);n.transport=h.name,r(),a('probe transport "%s" failed because of error: %s',t,e),f.emit("upgradeError",n)}function i(){o("transport closed")}function s(){o("socket closed")}function c(t){h&&t.name!==h.name&&(a('"%s" works - aborting "%s"',t.name,h.name),r())}function u(){h.removeListener("open",e),h.removeListener("error",o),h.removeListener("close",i),f.removeListener("close",s),f.removeListener("upgrading",c)}a('probing transport "%s"',t);var h=this.createTransport(t,{probe:1}),p=!1,f=this;n.priorWebsocketSuccess=!1,h.once("open",e),h.once("error",o),h.once("close",i),this.once("close",s),this.once("upgrading",c),h.open()},n.prototype.onOpen=function(){if(a("socket open"),this.readyState="open",n.priorWebsocketSuccess="websocket"===this.transport.name,this.emit("open"),this.flush(),"open"===this.readyState&&this.upgrade&&this.transport.pause){a("starting upgrade probes");for(var t=0,e=this.upgrades.length;t1?{type:b[o],data:t.substring(1)}:{type:b[o]}:w}var i=new Uint8Array(t),o=i[0],s=f(t,1);return k&&"blob"===r&&(s=new k([s])),{type:b[o],data:s}},e.decodeBase64Packet=function(t,e){var r=b[t.charAt(0)];if(!u)return{type:r,data:{base64:!0,data:t.substr(1)}};var n=u.decode(t.substr(1));return"blob"===e&&k&&(n=new k([n])),{type:r,data:n}},e.encodePayload=function(t,r,n){function o(t){return t.length+":"+t}function i(t,n){e.encodePacket(t,!!s&&r,!0,function(t){n(null,o(t))})}"function"==typeof r&&(n=r,r=null);var s=p(t);return r&&s?k&&!m?e.encodePayloadAsBlob(t,n):e.encodePayloadAsArrayBuffer(t,n):t.length?void c(t,i,function(t,e){return n(e.join(""))}):n("0:")},e.decodePayload=function(t,r,n){if("string"!=typeof t)return e.decodePayloadAsBinary(t,r,n);"function"==typeof r&&(n=r,r=null);var o;if(""==t)return n(w,0,1);for(var i,s,a="",c=0,u=t.length;c0;){for(var a=new Uint8Array(o),c=0===a[0],u="",h=1;255!=a[h];h++){if(u.length>310){s=!0;break}u+=a[h]}if(s)return n(w,0,1);o=f(o,2+u.length),u=parseInt(u);var p=f(o,0,u);if(c)try{p=String.fromCharCode.apply(null,new Uint8Array(p))}catch(t){var l=new Uint8Array(p);p="";for(var h=0;hn&&(r=n),e>=n||e>=r||0===n)return new ArrayBuffer(0);for(var o=new Uint8Array(t),i=new Uint8Array(r-e),s=e,a=0;s=55296&&e<=56319&&o65535&&(e-=65536,o+=b(e>>>10&1023|55296),e=56320|1023&e),o+=b(e);return o}function c(t,e){return b(t>>e&63|128)}function u(t){if(0==(4294967168&t))return b(t);var e="";return 0==(4294965248&t)?e=b(t>>6&31|192):0==(4294901760&t)?(e=b(t>>12&15|224),e+=c(t,6)):0==(4292870144&t)&&(e=b(t>>18&7|240),e+=c(t,12),e+=c(t,6)),e+=b(63&t|128)}function h(t){for(var e,r=s(t),n=r.length,o=-1,i="";++o=m)throw Error("Invalid byte index");var t=255&g[v];if(v++,128==(192&t))return 63&t;throw Error("Invalid continuation byte")}function f(){var t,e,r,n,o;if(v>m)throw Error("Invalid byte index");if(v==m)return!1;if(t=255&g[v],v++,0==(128&t))return t;if(192==(224&t)){var e=p();if(o=(31&t)<<6|e,o>=128)return o;throw Error("Invalid continuation byte")}if(224==(240&t)){if(e=p(),r=p(),o=(15&t)<<12|e<<6|r,o>=2048)return o;throw Error("Invalid continuation byte")}if(240==(248&t)&&(e=p(),r=p(),n=p(),o=(15&t)<<18|e<<12|r<<6|n,o>=65536&&o<=1114111))return o;throw Error("Invalid WTF-8 detected")}function l(t){g=s(t),m=g.length,v=0;for(var e,r=[];(e=f())!==!1;)r.push(e);return a(r)}var d="object"==typeof e&&e,y=("object"==typeof t&&t&&t.exports==d&&t,"object"==typeof o&&o);y.global!==y&&y.window!==y||(i=y);var g,m,v,b=String.fromCharCode,w={version:"1.0.0",encode:h,decode:l};n=function(){return w}.call(e,r,e,t),!(void 0!==n&&(t.exports=n))}(this)}).call(e,r(12)(t),function(){return this}())},function(t,e){!function(){"use strict";for(var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",r=new Uint8Array(256),n=0;n>2],i+=t[(3&n[r])<<4|n[r+1]>>4],i+=t[(15&n[r+1])<<2|n[r+2]>>6],i+=t[63&n[r+2]];return o%3===2?i=i.substring(0,i.length-1)+"=":o%3===1&&(i=i.substring(0,i.length-2)+"=="),i},e.decode=function(t){var e,n,o,i,s,a=.75*t.length,c=t.length,u=0;"="===t[t.length-1]&&(a--,"="===t[t.length-2]&&a--);var h=new ArrayBuffer(a),p=new Uint8Array(h);for(e=0;e>4,p[u++]=(15&o)<<4|i>>2,p[u++]=(3&i)<<6|63&s;return h}}()},function(t,e){(function(e){function r(t){for(var e=0;e0);return e}function n(t){var e=0;for(h=0;h';i=document.createElement(t)}catch(t){i=document.createElement("iframe"),i.name=o.iframeId,i.src="javascript:0"}i.id=o.iframeId,o.form.appendChild(i),o.iframe=i}var o=this;if(!this.form){var i,s=document.createElement("form"),a=document.createElement("textarea"),h=this.iframeId="eio_iframe_"+this.index;s.className="socketio",s.style.position="absolute",s.style.top="-1000px",s.style.left="-1000px",s.target=h,s.method="POST",s.setAttribute("accept-charset","utf-8"),a.name="d",s.appendChild(a),document.body.appendChild(s),this.form=s,this.area=a}this.form.action=this.uri(),n(),t=t.replace(u,"\\\n"),this.area.value=t.replace(c,"\\n");try{this.form.submit()}catch(t){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){"complete"===o.iframe.readyState&&r(); 3 | }:this.iframe.onload=r}}).call(e,function(){return this}())},function(t,e,r){(function(e){function n(t){var e=t&&t.forceBase64;e&&(this.supportsBinary=!1),this.perMessageDeflate=t.perMessageDeflate,this.usingBrowserWebSocket=p&&!t.forceNode,this.usingBrowserWebSocket||(f=o),i.call(this,t)}var o,i=r(26),s=r(27),a=r(36),c=r(37),u=r(38),h=r(3)("engine.io-client:websocket"),p=e.WebSocket||e.MozWebSocket;if("undefined"==typeof window)try{o=r(41)}catch(t){}var f=p;f||"undefined"!=typeof window||(f=o),t.exports=n,c(n,i),n.prototype.name="websocket",n.prototype.supportsBinary=!0,n.prototype.doOpen=function(){if(this.check()){var t=this.uri(),e=void 0,r={agent:this.agent,perMessageDeflate:this.perMessageDeflate};r.pfx=this.pfx,r.key=this.key,r.passphrase=this.passphrase,r.cert=this.cert,r.ca=this.ca,r.ciphers=this.ciphers,r.rejectUnauthorized=this.rejectUnauthorized,this.extraHeaders&&(r.headers=this.extraHeaders),this.localAddress&&(r.localAddress=this.localAddress);try{this.ws=this.usingBrowserWebSocket?new f(t):new f(t,e,r)}catch(t){return this.emit("error",t)}void 0===this.ws.binaryType&&(this.supportsBinary=!1),this.ws.supports&&this.ws.supports.binary?(this.supportsBinary=!0,this.ws.binaryType="nodebuffer"):this.ws.binaryType="arraybuffer",this.addEventListeners()}},n.prototype.addEventListeners=function(){var t=this;this.ws.onopen=function(){t.onOpen()},this.ws.onclose=function(){t.onClose()},this.ws.onmessage=function(e){t.onData(e.data)},this.ws.onerror=function(e){t.onError("websocket error",e)}},n.prototype.write=function(t){function r(){n.emit("flush"),setTimeout(function(){n.writable=!0,n.emit("drain")},0)}var n=this;this.writable=!1;for(var o=t.length,i=0,a=o;i0&&t.jitter<=1?t.jitter:0,this.attempts=0}t.exports=r,r.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),r=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-r:t+r}return 0|Math.min(t,this.max)},r.prototype.reset=function(){this.attempts=0},r.prototype.setMin=function(t){this.ms=t},r.prototype.setMax=function(t){this.max=t},r.prototype.setJitter=function(t){this.jitter=t}}])}); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic-networked-multiplayer-game", 3 | "version": "0.0.1", 4 | "description": "A basic networked multiplayer game example/template, with lots of explanations.", 5 | "dependencies": { 6 | "express": "^4.15.2", 7 | "socket.io": "^1.7.3" 8 | } 9 | } 10 | --------------------------------------------------------------------------------