diff --git a/.env.template b/.env.template index d0db42e3..330f3865 100644 --- a/.env.template +++ b/.env.template @@ -5,3 +5,4 @@ JITSI_PRIVATE_MODE=false JITSI_ISS= SECRET_JITSI_KEY= ADMIN_API_TOKEN=123 +START_ROOM_URL=/_/global/maps.workadventure.localhost/Floor0/floor0.json diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index 00750969..2cbfbf2e 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -1,5 +1,4 @@ const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY"; -const URL_ROOM_STARTED = "/Floor0/floor0.json"; const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64; const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48; const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false; @@ -16,7 +15,6 @@ export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as strin export { SECRET_KEY, - URL_ROOM_STARTED, MINIMUM_DISTANCE, ADMIN_API_URL, ADMIN_API_TOKEN, diff --git a/docker-compose.yaml b/docker-compose.yaml index 23fa58a4..286c12ba 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -29,10 +29,12 @@ services: API_URL: pusher.workadventure.localhost UPLOADER_URL: uploader.workadventure.localhost ADMIN_URL: workadventure.localhost - STARTUP_COMMAND_1: yarn install + STARTUP_COMMAND_1: ./templater.sh + STARTUP_COMMAND_2: yarn install TURN_SERVER: "turn:coturn.workadventu.re:443,turns:coturn.workadventu.re:443" TURN_USER: workadventure TURN_PASSWORD: WorkAdventure123 + START_ROOM_URL: "$START_ROOM_URL" command: yarn run start volumes: - ./front:/usr/src/app diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 552819bb..de8cd234 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -1,5 +1,5 @@ import Axios from "axios"; -import {API_URL, URL_ROOM_STARTED} from "../Enum/EnvironmentVariable"; +import {API_URL, START_ROOM_URL} from "../Enum/EnvironmentVariable"; import {RoomConnection} from "./RoomConnection"; import {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels"; import {GameConnexionTypes, urlManager} from "../Url/UrlManager"; @@ -27,9 +27,9 @@ class ConnectionManager { const organizationSlug = data.organizationSlug; const worldSlug = data.worldSlug; const roomSlug = data.roomSlug; - urlManager.editUrlForRoom(roomSlug, organizationSlug, worldSlug); - const room = new Room(window.location.pathname + window.location.hash); + const room = new Room('/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug + window.location.hash); + urlManager.pushRoomIdToUrl(room); return Promise.resolve(room); } else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) { const localUser = localUserStore.getLocalUser(); @@ -48,10 +48,7 @@ class ConnectionManager { } let roomId: string if (connexionType === GameConnexionTypes.empty) { - roomId = urlManager.editUrlForRoom(URL_ROOM_STARTED, null, null); - if (URL_ROOM_STARTED.startsWith('http://') || URL_ROOM_STARTED.startsWith('https://')) { - roomId = '/_/global/' + URL_ROOM_STARTED.replace('http://', '').replace('https://', ''); - } + roomId = START_ROOM_URL; } else { roomId = window.location.pathname + window.location.hash; } diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index c456da2f..a71506eb 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -1,5 +1,5 @@ const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true"; -const URL_ROOM_STARTED : string = process.env.URL_ROOM_STARTED || 'tcm/workadventure/floor0'; +const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json'; const API_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.API_URL || "pusher.workadventure.localhost"); const UPLOADER_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.UPLOADER_URL || 'uploader.workadventure.localhost'); const ADMIN_URL = (process.env.API_PROTOCOL || (typeof(window) !== 'undefined' ? window.location.protocol : 'http:')) + '//' + (process.env.ADMIN_URL || "workadventure.localhost"); @@ -15,7 +15,7 @@ const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new export { DEBUG_MODE, - URL_ROOM_STARTED, + START_ROOM_URL, API_URL, UPLOADER_URL, ADMIN_URL, diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index e0081bfa..96fd082b 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -606,8 +606,18 @@ export class GameScene extends ResizableScene implements CenterListener { if (url === undefined) { audioManager.unloadAudio(); } else { - const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); - const realAudioPath = mapDirUrl + '/' + url; + const audioPath = url as string; + let realAudioPath = ''; + + if (audioPath.indexOf('://') > 0) { + // remote file or stream + realAudioPath = audioPath; + } else { + // local file, include it relative to map directory + const mapDirUrl = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf('/')); + realAudioPath = mapDirUrl + '/' + url; + } + audioManager.loadAudio(realAudioPath); if (loop) { @@ -704,6 +714,10 @@ export class GameScene extends ResizableScene implements CenterListener { } public cleanupClosingScene(): void { + // stop playing audio, close any open website, stop any open Jitsi + coWebsiteManager.closeCoWebsite(); + this.stopJitsi(); + this.playAudio(undefined); // We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map. if(this.connection) { this.connection.closeConnection(); diff --git a/front/src/Url/UrlManager.ts b/front/src/Url/UrlManager.ts index 443dcee5..31862899 100644 --- a/front/src/Url/UrlManager.ts +++ b/front/src/Url/UrlManager.ts @@ -32,26 +32,12 @@ class UrlManager { return match ? match [1] : null; } - - //todo: simply use the roomId - //todo: test this with cypress - public editUrlForRoom(roomSlug: string, organizationSlug: string|null, worldSlug: string |null): string { - let newUrl:string; - if (organizationSlug) { - newUrl = '/@/'+organizationSlug+'/'+worldSlug+'/'+roomSlug; - } else { - newUrl = '/@/'+roomSlug; - } - history.pushState({}, 'WorkAdventure', newUrl); - return newUrl; - } - public pushRoomIdToUrl(room:Room): void { - if (window.location.pathname === room.id) return; + if (window.location.pathname === room.id) return; const hash = window.location.hash; history.pushState({}, 'WorkAdventure', room.id+hash); } - + public getStartLayerNameFromUrl(): string|null { const hash = window.location.hash; return hash.length > 1 ? hash.substring(1) : null; diff --git a/front/src/WebRtc/DiscussionManager.ts b/front/src/WebRtc/DiscussionManager.ts index 4282603b..7653bc7a 100644 --- a/front/src/WebRtc/DiscussionManager.ts +++ b/front/src/WebRtc/DiscussionManager.ts @@ -151,15 +151,6 @@ export class DiscussionManager { this.nbpParticipants.innerText = `PARTICIPANTS (${nb})`; } - private urlify(text: string) { - const urlRegex = /(https?:\/\/[^\s]+)/g; - return text.replace(urlRegex, (url: string) => { - return '' + url + ''; - }) - // or alternatively - // return text.replace(urlRegex, '$1') - } - public addMessage(name: string, message: string, isMe: boolean = false) { const divMessage: HTMLDivElement = document.createElement('div'); divMessage.classList.add('message'); @@ -170,7 +161,7 @@ export class DiscussionManager { const pMessage: HTMLParagraphElement = document.createElement('p'); const date = new Date(); if(isMe){ - name = 'Moi'; + name = 'Me'; } pMessage.innerHTML = `${name} @@ -179,7 +170,7 @@ export class DiscussionManager { divMessage.appendChild(pMessage); const userMessage: HTMLParagraphElement = document.createElement('p'); - userMessage.innerHTML = this.urlify(message); + userMessage.innerHTML = HtmlUtils.urlify(message); userMessage.classList.add('body'); divMessage.appendChild(userMessage); this.divMessages?.appendChild(divMessage); @@ -221,7 +212,7 @@ export class DiscussionManager { this.activeDiscussion = false; this.divDiscuss?.classList.remove('active'); } - + public setUserInputManager(userInputManager : UserInputManager){ this.userInputManager = userInputManager; } @@ -231,4 +222,4 @@ export class DiscussionManager { } } -export const discussionManager = new DiscussionManager(); \ No newline at end of file +export const discussionManager = new DiscussionManager(); diff --git a/front/src/WebRtc/HtmlUtils.ts b/front/src/WebRtc/HtmlUtils.ts index b7cb2124..81f069b3 100644 --- a/front/src/WebRtc/HtmlUtils.ts +++ b/front/src/WebRtc/HtmlUtils.ts @@ -17,4 +17,11 @@ export class HtmlUtils { elem.remove(); return elem as T; } + + public static urlify(text: string): string { + const urlRegex = /(https?:\/\/[^\s]+)/g; + return text.replace(urlRegex, (url: string) => { + return '' + url + ''; + }) + } } diff --git a/front/src/index.ts b/front/src/index.ts index acf66cf8..b9a00731 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -53,8 +53,28 @@ const fps : Phaser.Types.Core.FPSConfig = { smoothStep: false } +// the ?phaserMode=canvas parameter can be used to force Canvas usage +const params = new URLSearchParams(document.location.search.substring(1)); +const phaserMode = params.get("phaserMode"); +let mode: number; +switch (phaserMode) { + case 'auto': + case null: + mode = Phaser.AUTO; + break; + case 'canvas': + mode = Phaser.CANVAS; + break; + case 'webgl': + mode = Phaser.WEBGL; + break; + default: + throw new Error('phaserMode parameter must be one of "auto", "canvas" or "webgl"'); +} + + const config: GameConfig = { - type: Phaser.AUTO, + type: mode, title: "WorkAdventure", width: width / RESOLUTION, height: height / RESOLUTION, diff --git a/front/tests/Phaser/Game/HtmlUtilsTest.ts b/front/tests/Phaser/Game/HtmlUtilsTest.ts new file mode 100644 index 00000000..8ef1d476 --- /dev/null +++ b/front/tests/Phaser/Game/HtmlUtilsTest.ts @@ -0,0 +1,14 @@ +import "jasmine"; +import {HtmlUtils} from "../../../src/WebRtc/HtmlUtils"; + +describe("urlify()", () => { + it("should transform an url into a link", () => { + const text = HtmlUtils.urlify('https://google.com'); + expect(text).toEqual('https://google.com'); + }); + + it("should not transform a normal text into a link", () => { + const text = HtmlUtils.urlify('hello'); + expect(text).toEqual('hello'); + }); +}); \ No newline at end of file diff --git a/front/webpack.config.js b/front/webpack.config.js index e5e34e8f..4a108848 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -45,7 +45,7 @@ module.exports = { new webpack.ProvidePlugin({ Phaser: 'phaser' }), - new webpack.EnvironmentPlugin(['API_URL', 'UPLOADER_URL', 'ADMIN_URL', 'DEBUG_MODE', 'TURN_SERVER', 'TURN_USER', 'TURN_PASSWORD', 'JITSI_URL', 'JITSI_PRIVATE_MODE', 'URL_ROOM_STARTED']) + new webpack.EnvironmentPlugin(['API_URL', 'UPLOADER_URL', 'ADMIN_URL', 'DEBUG_MODE', 'TURN_SERVER', 'TURN_USER', 'TURN_PASSWORD', 'JITSI_URL', 'JITSI_PRIVATE_MODE', 'START_ROOM_URL']) ], }; diff --git a/pusher/src/Enum/EnvironmentVariable.ts b/pusher/src/Enum/EnvironmentVariable.ts index 17f80f9c..5b3ec9c4 100644 --- a/pusher/src/Enum/EnvironmentVariable.ts +++ b/pusher/src/Enum/EnvironmentVariable.ts @@ -1,5 +1,4 @@ const SECRET_KEY = process.env.SECRET_KEY || "THECODINGMACHINE_SECRET_KEY"; -const URL_ROOM_STARTED = "/Floor0/floor0.json"; const MINIMUM_DISTANCE = process.env.MINIMUM_DISTANCE ? Number(process.env.MINIMUM_DISTANCE) : 64; const GROUP_RADIUS = process.env.GROUP_RADIUS ? Number(process.env.GROUP_RADIUS) : 48; const ALLOW_ARTILLERY = process.env.ALLOW_ARTILLERY ? process.env.ALLOW_ARTILLERY == 'true' : false; @@ -16,7 +15,6 @@ export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as strin export { SECRET_KEY, - URL_ROOM_STARTED, MINIMUM_DISTANCE, API_URL, ADMIN_API_URL,