diff --git a/back/src/App.ts b/back/src/App.ts index 5853f4d6..b251290c 100644 --- a/back/src/App.ts +++ b/back/src/App.ts @@ -3,7 +3,6 @@ import {IoSocketController} from "./Controller/IoSocketController"; //TODO fix i import {AuthenticateController} from "./Controller/AuthenticateController"; //TODO fix import by "_Controller/..." import {MapController} from "./Controller/MapController"; import {PrometheusController} from "./Controller/PrometheusController"; -import {AdminController} from "./Controller/AdminController"; import {DebugController} from "./Controller/DebugController"; import {App as uwsApp} from "./Server/sifrr.server"; @@ -13,7 +12,6 @@ class App { public authenticateController: AuthenticateController; public mapController: MapController; public prometheusController: PrometheusController; - private adminController: AdminController; private debugController: DebugController; constructor() { @@ -39,7 +37,6 @@ class App { this.authenticateController = new AuthenticateController(this.app); this.mapController = new MapController(this.app); this.prometheusController = new PrometheusController(this.app, this.ioSocketController); - this.adminController = new AdminController(this.app); this.debugController = new DebugController(this.app, this.ioSocketController); } diff --git a/back/src/Controller/AdminController.ts b/back/src/Controller/AdminController.ts index 7bb536ca..e69de29b 100644 --- a/back/src/Controller/AdminController.ts +++ b/back/src/Controller/AdminController.ts @@ -1,40 +0,0 @@ -import {OK} from "http-status-codes"; -import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable"; -import Axios from "axios"; -import {HttpRequest, HttpResponse} from "uWebSockets.js"; -import {parse} from "query-string"; -import {App} from "../Server/sifrr.server"; - -export class AdminController { - constructor(private App : App) { - this.getLoginUrlByToken(); - } - - - getLoginUrlByToken(){ - this.App.get("/register/:token", (res: HttpResponse, req: HttpRequest) => { - (async () => { - if (!ADMIN_API_URL) { - return res.writeStatus("500 Internal Server Error").end('No admin backoffice set!'); - } - - const query = parse(req.getQuery()); - - const token:string = query.token as string; - - let response = null - try { - response = await Axios.get(ADMIN_API_URL+'/api/login-url/'+token, { headers: {"Authorization" : `${ADMIN_API_TOKEN}`} }) - } catch (e) { - console.log(e.message) - return res.status(e.status || 500).send('An error happened'); - } - - const organizationSlug = response.data.organizationSlug; - const worldSlug = response.data.worldSlug; - const roomSlug = response.data.roomSlug; - res.writeStatus("200 OK").end(JSON.stringify({organizationSlug, worldSlug, roomSlug})); - })(); - }); - } -} diff --git a/back/src/Controller/AuthenticateController.ts b/back/src/Controller/AuthenticateController.ts index 6585054d..b6381eea 100644 --- a/back/src/Controller/AuthenticateController.ts +++ b/back/src/Controller/AuthenticateController.ts @@ -1,11 +1,21 @@ import Jwt from "jsonwebtoken"; import {SECRET_KEY, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; //TODO fix import by "_Enum/..." +import {OK} from "http-status-codes"; +import {ADMIN_API_TOKEN, ADMIN_API_URL, SECRET_KEY, 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 Axios from "axios"; export interface TokenInterface { - name: string, + userUuid: string +} + +interface AdminApiData { + organizationSlug: string + worldSlug: string + roomSlug: string + mapUrlStart: string userUuid: string } @@ -16,21 +26,6 @@ export class AuthenticateController extends BaseController { this.login(); } - onAbortedOrFinishedResponse(res: HttpResponse/*, readStream: any*/) { - - console.log("ERROR! onAbortedOrFinishedResponse called!"); - /*if (res.id == -1) { - console.log("ERROR! onAbortedOrFinishedResponse called twice for the same res!"); - } else { - console.log('Stream was closed, openStreams: ' + --openStreams); - console.timeEnd(res.id); - readStream.destroy(); - }*/ - - /* Mark this response already accounted for */ - //res.id = -1; - } - //permit to login on application. Return token to connect on Websocket IO. login(){ this.App.options("/login", (res: HttpResponse, req: HttpRequest) => { @@ -47,14 +42,55 @@ export class AuthenticateController extends BaseController { console.warn('Login request was aborted'); }) const param = await res.json(); - const userUuid = uuid(); - const token = Jwt.sign({name: param.name, userUuid: userUuid} as TokenInterface, SECRET_KEY, {expiresIn: '24h'}); - res.writeStatus("200 OK").end(JSON.stringify({ - token: token, - mapUrlStart: URL_ROOM_STARTED, - userId: userUuid, - })); + + //todo: what to do if the organizationMemberToken is already used? + const organizationMemberToken:string|null = param.organizationMemberToken; + + try { + let userUuid; + let mapUrlStart; + let newUrl = 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); + + userUuid = data.userUuid; + mapUrlStart = data.mapUrlStart; + newUrl = this.getNewUrlOnAdminAuth(data) + } else { + userUuid = uuid(); + mapUrlStart = req.getHeader('host').replace('api.', 'maps.') + URL_ROOM_STARTED; + newUrl = null; + } + + const authToken = Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '24h'}); + res.writeStatus("200 OK").end(JSON.stringify({ + authToken, + userUuid, + mapUrlStart, + newUrl, + })); + + } catch (e) { + console.log(e.message) + res.writeStatus(e.status || "500 Internal Server Error").end('An error happened'); + } + + })(); }); } + + private getNewUrlOnAdminAuth(data:AdminApiData): string { + const organizationSlug = data.organizationSlug; + const worldSlug = data.worldSlug; + const roomSlug = data.roomSlug; + return '/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug; + } } diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index fb1f0680..dc116616 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -116,9 +116,6 @@ export class IoSocketController { if (typeof((token as TokenInterface).userUuid) !== 'string') { return false; } - if (typeof((token as TokenInterface).name) !== 'string') { - return false; - } return true; } @@ -142,45 +139,46 @@ export class IoSocketController { //console.log(socket.handshake.query.token); /*if (!socket.handshake.query || !socket.handshake.query.token) { - console.error('An authentication error happened, a user tried to connect without a token.'); - return next(new Error('Authentication error')); - } - if(socket.handshake.query.token === 'test'){ - if (ALLOW_ARTILLERY) { - (socket as ExSocketInterface).token = socket.handshake.query.token; - (socket as ExSocketInterface).userId = this.nextUserId; - (socket as ExSocketInterface).userUuid = uuid(); - this.nextUserId++; - (socket as ExSocketInterface).isArtillery = true; - console.log((socket as ExSocketInterface).userId); - next(); - return; - } else { - console.warn("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); - next(); - } - } - (socket as ExSocketInterface).isArtillery = false; - 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')); - } - Jwt.verify(socket.handshake.query.token, SECRET_KEY, (err: JsonWebTokenError, tokenDecoded: object) => { - if (err) { - console.error('An authentication error happened, invalid JsonWebToken.', err); + console.error('An authentication error happened, a user tried to connect without a token.'); return next(new Error('Authentication error')); } - - if (!this.isValidToken(tokenDecoded)) { - return next(new Error('Authentication error, invalid token structure')); + if(socket.handshake.query.token === 'test'){ + if (ALLOW_ARTILLERY) { + (socket as ExSocketInterface).token = socket.handshake.query.token; + (socket as ExSocketInterface).userId = this.nextUserId; + (socket as ExSocketInterface).userUuid = uuid(); + this.nextUserId++; + (socket as ExSocketInterface).isArtillery = true; + console.log((socket as ExSocketInterface).userId); + next(); + return; + } else { + console.warn("In order to perform a load-testing test on this environment, you must set the ALLOW_ARTILLERY environment variable to 'true'"); + next(); + } } + (socket as ExSocketInterface).isArtillery = false; + 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')); + } + Jwt.verify(socket.handshake.query.token, SECRET_KEY, (err: JsonWebTokenError, tokenDecoded: object) => { + const tokenInterface = tokenDecoded as TokenInterface; + if (err) { + console.error('An authentication error happened, invalid JsonWebToken.', err); + return next(new Error('Authentication error')); + } - (socket as ExSocketInterface).token = socket.handshake.query.token; - (socket as ExSocketInterface).userId = this.nextUserId; - (socket as ExSocketInterface).userUuid = tokenDecoded.userUuid; - this.nextUserId++; - next(); - });*/ + if (!this.isValidToken(tokenInterface)) { + return next(new Error('Authentication error, invalid token structure')); + } + + (socket as ExSocketInterface).token = socket.handshake.query.token; + (socket as ExSocketInterface).userId = this.nextUserId; + (socket as ExSocketInterface).userUuid = tokenInterface.userUuid; + this.nextUserId++; + next(); + });*/ const socket = ws as ExSocketInterface; socket.userId = this.nextUserId; this.nextUserId++; diff --git a/back/src/Controller/MapController.ts b/back/src/Controller/MapController.ts index ad6f5548..50e47a9d 100644 --- a/back/src/Controller/MapController.ts +++ b/back/src/Controller/MapController.ts @@ -3,6 +3,7 @@ import {URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; import {HttpRequest, HttpResponse, TemplatedApp} from "uWebSockets.js"; import {BaseController} from "./BaseController"; +//todo: delete this export class MapController extends BaseController{ constructor(private App : TemplatedApp) { diff --git a/back/src/Model/Websocket/ExSocketInterface.ts b/back/src/Model/Websocket/ExSocketInterface.ts index 36265143..46c4b787 100644 --- a/back/src/Model/Websocket/ExSocketInterface.ts +++ b/back/src/Model/Websocket/ExSocketInterface.ts @@ -1,6 +1,5 @@ import {PointInterface} from "./PointInterface"; import {Identificable} from "./Identificable"; -import {TokenInterface} from "../../Controller/AuthenticateController"; import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; import {BatchMessage, SubMessage} from "../../Messages/generated/messages_pb"; import {WebSocket} from "uWebSockets.js" diff --git a/benchmark/index.ts b/benchmark/index.ts index 50cd9e17..ebf58c6a 100644 --- a/benchmark/index.ts +++ b/benchmark/index.ts @@ -1,16 +1,16 @@ -import {Connection} from "../front/src/Connection"; +import {RoomConnection} from "../front/src/Connexion/Connection"; import * as WebSocket from "ws" function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } -Connection.setWebsocketFactory((url: string) => { +RoomConnection.setWebsocketFactory((url: string) => { return new WebSocket(url); }); async function startOneUser(): Promise { - const connection = await Connection.createConnection('foo', ['male3']); + const connection = await RoomConnection.createConnection('foo', ['male3']); await connection.joinARoom('global__maps.workadventure.localhost/Floor0/floor0', 783, 170, 'down', false, { top: 0, diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts new file mode 100644 index 00000000..311f0351 --- /dev/null +++ b/front/src/Connexion/ConnectionManager.ts @@ -0,0 +1,65 @@ +import Axios from "axios"; +import {API_URL} from "../Enum/EnvironmentVariable"; +import {RoomConnection} from "./RoomConnection"; + +interface LoginApiData { + authToken: string + userUuid: string + mapUrlStart: string + newUrl: string +} + +class ConnectionManager { + private initPromise: Promise = Promise.reject(); + private mapUrlStart: string|null = null; + + private authToken:string|null = null; + private userUuid: string|null = null; + + public async init(): Promise { + 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); + const data = await this.initPromise + this.authToken = data.authToken; + this.userUuid = data.userUuid; + this.mapUrlStart = data.mapUrlStart; + const newUrl = data.newUrl; + + if (newUrl) { + history.pushState({}, '', newUrl); + } + } + + public connectToRoomSocket(): Promise { + return new Promise((resolve, reject) => { + const connection = new RoomConnection(this.authToken as string); + connection.onConnectError((error: object) => { + console.log('An error occurred while connecting to socket server. Retrying'); + reject(error); + }); + connection.onConnect(() => { + resolve(connection); + }) + }).catch((err) => { + // Let's retry in 4-6 seconds + return new Promise((resolve, reject) => { + setTimeout(() => { + //todo: allow a way to break recurrsion? + this.connectToRoomSocket().then((connection) => resolve(connection)); + }, 4000 + Math.floor(Math.random() * 2000) ); + }); + }); + } + + public getMapUrlStart(): Promise { + return this.initPromise.then(() => { + if (!this.mapUrlStart) { + throw new Error('No map url set!'); + } + return this.mapUrlStart; + }) + } +} + +export const connectionManager = new ConnectionManager(); diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts new file mode 100644 index 00000000..794b9169 --- /dev/null +++ b/front/src/Connexion/ConnexionModels.ts @@ -0,0 +1,117 @@ +import {PlayerAnimationNames} from "../Phaser/Player/Animation"; +import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; +import {SignalData} from "simple-peer"; + +export enum EventMessage{ + WEBRTC_SIGNAL = "webrtc-signal", + WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", + WEBRTC_START = "webrtc-start", + JOIN_ROOM = "join-room", // bi-directional + USER_POSITION = "user-position", // From client to server + USER_MOVED = "user-moved", // From server to client + USER_LEFT = "user-left", // From server to client + MESSAGE_ERROR = "message-error", + WEBRTC_DISCONNECT = "webrtc-disconect", + GROUP_CREATE_UPDATE = "group-create-update", + GROUP_DELETE = "group-delete", + SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id. + ITEM_EVENT = 'item-event', + + CONNECT_ERROR = "connect_error", + SET_SILENT = "set_silent", // Set or unset the silent mode for this user. + SET_VIEWPORT = "set-viewport", + BATCH = "batch", +} + +export interface PointInterface { + x: number; + y: number; + direction : string; + moving: boolean; +} + +export class Point implements PointInterface{ + constructor(public x : number, public y : number, public direction : string = PlayerAnimationNames.WalkDown, public moving : boolean = false) { + if(x === null || y === null){ + throw Error("position x and y cannot be null"); + } + } +} + +export interface MessageUserPositionInterface { + userId: number; + name: string; + characterLayers: string[]; + position: PointInterface; +} + +export interface MessageUserMovedInterface { + userId: number; + position: PointInterface; +} + +export interface MessageUserJoined { + userId: number; + name: string; + characterLayers: string[]; + position: PointInterface +} + +export interface PositionInterface { + x: number, + y: number +} + +export interface GroupCreatedUpdatedMessageInterface { + position: PositionInterface, + groupId: number +} + +export interface WebRtcStartMessageInterface { + roomId: string, + clients: UserSimplePeerInterface[] +} + +export interface WebRtcDisconnectMessageInterface { + userId: number +} + +export interface WebRtcSignalSentMessageInterface { + receiverId: number, + signal: SignalData +} + +export interface WebRtcSignalReceivedMessageInterface { + userId: number, + signal: SignalData +} + +export interface StartMapInterface { + mapUrlStart: string, + startInstance: string +} + +export interface ViewportInterface { + left: number, + top: number, + right: number, + bottom: number, +} + +export interface BatchedMessageInterface { + event: string, + payload: unknown +} + +export interface ItemEventMessageInterface { + itemId: number, + event: string, + state: unknown, + parameters: unknown +} + +export interface RoomJoinedMessageInterface { + users: MessageUserPositionInterface[], + groups: GroupCreatedUpdatedMessageInterface[], + items: { [itemId: number] : unknown } +} \ No newline at end of file diff --git a/front/src/Connection.ts b/front/src/Connexion/RoomConnection.ts similarity index 75% rename from front/src/Connection.ts rename to front/src/Connexion/RoomConnection.ts index e0c3121d..e64164b8 100644 --- a/front/src/Connection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -1,6 +1,4 @@ -import Axios from "axios"; -import {API_URL} from "./Enum/EnvironmentVariable"; -import {MessageUI} from "./Logger/MessageUI"; +import {API_URL} from "../Enum/EnvironmentVariable"; import { BatchMessage, ClientToServerMessage, @@ -23,144 +21,39 @@ import { WebRtcSignalToClientMessage, WebRtcSignalToServerMessage, WebRtcStartMessage -} from "./Messages/generated/messages_pb" +} from "../Messages/generated/messages_pb" -import {PlayerAnimationNames} from "./Phaser/Player/Animation"; -import {UserSimplePeerInterface} from "./WebRtc/SimplePeer"; -import {SignalData} from "simple-peer"; +import {UserSimplePeerInterface} from "../WebRtc/SimplePeer"; import Direction = PositionMessage.Direction; -import {ProtobufClientUtils} from "./Network/ProtobufClientUtils"; +import {ProtobufClientUtils} from "../Network/ProtobufClientUtils"; +import { + EventMessage, + GroupCreatedUpdatedMessageInterface, ItemEventMessageInterface, + MessageUserJoined, + RoomJoinedMessageInterface, + ViewportInterface, WebRtcDisconnectMessageInterface, + WebRtcSignalReceivedMessageInterface, + WebRtcSignalSentMessageInterface, + WebRtcStartMessageInterface +} from "./ConnexionModels"; -enum EventMessage{ - WEBRTC_SIGNAL = "webrtc-signal", - WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", - WEBRTC_START = "webrtc-start", - JOIN_ROOM = "join-room", // bi-directional - USER_POSITION = "user-position", // From client to server - USER_MOVED = "user-moved", // From server to client - USER_LEFT = "user-left", // From server to client - MESSAGE_ERROR = "message-error", - WEBRTC_DISCONNECT = "webrtc-disconect", - GROUP_CREATE_UPDATE = "group-create-update", - GROUP_DELETE = "group-delete", - SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id. - ITEM_EVENT = 'item-event', - CONNECT_ERROR = "connect_error", - SET_SILENT = "set_silent", // Set or unset the silent mode for this user. - SET_VIEWPORT = "set-viewport", - BATCH = "batch", -} - -export interface PointInterface { - x: number; - y: number; - direction : string; - moving: boolean; -} - -export class Point implements PointInterface{ - constructor(public x : number, public y : number, public direction : string = PlayerAnimationNames.WalkDown, public moving : boolean = false) { - if(x === null || y === null){ - throw Error("position x and y cannot be null"); - } - } -} - -export interface MessageUserPositionInterface { - userId: number; - name: string; - characterLayers: string[]; - position: PointInterface; -} - -export interface MessageUserMovedInterface { - userId: number; - position: PointInterface; -} - -export interface MessageUserJoined { - userId: number; - name: string; - characterLayers: string[]; - position: PointInterface -} - -export interface PositionInterface { - x: number, - y: number -} - -export interface GroupCreatedUpdatedMessageInterface { - position: PositionInterface, - groupId: number -} - -export interface WebRtcStartMessageInterface { - roomId: string, - clients: UserSimplePeerInterface[] -} - -export interface WebRtcDisconnectMessageInterface { - userId: number -} - -export interface WebRtcSignalSentMessageInterface { - receiverId: number, - signal: SignalData -} - -export interface WebRtcSignalReceivedMessageInterface { - userId: number, - signal: SignalData -} - -export interface StartMapInterface { - mapUrlStart: string, - startInstance: string -} - -export interface ViewportInterface { - left: number, - top: number, - right: number, - bottom: number, -} - -export interface BatchedMessageInterface { - event: string, - payload: unknown -} - -export interface ItemEventMessageInterface { - itemId: number, - event: string, - state: unknown, - parameters: unknown -} - -export interface RoomJoinedMessageInterface { - users: MessageUserPositionInterface[], - groups: GroupCreatedUpdatedMessageInterface[], - items: { [itemId: number] : unknown } -} - -export class Connection implements Connection { +export class RoomConnection implements RoomConnection { private readonly socket: WebSocket; private userId: number|null = null; private listeners: Map = new Map(); private static websocketFactory: null|((url: string)=>any) = null; // eslint-disable-line @typescript-eslint/no-explicit-any public static setWebsocketFactory(websocketFactory: (url: string)=>any): void { // eslint-disable-line @typescript-eslint/no-explicit-any - Connection.websocketFactory = websocketFactory; + RoomConnection.websocketFactory = websocketFactory; } - private constructor(token: string) { + public constructor(token: string) { let url = API_URL.replace('http://', 'ws://').replace('https://', 'wss://'); url += '?token='+token; - if (Connection.websocketFactory) { - this.socket = Connection.websocketFactory(url); + if (RoomConnection.websocketFactory) { + this.socket = RoomConnection.websocketFactory(url); } else { this.socket = new WebSocket(url); } @@ -248,42 +141,15 @@ export class Connection implements Connection { } } - public static createConnection(name: string, characterLayersSelected: string[]): Promise { - return Axios.post(`${API_URL}/login`, {name: name}) - .then((res) => { + public emitPlayerDetailsMessage(userName: string, characterLayersSelected: string[]) { + const message = new SetPlayerDetailsMessage(); + message.setName(name); + message.setCharacterlayersList(characterLayersSelected); - return new Promise((resolve, reject) => { - const connection = new Connection(res.data.token); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setSetplayerdetailsmessage(message); - connection.onConnectError((error: object) => { - console.log('An error occurred while connecting to socket server. Retrying'); - reject(error); - }); - - connection.onConnect(() => { - const message = new SetPlayerDetailsMessage(); - message.setName(name); - message.setCharacterlayersList(characterLayersSelected); - - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setSetplayerdetailsmessage(message); - - connection.socket.send(clientToServerMessage.serializeBinary().buffer); - - resolve(connection); - }); - }); - }) - .catch((err) => { - // Let's retry in 4-6 seconds - console.error('Connection failed. Retrying', err); - return new Promise((resolve, reject) => { - setTimeout(() => { - Connection.createConnection(name, characterLayersSelected).then((connection) => resolve(connection)) - .catch((error) => reject(error)); - }, 4000 + Math.floor(Math.random() * 2000) ); - }); - }); + this.socket.send(clientToServerMessage.serializeBinary().buffer); } public closeConnection(): void { diff --git a/front/src/Network/ProtobufClientUtils.ts b/front/src/Network/ProtobufClientUtils.ts index 1eb5b923..6a402e97 100644 --- a/front/src/Network/ProtobufClientUtils.ts +++ b/front/src/Network/ProtobufClientUtils.ts @@ -1,6 +1,6 @@ import {PositionMessage} from "../Messages/generated/messages_pb"; -import {PointInterface} from "../Connection"; import Direction = PositionMessage.Direction; +import {PointInterface} from "../Connexion/ConnexionModels"; export class ProtobufClientUtils { diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 00a3e4c4..ba0a74d2 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -1,5 +1,5 @@ import {GameScene} from "../Game/GameScene"; -import {PointInterface} from "../../Connection"; +import {PointInterface} from "../../Connexion/ConnexionModels"; import {Character} from "../Entity/Character"; /** diff --git a/front/src/Phaser/Game/AddPlayerInterface.ts b/front/src/Phaser/Game/AddPlayerInterface.ts index d0ed2dad..91563dd0 100644 --- a/front/src/Phaser/Game/AddPlayerInterface.ts +++ b/front/src/Phaser/Game/AddPlayerInterface.ts @@ -1,4 +1,4 @@ -import {PointInterface} from "../../Connection"; +import {PointInterface} from "../../Connexion/ConnexionModels"; export interface AddPlayerInterface { userId: number; diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index db119a13..960ce7e2 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -1,9 +1,10 @@ import {GameScene} from "./GameScene"; import { StartMapInterface -} from "../../Connection"; +} from "../../Connexion/ConnexionModels"; import Axios from "axios"; import {API_URL} from "../../Enum/EnvironmentVariable"; +import {connectionManager} from "../../Connexion/ConnectionManager"; export interface HasMovedEvent { direction: string; @@ -29,13 +30,12 @@ export class GameManager { } loadStartMap() : Promise { - return Axios.get(`${API_URL}/start-map`) - .then((res) => { - return res.data; - }).catch((err) => { - console.error(err); - throw err; - }); + return connectionManager.getMapUrlStart().then(mapUrlStart => { + return { + mapUrlStart: mapUrlStart, + startInstance: "global", //todo: is this property still usefull? + } + }); } getPlayerName(): string { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index f971a1e3..425aaf36 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,6 +1,5 @@ import {GameManager, gameManager, HasMovedEvent} from "./GameManager"; import { - Connection, GroupCreatedUpdatedMessageInterface, MessageUserJoined, MessageUserMovedInterface, @@ -8,7 +7,7 @@ import { PointInterface, PositionInterface, RoomJoinedMessageInterface -} from "../../Connection"; +} from "../../Connexion/ConnexionModels"; import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player"; import {DEBUG_MODE, JITSI_URL, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; import { @@ -42,6 +41,8 @@ import {ActionableItem} from "../Items/ActionableItem"; import {UserInputManager} from "../UserInput/UserInputManager"; import {UserMovedMessage} from "../../Messages/generated/messages_pb"; import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils"; +import {connectionManager} from "../../Connexion/ConnectionManager"; +import {RoomConnection} from "../../Connexion/RoomConnection"; export enum Textures { @@ -100,9 +101,9 @@ export class GameScene extends Phaser.Scene implements CenterListener { pendingEvents: Queue = new Queue(); private initPosition: PositionInterface|null = null; private playersPositionInterpolator = new PlayersPositionInterpolator(); - private connection!: Connection; + private connection!: RoomConnection; private simplePeer!: SimplePeer; - private connectionPromise!: Promise + 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) @@ -202,9 +203,11 @@ export class GameScene extends Phaser.Scene implements CenterListener { this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); - this.connectionPromise = Connection.createConnection(gameManager.getPlayerName(), gameManager.getCharacterSelected()).then((connection : Connection) => { + 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, @@ -778,7 +781,7 @@ export class GameScene extends Phaser.Scene implements CenterListener { this.createCollisionObject(); //join room - this.connectionPromise.then((connection: Connection) => { + this.connectionPromise.then((connection: RoomConnection) => { const camera = this.cameras.main; connection.joinARoom(this.RoomId, this.startX, diff --git a/front/src/Phaser/Game/PlayerMovement.ts b/front/src/Phaser/Game/PlayerMovement.ts index 56c4f113..eb1a5d1b 100644 --- a/front/src/Phaser/Game/PlayerMovement.ts +++ b/front/src/Phaser/Game/PlayerMovement.ts @@ -1,6 +1,6 @@ import {HasMovedEvent} from "./GameManager"; import {MAX_EXTRAPOLATION_TIME} from "../../Enum/EnvironmentVariable"; -import {PositionInterface} from "../../Connection"; +import {PositionInterface} from "../../Connexion/ConnexionModels"; export class PlayerMovement { public constructor(private startPosition: PositionInterface, private startTick: number, private endPosition: HasMovedEvent, private endTick: number) { diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index 6fc1cd54..6ac1ad47 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -1,12 +1,9 @@ import {gameManager} from "../Game/GameManager"; import {TextField} from "../Components/TextField"; -import {ClickButton} from "../Components/ClickButton"; import Image = Phaser.GameObjects.Image; -import Rectangle = Phaser.GameObjects.Rectangle; -import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character"; import {GameSceneInitInterface} from "../Game/GameScene"; -import {StartMapInterface} from "../../Connection"; -import {mediaManager, MediaManager} from "../../WebRtc/MediaManager"; +import {StartMapInterface} from "../../Connexion/ConnexionModels"; +import {mediaManager} from "../../WebRtc/MediaManager"; import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {SoundMeter} from "../Components/SoundMeter"; import {SoundMeterSprite} from "../Components/SoundMeterSprite"; diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 64285766..8d3c7ab1 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -3,8 +3,6 @@ import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; import {PLAYER_RESOURCES, PlayerResourceDescriptionInterface} from "../Entity/Character"; -import {GameSceneInitInterface} from "../Game/GameScene"; -import {StartMapInterface} from "../../Connection"; import {EnableCameraSceneName} from "./EnableCameraScene"; import {CustomizeSceneName} from "./CustomizeScene"; diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 0fde84ae..b9c1c91a 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -1,9 +1,7 @@ import {PlayerAnimationNames} from "./Animation"; -import {GameScene, Textures} from "../Game/GameScene"; -import {MessageUserPositionInterface, PointInterface} from "../../Connection"; -import {ActiveEventList, UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; +import {GameScene} from "../Game/GameScene"; +import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {Character} from "../Entity/Character"; -import {OutlinePipeline} from "../Shaders/OutlinePipeline"; export const hasMovedEventName = "hasMoved"; diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts index 9c2022a6..3cbc4154 100644 --- a/front/src/WebRtc/ScreenSharingPeer.ts +++ b/front/src/WebRtc/ScreenSharingPeer.ts @@ -1,7 +1,7 @@ import * as SimplePeerNamespace from "simple-peer"; import {mediaManager} from "./MediaManager"; -import {Connection} from "../Connection"; import {TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable"; +import {RoomConnection} from "../Connexion/RoomConnection"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); @@ -14,7 +14,7 @@ export class ScreenSharingPeer extends Peer { */ private isReceivingStream:boolean = false; - constructor(private userId: number, initiator: boolean, private connection: Connection) { + constructor(private userId: number, initiator: boolean, private connection: RoomConnection) { super({ initiator: initiator ? initiator : false, reconnectTimer: 10000, diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index c32e4305..4fc32f1d 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -1,9 +1,8 @@ import { - Connection, WebRtcDisconnectMessageInterface, WebRtcSignalReceivedMessageInterface, WebRtcStartMessageInterface -} from "../Connection"; +} from "../Connexion/ConnexionModels"; import { mediaManager, StartScreenSharingCallback, @@ -13,6 +12,7 @@ import { import * as SimplePeerNamespace from "simple-peer"; import {ScreenSharingPeer} from "./ScreenSharingPeer"; import {VideoPeer} from "./VideoPeer"; +import {RoomConnection} from "../Connexion/RoomConnection"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); export interface UserSimplePeerInterface{ @@ -31,7 +31,7 @@ export interface PeerConnectionListener { * This class manages connections to all the peers in the same group as me. */ export class SimplePeer { - private Connection: Connection; + private Connection: RoomConnection; private WebRtcRoomId: string; private Users: Array = new Array(); @@ -42,7 +42,7 @@ export class SimplePeer { private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback; private readonly peerConnectionListeners: Array = new Array(); - constructor(Connection: Connection, WebRtcRoomId: string = "test-webrtc") { + constructor(Connection: RoomConnection, WebRtcRoomId: string = "test-webrtc") { this.Connection = Connection; this.WebRtcRoomId = WebRtcRoomId; // We need to go through this weird bound function pointer in order to be able to "free" this reference later. diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index e046ffe2..9331bea7 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -1,7 +1,7 @@ import * as SimplePeerNamespace from "simple-peer"; import {mediaManager} from "./MediaManager"; -import {Connection} from "../Connection"; import {TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable"; +import {RoomConnection} from "../Connexion/RoomConnection"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); @@ -9,7 +9,7 @@ const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); * A peer connection used to transmit video / audio signals between 2 peers. */ export class VideoPeer extends Peer { - constructor(private userId: number, initiator: boolean, private connection: Connection) { + constructor(private userId: number, initiator: boolean, private connection: RoomConnection) { super({ initiator: initiator ? initiator : false, reconnectTimer: 10000, diff --git a/front/src/index.ts b/front/src/index.ts index 1f6b1d4a..8e235c7a 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -11,11 +11,10 @@ import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; import {CoWebsiteManager} from "./WebRtc/CoWebsiteManager"; -import {redirectIfToken} from "./register"; +import {connectionManager} from "./Connexion/ConnectionManager"; //CoWebsiteManager.loadCoWebsite('https://thecodingmachine.com'); -let connectionData //todo: do something with this data -redirectIfToken().then(res => connectionData = res); +connectionManager.init(); // Load Jitsi if the environment variable is set. if (JITSI_URL) { diff --git a/front/src/register.ts b/front/src/register.ts deleted file mode 100644 index 98fe0d1e..00000000 --- a/front/src/register.ts +++ /dev/null @@ -1,29 +0,0 @@ -import Axios from "axios"; -import {API_URL} from "./Enum/EnvironmentVariable"; -declare let history:History; - -//todo: better naming -export interface ConnexionData { - organizationSlug: string, - worldSlug: string, - roomSlug: string, -} - -export async function redirectIfToken(): Promise { - const match = /\/register\/(.+)/.exec(window.location.toString()); - if (!match) { - return null - } - let res = null; - try { - res = await Axios.get(`${API_URL}/register/`+match[1]) - } catch (e) { - return null; - } - const organizationSlug = res.data.organizationSlug; - const worldSlug = res.data.worldSlug; - const roomSlug = res.data.roomSlug; - const connexionUrl = '/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug; - history.pushState({}, '', connexionUrl); - return {organizationSlug, worldSlug, roomSlug}; -} \ No newline at end of file