├── .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 |
--------------------------------------------------------------------------------