From 0b78eb62772129ab9df8f9e8ff97aeaa015af1be Mon Sep 17 00:00:00 2001 From: arp Date: Tue, 6 Oct 2020 15:37:00 +0200 Subject: [PATCH 1/6] temp --- back/src/Controller/AuthenticateController.ts | 18 +-- back/src/Controller/IoSocketController.ts | 136 +++++++----------- back/src/Model/{World.ts => GameRoom.ts} | 107 +------------- back/src/Model/Group.ts | 3 +- back/src/Services/AdminApi.ts | 36 +++++ back/tests/PositionNotifierTest.ts | 2 +- back/tests/WorldTest.ts | 8 +- 7 files changed, 102 insertions(+), 208 deletions(-) rename back/src/Model/{World.ts => GameRoom.ts} (68%) create mode 100644 back/src/Services/AdminApi.ts diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index aa27a468..7a8a95dd 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -3,20 +3,12 @@ import {ADMIN_API_TOKEN, ADMIN_API_URL, SECRET_KEY, URL_ROOM_STARTED} from "../E import { uuid } from 'uuidv4'; import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; import {BaseController} from "./BaseController"; -import Axios from "axios"; +import {adminApi, AdminApiData} from "../Services/AdminApi"; export interface TokenInterface { userUuid: string } -interface AdminApiData { - organizationSlug: string - worldSlug: string - roomSlug: string - mapUrlStart: string - userUuid: string -} - export class AuthenticateController extends BaseController { constructor(private App : TemplatedApp) { @@ -51,13 +43,7 @@ export class AuthenticateController extends BaseController { let newUrl: string|null = null; if (organizationMemberToken) { - if (!ADMIN_API_URL) { - return res.status(401).send('No admin backoffice set!'); - } - //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case. - const data = await Axios.get(ADMIN_API_URL+'/api/login-url/'+organizationMemberToken, - { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } - ).then((res): AdminApiData => res.data); + const data = await adminApi.fetchMemberDataByToken(organizationMemberToken); userUuid = data.userUuid; mapUrlStart = data.mapUrlStart; diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 9696748a..1591a5f1 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -1,23 +1,14 @@ -import * as http from "http"; -import {MessageUserPosition, Point} 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 Jwt from "jsonwebtoken"; import {SECRET_KEY, MINIMUM_DISTANCE, GROUP_RADIUS, ALLOW_ARTILLERY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." -import {World} from "../Model/World"; +import {GameRoom} from "../Model/GameRoom"; import {Group} from "../Model/Group"; import {User} from "../Model/User"; import {isSetPlayerDetailsMessage,} from "../Model/Websocket/SetPlayerDetailsMessage"; -import {MessageUserJoined} from "../Model/Websocket/MessageUserJoined"; -import si from "systeminformation"; import {Gauge} from "prom-client"; import {TokenInterface} from "../Controller/AuthenticateController"; -import {isJoinRoomMessageInterface} from "../Model/Websocket/JoinRoomMessage"; import {PointInterface} from "../Model/Websocket/PointInterface"; -import {isWebRtcSignalMessageInterface} from "../Model/Websocket/WebRtcSignalMessage"; -import {UserInGroupInterface} from "../Model/Websocket/UserInGroupInterface"; -import {isItemEventMessageInterface} from "../Model/Websocket/ItemEventMessage"; import {uuid} from 'uuidv4'; -import {GroupUpdateInterface} from "_Model/Websocket/GroupUpdateInterface"; import {Movable} from "../Model/Movable"; import { PositionMessage, @@ -42,14 +33,17 @@ import { SilentMessage, WebRtcSignalToClientMessage, WebRtcSignalToServerMessage, - WebRtcStartMessage, WebRtcDisconnectMessage, PlayGlobalMessage + WebRtcStartMessage, + WebRtcDisconnectMessage, + PlayGlobalMessage, } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import Direction = PositionMessage.Direction; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; -import {App, HttpRequest, TemplatedApp, WebSocket} from "uWebSockets.js" +import {HttpRequest, TemplatedApp} from "uWebSockets.js" import {parse} from "query-string"; import {cpuTracker} from "../Services/CpuTracker"; +import {adminApi} from "../Services/AdminApi"; function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void { socket.batchedMessages.addPayload(payload); @@ -71,7 +65,7 @@ function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void { } export class IoSocketController { - private Worlds: Map = new Map(); + private Worlds: Map = new Map(); private sockets: Map = new Map(); private nbClientsGauge: Gauge; private nbClientsPerRoomGauge: Gauge; @@ -100,32 +94,11 @@ export class IoSocketController { return true; } - /** - * - * @param token - */ -/* searchClientByToken(token: string): ExSocketInterface | null { - const clients: ExSocketInterface[] = Object.values(this.Io.sockets.sockets) as ExSocketInterface[]; - for (let i = 0; i < clients.length; i++) { - const client = clients[i]; - if (client.token !== token) { - continue - } - return client; - } - return null; - }*/ - - private async authenticate(req: HttpRequest): Promise<{ token: string, userUuid: string }> { - //console.log(socket.handshake.query.token); - - const query = parse(req.getQuery()); - - if (!query.token) { + private async getUserUuidFromToken(token: unknown): Promise { + + if (!token) { throw new Error('An authentication error happened, a user tried to connect without a token.'); } - - const token = query.token; if (typeof(token) !== "string") { throw new Error('Token is expected to be a string'); } @@ -133,22 +106,15 @@ export class IoSocketController { if(token === 'test') { if (ALLOW_ARTILLERY) { - return { - token, - userUuid: uuid() - } + return uuid(); } else { throw new Error("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); } } - /*if(this.searchClientByToken(socket.handshake.query.token)){ - console.error('An authentication error happened, a user tried to connect while its token is already connected.'); - return next(new Error('Authentication error')); - }*/ - - const promise = new Promise<{ token: string, userUuid: string }>((resolve, reject) => { + return new Promise((resolve, reject) => { Jwt.verify(token, SECRET_KEY, {},(err, tokenDecoded) => { + const tokenInterface = tokenDecoded as TokenInterface; if (err) { console.error('An authentication error happened, invalid JsonWebToken.', err); reject(new Error('An authentication error happened, invalid JsonWebToken. '+err.message)); @@ -159,25 +125,19 @@ export class IoSocketController { reject(new Error('Empty token found.')); return; } - const tokenInterface = tokenDecoded as TokenInterface; if (!this.isValidToken(tokenInterface)) { reject(new Error('Authentication error, invalid token structure.')); return; } - resolve({ - token, - userUuid: tokenInterface.userUuid - }); + resolve(tokenInterface.userUuid); }); }); - - return promise; } ioConnection() { - this.app.ws('/*', { + this.app.ws('/room', { /* Options */ //compression: uWS.SHARED_COMPRESSOR, @@ -197,7 +157,21 @@ export class IoSocketController { }); try { - const result = await this.authenticate(req); + const query = parse(req.getQuery()); + + const moderated = query.moderated || false; + const roomId = query.roomId || null; + const token = query.token; + + + const userUuid = await this.getUserUuidFromToken(token); + + this.handleJoinRoom(client, message.getJoinroommessage() as JoinRoomMessage); + + const isGranted = await adminApi.memberIsGrantedAccessToRoom(client.userUuid, roomId); + if (!isGranted) { + throw Error('Client cannot acces this ressource.'); + } if (upgradeAborted.aborted) { console.log("Ouch! Client disconnected before we could upgrade it!"); @@ -209,8 +183,8 @@ export class IoSocketController { res.upgrade({ // Data passed here is accessible on the "websocket" socket object. url: req.getUrl(), - token: result.token, - userUuid: result.userUuid + token, + userUuid }, /* Spell these correctly */ req.getHeader('sec-websocket-key'), @@ -250,13 +224,11 @@ export class IoSocketController { console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)'); }, - message: (ws, arrayBuffer, isBinary) => { + message: (ws, arrayBuffer, isBinary): void => { const client = ws as ExSocketInterface; const message = ClientToServerMessage.deserializeBinary(new Uint8Array(arrayBuffer)); - if (message.hasJoinroommessage()) { - this.handleJoinRoom(client, message.getJoinroommessage() as JoinRoomMessage); - } else if (message.hasViewportmessage()) { + if (message.hasViewportmessage()) { this.handleViewport(client, message.getViewportmessage() as ViewportMessage); } else if (message.hasUsermovesmessage()) { this.handleUserMovesMessage(client, message.getUsermovesmessage() as UserMovesMessage); @@ -333,26 +305,22 @@ export class IoSocketController { console.warn(message); } - private handleJoinRoom(Client: ExSocketInterface, message: JoinRoomMessage): void { + private async handleJoinRoom(client: ExSocketInterface, message: JoinRoomMessage): Promise { try { - /*if (!isJoinRoomMessageInterface(message.toObject())) { - console.log(message.toObject()) - this.emitError(Client, 'Invalid JOIN_ROOM message received: ' + message.toObject().toString()); - return; - }*/ const roomId = message.getRoomid(); - if (Client.roomId === roomId) { + if (client.roomId === roomId) { return; } + //leave previous room //this.leaveRoom(Client); // Useless now, there is only one room per connection //join new previous room - const world = this.joinRoom(Client, roomId, ProtobufUtils.toPointInterface(message.getPosition() as PositionMessage)); + const gameRoom = await this.joinRoom(client, roomId, ProtobufUtils.toPointInterface(message.getPosition() as PositionMessage)); - const things = world.setViewport(Client, (message.getViewport() as ViewportMessage).toObject()); + const things = gameRoom.setViewport(client, (message.getViewport() as ViewportMessage).toObject()); const roomJoinedMessage = new RoomJoinedMessage(); @@ -382,7 +350,7 @@ export class IoSocketController { } } - for (const [itemId, item] of world.getItemsState().entries()) { + for (const [itemId, item] of gameRoom.getItemsState().entries()) { const itemStateMessage = new ItemStateMessage(); itemStateMessage.setItemid(itemId); itemStateMessage.setStatejson(JSON.stringify(item)); @@ -393,8 +361,8 @@ export class IoSocketController { const serverToClientMessage = new ServerToClientMessage(); serverToClientMessage.setRoomjoinedmessage(roomJoinedMessage); - if (!Client.disconnecting) { - Client.send(serverToClientMessage.serializeBinary().buffer, true); + if (!client.disconnecting) { + client.send(serverToClientMessage.serializeBinary().buffer, true); } } catch (e) { console.error('An error occurred on "join_room" event'); @@ -600,7 +568,7 @@ export class IoSocketController { if(Client.roomId){ try { //user leave previous world - const world: World | undefined = this.Worlds.get(Client.roomId); + const world: GameRoom | undefined = this.Worlds.get(Client.roomId); if (world) { world.leave(Client); if (world.isEmpty()) { @@ -616,17 +584,17 @@ export class IoSocketController { } } - private joinRoom(Client : ExSocketInterface, roomId: string, position: PointInterface): World { + private joinRoom(client : ExSocketInterface, roomId: string, position: PointInterface): GameRoom { + //join user in room - //Client.join(roomId); this.nbClientsPerRoomGauge.inc({ room: roomId }); - Client.roomId = roomId; - Client.position = position; + client.roomId = roomId; + client.position = position; //check and create new world for a room let world = this.Worlds.get(roomId) if(world === undefined){ - world = new World((user1: User, group: Group) => { + world = new GameRoom((user1: User, group: Group) => { this.joinWebRtcRoom(user1, group); }, (user1: User, group: Group) => { this.disConnectedUser(user1, group); @@ -689,10 +657,10 @@ export class IoSocketController { // Dispatch groups position to newly connected user world.getGroups().forEach((group: Group) => { - this.emitCreateUpdateGroupEvent(Client, group); + this.emitCreateUpdateGroupEvent(client, group); }); //join world - world.join(Client, Client.position); + world.join(client, client.position); return world; } @@ -882,7 +850,7 @@ export class IoSocketController { } - public getWorlds(): Map { + public getWorlds(): Map { return this.Worlds; } } diff --git a/back/src/Model/World.ts b/back/src/Model/GameRoom.ts similarity index 68% rename from back/src/Model/World.ts rename to back/src/Model/GameRoom.ts index c276d04e..1f438e61 100644 --- a/back/src/Model/World.ts +++ b/back/src/Model/GameRoom.ts @@ -1,12 +1,10 @@ -import {MessageUserPosition, Point} from "./Websocket/MessageUserPosition"; import {PointInterface} from "./Websocket/PointInterface"; import {Group} from "./Group"; -import {Distance} from "./Distance"; import {User} from "./User"; import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; import {PositionInterface} from "_Model/PositionInterface"; import {Identificable} from "_Model/Websocket/Identificable"; -import {EntersCallback, LeavesCallback, MovesCallback, Zone} from "_Model/Zone"; +import {EntersCallback, LeavesCallback, MovesCallback} from "_Model/Zone"; import {PositionNotifier} from "./PositionNotifier"; import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; import {Movable} from "_Model/Movable"; @@ -14,7 +12,7 @@ import {Movable} from "_Model/Movable"; export type ConnectCallback = (user: User, group: Group) => void; export type DisconnectCallback = (user: User, group: Group) => void; -export class World { +export class GameRoom { private readonly minDistance: number; private readonly groupRadius: number; @@ -123,7 +121,7 @@ export class World { } else { // If the user is part of a group: // should he leave the group? - const distance = World.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition()); + const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition()); if (distance > this.groupRadius) { this.leaveGroup(user); } @@ -199,53 +197,19 @@ export class World { return; } - const distance = World.computeDistance(user, currentUser); // compute distance between peers. + const distance = GameRoom.computeDistance(user, currentUser); // compute distance between peers. if(distance <= minimumDistanceFound && distance <= this.minDistance) { minimumDistanceFound = distance; matchingItem = currentUser; } - /*if (typeof currentUser.group === 'undefined' || !currentUser.group.isFull()) { - // We found a user we can bind to. - return; - }*/ - /* - if(context.groups.length > 0) { - - context.groups.forEach(group => { - if(group.isPartOfGroup(userPosition)) { // Is the user in a group ? - if(group.isStillIn(userPosition)) { // Is the user leaving the group ? (is the user at more than max distance of each player) - - // Should we split the group? (is each player reachable from the current player?) - // This is needed if - // A <==> B <==> C <===> D - // becomes A <==> B <=====> C <> D - // If C moves right, the distance between B and C is too great and we must form 2 groups - - } - } else { - // If the user is in no group - // Is there someone in a group close enough and with room in the group ? - } - }); - - } else { - // Aucun groupe n'existe donc je stock les users assez proches de moi - let dist: Distance = { - distance: distance, - first: userPosition, - second: user // TODO: convertir en messageUserPosition - } - usersToBeGroupedWith.push(dist); - } - */ }); this.groups.forEach((group: Group) => { if (group.isFull()) { return; } - const distance = World.computeDistanceBetweenPositions(user.getPosition(), group.getPosition()); + const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), group.getPosition()); if(distance <= minimumDistanceFound && distance <= this.groupRadius) { minimumDistanceFound = distance; matchingItem = group; @@ -275,66 +239,7 @@ export class World { return this.itemsState; } - /*getDistancesBetweenGroupUsers(group: Group): Distance[] - { - let i = 0; - let users = group.getUsers(); - let distances: Distance[] = []; - users.forEach(function(user1, key1) { - users.forEach(function(user2, key2) { - if(key1 < key2) { - distances[i] = { - distance: World.computeDistance(user1, user2), - first: user1, - second: user2 - }; - i++; - } - }); - }); - - distances.sort(World.compareDistances); - - return distances; - } - - filterGroup(distances: Distance[], group: Group): void - { - let users = group.getUsers(); - let usersToRemove = false; - let groupTmp: MessageUserPosition[] = []; - distances.forEach(dist => { - if(dist.distance <= World.MIN_DISTANCE) { - let users = [dist.first]; - let usersbis = [dist.second] - groupTmp.push(dist.first); - groupTmp.push(dist.second); - } else { - usersToRemove = true; - } - }); - - if(usersToRemove) { - // Detecte le ou les users qui se sont fait sortir du groupe - let difference = users.filter(x => !groupTmp.includes(x)); - - // TODO : Notify users un difference that they have left the group - } - - let newgroup = new Group(groupTmp); - this.groups.push(newgroup); - } - - private static compareDistances(distA: Distance, distB: Distance): number - { - if (distA.distance < distB.distance) { - return -1; - } - if (distA.distance > distB.distance) { - return 1; - } - return 0; - }*/ + setViewport(socket : Identificable, viewport: ViewportInterface): Movable[] { const user = this.users.get(socket.userId); if(typeof user === 'undefined') { diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 16dd6cd5..9afa9764 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -1,7 +1,6 @@ -import { World, ConnectCallback, DisconnectCallback } from "./World"; +import { ConnectCallback, DisconnectCallback } from "./GameRoom"; import { User } from "./User"; import {PositionInterface} from "_Model/PositionInterface"; -import {uuid} from "uuidv4"; import {Movable} from "_Model/Movable"; import {PositionNotifier} from "_Model/PositionNotifier"; diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts new file mode 100644 index 00000000..de28e4ef --- /dev/null +++ b/back/src/Services/AdminApi.ts @@ -0,0 +1,36 @@ +import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable"; +import Axios from "axios"; + +export interface AdminApiData { + organizationSlug: string + worldSlug: string + roomSlug: string + mapUrlStart: string + userUuid: string +} + +class AdminApi { + + async fetchMemberDataByToken(organizationMemberToken: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + //todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case. + const res = await Axios.get(ADMIN_API_URL+'/api/login-url/'+organizationMemberToken, + { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } + ) + return res.data; + } + + async memberIsGrantedAccessToRoom(memberId: string, roomId: string): Promise { + if (!ADMIN_API_URL) { + return Promise.reject('No admin backoffice set!'); + } + const res = await Axios.get(ADMIN_API_URL+'/api/member/'+memberId+'/is-granted-access/'+roomId, + { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } + ) + return res.data === true; + } +} + +export const adminApi = new AdminApi(); \ No newline at end of file diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 253283af..0f556866 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -1,5 +1,5 @@ import "jasmine"; -import {World, ConnectCallback, DisconnectCallback } from "../src/Model/World"; +import {GameRoom, ConnectCallback, DisconnectCallback } from "_Model/GameRoom"; import {Point} from "../src/Model/Websocket/MessageUserPosition"; import { Group } from "../src/Model/Group"; import {PositionNotifier} from "../src/Model/PositionNotifier"; diff --git a/back/tests/WorldTest.ts b/back/tests/WorldTest.ts index 8d3b1a2d..5ab421bb 100644 --- a/back/tests/WorldTest.ts +++ b/back/tests/WorldTest.ts @@ -1,5 +1,5 @@ import "jasmine"; -import {World, ConnectCallback, DisconnectCallback } from "../src/Model/World"; +import {GameRoom, ConnectCallback, DisconnectCallback } from "_Model/GameRoom"; import {Point} from "../src/Model/Websocket/MessageUserPosition"; import { Group } from "../src/Model/Group"; import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; @@ -21,7 +21,7 @@ describe("World", () => { } - const world = new World(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + const world = new GameRoom(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); world.join(createMockUser(1), new Point(100, 100)); @@ -48,7 +48,7 @@ describe("World", () => { } - const world = new World(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + const world = new GameRoom(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); world.join(createMockUser(1), new Point(100, 100)); @@ -77,7 +77,7 @@ describe("World", () => { disconnectCallNumber++; } - const world = new World(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); + const world = new GameRoom(connect, disconnect, 160, 160, () => {}, () => {}, () => {}); world.join(createMockUser(1), new Point(100, 100)); From f542b117a8686254a43841ffd7b33de6623a6f2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 6 Oct 2020 18:09:23 +0200 Subject: [PATCH 2/6] Refactoring connection to pass room info on connect --- back/src/Controller/IoSocketController.ts | 114 ++++++---- front/package.json | 2 +- front/src/Connexion/ConnectionManager.ts | 7 +- front/src/Connexion/ConnexionModels.ts | 1 + front/src/Connexion/RoomConnection.ts | 58 ++--- front/src/Phaser/Game/GameScene.ts | 255 +++++++++++----------- front/yarn.lock | 12 +- messages/messages.proto | 7 - 8 files changed, 239 insertions(+), 217 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 1591a5f1..943f949c 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -24,7 +24,6 @@ import { ItemEventMessage, ViewportMessage, ClientToServerMessage, - JoinRoomMessage, ErrorMessage, RoomJoinedMessage, ItemStateMessage, @@ -33,17 +32,17 @@ import { SilentMessage, WebRtcSignalToClientMessage, WebRtcSignalToServerMessage, - WebRtcStartMessage, - WebRtcDisconnectMessage, + WebRtcStartMessage, + WebRtcDisconnectMessage, PlayGlobalMessage, } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import Direction = PositionMessage.Direction; import {ProtobufUtils} from "../Model/Websocket/ProtobufUtils"; -import {HttpRequest, TemplatedApp} from "uWebSockets.js" +import {TemplatedApp} from "uWebSockets.js" import {parse} from "query-string"; import {cpuTracker} from "../Services/CpuTracker"; -import {adminApi} from "../Services/AdminApi"; +import {ViewportInterface} from "../Model/Websocket/ViewportMessage"; function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void { socket.batchedMessages.addPayload(payload); @@ -95,7 +94,7 @@ export class IoSocketController { } private async getUserUuidFromToken(token: unknown): Promise { - + if (!token) { throw new Error('An authentication error happened, a user tried to connect without a token.'); } @@ -137,8 +136,7 @@ export class IoSocketController { } ioConnection() { - this.app.ws('/room', { - + this.app.ws('/room/*', { /* Options */ //compression: uWS.SHARED_COMPRESSOR, maxPayloadLength: 16 * 1024 * 1024, @@ -147,7 +145,6 @@ export class IoSocketController { upgrade: (res, req, context) => { //console.log('An Http connection wants to become WebSocket, URL: ' + req.getUrl() + '!'); (async () => { - /* Keep track of abortions */ const upgradeAborted = {aborted: false}; @@ -159,19 +156,32 @@ export class IoSocketController { try { const query = parse(req.getQuery()); - const moderated = query.moderated || false; - const roomId = query.roomId || null; + const roomId = req.getUrl().substr(6); + const token = query.token; + const x = Number(query.x); + const y = Number(query.y); + const top = Number(query.top); + const bottom = Number(query.bottom); + const left = Number(query.left); + const right = Number(query.right); + const name = query.name; + if (typeof name !== 'string') { + throw new Error('Expecting name'); + } + if (name === '') { + throw new Error('No empty name'); + } + let characterLayers = query.characterLayers; + if (characterLayers === null) { + throw new Error('Expecting skin'); + } + if (typeof characterLayers === 'string') { + characterLayers = [ characterLayers ]; + } const userUuid = await this.getUserUuidFromToken(token); - - this.handleJoinRoom(client, message.getJoinroommessage() as JoinRoomMessage); - - const isGranted = await adminApi.memberIsGrantedAccessToRoom(client.userUuid, roomId); - if (!isGranted) { - throw Error('Client cannot acces this ressource.'); - } if (upgradeAborted.aborted) { console.log("Ouch! Client disconnected before we could upgrade it!"); @@ -184,7 +194,22 @@ export class IoSocketController { // Data passed here is accessible on the "websocket" socket object. url: req.getUrl(), token, - userUuid + userUuid, + roomId, + name, + characterLayers, + position: { + x: x, + y: y, + direction: 'down', + moving: false + } as PointInterface, + viewport: { + top, + right, + bottom, + left + } }, /* Spell these correctly */ req.getHeader('sec-websocket-key'), @@ -217,12 +242,34 @@ export class IoSocketController { emitInBatch(client, payload); } client.disconnecting = false; + + client.name = ws.name; + client.characterLayers = ws.characterLayers; + client.roomId = ws.roomId; + this.sockets.set(client.userId, client); // Let's log server load when a user joins this.nbClientsGauge.inc(); console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)'); + // Let's join the room + this.handleJoinRoom(client, client.roomId, client.position, client.viewport, client.name, client.characterLayers); + + /*const isGranted = await adminApi.memberIsGrantedAccessToRoom(client.userUuid, roomId); + if (!isGranted) { + throw Error('Client cannot acces this ressource.'); + }*/ + + const setUserIdMessage = new SetUserIdMessage(); + setUserIdMessage.setUserid(client.userId); + + const serverToClientMessage = new ServerToClientMessage(); + serverToClientMessage.setSetuseridmessage(setUserIdMessage); + + if (!client.disconnecting) { + client.send(serverToClientMessage.serializeBinary().buffer, true); + } }, message: (ws, arrayBuffer, isBinary): void => { const client = ws as ExSocketInterface; @@ -305,22 +352,12 @@ export class IoSocketController { console.warn(message); } - private async handleJoinRoom(client: ExSocketInterface, message: JoinRoomMessage): Promise { + private async handleJoinRoom(client: ExSocketInterface, roomId: string, position: PointInterface, viewport: ViewportInterface, name: string, characterLayers: string[]): Promise { try { - const roomId = message.getRoomid(); - - if (client.roomId === roomId) { - return; - } - - - //leave previous room - //this.leaveRoom(Client); // Useless now, there is only one room per connection - //join new previous room - const gameRoom = await this.joinRoom(client, roomId, ProtobufUtils.toPointInterface(message.getPosition() as PositionMessage)); + const gameRoom = await this.joinRoom(client, roomId, position); - const things = gameRoom.setViewport(client, (message.getViewport() as ViewportMessage).toObject()); + const things = gameRoom.setViewport(client, viewport); const roomJoinedMessage = new RoomJoinedMessage(); @@ -448,6 +485,7 @@ export class IoSocketController { } } + // Useless now, will be useful again if we allow editing details in game private handleSetPlayerDetails(client: ExSocketInterface, playerDetailsMessage: SetPlayerDetailsMessage) { const playerDetails = { name: playerDetailsMessage.getName(), @@ -461,16 +499,6 @@ export class IoSocketController { client.name = playerDetails.name; client.characterLayers = playerDetails.characterLayers; - - const setUserIdMessage = new SetUserIdMessage(); - setUserIdMessage.setUserid(client.userId); - - const serverToClientMessage = new ServerToClientMessage(); - serverToClientMessage.setSetuseridmessage(setUserIdMessage); - - if (!client.disconnecting) { - client.send(serverToClientMessage.serializeBinary().buffer, true); - } } private handleSilentMessage(client: ExSocketInterface, silentMessage: SilentMessage) { @@ -585,7 +613,7 @@ export class IoSocketController { } private joinRoom(client : ExSocketInterface, roomId: string, position: PointInterface): GameRoom { - + //join user in room this.nbClientsPerRoomGauge.inc({ room: roomId }); client.roomId = roomId; diff --git a/front/package.json b/front/package.json index c3b9f880..76e71f35 100644 --- a/front/package.json +++ b/front/package.json @@ -27,7 +27,7 @@ "google-protobuf": "^3.13.0", "phaser": "^3.22.0", "queue-typescript": "^1.0.1", - "quill": "1.3.6", + "quill": "^1.3.7", "simple-peer": "^9.6.2", "socket.io-client": "^2.3.0", "webpack-require-http": "^0.4.3" diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 4df45099..217f9e8d 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -1,6 +1,7 @@ import Axios from "axios"; import {API_URL} from "../Enum/EnvironmentVariable"; import {RoomConnection} from "./RoomConnection"; +import {PositionInterface, ViewportInterface} from "./ConnexionModels"; interface LoginApiData { authToken: string @@ -35,9 +36,9 @@ class ConnectionManager { this.authToken = 'test'; } - public connectToRoomSocket(): Promise { + public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise { return new Promise((resolve, reject) => { - const connection = new RoomConnection(this.authToken as string); + const connection = new RoomConnection(this.authToken as string, roomId, name, characterLayers, position, viewport); connection.onConnectError((error: object) => { console.log('An error occurred while connecting to socket server. Retrying'); reject(error); @@ -50,7 +51,7 @@ class ConnectionManager { return new Promise((resolve, reject) => { setTimeout(() => { //todo: allow a way to break recurrsion? - this.connectToRoomSocket().then((connection) => resolve(connection)); + this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection)); }, 4000 + Math.floor(Math.random() * 2000) ); }); }); diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 4ec76198..3df32331 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -6,6 +6,7 @@ export enum EventMessage{ WEBRTC_SIGNAL = "webrtc-signal", WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", WEBRTC_START = "webrtc-start", + START_ROOM = "start-room", // From server to client: list of all room users/groups/items JOIN_ROOM = "join-room", // bi-directional USER_POSITION = "user-position", // From client to server USER_MOVED = "user-moved", // From server to client diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 3492f0f9..8799f977 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -6,7 +6,7 @@ import { GroupDeleteMessage, GroupUpdateMessage, ItemEventMessage, - JoinRoomMessage, PlayGlobalMessage, + PlayGlobalMessage, PositionMessage, RoomJoinedMessage, ServerToClientMessage, @@ -30,7 +30,7 @@ import {ProtobufClientUtils} from "../Network/ProtobufClientUtils"; import { EventMessage, GroupCreatedUpdatedMessageInterface, ItemEventMessageInterface, - MessageUserJoined, PlayGlobalMessageInterface, + MessageUserJoined, PlayGlobalMessageInterface, PositionInterface, RoomJoinedMessageInterface, ViewportInterface, WebRtcDisconnectMessageInterface, WebRtcSignalReceivedMessageInterface, @@ -48,9 +48,25 @@ export class RoomConnection implements RoomConnection { RoomConnection.websocketFactory = websocketFactory; } - public constructor(token: string) { + /** + * + * @param token A JWT token containing the UUID of the user + * @param roomId The ID of the room in the form "_/[instance]/[map_url]" or "@/[org]/[event]/[map]" + */ + public constructor(token: string, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { let url = API_URL.replace('http://', 'ws://').replace('https://', 'wss://'); - url += '?token='+token; + url += '/room/'+roomId + url += '?token='+encodeURIComponent(token); + url += '&name='+encodeURIComponent(name); + for (let layer of characterLayers) { + url += '&characterLayers='+encodeURIComponent(layer); + } + url += '&x='+Math.floor(position.x); + url += '&y='+Math.floor(position.y); + url += '&top='+Math.floor(viewport.top); + url += '&bottom='+Math.floor(viewport.bottom); + url += '&left='+Math.floor(viewport.left); + url += '&right='+Math.floor(viewport.right); if (RoomConnection.websocketFactory) { this.socket = RoomConnection.websocketFactory(url); @@ -106,11 +122,11 @@ export class RoomConnection implements RoomConnection { items[item.getItemid()] = JSON.parse(item.getStatejson()); } - this.resolveJoinRoom({ + this.dispatch(EventMessage.START_ROOM, { users, groups, items - }) + }); } else if (message.hasSetuseridmessage()) { this.userId = (message.getSetuseridmessage() as SetUserIdMessage).getUserid(); } else if (message.hasErrormessage()) { @@ -159,29 +175,6 @@ export class RoomConnection implements RoomConnection { this.socket?.close(); } - private resolveJoinRoom!: (value?: (RoomJoinedMessageInterface | PromiseLike | undefined)) => void; - - public joinARoom(roomId: string, startX: number, startY: number, direction: string, moving: boolean, viewport: ViewportInterface): Promise { - const promise = new Promise((resolve, reject) => { - this.resolveJoinRoom = resolve; - - const positionMessage = this.toPositionMessage(startX, startY, direction, moving); - const viewportMessage = this.toViewportMessage(viewport); - - const joinRoomMessage = new JoinRoomMessage(); - joinRoomMessage.setRoomid(roomId); - joinRoomMessage.setPosition(positionMessage); - joinRoomMessage.setViewport(viewportMessage); - - //console.log('Sending position ', positionMessage.getX(), positionMessage.getY()); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setJoinroommessage(joinRoomMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); - }) - return promise; - } - private toPositionMessage(x : number, y : number, direction : string, moving: boolean): PositionMessage { const positionMessage = new PositionMessage(); positionMessage.setX(Math.floor(x)); @@ -337,6 +330,13 @@ export class RoomConnection implements RoomConnection { this.socket.addEventListener('open', callback) } + /** + * Triggered when we receive all the details of a room (users, groups, ...) + */ + public onStartRoom(callback: (event: RoomJoinedMessageInterface) => void): void { + this.onMessage(EventMessage.START_ROOM, callback); + } + public sendWebrtcSignal(signal: unknown, receiverId: number) { const webRtcSignal = new WebRtcSignalToServerMessage(); webRtcSignal.setReceiverid(receiverId); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index bdaf3ac6..b3a5f104 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -107,7 +107,6 @@ export class GameScene extends Phaser.Scene implements CenterListener { private simplePeer!: SimplePeer; private GlobalMessageManager!: GlobalMessageManager; private ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager; - private connectionPromise!: Promise private connectionAnswerPromise: Promise; private connectionAnswerPromiseResolve!: (value?: RoomJoinedMessageInterface | PromiseLike) => void; // A promise that will resolve when the "create" method is called (signaling loading is ended) @@ -206,106 +205,6 @@ export class GameScene extends Phaser.Scene implements CenterListener { loadAllLayers(this.load); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); - - this.connectionPromise = connectionManager.connectToRoomSocket().then((connection : RoomConnection) => { - this.connection = connection; - - this.connection.emitPlayerDetailsMessage(gameManager.getPlayerName(), gameManager.getCharacterSelected()) - - connection.onUserJoins((message: MessageUserJoined) => { - const userMessage: AddPlayerInterface = { - userId: message.userId, - characterLayers: message.characterLayers, - name: message.name, - position: message.position - } - this.addPlayer(userMessage); - }); - - connection.onUserMoved((message: UserMovedMessage) => { - const position = message.getPosition(); - if (position === undefined) { - throw new Error('Position missing from UserMovedMessage'); - } - //console.log('Received position ', position.getX(), position.getY(), "from user", message.getUserid()); - - const messageUserMoved: MessageUserMovedInterface = { - userId: message.getUserid(), - position: ProtobufClientUtils.toPointInterface(position) - } - - this.updatePlayerPosition(messageUserMoved); - }); - - connection.onUserLeft((userId: number) => { - this.removePlayer(userId); - }); - - connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { - this.shareGroupPosition(groupPositionMessage); - }) - - connection.onGroupDeleted((groupId: number) => { - try { - this.deleteGroup(groupId); - } catch (e) { - console.error(e); - } - }) - - connection.onServerDisconnected(() => { - console.log('Player disconnected from server. Reloading scene.'); - - this.simplePeer.closeAllConnections(); - this.simplePeer.unregister(); - - const key = 'somekey'+Math.round(Math.random()*10000); - const game : Phaser.Scene = GameScene.createFromUrl(this.MapUrlFile, this.instance, key); - this.scene.add(key, game, true, - { - initPosition: { - x: this.CurrentPlayer.x, - y: this.CurrentPlayer.y - } - }); - - this.scene.stop(this.scene.key); - this.scene.remove(this.scene.key); - window.removeEventListener('resize', this.onResizeCallback); - }) - - connection.onActionableEvent((message => { - const item = this.actionableItems.get(message.itemId); - if (item === undefined) { - console.warn('Received an event about object "'+message.itemId+'" but cannot find this item on the map.'); - return; - } - item.fire(message.event, message.state, message.parameters); - })); - - // When connection is performed, let's connect SimplePeer - this.simplePeer = new SimplePeer(this.connection); - this.GlobalMessageManager = new GlobalMessageManager(this.connection); - - const self = this; - this.simplePeer.registerPeerConnectionListener({ - onConnect(user: UserSimplePeerInterface) { - self.presentationModeSprite.setVisible(true); - self.chatModeSprite.setVisible(true); - }, - onDisconnect(userId: number) { - if (self.simplePeer.getNbConnections() === 0) { - self.presentationModeSprite.setVisible(false); - self.chatModeSprite.setVisible(false); - } - } - }) - - this.scene.wake(); - this.scene.sleep(ReconnectingSceneName); - - return connection; - }); } // FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving. @@ -617,6 +516,133 @@ export class GameScene extends Phaser.Scene implements CenterListener { this.connection.setSilent(true); } }); + + const camera = this.cameras.main; + + connectionManager.connectToRoomSocket( + this.RoomId, + gameManager.getPlayerName(), + gameManager.getCharacterSelected(), + { + x: this.startX, + y: this.startY + }, + { + left: camera.scrollX, + top: camera.scrollY, + right: camera.scrollX + camera.width, + bottom: camera.scrollY + camera.height, + }).then((connection : RoomConnection) => { + this.connection = connection; + + //this.connection.emitPlayerDetailsMessage(gameManager.getPlayerName(), gameManager.getCharacterSelected()) + connection.onStartRoom((roomJoinedMessage: RoomJoinedMessageInterface) => { + this.initUsersPosition(roomJoinedMessage.users); + this.connectionAnswerPromiseResolve(roomJoinedMessage); + }); + + connection.onUserJoins((message: MessageUserJoined) => { + const userMessage: AddPlayerInterface = { + userId: message.userId, + characterLayers: message.characterLayers, + name: message.name, + position: message.position + } + this.addPlayer(userMessage); + }); + + connection.onUserMoved((message: UserMovedMessage) => { + const position = message.getPosition(); + if (position === undefined) { + throw new Error('Position missing from UserMovedMessage'); + } + //console.log('Received position ', position.getX(), position.getY(), "from user", message.getUserid()); + + const messageUserMoved: MessageUserMovedInterface = { + userId: message.getUserid(), + position: ProtobufClientUtils.toPointInterface(position) + } + + this.updatePlayerPosition(messageUserMoved); + }); + + connection.onUserLeft((userId: number) => { + this.removePlayer(userId); + }); + + connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { + this.shareGroupPosition(groupPositionMessage); + }) + + connection.onGroupDeleted((groupId: number) => { + try { + this.deleteGroup(groupId); + } catch (e) { + console.error(e); + } + }) + + connection.onServerDisconnected(() => { + console.log('Player disconnected from server. Reloading scene.'); + + this.simplePeer.closeAllConnections(); + this.simplePeer.unregister(); + + const key = 'somekey'+Math.round(Math.random()*10000); + const game : Phaser.Scene = GameScene.createFromUrl(this.MapUrlFile, this.instance, key); + this.scene.add(key, game, true, + { + initPosition: { + x: this.CurrentPlayer.x, + y: this.CurrentPlayer.y + } + }); + + this.scene.stop(this.scene.key); + this.scene.remove(this.scene.key); + window.removeEventListener('resize', this.onResizeCallback); + }) + + connection.onActionableEvent((message => { + const item = this.actionableItems.get(message.itemId); + if (item === undefined) { + console.warn('Received an event about object "'+message.itemId+'" but cannot find this item on the map.'); + return; + } + item.fire(message.event, message.state, message.parameters); + })); + + // When connection is performed, let's connect SimplePeer + this.simplePeer = new SimplePeer(this.connection); + this.GlobalMessageManager = new GlobalMessageManager(this.connection); + + const self = this; + this.simplePeer.registerPeerConnectionListener({ + onConnect(user: UserSimplePeerInterface) { + self.presentationModeSprite.setVisible(true); + self.chatModeSprite.setVisible(true); + }, + onDisconnect(userId: number) { + if (self.simplePeer.getNbConnections() === 0) { + self.presentationModeSprite.setVisible(false); + self.chatModeSprite.setVisible(false); + } + } + }) + + //listen event to share position of user + this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) + this.CurrentPlayer.on(hasMovedEventName, this.outlineItem.bind(this)) + this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => { + this.gameMap.setPosition(event.x, event.y); + }) + + + this.scene.wake(); + this.scene.sleep(ReconnectingSceneName); + + return connection; + }); } private switchLayoutMode(): void { @@ -787,32 +813,6 @@ export class GameScene extends Phaser.Scene implements CenterListener { //create collision this.createCollisionWithPlayer(); this.createCollisionObject(); - - //join room - this.connectionPromise.then((connection: RoomConnection) => { - const camera = this.cameras.main; - connection.joinARoom(this.RoomId, - this.startX, - this.startY, - PlayerAnimationNames.WalkDown, - false, { - left: camera.scrollX, - top: camera.scrollY, - right: camera.scrollX + camera.width, - bottom: camera.scrollY + camera.height, - }).then((roomJoinedMessage: RoomJoinedMessageInterface) => { - this.initUsersPosition(roomJoinedMessage.users); - this.connectionAnswerPromiseResolve(roomJoinedMessage); - }); - // FIXME: weirdly enough we don't use the result of joinARoom !!!!!! - - //listen event to share position of user - this.CurrentPlayer.on(hasMovedEventName, this.pushPlayerPosition.bind(this)) - this.CurrentPlayer.on(hasMovedEventName, this.outlineItem.bind(this)) - this.CurrentPlayer.on(hasMovedEventName, (event: HasMovedEvent) => { - this.gameMap.setPosition(event.x, event.y); - }) - }); } pushPlayerPosition(event: HasMovedEvent) { @@ -983,7 +983,6 @@ export class GameScene extends Phaser.Scene implements CenterListener { type: "InitUserPositionEvent", event: usersPosition }); - } /** diff --git a/front/yarn.lock b/front/yarn.lock index c014d18d..5d235a82 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -1893,7 +1893,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.1, extend@^3.0.2: +extend@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -3871,15 +3871,15 @@ quill-delta@^3.6.2: extend "^3.0.2" fast-diff "1.1.2" -quill@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.6.tgz#99f4de1fee85925a0d7d4163b6d8328f23317a4d" - integrity sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug== +quill@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8" + integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g== dependencies: clone "^2.1.1" deep-equal "^1.0.1" eventemitter3 "^2.0.3" - extend "^3.0.1" + extend "^3.0.2" parchment "^1.1.4" quill-delta "^3.6.2" diff --git a/messages/messages.proto b/messages/messages.proto index 87cf2231..63c3ddaf 100644 --- a/messages/messages.proto +++ b/messages/messages.proto @@ -38,12 +38,6 @@ message SetPlayerDetailsMessage { repeated string characterLayers = 2; } -message JoinRoomMessage { - string roomId = 1; - PositionMessage position = 2; - ViewportMessage viewport = 3; -} - message UserMovesMessage { PositionMessage position = 1; ViewportMessage viewport = 2; @@ -56,7 +50,6 @@ message WebRtcSignalToServerMessage { message ClientToServerMessage { oneof message { - JoinRoomMessage joinRoomMessage = 1; UserMovesMessage userMovesMessage = 2; SilentMessage silentMessage = 3; ViewportMessage viewportMessage = 4; From 4af46b1b3fe10d51c472bdc1a9023c6495504c05 Mon Sep 17 00:00:00 2001 From: arp Date: Thu, 8 Oct 2020 18:51:24 +0200 Subject: [PATCH 3/6] simplified mapUrl parsing --- back/src/Controller/AuthenticateController.ts | 7 +- front/src/Connexion/ConnectionManager.ts | 14 +++- .../src/Phaser/Entity/GameSceneDescriptor.ts | 6 ++ front/src/Phaser/Game/GameManager.ts | 66 +++++++++++++++---- front/src/Phaser/Game/GameScene.ts | 27 ++++---- front/src/Phaser/Login/EnableCameraScene.ts | 46 ++----------- 6 files changed, 93 insertions(+), 73 deletions(-) create mode 100644 front/src/Phaser/Entity/GameSceneDescriptor.ts diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index 7a8a95dd..a178b530 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -36,6 +36,7 @@ export class AuthenticateController extends BaseController { //todo: what to do if the organizationMemberToken is already used? const organizationMemberToken:string|null = param.organizationMemberToken; + const mapSlug:string|null = param.mapSlug; try { let userUuid; @@ -48,10 +49,14 @@ export class AuthenticateController extends BaseController { userUuid = data.userUuid; mapUrlStart = data.mapUrlStart; newUrl = this.getNewUrlOnAdminAuth(data) + } else if (mapSlug !== null) { + userUuid = uuid(); + mapUrlStart = mapSlug; + newUrl = null; } else { userUuid = uuid(); mapUrlStart = host.replace('api.', 'maps.') + URL_ROOM_STARTED; - newUrl = null; + newUrl = '_/global/'+mapUrlStart; } const authToken = Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '24h'}); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 217f9e8d..a9d15dd9 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -17,10 +17,20 @@ class ConnectionManager { private authToken:string|null = null; private userUuid: string|null = null; + //todo: get map infos from url in anonym case public async init(): Promise { + let organizationMemberToken = null; + let teamSlug = null; + let mapSlug = null; const match = /\/register\/(.+)/.exec(window.location.toString()); - const organizationMemberToken = match ? match[1] : null; - this.initPromise = Axios.post(`${API_URL}/login`, {organizationMemberToken}).then(res => res.data); + if (match) { + organizationMemberToken = match[1]; + } else { + const match = /\/_\/(.+)\/(.+)/.exec(window.location.toString()); + teamSlug = match ? match[1] : null; + mapSlug = match ? match[2] : null; + } + this.initPromise = Axios.post(`${API_URL}/login`, {organizationMemberToken, teamSlug, mapSlug}).then(res => res.data); const data = await this.initPromise this.authToken = data.authToken; this.userUuid = data.userUuid; diff --git a/front/src/Phaser/Entity/GameSceneDescriptor.ts b/front/src/Phaser/Entity/GameSceneDescriptor.ts new file mode 100644 index 00000000..df114c28 --- /dev/null +++ b/front/src/Phaser/Entity/GameSceneDescriptor.ts @@ -0,0 +1,6 @@ +export class GameSceneDescriptor { + + constructor(MapKey : string, MapUrlFile: string, instance: string, key: string) { + this.roomId = '';// + } +} \ No newline at end of file diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 960ce7e2..5188d2fe 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,4 +1,4 @@ -import {GameScene} from "./GameScene"; +import {GameScene, GameSceneInitInterface} from "./GameScene"; import { StartMapInterface } from "../../Connexion/ConnexionModels"; @@ -13,6 +13,11 @@ export interface HasMovedEvent { y: number; } +export interface loadMapResponseInterface { + key: string, + startLayerName: string; +} + export class GameManager { private playerName!: string; private characterLayers!: string[]; @@ -29,15 +34,6 @@ export class GameManager { this.characterLayers = layers; } - loadStartMap() : Promise { - return connectionManager.getMapUrlStart().then(mapUrlStart => { - return { - mapUrlStart: mapUrlStart, - startInstance: "global", //todo: is this property still usefull? - } - }); - } - getPlayerName(): string { return this.playerName; } @@ -46,8 +42,47 @@ export class GameManager { return this.characterLayers; } - loadMap(mapUrl: string, scene: Phaser.Scenes.ScenePlugin, instance: string): string { - const sceneKey = GameScene.getMapKeyByUrl(mapUrl); + /** + * Returns the map URL and the instance from the current URL + */ + private findMapUrl(): [string, string]|null { + const path = window.location.pathname; + if (!path.startsWith('/_/')) { + return null; + } + const instanceAndMap = path.substr(3); + const firstSlash = instanceAndMap.indexOf('/'); + if (firstSlash === -1) { + return null; + } + const instance = instanceAndMap.substr(0, firstSlash); + return [window.location.protocol+'//'+instanceAndMap.substr(firstSlash+1), instance]; + } + + public loadStartingMap(scene: Phaser.Scenes.ScenePlugin): Promise { + // Do we have a start URL in the address bar? If so, let's redirect to this address + const instanceAndMapUrl = this.findMapUrl(); + if (instanceAndMapUrl !== null) { + const [mapUrl, instance] = instanceAndMapUrl; + const key = gameManager.loadMap(mapUrl, scene, instance); + const startLayerName = window.location.hash ? window.location.hash.substr(1) : ''; + return Promise.resolve({key, startLayerName}); + + } else { + // If we do not have a map address in the URL, let's ask the server for a start map. + return connectionManager.getMapUrlStart().then((mapUrlStart: string) => { + const key = gameManager.loadMap(window.location.protocol + "//" + mapUrlStart, scene, 'global'); + return {key, startLayerName: ''} + }).catch((err) => { + console.error(err); + throw err; + }); + } + + } + + public loadMap(mapUrl: string, scene: Phaser.Scenes.ScenePlugin, instance: string): string { + const sceneKey = this.getMapKeyByUrl(mapUrl); const gameIndex = scene.getIndex(sceneKey); if(gameIndex === -1){ @@ -56,6 +91,13 @@ export class GameManager { } return sceneKey; } + + public getMapKeyByUrl(mapUrlStart: string) : string { + // FIXME: the key should be computed from the full URL of the map. + const startPos = mapUrlStart.indexOf('://')+3; + const endPos = mapUrlStart.indexOf(".json"); + return mapUrlStart.substring(startPos, endPos); + } } export const gameManager = new GameManager(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index b3a5f104..a9591f21 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -138,17 +138,17 @@ export class GameScene extends Phaser.Scene implements CenterListener { private outlinedItem: ActionableItem|null = null; private userInputManager!: UserInputManager; - static createFromUrl(mapUrlFile: string, instance: string, key: string|null = null): GameScene { - const mapKey = GameScene.getMapKeyByUrl(mapUrlFile); - if (key === null) { - key = mapKey; + static createFromUrl(mapUrlFile: string, instance: string, gameSceneKey: string|null = null): GameScene { + const mapKey = gameManager.getMapKeyByUrl(mapUrlFile); + if (gameSceneKey === null) { + gameSceneKey = mapKey; } - return new GameScene(mapKey, mapUrlFile, instance, key); + return new GameScene(mapKey, mapUrlFile, instance, gameSceneKey); } - constructor(MapKey : string, MapUrlFile: string, instance: string, key: string) { + constructor(MapKey : string, MapUrlFile: string, instance: string, gameSceneKey: string) { super({ - key: key + key: gameSceneKey }); this.GameManager = gameManager; @@ -588,9 +588,9 @@ export class GameScene extends Phaser.Scene implements CenterListener { this.simplePeer.closeAllConnections(); this.simplePeer.unregister(); - const key = 'somekey'+Math.round(Math.random()*10000); - const game : Phaser.Scene = GameScene.createFromUrl(this.MapUrlFile, this.instance, key); - this.scene.add(key, game, true, + const gameSceneKey = 'somekey'+Math.round(Math.random()*10000); + const game : Phaser.Scene = GameScene.createFromUrl(this.MapUrlFile, this.instance, gameSceneKey); + this.scene.add(gameSceneKey, game, true, { initPosition: { x: this.CurrentPlayer.x, @@ -1136,12 +1136,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { this.groups.delete(groupId); } - public static getMapKeyByUrl(mapUrlStart: string) : string { - // FIXME: the key should be computed from the full URL of the map. - const startPos = mapUrlStart.indexOf('://')+3; - const endPos = mapUrlStart.indexOf(".json"); - return mapUrlStart.substring(startPos, endPos); - } + /** * Sends to the server an event emitted by one of the ActionableItems. diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 6ac1ad47..672facbb 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -94,7 +94,7 @@ export class EnableCameraScene extends Phaser.Scene { this.add.existing(this.logo); this.input.keyboard.on('keyup-ENTER', () => { - return this.login(); + this.login(); }); this.getElementByIdOrFail('webRtcSetup').classList.add('active'); @@ -258,7 +258,7 @@ export class EnableCameraScene extends Phaser.Scene { this.soundMeterSprite.setVolume(this.soundMeter.getVolume()); } - private async login(): Promise { + private async login(): Promise { this.getElementByIdOrFail('webRtcSetup').style.display = 'none'; this.soundMeter.stop(); window.removeEventListener('resize', this.repositionCallback); @@ -266,46 +266,8 @@ export class EnableCameraScene extends Phaser.Scene { mediaManager.stopCamera(); mediaManager.stopMicrophone(); - // Do we have a start URL in the address bar? If so, let's redirect to this address - const instanceAndMapUrl = this.findMapUrl(); - if (instanceAndMapUrl !== null) { - const [mapUrl, instance] = instanceAndMapUrl; - const key = gameManager.loadMap(mapUrl, this.scene, instance); - this.scene.start(key, { - startLayerName: window.location.hash ? window.location.hash.substr(1) : undefined - } as GameSceneInitInterface); - return { - mapUrlStart: mapUrl, - startInstance: instance - }; - } else { - // If we do not have a map address in the URL, let's ask the server for a start map. - return gameManager.loadStartMap().then((startMap: StartMapInterface) => { - const key = gameManager.loadMap(window.location.protocol + "//" + startMap.mapUrlStart, this.scene, startMap.startInstance); - this.scene.start(key); - return startMap; - }).catch((err) => { - console.error(err); - throw err; - }); - } - } - - /** - * Returns the map URL and the instance from the current URL - */ - private findMapUrl(): [string, string]|null { - const path = window.location.pathname; - if (!path.startsWith('/_/')) { - return null; - } - const instanceAndMap = path.substr(3); - const firstSlash = instanceAndMap.indexOf('/'); - if (firstSlash === -1) { - return null; - } - const instance = instanceAndMap.substr(0, firstSlash); - return [window.location.protocol+'//'+instanceAndMap.substr(firstSlash+1), instance]; + let {key, startLayerName} = await gameManager.loadStartingMap(this.scene); + this.scene.start(key, {startLayerName}); } private async getDevices() { From 5e54fc2c26901810572842fbeea66bb10e983e18 Mon Sep 17 00:00:00 2001 From: arp Date: Fri, 9 Oct 2020 14:53:18 +0200 Subject: [PATCH 4/6] some fixes --- back/src/Controller/AuthenticateController.ts | 6 +- back/src/Controller/IoSocketController.ts | 68 +++---------------- back/src/Services/JWTTokenManager.ts | 60 ++++++++++++++++ front/src/Connexion/ConnectionManager.ts | 2 +- front/src/Connexion/RoomConnection.ts | 4 +- .../src/Phaser/Entity/GameSceneDescriptor.ts | 6 -- 6 files changed, 76 insertions(+), 70 deletions(-) create mode 100644 back/src/Services/JWTTokenManager.ts delete mode 100644 front/src/Phaser/Entity/GameSceneDescriptor.ts diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index a178b530..d84ccb56 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -1,9 +1,9 @@ -import Jwt from "jsonwebtoken"; -import {ADMIN_API_TOKEN, ADMIN_API_URL, SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import {URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import { uuid } from 'uuidv4'; import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; import {BaseController} from "./BaseController"; import {adminApi, AdminApiData} from "../Services/AdminApi"; +import {jwtTokenManager} from "../Services/JWTTokenManager"; export interface TokenInterface { userUuid: string @@ -59,7 +59,7 @@ export class AuthenticateController extends BaseController { newUrl = '_/global/'+mapUrlStart; } - const authToken = Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '24h'}); + const authToken = jwtTokenManager.createJWTToken(userUuid); res.writeStatus("200 OK").end(JSON.stringify({ authToken, userUuid, diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 943f949c..30f14134 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -1,14 +1,11 @@ import {ExSocketInterface} from "../Model/Websocket/ExSocketInterface"; //TODO fix import by "_Model/.." -import Jwt from "jsonwebtoken"; -import {SECRET_KEY, MINIMUM_DISTANCE, GROUP_RADIUS, ALLOW_ARTILLERY} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import {MINIMUM_DISTANCE, GROUP_RADIUS} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." import {GameRoom} from "../Model/GameRoom"; import {Group} from "../Model/Group"; import {User} from "../Model/User"; import {isSetPlayerDetailsMessage,} from "../Model/Websocket/SetPlayerDetailsMessage"; import {Gauge} from "prom-client"; -import {TokenInterface} from "../Controller/AuthenticateController"; import {PointInterface} from "../Model/Websocket/PointInterface"; -import {uuid} from 'uuidv4'; import {Movable} from "../Model/Movable"; import { PositionMessage, @@ -43,6 +40,8 @@ import {TemplatedApp} from "uWebSockets.js" import {parse} from "query-string"; import {cpuTracker} from "../Services/CpuTracker"; import {ViewportInterface} from "../Model/Websocket/ViewportMessage"; +import {jwtTokenManager} from "../Services/JWTTokenManager"; +import {adminApi} from "../Services/AdminApi"; function emitInBatch(socket: ExSocketInterface, payload: SubMessage): void { socket.batchedMessages.addPayload(payload); @@ -86,54 +85,7 @@ export class IoSocketController { this.ioConnection(); } - private isValidToken(token: object): token is TokenInterface { - if (typeof((token as TokenInterface).userUuid) !== 'string') { - return false; - } - return true; - } - - private async getUserUuidFromToken(token: unknown): Promise { - - if (!token) { - throw new Error('An authentication error happened, a user tried to connect without a token.'); - } - if (typeof(token) !== "string") { - throw new Error('Token is expected to be a string'); - } - - - if(token === 'test') { - if (ALLOW_ARTILLERY) { - return uuid(); - } else { - throw new Error("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); - } - } - - return new Promise((resolve, reject) => { - Jwt.verify(token, SECRET_KEY, {},(err, tokenDecoded) => { - const tokenInterface = tokenDecoded as TokenInterface; - if (err) { - console.error('An authentication error happened, invalid JsonWebToken.', err); - reject(new Error('An authentication error happened, invalid JsonWebToken. '+err.message)); - return; - } - if (tokenDecoded === undefined) { - console.error('Empty token found.'); - reject(new Error('Empty token found.')); - return; - } - - if (!this.isValidToken(tokenInterface)) { - reject(new Error('Authentication error, invalid token structure.')); - return; - } - - resolve(tokenInterface.userUuid); - }); - }); - } + ioConnection() { this.app.ws('/room/*', { @@ -181,7 +133,12 @@ export class IoSocketController { } - const userUuid = await this.getUserUuidFromToken(token); + const userUuid = await jwtTokenManager.getUserUuidFromToken(token); + + const isGranted = await adminApi.memberIsGrantedAccessToRoom(userUuid, roomId); + if (!isGranted) { + throw Error('Client cannot acces this ressource.'); + } if (upgradeAborted.aborted) { console.log("Ouch! Client disconnected before we could upgrade it!"); @@ -256,11 +213,6 @@ export class IoSocketController { // Let's join the room this.handleJoinRoom(client, client.roomId, client.position, client.viewport, client.name, client.characterLayers); - /*const isGranted = await adminApi.memberIsGrantedAccessToRoom(client.userUuid, roomId); - if (!isGranted) { - throw Error('Client cannot acces this ressource.'); - }*/ - const setUserIdMessage = new SetUserIdMessage(); setUserIdMessage.setUserid(client.userId); diff --git a/back/src/Services/JWTTokenManager.ts b/back/src/Services/JWTTokenManager.ts new file mode 100644 index 00000000..905e0ac6 --- /dev/null +++ b/back/src/Services/JWTTokenManager.ts @@ -0,0 +1,60 @@ +import {ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVariable"; +import {uuid} from "uuidv4"; +import Jwt from "jsonwebtoken"; +import {TokenInterface} from "../Controller/AuthenticateController"; + +class JWTTokenManager { + + public createJWTToken(userUuid: string) { + return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '24h'}); + } + + public async getUserUuidFromToken(token: unknown): Promise { + + if (!token) { + throw new Error('An authentication error happened, a user tried to connect without a token.'); + } + if (typeof(token) !== "string") { + throw new Error('Token is expected to be a string'); + } + + + if(token === 'test') { + if (ALLOW_ARTILLERY) { + return uuid(); + } else { + throw new Error("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); + } + } + + return new Promise((resolve, reject) => { + Jwt.verify(token, SECRET_KEY, {},(err, tokenDecoded) => { + const tokenInterface = tokenDecoded as TokenInterface; + if (err) { + console.error('An authentication error happened, invalid JsonWebToken.', err); + reject(new Error('An authentication error happened, invalid JsonWebToken. '+err.message)); + return; + } + if (tokenDecoded === undefined) { + console.error('Empty token found.'); + reject(new Error('Empty token found.')); + return; + } + + if (!this.isValidToken(tokenInterface)) { + reject(new Error('Authentication error, invalid token structure.')); + return; + } + + resolve(tokenInterface.userUuid); + }); + }); + } + + private isValidToken(token: object): token is TokenInterface { + return !(typeof((token as TokenInterface).userUuid) !== 'string'); + } + +} + +export const jwtTokenManager = new JWTTokenManager(); \ No newline at end of file diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index a9d15dd9..06f8fe03 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -48,7 +48,7 @@ class ConnectionManager { public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise { return new Promise((resolve, reject) => { - const connection = new RoomConnection(this.authToken as string, roomId, name, characterLayers, position, viewport); + const connection = new RoomConnection(this.authToken, roomId, name, characterLayers, position, viewport); connection.onConnectError((error: object) => { console.log('An error occurred while connecting to socket server. Retrying'); reject(error); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 8799f977..f34197d3 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -53,10 +53,10 @@ export class RoomConnection implements RoomConnection { * @param token A JWT token containing the UUID of the user * @param roomId The ID of the room in the form "_/[instance]/[map_url]" or "@/[org]/[event]/[map]" */ - public constructor(token: string, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { + public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { let url = API_URL.replace('http://', 'ws://').replace('https://', 'wss://'); url += '/room/'+roomId - url += '?token='+encodeURIComponent(token); + url += '?token='+(token ?encodeURIComponent(token):''); url += '&name='+encodeURIComponent(name); for (let layer of characterLayers) { url += '&characterLayers='+encodeURIComponent(layer); diff --git a/front/src/Phaser/Entity/GameSceneDescriptor.ts b/front/src/Phaser/Entity/GameSceneDescriptor.ts deleted file mode 100644 index df114c28..00000000 --- a/front/src/Phaser/Entity/GameSceneDescriptor.ts +++ /dev/null @@ -1,6 +0,0 @@ -export class GameSceneDescriptor { - - constructor(MapKey : string, MapUrlFile: string, instance: string, key: string) { - this.roomId = '';// - } -} \ No newline at end of file From c5f8b43fec79ac7f480b72acfdd8cd89530c103a Mon Sep 17 00:00:00 2001 From: arp Date: Fri, 9 Oct 2020 16:18:25 +0200 Subject: [PATCH 5/6] more fixes --- back/src/Controller/IoSocketController.ts | 22 +++++++++++++++------- back/src/Services/AdminApi.ts | 15 ++++++++++----- front/src/Connexion/ConnectionManager.ts | 1 + 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 30f14134..36a52e46 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -106,7 +106,11 @@ export class IoSocketController { }); try { + const url = req.getUrl(); const query = parse(req.getQuery()); + const websocketKey = req.getHeader('sec-websocket-key'); + const websocketProtocol = req.getHeader('sec-websocket-protocol'); + const websocketExtensions = req.getHeader('sec-websocket-extensions'); const roomId = req.getUrl().substr(6); @@ -134,10 +138,14 @@ export class IoSocketController { const userUuid = await jwtTokenManager.getUserUuidFromToken(token); + console.log('uuid', userUuid); const isGranted = await adminApi.memberIsGrantedAccessToRoom(userUuid, roomId); if (!isGranted) { - throw Error('Client cannot acces this ressource.'); + console.log('access not granted for user '+userUuid+' and room '+roomId); + throw new Error('Client cannot acces this ressource.') + } else { + console.log('access granted for user '+userUuid+' and room '+roomId); } if (upgradeAborted.aborted) { @@ -149,7 +157,7 @@ export class IoSocketController { /* This immediately calls open handler, you must not use res after this call */ res.upgrade({ // Data passed here is accessible on the "websocket" socket object. - url: req.getUrl(), + url, token, userUuid, roomId, @@ -169,17 +177,17 @@ export class IoSocketController { } }, /* Spell these correctly */ - req.getHeader('sec-websocket-key'), - req.getHeader('sec-websocket-protocol'), - req.getHeader('sec-websocket-extensions'), + websocketKey, + websocketProtocol, + websocketExtensions, context); } catch (e) { if (e instanceof Error) { - console.warn(e.message); + console.log(e.message); res.writeStatus("401 Unauthorized").end(e.message); } else { - console.warn(e); + console.log(e); res.writeStatus("500 Internal Server Error").end('An error occurred'); } return; diff --git a/back/src/Services/AdminApi.ts b/back/src/Services/AdminApi.ts index de28e4ef..79a68810 100644 --- a/back/src/Services/AdminApi.ts +++ b/back/src/Services/AdminApi.ts @@ -1,5 +1,5 @@ import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable"; -import Axios from "axios"; +import Axios, {AxiosError} from "axios"; export interface AdminApiData { organizationSlug: string @@ -26,10 +26,15 @@ class AdminApi { if (!ADMIN_API_URL) { return Promise.reject('No admin backoffice set!'); } - const res = await Axios.get(ADMIN_API_URL+'/api/member/'+memberId+'/is-granted-access/'+roomId, - { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} } - ) - return res.data === true; + try { + const res = await Axios.get(ADMIN_API_URL+'/api/member/is-granted-access', + { headers: {"Authorization" : `${ADMIN_API_TOKEN}`}, params: {memberId, roomIdentifier: roomId} } + ) + return !!res.data; + } catch (e) { + console.log(e.message) + return false; + } } } diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 06f8fe03..7863df2f 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -36,6 +36,7 @@ class ConnectionManager { this.userUuid = data.userUuid; this.mapUrlStart = data.mapUrlStart; const newUrl = data.newUrl; + console.log('u', this.userUuid) if (newUrl) { history.pushState({}, '', newUrl); From 032facb75fe5b1d10186b26f585534a1efbd5220 Mon Sep 17 00:00:00 2001 From: arp Date: Fri, 9 Oct 2020 17:14:03 +0200 Subject: [PATCH 6/6] lint fixes --- back/src/Controller/IoSocketController.ts | 4 ++-- back/tests/WorldTest.ts | 2 +- front/src/Connexion/RoomConnection.ts | 2 +- front/src/Phaser/Game/GameScene.ts | 1 - front/src/Phaser/Login/EnableCameraScene.ts | 2 +- front/yarn.lock | 2 +- 6 files changed, 6 insertions(+), 7 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 36a52e46..7ef0d811 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -312,10 +312,10 @@ export class IoSocketController { console.warn(message); } - private async handleJoinRoom(client: ExSocketInterface, roomId: string, position: PointInterface, viewport: ViewportInterface, name: string, characterLayers: string[]): Promise { + private handleJoinRoom(client: ExSocketInterface, roomId: string, position: PointInterface, viewport: ViewportInterface, name: string, characterLayers: string[]): void { try { //join new previous room - const gameRoom = await this.joinRoom(client, roomId, position); + const gameRoom = this.joinRoom(client, roomId, position); const things = gameRoom.setViewport(client, viewport); diff --git a/back/tests/WorldTest.ts b/back/tests/WorldTest.ts index 5ab421bb..5e06414c 100644 --- a/back/tests/WorldTest.ts +++ b/back/tests/WorldTest.ts @@ -1,5 +1,5 @@ import "jasmine"; -import {GameRoom, ConnectCallback, DisconnectCallback } from "_Model/GameRoom"; +import {GameRoom, ConnectCallback, DisconnectCallback } from "../src/Model/GameRoom"; import {Point} from "../src/Model/Websocket/MessageUserPosition"; import { Group } from "../src/Model/Group"; import {ExSocketInterface} from "_Model/Websocket/ExSocketInterface"; diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index f4290689..ed669fed 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -59,7 +59,7 @@ export class RoomConnection implements RoomConnection { url += '/room/'+roomId url += '?token='+(token ?encodeURIComponent(token):''); url += '&name='+encodeURIComponent(name); - for (let layer of characterLayers) { + for (const layer of characterLayers) { url += '&characterLayers='+encodeURIComponent(layer); } url += '&x='+Math.floor(position.x); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9f041eab..ba1d75fa 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -598,7 +598,6 @@ export class GameScene extends ResizableScene implements CenterListener { this.scene.stop(this.scene.key); this.scene.remove(this.scene.key); - window.removeEventListener('resize', this.onResizeCallback); }) connection.onActionableEvent((message => { diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 672facbb..5d5339d9 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -266,7 +266,7 @@ export class EnableCameraScene extends Phaser.Scene { mediaManager.stopCamera(); mediaManager.stopMicrophone(); - let {key, startLayerName} = await gameManager.loadStartingMap(this.scene); + const {key, startLayerName} = await gameManager.loadStartingMap(this.scene); this.scene.start(key, {startLayerName}); } diff --git a/front/yarn.lock b/front/yarn.lock index 933a02b4..5d235a82 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -3871,7 +3871,7 @@ quill-delta@^3.6.2: extend "^3.0.2" fast-diff "1.1.2" -quill@1.3.7: +quill@^1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.7.tgz#da5b2f3a2c470e932340cdbf3668c9f21f9286e8" integrity sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==