├── .gitignore ├── README.md ├── frontend ├── index.html └── index.js └── server ├── constants.js ├── game.js ├── package.json ├── server.js ├── utils.js └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | out 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and not Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | 108 | # Stores VSCode versions used for testing VSCode extensions 109 | .vscode-test 110 | 111 | # yarn v2 112 | .yarn/cache 113 | .yarn/unplugged 114 | .yarn/build-state.yml 115 | .yarn/install-state.gz 116 | .pnp.* 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # multiplayerSnake 2 | Code for multiplayer snake game with [socket.io tutorial](https://www.youtube.com/watch?v=ppcBIHv_ZPs) on the Traversy Media YouTube channel. 3 | 4 | If you want to see how to deploy the game check out [this video](https://www.youtube.com/watch?v=M9RDYkFs-EQ) 5 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | MultiPlayer Snake 6 | 7 | 8 | 13 | 14 | 15 |
16 |
17 | 18 |
19 |
20 |

Multiplayer Snake

21 | 28 |
OR
29 |
30 | 31 |
32 | 39 |
40 |
41 | 42 |
43 |
44 | 45 |

Your game code is:

46 | 47 | 48 |
49 |
50 | 51 |
52 |
53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /frontend/index.js: -------------------------------------------------------------------------------- 1 | const BG_COLOUR = '#231f20'; 2 | const SNAKE_COLOUR = '#c2c2c2'; 3 | const FOOD_COLOUR = '#e66916'; 4 | 5 | const socket = io('https://sleepy-island-33889.herokuapp.com/'); 6 | 7 | socket.on('init', handleInit); 8 | socket.on('gameState', handleGameState); 9 | socket.on('gameOver', handleGameOver); 10 | socket.on('gameCode', handleGameCode); 11 | socket.on('unknownCode', handleUnknownCode); 12 | socket.on('tooManyPlayers', handleTooManyPlayers); 13 | 14 | const gameScreen = document.getElementById('gameScreen'); 15 | const initialScreen = document.getElementById('initialScreen'); 16 | const newGameBtn = document.getElementById('newGameButton'); 17 | const joinGameBtn = document.getElementById('joinGameButton'); 18 | const gameCodeInput = document.getElementById('gameCodeInput'); 19 | const gameCodeDisplay = document.getElementById('gameCodeDisplay'); 20 | 21 | newGameBtn.addEventListener('click', newGame); 22 | joinGameBtn.addEventListener('click', joinGame); 23 | 24 | 25 | function newGame() { 26 | socket.emit('newGame'); 27 | init(); 28 | } 29 | 30 | function joinGame() { 31 | const code = gameCodeInput.value; 32 | socket.emit('joinGame', code); 33 | init(); 34 | } 35 | 36 | let canvas, ctx; 37 | let playerNumber; 38 | let gameActive = false; 39 | 40 | function init() { 41 | initialScreen.style.display = "none"; 42 | gameScreen.style.display = "block"; 43 | 44 | canvas = document.getElementById('canvas'); 45 | ctx = canvas.getContext('2d'); 46 | 47 | canvas.width = canvas.height = 600; 48 | 49 | ctx.fillStyle = BG_COLOUR; 50 | ctx.fillRect(0, 0, canvas.width, canvas.height); 51 | 52 | document.addEventListener('keydown', keydown); 53 | gameActive = true; 54 | } 55 | 56 | function keydown(e) { 57 | socket.emit('keydown', e.keyCode); 58 | } 59 | 60 | function paintGame(state) { 61 | ctx.fillStyle = BG_COLOUR; 62 | ctx.fillRect(0, 0, canvas.width, canvas.height); 63 | 64 | const food = state.food; 65 | const gridsize = state.gridsize; 66 | const size = canvas.width / gridsize; 67 | 68 | ctx.fillStyle = FOOD_COLOUR; 69 | ctx.fillRect(food.x * size, food.y * size, size, size); 70 | 71 | paintPlayer(state.players[0], size, SNAKE_COLOUR); 72 | paintPlayer(state.players[1], size, 'red'); 73 | } 74 | 75 | function paintPlayer(playerState, size, colour) { 76 | const snake = playerState.snake; 77 | 78 | ctx.fillStyle = colour; 79 | for (let cell of snake) { 80 | ctx.fillRect(cell.x * size, cell.y * size, size, size); 81 | } 82 | } 83 | 84 | function handleInit(number) { 85 | playerNumber = number; 86 | } 87 | 88 | function handleGameState(gameState) { 89 | if (!gameActive) { 90 | return; 91 | } 92 | gameState = JSON.parse(gameState); 93 | requestAnimationFrame(() => paintGame(gameState)); 94 | } 95 | 96 | function handleGameOver(data) { 97 | if (!gameActive) { 98 | return; 99 | } 100 | data = JSON.parse(data); 101 | 102 | gameActive = false; 103 | 104 | if (data.winner === playerNumber) { 105 | alert('You Win!'); 106 | } else { 107 | alert('You Lose :('); 108 | } 109 | } 110 | 111 | function handleGameCode(gameCode) { 112 | gameCodeDisplay.innerText = gameCode; 113 | } 114 | 115 | function handleUnknownCode() { 116 | reset(); 117 | alert('Unknown Game Code') 118 | } 119 | 120 | function handleTooManyPlayers() { 121 | reset(); 122 | alert('This game is already in progress'); 123 | } 124 | 125 | function reset() { 126 | playerNumber = null; 127 | gameCodeInput.value = ''; 128 | initialScreen.style.display = "block"; 129 | gameScreen.style.display = "none"; 130 | } 131 | -------------------------------------------------------------------------------- /server/constants.js: -------------------------------------------------------------------------------- 1 | const FRAME_RATE = 10; 2 | const GRID_SIZE = 20; 3 | 4 | module.exports = { 5 | FRAME_RATE, 6 | GRID_SIZE, 7 | } 8 | -------------------------------------------------------------------------------- /server/game.js: -------------------------------------------------------------------------------- 1 | const { GRID_SIZE } = require('./constants'); 2 | 3 | module.exports = { 4 | initGame, 5 | gameLoop, 6 | getUpdatedVelocity, 7 | } 8 | 9 | function initGame() { 10 | const state = createGameState() 11 | randomFood(state); 12 | return state; 13 | } 14 | 15 | function createGameState() { 16 | return { 17 | players: [{ 18 | pos: { 19 | x: 3, 20 | y: 10, 21 | }, 22 | vel: { 23 | x: 1, 24 | y: 0, 25 | }, 26 | snake: [ 27 | {x: 1, y: 10}, 28 | {x: 2, y: 10}, 29 | {x: 3, y: 10}, 30 | ], 31 | }, { 32 | pos: { 33 | x: 18, 34 | y: 10, 35 | }, 36 | vel: { 37 | x: 0, 38 | y: 0, 39 | }, 40 | snake: [ 41 | {x: 20, y: 10}, 42 | {x: 19, y: 10}, 43 | {x: 18, y: 10}, 44 | ], 45 | }], 46 | food: {}, 47 | gridsize: GRID_SIZE, 48 | }; 49 | } 50 | 51 | function gameLoop(state) { 52 | if (!state) { 53 | return; 54 | } 55 | 56 | const playerOne = state.players[0]; 57 | const playerTwo = state.players[1]; 58 | 59 | playerOne.pos.x += playerOne.vel.x; 60 | playerOne.pos.y += playerOne.vel.y; 61 | 62 | playerTwo.pos.x += playerTwo.vel.x; 63 | playerTwo.pos.y += playerTwo.vel.y; 64 | 65 | if (playerOne.pos.x < 0 || playerOne.pos.x > GRID_SIZE || playerOne.pos.y < 0 || playerOne.pos.y > GRID_SIZE) { 66 | return 2; 67 | } 68 | 69 | if (playerTwo.pos.x < 0 || playerTwo.pos.x > GRID_SIZE || playerTwo.pos.y < 0 || playerTwo.pos.y > GRID_SIZE) { 70 | return 1; 71 | } 72 | 73 | if (state.food.x === playerOne.pos.x && state.food.y === playerOne.pos.y) { 74 | playerOne.snake.push({ ...playerOne.pos }); 75 | playerOne.pos.x += playerOne.vel.x; 76 | playerOne.pos.y += playerOne.vel.y; 77 | randomFood(state); 78 | } 79 | 80 | if (state.food.x === playerTwo.pos.x && state.food.y === playerTwo.pos.y) { 81 | playerTwo.snake.push({ ...playerTwo.pos }); 82 | playerTwo.pos.x += playerTwo.vel.x; 83 | playerTwo.pos.y += playerTwo.vel.y; 84 | randomFood(state); 85 | } 86 | 87 | if (playerOne.vel.x || playerOne.vel.y) { 88 | for (let cell of playerOne.snake) { 89 | if (cell.x === playerOne.pos.x && cell.y === playerOne.pos.y) { 90 | return 2; 91 | } 92 | } 93 | 94 | playerOne.snake.push({ ...playerOne.pos }); 95 | playerOne.snake.shift(); 96 | } 97 | 98 | if (playerTwo.vel.x || playerTwo.vel.y) { 99 | for (let cell of playerTwo.snake) { 100 | if (cell.x === playerTwo.pos.x && cell.y === playerTwo.pos.y) { 101 | return 1; 102 | } 103 | } 104 | 105 | playerTwo.snake.push({ ...playerTwo.pos }); 106 | playerTwo.snake.shift(); 107 | } 108 | 109 | return false; 110 | } 111 | 112 | function randomFood(state) { 113 | food = { 114 | x: Math.floor(Math.random() * GRID_SIZE), 115 | y: Math.floor(Math.random() * GRID_SIZE), 116 | } 117 | 118 | for (let cell of state.players[0].snake) { 119 | if (cell.x === food.x && cell.y === food.y) { 120 | return randomFood(state); 121 | } 122 | } 123 | 124 | for (let cell of state.players[1].snake) { 125 | if (cell.x === food.x && cell.y === food.y) { 126 | return randomFood(state); 127 | } 128 | } 129 | 130 | state.food = food; 131 | } 132 | 133 | function getUpdatedVelocity(keyCode) { 134 | switch (keyCode) { 135 | case 37: { // left 136 | return { x: -1, y: 0 }; 137 | } 138 | case 38: { // down 139 | return { x: 0, y: -1 }; 140 | } 141 | case 39: { // right 142 | return { x: 1, y: 0 }; 143 | } 144 | case 40: { // up 145 | return { x: 0, y: 1 }; 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "socket.io": "^2.3.0" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /server/server.js: -------------------------------------------------------------------------------- 1 | const io = require('socket.io')(); 2 | const { initGame, gameLoop, getUpdatedVelocity } = require('./game'); 3 | const { FRAME_RATE } = require('./constants'); 4 | const { makeid } = require('./utils'); 5 | 6 | const state = {}; 7 | const clientRooms = {}; 8 | 9 | io.on('connection', client => { 10 | 11 | client.on('keydown', handleKeydown); 12 | client.on('newGame', handleNewGame); 13 | client.on('joinGame', handleJoinGame); 14 | 15 | function handleJoinGame(roomName) { 16 | const room = io.sockets.adapter.rooms[roomName]; 17 | 18 | let allUsers; 19 | if (room) { 20 | allUsers = room.sockets; 21 | } 22 | 23 | let numClients = 0; 24 | if (allUsers) { 25 | numClients = Object.keys(allUsers).length; 26 | } 27 | 28 | if (numClients === 0) { 29 | client.emit('unknownCode'); 30 | return; 31 | } else if (numClients > 1) { 32 | client.emit('tooManyPlayers'); 33 | return; 34 | } 35 | 36 | clientRooms[client.id] = roomName; 37 | 38 | client.join(roomName); 39 | client.number = 2; 40 | client.emit('init', 2); 41 | 42 | startGameInterval(roomName); 43 | } 44 | 45 | function handleNewGame() { 46 | let roomName = makeid(5); 47 | clientRooms[client.id] = roomName; 48 | client.emit('gameCode', roomName); 49 | 50 | state[roomName] = initGame(); 51 | 52 | client.join(roomName); 53 | client.number = 1; 54 | client.emit('init', 1); 55 | } 56 | 57 | function handleKeydown(keyCode) { 58 | const roomName = clientRooms[client.id]; 59 | if (!roomName) { 60 | return; 61 | } 62 | try { 63 | keyCode = parseInt(keyCode); 64 | } catch(e) { 65 | console.error(e); 66 | return; 67 | } 68 | 69 | const vel = getUpdatedVelocity(keyCode); 70 | 71 | if (vel) { 72 | state[roomName].players[client.number - 1].vel = vel; 73 | } 74 | } 75 | }); 76 | 77 | function startGameInterval(roomName) { 78 | const intervalId = setInterval(() => { 79 | const winner = gameLoop(state[roomName]); 80 | 81 | if (!winner) { 82 | emitGameState(roomName, state[roomName]) 83 | } else { 84 | emitGameOver(roomName, winner); 85 | state[roomName] = null; 86 | clearInterval(intervalId); 87 | } 88 | }, 1000 / FRAME_RATE); 89 | } 90 | 91 | function emitGameState(room, gameState) { 92 | // Send this event to everyone in the room. 93 | io.sockets.in(room) 94 | .emit('gameState', JSON.stringify(gameState)); 95 | } 96 | 97 | function emitGameOver(room, winner) { 98 | io.sockets.in(room) 99 | .emit('gameOver', JSON.stringify({ winner })); 100 | } 101 | 102 | io.listen(process.env.PORT || 3000); 103 | -------------------------------------------------------------------------------- /server/utils.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | makeid, 3 | } 4 | 5 | function makeid(length) { 6 | var result = ''; 7 | var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 8 | var charactersLength = characters.length; 9 | for ( var i = 0; i < length; i++ ) { 10 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 11 | } 12 | return result; 13 | } 14 | -------------------------------------------------------------------------------- /server/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | accepts@~1.3.4: 6 | version "1.3.7" 7 | resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" 8 | integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== 9 | dependencies: 10 | mime-types "~2.1.24" 11 | negotiator "0.6.2" 12 | 13 | after@0.8.2: 14 | version "0.8.2" 15 | resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" 16 | integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= 17 | 18 | arraybuffer.slice@~0.0.7: 19 | version "0.0.7" 20 | resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" 21 | integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== 22 | 23 | async-limiter@~1.0.0: 24 | version "1.0.1" 25 | resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" 26 | integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== 27 | 28 | backo2@1.0.2: 29 | version "1.0.2" 30 | resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" 31 | integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= 32 | 33 | base64-arraybuffer@0.1.5: 34 | version "0.1.5" 35 | resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" 36 | integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= 37 | 38 | base64id@2.0.0: 39 | version "2.0.0" 40 | resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" 41 | integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== 42 | 43 | better-assert@~1.0.0: 44 | version "1.0.2" 45 | resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" 46 | integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= 47 | dependencies: 48 | callsite "1.0.0" 49 | 50 | blob@0.0.5: 51 | version "0.0.5" 52 | resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" 53 | integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== 54 | 55 | callsite@1.0.0: 56 | version "1.0.0" 57 | resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" 58 | integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= 59 | 60 | component-bind@1.0.0: 61 | version "1.0.0" 62 | resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" 63 | integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= 64 | 65 | component-emitter@1.2.1: 66 | version "1.2.1" 67 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 68 | integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= 69 | 70 | component-emitter@~1.3.0: 71 | version "1.3.0" 72 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" 73 | integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== 74 | 75 | component-inherit@0.0.3: 76 | version "0.0.3" 77 | resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" 78 | integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= 79 | 80 | cookie@0.3.1: 81 | version "0.3.1" 82 | resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" 83 | integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= 84 | 85 | debug@~3.1.0: 86 | version "3.1.0" 87 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 88 | integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== 89 | dependencies: 90 | ms "2.0.0" 91 | 92 | debug@~4.1.0: 93 | version "4.1.1" 94 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 95 | integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 96 | dependencies: 97 | ms "^2.1.1" 98 | 99 | engine.io-client@~3.4.0: 100 | version "3.4.3" 101 | resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.3.tgz#192d09865403e3097e3575ebfeb3861c4d01a66c" 102 | integrity sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw== 103 | dependencies: 104 | component-emitter "~1.3.0" 105 | component-inherit "0.0.3" 106 | debug "~4.1.0" 107 | engine.io-parser "~2.2.0" 108 | has-cors "1.1.0" 109 | indexof "0.0.1" 110 | parseqs "0.0.5" 111 | parseuri "0.0.5" 112 | ws "~6.1.0" 113 | xmlhttprequest-ssl "~1.5.4" 114 | yeast "0.1.2" 115 | 116 | engine.io-parser@~2.2.0: 117 | version "2.2.0" 118 | resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed" 119 | integrity sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w== 120 | dependencies: 121 | after "0.8.2" 122 | arraybuffer.slice "~0.0.7" 123 | base64-arraybuffer "0.1.5" 124 | blob "0.0.5" 125 | has-binary2 "~1.0.2" 126 | 127 | engine.io@~3.4.0: 128 | version "3.4.2" 129 | resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.4.2.tgz#8fc84ee00388e3e228645e0a7d3dfaeed5bd122c" 130 | integrity sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg== 131 | dependencies: 132 | accepts "~1.3.4" 133 | base64id "2.0.0" 134 | cookie "0.3.1" 135 | debug "~4.1.0" 136 | engine.io-parser "~2.2.0" 137 | ws "^7.1.2" 138 | 139 | has-binary2@~1.0.2: 140 | version "1.0.3" 141 | resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" 142 | integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== 143 | dependencies: 144 | isarray "2.0.1" 145 | 146 | has-cors@1.1.0: 147 | version "1.1.0" 148 | resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" 149 | integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= 150 | 151 | indexof@0.0.1: 152 | version "0.0.1" 153 | resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" 154 | integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= 155 | 156 | isarray@2.0.1: 157 | version "2.0.1" 158 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" 159 | integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= 160 | 161 | mime-db@1.44.0: 162 | version "1.44.0" 163 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" 164 | integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== 165 | 166 | mime-types@~2.1.24: 167 | version "2.1.27" 168 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" 169 | integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== 170 | dependencies: 171 | mime-db "1.44.0" 172 | 173 | ms@2.0.0: 174 | version "2.0.0" 175 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 176 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 177 | 178 | ms@^2.1.1: 179 | version "2.1.2" 180 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 181 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 182 | 183 | negotiator@0.6.2: 184 | version "0.6.2" 185 | resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" 186 | integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== 187 | 188 | object-component@0.0.3: 189 | version "0.0.3" 190 | resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" 191 | integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= 192 | 193 | parseqs@0.0.5: 194 | version "0.0.5" 195 | resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" 196 | integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= 197 | dependencies: 198 | better-assert "~1.0.0" 199 | 200 | parseuri@0.0.5: 201 | version "0.0.5" 202 | resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" 203 | integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= 204 | dependencies: 205 | better-assert "~1.0.0" 206 | 207 | socket.io-adapter@~1.1.0: 208 | version "1.1.2" 209 | resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" 210 | integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== 211 | 212 | socket.io-client@2.3.0: 213 | version "2.3.0" 214 | resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4" 215 | integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== 216 | dependencies: 217 | backo2 "1.0.2" 218 | base64-arraybuffer "0.1.5" 219 | component-bind "1.0.0" 220 | component-emitter "1.2.1" 221 | debug "~4.1.0" 222 | engine.io-client "~3.4.0" 223 | has-binary2 "~1.0.2" 224 | has-cors "1.1.0" 225 | indexof "0.0.1" 226 | object-component "0.0.3" 227 | parseqs "0.0.5" 228 | parseuri "0.0.5" 229 | socket.io-parser "~3.3.0" 230 | to-array "0.1.4" 231 | 232 | socket.io-parser@~3.3.0: 233 | version "3.3.0" 234 | resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" 235 | integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== 236 | dependencies: 237 | component-emitter "1.2.1" 238 | debug "~3.1.0" 239 | isarray "2.0.1" 240 | 241 | socket.io-parser@~3.4.0: 242 | version "3.4.1" 243 | resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.1.tgz#b06af838302975837eab2dc980037da24054d64a" 244 | integrity sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A== 245 | dependencies: 246 | component-emitter "1.2.1" 247 | debug "~4.1.0" 248 | isarray "2.0.1" 249 | 250 | socket.io@^2.3.0: 251 | version "2.3.0" 252 | resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.3.0.tgz#cd762ed6a4faeca59bc1f3e243c0969311eb73fb" 253 | integrity sha512-2A892lrj0GcgR/9Qk81EaY2gYhCBxurV0PfmmESO6p27QPrUK1J3zdns+5QPqvUYK2q657nSj0guoIil9+7eFg== 254 | dependencies: 255 | debug "~4.1.0" 256 | engine.io "~3.4.0" 257 | has-binary2 "~1.0.2" 258 | socket.io-adapter "~1.1.0" 259 | socket.io-client "2.3.0" 260 | socket.io-parser "~3.4.0" 261 | 262 | to-array@0.1.4: 263 | version "0.1.4" 264 | resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" 265 | integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= 266 | 267 | ws@^7.1.2: 268 | version "7.3.1" 269 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" 270 | integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== 271 | 272 | ws@~6.1.0: 273 | version "6.1.4" 274 | resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" 275 | integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== 276 | dependencies: 277 | async-limiter "~1.0.0" 278 | 279 | xmlhttprequest-ssl@~1.5.4: 280 | version "1.5.5" 281 | resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" 282 | integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= 283 | 284 | yeast@0.1.2: 285 | version "0.1.2" 286 | resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" 287 | integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= 288 | --------------------------------------------------------------------------------