diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index f6f0ad7e..302e50b7 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -61,3 +61,7 @@ jobs: - name: "Lint" run: yarn run lint working-directory: "back" + + - name: "Jasmine" + run: yarn test + working-directory: "back" diff --git a/README.md b/README.md index 9ed3bea1..d71c3f0b 100644 --- a/README.md +++ b/README.md @@ -96,3 +96,6 @@ Vagrant destroy * `Vagrant ssh`: connect on your VM Vagrant. * `Vagrant halt`: stop your VM Vagrant. * `Vagrant destroy`: delete your VM Vagrant. + +## Features developed +You have more details of features developed in back [README.md](./back/README.md). \ No newline at end of file diff --git a/back/README.md b/back/README.md new file mode 100644 index 00000000..8a78f403 --- /dev/null +++ b/back/README.md @@ -0,0 +1,61 @@ +# Back Features + +## Login +To start your game, you must authenticate on the server back. +When you are authenticated, the back server return token and room starting. +``` +POST => /login +Params : + email: email of user. +``` + +## Join a room +When a user is connected, the user can join a room. +So you must send emit `join-room` with information user: +``` +Socket.io => 'join-room' + + userId: user id of gamer + roomId: room id when user enter in game + position: { + x: position x on map + y: position y on map + } +``` +All data users are stocked on socket client. + +## Send position user +When user move on the map, you can share new position on back with event `user-position`. +The information sent: +``` +Socket.io => 'user-position' + + userId: user id of gamer + roomId: room id when user enter in game + position: { + x: position x on map + y: position y on map + } +``` +All data users are updated on socket client. + +## Receive positions of all users +The application sends position of all users in each room in every few 10 milliseconds. +The data will pushed on event `user-position`: +``` +Socket.io => 'user-position' + + [ + { + userId: user id of gamer + roomId: room id when user enter in game + position: { + x: position x on map + y: position y on map + } + }, + ... + ] +``` + +[<<< back](../README.md) \ No newline at end of file diff --git a/back/jasmine.json b/back/jasmine.json new file mode 100644 index 00000000..b51ed79d --- /dev/null +++ b/back/jasmine.json @@ -0,0 +1,5 @@ +{ + "spec_dir": "tests", + "spec_files": ["**/*[tT]est.ts"], + "stopSpecOnExpectationFailure": false +} \ No newline at end of file diff --git a/back/package.json b/back/package.json index b86e21c8..4fe7681d 100644 --- a/back/package.json +++ b/back/package.json @@ -7,6 +7,7 @@ "tsc": "tsc", "dev": "ts-node-dev --respawn --transpileOnly ./server.ts", "prod": "tsc && node ./dist/server.js", + "test": "ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "lint": "node_modules/.bin/eslint src/ . --ext .ts" }, "repository": { @@ -21,16 +22,22 @@ "homepage": "https://github.com/thecodingmachine/workadventure#readme", "dependencies": { "@types/express": "^4.17.4", + "@types/http-status-codes": "^1.2.0", + "@types/jsonwebtoken": "^8.3.8", "@types/socket.io": "^2.1.4", "body-parser": "^1.19.0", "express": "^4.17.1", + "http-status-codes": "^1.4.0", + "jsonwebtoken": "^8.5.1", "socket.io": "^2.3.0", "ts-node-dev": "^1.0.0-pre.44", "typescript": "^3.8.3" }, "devDependencies": { + "@types/jasmine": "^3.5.10", "@typescript-eslint/eslint-plugin": "^2.26.0", "@typescript-eslint/parser": "^2.26.0", - "eslint": "^6.8.0" + "eslint": "^6.8.0", + "jasmine": "^3.5.0" } } diff --git a/back/src/App.ts b/back/src/App.ts index f0d391b7..4b3b3c53 100644 --- a/back/src/App.ts +++ b/back/src/App.ts @@ -1,7 +1,8 @@ // lib/app.ts -import {IoSocketController} from "./Controller/IoSocketController"; +import {IoSocketController} from "./Controller/IoSocketController"; //TODO fix import by "_Controller/..." +import {AuthenticateController} from "./Controller/AuthenticateController"; //TODO fix import by "_Controller/..." import express from "express"; -import {Application} from 'express'; +import {Application, Request, Response} from 'express'; import bodyParser = require('body-parser'); import * as http from "http"; @@ -9,17 +10,29 @@ class App { public app: Application; public server: http.Server; public ioSocketController: IoSocketController; + public authenticateController: AuthenticateController; constructor() { this.app = express(); + + //config server http this.config(); this.server = http.createServer(this.app); + + //create controllers this.ioSocketController = new IoSocketController(this.server); + this.authenticateController = new AuthenticateController(this.app); } + // TODO add session user private config(): void { this.app.use(bodyParser.json()); this.app.use(bodyParser.urlencoded({extended: false})); + this.app.use(function (req: Request, res: Response, next) { + res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + next(); + }); } } diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts new file mode 100644 index 00000000..7555c67a --- /dev/null +++ b/back/src/Controller/AuthenticateController.ts @@ -0,0 +1,28 @@ +import {Application, Request, Response} from "express"; +import Jwt from "jsonwebtoken"; +import {BAD_REQUEST, OK} from "http-status-codes"; +import {SECRET_KEY, ROOM} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." + +export class AuthenticateController{ + App : Application; + + constructor(App : Application) { + this.App = App; + this.login(); + } + + //permit to login on application. Return token to connect on Websocket IO. + login(){ + this.App.post("/login", (req: Request, res: Response) => { + let param = req.body; + if(!param.email){ + return res.status(BAD_REQUEST).send({ + message: "email parameter is empty" + }); + } + //TODO check user email for The Coding Machine game + let token = Jwt.sign({email: param.email, roomId: ROOM}, SECRET_KEY, {expiresIn: '24h'}); + return res.status(OK).send({token: token, roomId: ROOM}); + }); + } +} \ No newline at end of file diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 7317ceb7..aa5dfdc9 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -1,36 +1,134 @@ import socketIO = require('socket.io'); import {Socket} from "socket.io"; import * as http from "http"; -import {MessageUserPosition} from "@Model/Websocket/MessageUserPosition"; +import {MessageUserPosition} from "../Model/Websocket/MessageUserPosition"; //TODO fix import by "_Model/.." +import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." +import Jwt, {JsonWebTokenError} from "jsonwebtoken"; +import {SECRET_KEY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import {ExtRooms, RefreshUserPositionFunction} from "../Model/Websocket/ExtRoom"; +import {ExtRoomsInterface} from "_Model/Websocket/ExtRoomsInterface"; export class IoSocketController{ Io: socketIO.Server; constructor(server : http.Server) { this.Io = socketIO(server); + + // Authentication with token. it will be decoded and stored in the socket. + this.Io.use( (socket: Socket, next) => { + if (!socket.handshake.query || !socket.handshake.query.token) { + return next(new Error('Authentication error')); + } + Jwt.verify(socket.handshake.query.token, SECRET_KEY, (err: JsonWebTokenError, tokenDecoded: object) => { + if (err) { + return next(new Error('Authentication error')); + } + (socket as ExSocketInterface).token = tokenDecoded; + next(); + }); + }); + this.ioConnection(); + this.shareUsersPosition(); } ioConnection() { this.Io.on('connection', (socket: Socket) => { - //TODO check token access - /*join-rom event permit to join one room. message : userId : user identification roomId: room identification - positionXUser: user x position map - positionYUser: user y position on map + position: position of user in map + x: user x position on map + y: user y position on map */ - socket.on('join-room', (message : MessageUserPosition) => { - socket.join(message.roomId); + socket.on('join-room', (message : string) => { + let messageUserPosition = this.hydrateMessageReceive(message); + if(messageUserPosition instanceof Error){ + return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message})) + } + + //join user in room + socket.join(messageUserPosition.roomId); + // sending to all clients in room except sender - socket.to(message.roomId).emit('join-room', message.toString()); + this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); + + //add function to refresh position user in real time. + let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface); + rooms.refreshUserPosition = RefreshUserPositionFunction; + rooms.refreshUserPosition(rooms, this.Io); + + socket.to(messageUserPosition.roomId).emit('join-room', messageUserPosition.toString()); }); - socket.on('user-position', (message : MessageUserPosition) => { + socket.on('user-position', (message : string) => { + let messageUserPosition = this.hydrateMessageReceive(message); + if (messageUserPosition instanceof Error) { + return socket.emit("message-error", JSON.stringify({message: messageUserPosition.message})); + } + // sending to all clients in room except sender - socket.to(message.roomId).emit('join-room', message.toString()); + this.saveUserInformation((socket as ExSocketInterface), messageUserPosition); + + //refresh position of all user in all rooms in real time + let rooms = (this.Io.sockets.adapter.rooms as ExtRoomsInterface) + rooms.refreshUserPosition(rooms, this.Io); }); }); } -} \ No newline at end of file + + //permit to save user position in socket + saveUserInformation(socket : ExSocketInterface, message : MessageUserPosition){ + socket.position = message.position; + socket.roomId = message.roomId; + socket.userId = message.userId; + } + + //Hydrate and manage error + hydrateMessageReceive(message : string) : MessageUserPosition | Error{ + try { + return new MessageUserPosition(message); + }catch (err) { + //TODO log error + return new Error(err); + } + } + + /** permit to share user position + ** users position will send in event 'user-position' + ** The data sent is an array with information for each user : + [ + { + userId: , + roomId: , + position: { + x : , + y : + } + }, + ... + ] + **/ + seTimeOutInProgress : any = null; + shareUsersPosition(){ + if(this.seTimeOutInProgress){ + clearTimeout(this.seTimeOutInProgress); + } + //send for each room, all data of position user + let arrayMap = (this.Io.sockets.adapter.rooms as ExtRooms).userPositionMapByRoom; + if(!arrayMap){ + this.seTimeOutInProgress = setTimeout(() => { + this.shareUsersPosition(); + }, 10); + return; + } + arrayMap.forEach((value : any) => { + let roomId = value[0]; + let data = value[1]; + this.Io.in(roomId).emit('user-position', JSON.stringify(data)); + }); + this.seTimeOutInProgress = setTimeout(() => { + this.shareUsersPosition(); + }, 10); + } +} diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts new file mode 100644 index 00000000..f795bf90 --- /dev/null +++ b/back/src/Enum/EnvironmentVariable.ts @@ -0,0 +1,7 @@ +const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY"; +const ROOM = process.env.ROOM || "THECODINGMACHINE"; + +export { + SECRET_KEY, + ROOM +} \ No newline at end of file diff --git a/back/src/Model/Websocket/ExSocketInterface.ts b/back/src/Model/Websocket/ExSocketInterface.ts new file mode 100644 index 00000000..095d3cbc --- /dev/null +++ b/back/src/Model/Websocket/ExSocketInterface.ts @@ -0,0 +1,9 @@ +import {Socket} from "socket.io"; +import {PointInterface} from "./PointInterface"; + +export interface ExSocketInterface extends Socket { + token: any; + roomId: string; + userId: string; + position: PointInterface; +} \ No newline at end of file diff --git a/back/src/Model/Websocket/ExtRoom.ts b/back/src/Model/Websocket/ExtRoom.ts new file mode 100644 index 00000000..0cbe2f61 --- /dev/null +++ b/back/src/Model/Websocket/ExtRoom.ts @@ -0,0 +1,42 @@ +import {ExtRoomsInterface} from "./ExtRoomsInterface"; +import socketIO = require('socket.io'); +import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; + +export class ExtRooms implements ExtRoomsInterface{ + userPositionMapByRoom: any; + refreshUserPosition: any; + + [room: string]: SocketIO.Room; +} + +let RefreshUserPositionFunction = function(rooms : ExtRooms, Io: socketIO.Server){ + let clients = Io.clients(); + let socketsKey = Object.keys(Io.clients().sockets); + + //create mapping with all users in all rooms + let mapPositionUserByRoom = new Map(); + for(let i = 0; i < socketsKey.length; i++){ + let socket = clients.sockets[socketsKey[i]] as ExSocketInterface; + if(!socket.position){ + continue; + } + let data = { + userId : socket.userId, + roomId : socket.roomId, + position : socket.position, + }; + let dataArray = []; + if(mapPositionUserByRoom.get(data.roomId)){ + dataArray = mapPositionUserByRoom.get(data.roomId); + dataArray.push(data); + }else{ + dataArray = [data]; + } + mapPositionUserByRoom.set(data.roomId, dataArray); + } + rooms.userPositionMapByRoom = Array.from(mapPositionUserByRoom); +} + +export { + RefreshUserPositionFunction +} \ No newline at end of file diff --git a/back/src/Model/Websocket/ExtRoomsInterface.ts b/back/src/Model/Websocket/ExtRoomsInterface.ts new file mode 100644 index 00000000..a71fcbf9 --- /dev/null +++ b/back/src/Model/Websocket/ExtRoomsInterface.ts @@ -0,0 +1,6 @@ +import {Rooms} from "socket.io"; + +export interface ExtRoomsInterface extends Rooms{ + userPositionMapByRoom: any; + refreshUserPosition: any; +} \ No newline at end of file diff --git a/back/src/Model/Websocket/Message.ts b/back/src/Model/Websocket/Message.ts index 4fc340a6..8e6f2c9a 100644 --- a/back/src/Model/Websocket/Message.ts +++ b/back/src/Model/Websocket/Message.ts @@ -4,6 +4,9 @@ export class Message { constructor(message: string) { let data = JSON.parse(message); + if(!data.userId || !data.roomId){ + throw Error("userId or roomId cannot be null"); + } this.userId = data.userId; this.roomId = data.roomId; } diff --git a/back/src/Model/Websocket/MessageUserPosition.ts b/back/src/Model/Websocket/MessageUserPosition.ts index b37b5089..493f1457 100644 --- a/back/src/Model/Websocket/MessageUserPosition.ts +++ b/back/src/Model/Websocket/MessageUserPosition.ts @@ -1,14 +1,33 @@ import {Message} from "./Message"; +import {PointInterface} from "./PointInterface"; + +export class Point implements PointInterface{ + x: number; + y: number; + + constructor(x : number, y : number) { + if(x === null || y === null){ + throw Error("position x and y cannot be null"); + } + this.x = x; + this.y = y; + } + + toJson(){ + return { + x : this.x, + y: this.y + } + } +} export class MessageUserPosition extends Message{ - positionXUser: string; - positionYUser: string; + position: PointInterface; constructor(message: string) { super(message); let data = JSON.parse(message); - this.positionXUser = data.positionXUser; - this.positionYUser = data.positionYUser; + this.position = new Point(data.position.x, data.position.y); } toString() { @@ -16,8 +35,7 @@ export class MessageUserPosition extends Message{ Object.assign( super.toJson(), { - positionXUser: this.positionXUser, - positionYUser: this.positionYUser + position: this.position.toJson() }) ); } diff --git a/back/src/Model/Websocket/PointInterface.ts b/back/src/Model/Websocket/PointInterface.ts new file mode 100644 index 00000000..7f2ab39a --- /dev/null +++ b/back/src/Model/Websocket/PointInterface.ts @@ -0,0 +1,5 @@ +export interface PointInterface { + x: number; + y: number; + toJson() : object; +} \ No newline at end of file diff --git a/back/tests/MessageTest.ts b/back/tests/MessageTest.ts new file mode 100644 index 00000000..69d57fce --- /dev/null +++ b/back/tests/MessageTest.ts @@ -0,0 +1,31 @@ +import "jasmine"; +import {Message} from "../src/Model/Websocket/Message"; + +describe("Message Model", () => { + it("should find userId and roomId", () => { + let message = JSON.stringify({userId: "test1", roomId: "test2"}); + let messageObject = new Message(message); + expect(messageObject.userId).toBe("test1"); + expect(messageObject.roomId).toBe("test2"); + }) + + it("should expose a toJson method", () => { + let message = JSON.stringify({userId: "test1", roomId: "test2"}); + let messageObject = new Message(message); + expect(messageObject.toJson()).toEqual({userId: "test1", roomId: "test2"}); + }) + + it("should find throw error when no userId", () => { + let message = JSON.stringify({roomId: "test2"}); + expect(() => { + let messageObject = new Message(message); + }).toThrow(new Error("userId or roomId cannot be null")); + }) + + it("should find throw error when no roomId", () => { + let message = JSON.stringify({userId: "test1"}); + expect(() => { + let messageObject = new Message(message); + }).toThrow(new Error("userId or roomId cannot be null")); + }) +}) \ No newline at end of file diff --git a/back/tsconfig.json b/back/tsconfig.json index d208ee64..7686fb67 100644 --- a/back/tsconfig.json +++ b/back/tsconfig.json @@ -43,8 +43,9 @@ "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ "baseUrl": ".", /* Base directory to resolve non-absolute module names. */ "paths": { - "@Controller/*": ["src/Controller/*"], - "@Model/*": ["src/Model/*"] + "_Controller/*": ["src/Controller/*"], + "_Model/*": ["src/Model/*"], + "_Enum/*": ["src/Enum/*"] }, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ // "typeRoots": [], /* List of folders to include type definitions from. */ diff --git a/back/yarn.lock b/back/yarn.lock index e2c5879c..27f4503e 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -66,11 +66,30 @@ "@types/qs" "*" "@types/serve-static" "*" +"@types/http-status-codes@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@types/http-status-codes/-/http-status-codes-1.2.0.tgz#6e5244835aaf7164dd306f1d4d2dfdbb2159d909" + integrity sha512-vjpjevMaxtrtdrrV/TQNIFT7mKL8nvIKG7G/LjMDZdVvqRxRg5SNfGkeuSaowVc0rbK8xDA2d/Etunyb5GyzzA== + dependencies: + http-status-codes "*" + +"@types/jasmine@^3.5.10": + version "3.5.10" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.10.tgz#a1a41012012b5da9d4b205ba9eba58f6cce2ab7b" + integrity sha512-3F8qpwBAiVc5+HPJeXJpbrl+XjawGmciN5LgiO7Gv1pl1RHtjoMNqZpqEksaPJW05ViKe8snYInRs6xB25Xdew== + "@types/json-schema@^7.0.3": version "7.0.4" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/jsonwebtoken@^8.3.8": + version "8.3.8" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.3.8.tgz#b27c9156dde2049ae03e56528a53ef5a8294aa82" + integrity sha512-g2ke5+AR/RKYpQxd+HJ2yisLHGuOV0uourOcPtKlcT5Zqv4wFg9vKhFpXEztN4H/6Y6RSUKioz/2PTFPP30CTA== + dependencies: + "@types/node" "*" + "@types/mime@*": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.1.tgz#dc488842312a7f075149312905b5e3c0b054c79d" @@ -317,6 +336,11 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +buffer-equal-constant-time@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= + buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" @@ -546,6 +570,13 @@ dynamic-dedupe@^0.3.0: dependencies: xtend "^4.0.0" +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -889,7 +920,7 @@ glob-parent@^5.0.0: dependencies: is-glob "^4.0.1" -glob@^7.1.3, glob@^7.1.6: +glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -967,6 +998,11 @@ http-errors@~1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" +http-status-codes@*, http-status-codes@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/http-status-codes/-/http-status-codes-1.4.0.tgz#6e4c15d16ff3a9e2df03b89f3a55e1aae05fb477" + integrity sha512-JrT3ua+WgH8zBD3HEJYbeEgnuQaAnUeRRko/YojPAJjGmIfGD3KPU/asLdsLwKjfxOmQe5nXMQ0pt/7MyapVbQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -1103,6 +1139,19 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +jasmine-core@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4" + integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA== + +jasmine@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.5.0.tgz#7101eabfd043a1fc82ac24e0ab6ec56081357f9e" + integrity sha512-DYypSryORqzsGoMazemIHUfMkXM7I7easFaxAvNM3Mr6Xz3Fy36TupTrAOxZWN8MVKEU5xECv22J4tUQf3uBzQ== + dependencies: + glob "^7.1.4" + jasmine-core "~3.5.0" + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -1126,6 +1175,39 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= +jsonwebtoken@^8.5.1: + version "8.5.1" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" + integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== + dependencies: + jws "^3.2.2" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^5.6.0" + +jwa@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" + integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== + dependencies: + buffer-equal-constant-time "1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^3.2.2: + version "3.2.2" + resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" + integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== + dependencies: + jwa "^1.4.1" + safe-buffer "^5.0.1" + levn@^0.3.0, levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -1145,6 +1227,41 @@ load-json-file@^1.0.0: pinkie-promise "^2.0.0" strip-bom "^2.0.0" +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= + +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= + +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= + lodash@^4.17.14, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" @@ -1568,12 +1685,17 @@ safe-buffer@5.1.2: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +safe-buffer@^5.0.1: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -"semver@2 || 3 || 4 || 5", semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== diff --git a/docker-compose.yaml b/docker-compose.yaml index 86923563..a23a5204 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -29,6 +29,7 @@ services: command: yarn dev environment: STARTUP_COMMAND_1: yarn install + SECRET_KEY: yourSecretKey volumes: - ./back:/usr/src/app labels: diff --git a/front/package.json b/front/package.json index dae1eb8c..25d613e6 100644 --- a/front/package.json +++ b/front/package.json @@ -14,7 +14,10 @@ "webpack-dev-server": "^3.10.3" }, "dependencies": { - "phaser": "^3.22.0" + "@types/axios": "^0.14.0", + "@types/socket.io-client": "^1.4.32", + "phaser": "^3.22.0", + "socket.io-client": "^2.3.0" }, "scripts": { "start": "webpack-dev-server --open", diff --git a/front/src/Connexion.ts b/front/src/Connexion.ts new file mode 100644 index 00000000..4f094152 --- /dev/null +++ b/front/src/Connexion.ts @@ -0,0 +1,148 @@ +const SocketIo = require('socket.io-client'); +import Axios from "axios"; +import {API_URL} from "./Enum/EnvironmentVariable"; + +export interface PointInterface { + x: number; + y: number; + toJson() : object; +} + +export class Message { + userId: string; + roomId: string; + + constructor(userId : string, roomId : string) { + this.userId = userId; + this.roomId = roomId; + } + + toJson() { + return { + userId: this.userId, + roomId: this.roomId, + } + } +} + +export class Point implements PointInterface{ + x: number; + y: number; + + constructor(x : number, y : number) { + if(x === null || y === null){ + throw Error("position x and y cannot be null"); + } + this.x = x; + this.y = y; + } + + toJson(){ + return { + x : this.x, + y: this.y + } + } +} + +export class MessageUserPosition extends Message{ + position: PointInterface; + + constructor(userId : string, roomId : string, point : Point) { + super(userId, roomId); + this.position = point; + } + + toString() { + return JSON.stringify( + Object.assign( + super.toJson(), + { + position: this.position.toJson() + }) + ); + } +} + +export class Connexion { + socket : any; + token : string; + email : string; + startedRoom : string; + + constructor(email : string) { + this.email = email; + Axios.post(`${API_URL}/login`, {email: email}) + .then((res) => { + this.token = res.data.token; + this.startedRoom = res.data.roomId; + + this.socket = SocketIo(`${API_URL}`, { + query: { + token: this.token + } + }); + + //join the room + this.joinARoom(this.startedRoom); + + //share your first position + this.sharePosition(0, 0); + + //create listen event to get all data user shared by the back + this.positionOfAllUser(); + + this.errorMessage(); + }) + .catch((err) => { + console.error(err); + throw err; + }); + } + + /** + * Permit to join a room + * @param roomId + */ + joinARoom(roomId : string){ + let messageUserPosition = new MessageUserPosition(this.email, this.startedRoom, new Point(0, 0)); + this.socket.emit('join-room', messageUserPosition.toString()); + } + + /** + * Permit to share your position in map + * @param x + * @param y + */ + sharePosition(x : number, y : number){ + let messageUserPosition = new MessageUserPosition(this.email, this.startedRoom, new Point(x, y)); + this.socket.emit('user-position', messageUserPosition.toString()); + } + + /** + * The data sent is an array with information for each user : + * [ + * { + * userId: , + * roomId: , + * position: { + * x : , + * y : + * } + * }, + * ... + * ] + **/ + positionOfAllUser(){ + this.socket.on("user-position", (message : string) => { + //TODO show all user in map + console.info("user-position", message); + }); + } + + errorMessage(){ + this.socket.on('message-error', (message : string) => { + console.error("message-error", message); + }) + } +} \ No newline at end of file diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts new file mode 100644 index 00000000..0e10e789 --- /dev/null +++ b/front/src/Enum/EnvironmentVariable.ts @@ -0,0 +1,5 @@ +const API_URL = process.env.API_URL || "http://api.workadventure.localhost"; + +export { + API_URL +} \ No newline at end of file diff --git a/front/src/GameScene.ts b/front/src/GameScene.ts index b8723574..114fb918 100644 --- a/front/src/GameScene.ts +++ b/front/src/GameScene.ts @@ -1,5 +1,14 @@ export class GameScene extends Phaser.Scene { + private keyZ: Phaser.Input.Keyboard.Key; + private keyQ: Phaser.Input.Keyboard.Key; + private keyS: Phaser.Input.Keyboard.Key; + private keyD: Phaser.Input.Keyboard.Key; + private keyRight: Phaser.Input.Keyboard.Key; + private keyLeft: Phaser.Input.Keyboard.Key; + private keyUp: Phaser.Input.Keyboard.Key; + private keyDown: Phaser.Input.Keyboard.Key; + private keyShift: Phaser.Input.Keyboard.Key; constructor() { super({ @@ -17,6 +26,22 @@ export class GameScene extends Phaser.Scene { } init(): void { + this.keyShift = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT); + + this.keyZ = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z); + this.keyQ = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q); + this.keyS = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S); + this.keyD = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D); + + this.keyUp = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP); + this.keyLeft = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT); + this.keyDown = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN); + this.keyRight = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT); + } + + private moveCamera(x:number, y:number, speedMultiplier: number): void { + this.cameras.main.scrollX += speedMultiplier * 2 * x; + this.cameras.main.scrollY += speedMultiplier * 2 * y; } create(): void { @@ -66,9 +91,25 @@ export class GameScene extends Phaser.Scene { private angle: number = 0; update(dt: number): void { - this.cameras.main.scrollX = Math.floor(300 + 300 * Math.cos(this.angle)); + let speedMultiplier = this.keyShift.isDown ? 5 : 1; + + + if (this.keyZ.isDown || this.keyUp.isDown) { + this.moveCamera(0, -1, speedMultiplier); + } + if (this.keyQ.isDown || this.keyLeft.isDown) { + this.moveCamera(-1, 0, speedMultiplier); + } + if (this.keyS.isDown || this.keyDown.isDown) { + this.moveCamera(0, 1, speedMultiplier); + } + if (this.keyD.isDown || this.keyRight.isDown) { + this.moveCamera(1, 0, speedMultiplier); + } + + /*this.cameras.main.scrollX = Math.floor(300 + 300 * Math.cos(this.angle)); this.cameras.main.scrollY = Math.floor(300 + 300 * Math.sin(this.angle)); - this.angle = dt * 0.0001; + this.angle = dt * 0.0001;*/ } } diff --git a/front/src/index.ts b/front/src/index.ts index 9038a9c7..9c7c0e8a 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -1,6 +1,7 @@ import 'phaser'; import GameConfig = Phaser.Types.Core.GameConfig; import {GameScene} from "./GameScene"; +import {Connexion} from "./Connexion"; const resolution = 2; @@ -18,3 +19,5 @@ let game = new Phaser.Game(config); window.addEventListener('resize', function (event) { game.scale.resize(window.innerWidth / resolution, window.innerHeight / resolution); }); + +const connexion = new Connexion("test@gmail.com"); diff --git a/front/yarn.lock b/front/yarn.lock index e2ec8385..c9b34f3f 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -23,6 +23,13 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@types/axios@^0.14.0": + version "0.14.0" + resolved "https://registry.yarnpkg.com/@types/axios/-/axios-0.14.0.tgz#ec2300fbe7d7dddd7eb9d3abf87999964cafce46" + integrity sha1-7CMA++fX3d1+udOr+HmZlkyvzkY= + dependencies: + axios "*" + "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -58,6 +65,11 @@ version "13.11.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b" +"@types/socket.io-client@^1.4.32": + version "1.4.32" + resolved "https://registry.yarnpkg.com/@types/socket.io-client/-/socket.io-client-1.4.32.tgz#988a65a0386c274b1c22a55377fab6a30789ac14" + integrity sha512-Vs55Kq8F+OWvy1RLA31rT+cAyemzgm0EWNeax6BWF8H7QiiOYMJIdcwSDdm5LVgfEkoepsWkS+40+WNb7BUMbg== + "@typescript-eslint/eslint-plugin@^2.26.0": version "2.26.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.26.0.tgz#04c96560c8981421e5a9caad8394192363cc423f" @@ -257,6 +269,11 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf" integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg== +after@0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" + integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= + ajv-errors@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" @@ -372,6 +389,11 @@ array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" +arraybuffer.slice@~0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" + integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== + asn1.js@^4.0.0: version "4.10.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" @@ -414,10 +436,27 @@ atob@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" +axios@*: + version "0.19.2" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.2.tgz#3ea36c5d8818d0d5f8a8a97a6d36b86cdc00cb27" + integrity sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA== + dependencies: + follow-redirects "1.5.10" + +backo2@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" + integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= + balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" +base64-arraybuffer@0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8" + integrity sha1-c5JncZI7Whl0etZmqlzUv5xunOg= + base64-js@^1.0.2: version "1.3.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1" @@ -438,6 +477,13 @@ batch@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" +better-assert@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/better-assert/-/better-assert-1.0.2.tgz#40866b9e1b9e0b55b481894311e68faffaebc522" + integrity sha1-QIZrnhueC1W0gYlDEeaPr/rrxSI= + dependencies: + callsite "1.0.0" + big.js@^5.2.2: version "5.2.2" resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" @@ -452,6 +498,11 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +blob@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" + integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== + bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -637,6 +688,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +callsite@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" + integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -780,10 +836,25 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +component-bind@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" + integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= + +component-emitter@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" +component-inherit@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" + integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= + compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -928,13 +999,20 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" +debug@=3.1.0, debug@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" dependencies: @@ -1119,6 +1197,34 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0: dependencies: once "^1.4.0" +engine.io-client@~3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.4.0.tgz#82a642b42862a9b3f7a188f41776b2deab643700" + integrity sha512-a4J5QO2k99CM2a0b12IznnyQndoEvtA4UAldhGzKqnHf42I3Qs2W5SPnDvatZRcMaNZs4IevVicBPayxYt6FwA== + dependencies: + component-emitter "1.2.1" + component-inherit "0.0.3" + debug "~4.1.0" + engine.io-parser "~2.2.0" + has-cors "1.1.0" + indexof "0.0.1" + parseqs "0.0.5" + parseuri "0.0.5" + ws "~6.1.0" + xmlhttprequest-ssl "~1.5.4" + yeast "0.1.2" + +engine.io-parser@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.0.tgz#312c4894f57d52a02b420868da7b5c1c84af80ed" + integrity sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w== + dependencies: + after "0.8.2" + arraybuffer.slice "~0.0.7" + base64-arraybuffer "0.1.5" + blob "0.0.5" + has-binary2 "~1.0.2" + enhanced-resolve@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" @@ -1545,6 +1651,13 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" +follow-redirects@1.5.10: + version "1.5.10" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" + integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== + dependencies: + debug "=3.1.0" + follow-redirects@^1.0.0: version "1.11.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.11.0.tgz#afa14f08ba12a52963140fe43212658897bc0ecb" @@ -1705,6 +1818,18 @@ handle-thing@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== + dependencies: + isarray "2.0.1" + +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -1895,6 +2020,11 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + infer-owner@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -2140,6 +2270,11 @@ isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" +isarray@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" + integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2566,6 +2701,11 @@ object-assign@^4.0.1, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" +object-component@0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" + integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= + object-copy@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" @@ -2747,6 +2887,20 @@ parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" +parseqs@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d" + integrity sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0= + dependencies: + better-assert "~1.0.0" + +parseuri@0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.5.tgz#80204a50d4dbb779bfdc6ebe2778d90e4bce320a" + integrity sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo= + dependencies: + better-assert "~1.0.0" + parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -3300,6 +3454,35 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +socket.io-client@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.3.0.tgz#14d5ba2e00b9bcd145ae443ab96b3f86cbcc1bb4" + integrity sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA== + dependencies: + backo2 "1.0.2" + base64-arraybuffer "0.1.5" + component-bind "1.0.0" + component-emitter "1.2.1" + debug "~4.1.0" + engine.io-client "~3.4.0" + has-binary2 "~1.0.2" + has-cors "1.1.0" + indexof "0.0.1" + object-component "0.0.3" + parseqs "0.0.5" + parseuri "0.0.5" + socket.io-parser "~3.3.0" + to-array "0.1.4" + +socket.io-parser@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.0.tgz#2b52a96a509fdf31440ba40fed6094c7d4f1262f" + integrity sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng== + dependencies: + component-emitter "1.2.1" + debug "~3.1.0" + isarray "2.0.1" + sockjs-client@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" @@ -3629,6 +3812,11 @@ tmp@^0.0.33: dependencies: os-tmpdir "~1.0.2" +to-array@0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" + integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= + to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -4018,6 +4206,18 @@ ws@^6.2.1: dependencies: async-limiter "~1.0.0" +ws@~6.1.0: + version "6.1.4" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.4.tgz#5b5c8800afab925e94ccb29d153c8d02c1776ef9" + integrity sha512-eqZfL+NE/YQc1/ZynhojeV8q+H050oR8AZ2uIev7RU10svA9ZnJUddHcOUZTJLinZ9yEfdA2kSATS2qZK5fhJA== + dependencies: + async-limiter "~1.0.0" + +xmlhttprequest-ssl@~1.5.4: + version "1.5.5" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" + integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= + xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -4076,3 +4276,8 @@ yargs@13.2.4: which-module "^2.0.0" y18n "^4.0.0" yargs-parser "^13.1.0" + +yeast@0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk=