diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index a71506eb..97d7fa52 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -3,6 +3,7 @@ const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.wo 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"); +const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302"; const TURN_SERVER: string = process.env.TURN_SERVER || "turn:numb.viagenie.ca"; const TURN_USER: string = process.env.TURN_USER || 'g.parant@thecodingmachine.com'; const TURN_PASSWORD: string = process.env.TURN_PASSWORD || 'itcugcOHxle9Acqi$'; @@ -23,6 +24,7 @@ export { ZOOM_LEVEL, POSITION_DELAY, MAX_EXTRAPOLATION_TIME, + STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD, diff --git a/front/src/Phaser/Components/Loader.ts b/front/src/Phaser/Components/Loader.ts index 0eda58dc..1ee18b32 100644 --- a/front/src/Phaser/Components/Loader.ts +++ b/front/src/Phaser/Components/Loader.ts @@ -6,6 +6,10 @@ const LogoResource: string = 'resources/logos/logo.png'; const LogoFrame: ImageFrameConfig = {frameWidth: 307, frameHeight: 59}; export const addLoader = (scene: Phaser.Scene): void => { + // If there is nothing to load, do not display the loader. + if (scene.load.list.entries.length === 0) { + return; + } let loadingText: Phaser.GameObjects.Text|null = null; const loadingBarWidth: number = Math.floor(scene.game.renderer.width / 3); const loadingBarHeight: number = 16; diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index 9f3157a0..12da0514 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -49,6 +49,10 @@ export class GameMap { this.lastProperties = newProps; } + public getCurrentProperties(): Map { + return this.lastProperties; + } + private getProperties(key: number): Map { const properties = new Map(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 5ed2db87..7138330a 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -183,8 +183,6 @@ export class GameScene extends ResizableScene implements CenterListener { //hook preload scene preload(): void { - addLoader(this); - const localUser = localUserStore.getLocalUser(); const textures = localUser?.textures; if (textures) { @@ -215,6 +213,8 @@ export class GameScene extends ResizableScene implements CenterListener { this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32}); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + + addLoader(this); } // FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving. @@ -631,6 +631,15 @@ export class GameScene extends ResizableScene implements CenterListener { } } + private safeParseJSONstring(jsonString: string|undefined, propertyName: string) { + try { + return jsonString ? JSON.parse(jsonString) : {}; + } catch(e) { + console.warn('Invalid JSON found in property "' + propertyName + '" of the map:' + jsonString, e); + return {} + } + } + private triggerOnMapLayerPropertyChange(){ this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => { if (newValue) this.onMapExit(newValue as string); @@ -664,12 +673,13 @@ export class GameScene extends ResizableScene implements CenterListener { this.stopJitsi(); }else{ const openJitsiRoomFunction = () => { + const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance); if (JITSI_PRIVATE_MODE) { const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; - this.connection.emitQueryJitsiJwtMessage(this.instance.replace('/', '-') + "-" + newValue, adminTag); + this.connection.emitQueryJitsiJwtMessage(roomName, adminTag); } else { - this.startJitsi(newValue as string); + this.startJitsi(roomName, undefined); } layoutManager.removeActionButton('jitsiRoom', this.userInputManager); } @@ -1229,7 +1239,11 @@ export class GameScene extends ResizableScene implements CenterListener { } public startJitsi(roomName: string, jwt?: string): void { - jitsiFactory.start(roomName, this.playerName, jwt); + const allProps = this.gameMap.getCurrentProperties(); + const jitsiConfig = this.safeParseJSONstring(allProps.get("jitsiConfig") as string|undefined, 'jitsiConfig'); + const jitsiInterfaceConfig = this.safeParseJSONstring(allProps.get("jitsiInterfaceConfig") as string|undefined, 'jitsiInterfaceConfig'); + + jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig); this.connection.setSilent(true); mediaManager.hideGameOverlay(); diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 905c9ae4..e47cf38a 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -59,6 +59,8 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.playerModels = loadAllDefaultModels(this.load); this.load.image(LoginTextures.customizeButton, 'resources/objects/customize.png'); this.load.image(LoginTextures.customizeButtonSelected, 'resources/objects/customize_selected.png'); + + addLoader(this); } create() { @@ -123,7 +125,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { /*create user*/ this.createCurrentPlayer(); - + const playerNumber = localUserStore.getPlayerCharacterIndex(); if (playerNumber && playerNumber !== -1) { this.selectedRectangleXPos = playerNumber % this.nbCharactersPerRow; diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 736b5244..7d470754 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -3,11 +3,19 @@ import {mediaManager} from "./MediaManager"; import {coWebsiteManager} from "./CoWebsiteManager"; declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any -const interfaceConfig = { +const defaultConfig = { + startWithAudioMuted: !mediaManager.constraintsMedia.audio, + startWithVideoMuted: mediaManager.constraintsMedia.video === false, + prejoinPageEnabled: false +} + +const defaultInterfaceConfig = { SHOW_CHROME_EXTENSION_BANNER: false, MOBILE_APP_PROMO: false, HIDE_INVITE_MORE_HEADER: true, + DISABLE_JOIN_LEAVE_NOTIFICATIONS: true, + DISABLE_VIDEO_BACKGROUND: true, // Note: hiding brand does not seem to work, we probably need to put this on the server side. SHOW_BRAND_WATERMARK: false, @@ -25,12 +33,31 @@ const interfaceConfig = { ], }; +const slugify = (...args: (string | number)[]): string => { + const value = args.join(' ') + + return value + .normalize('NFD') // split an accented letter in the base letter and the accent + .replace(/[\u0300-\u036f]/g, '') // remove all previously split accents + .toLowerCase() + .trim() + .replace(/[^a-z0-9 ]/g, '') // remove all chars not letters, numbers and spaces (to be replaced) + .replace(/\s+/g, '-') // separator +} + class JitsiFactory { private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any private audioCallback = this.onAudioChange.bind(this); private videoCallback = this.onVideoChange.bind(this); - - public start(roomName: string, playerName:string, jwt?: string): void { + + /** + * Slugifies the room name and prepends the room name with the instance + */ + public getRoomName(roomName: string, instance: string): string { + return slugify(instance.replace('/', '-') + "-" + roomName); + } + + public start(roomName: string, playerName:string, jwt?: string, config?: object, interfaceConfig?: object): void { coWebsiteManager.insertCoWebsite((cowebsiteDiv => { const domain = JITSI_URL; const options: any = { // eslint-disable-line @typescript-eslint/no-explicit-any @@ -39,17 +66,13 @@ class JitsiFactory { width: "100%", height: "100%", parentNode: cowebsiteDiv, - configOverwrite: { - startWithAudioMuted: !mediaManager.constraintsMedia.audio, - startWithVideoMuted: mediaManager.constraintsMedia.video === false, - prejoinPageEnabled: false - }, - interfaceConfigOverwrite: interfaceConfig, + configOverwrite: {...defaultConfig, ...config}, + interfaceConfigOverwrite: {...defaultInterfaceConfig, ...interfaceConfig} }; if (!options.jwt) { delete options.jwt; } - + return new Promise((resolve, reject) => { options.onload = () => resolve(); //we want for the iframe to be loaded before triggering animations. setTimeout(() => resolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load @@ -87,7 +110,6 @@ class JitsiFactory { mediaManager.enableCamera(); } } - } -export const jitsiFactory = new JitsiFactory(); \ No newline at end of file +export const jitsiFactory = new JitsiFactory(); diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts index a6cf679b..1b8680cf 100644 --- a/front/src/WebRtc/ScreenSharingPeer.ts +++ b/front/src/WebRtc/ScreenSharingPeer.ts @@ -1,6 +1,6 @@ import * as SimplePeerNamespace from "simple-peer"; import {mediaManager} from "./MediaManager"; -import {TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable"; +import {STUN_SERVER, TURN_SERVER, TURN_USER, TURN_PASSWORD} from "../Enum/EnvironmentVariable"; import {RoomConnection} from "../Connexion/RoomConnection"; import {MESSAGE_TYPE_CONSTRAINT} from "./VideoPeer"; @@ -24,7 +24,7 @@ export class ScreenSharingPeer extends Peer { config: { iceServers: [ { - urls: 'stun:stun.l.google.com:19302' + urls: STUN_SERVER.split(',') }, { urls: TURN_SERVER.split(','), diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts index 877f4e64..cc5ac62e 100644 --- a/front/src/WebRtc/VideoPeer.ts +++ b/front/src/WebRtc/VideoPeer.ts @@ -1,6 +1,6 @@ import * as SimplePeerNamespace from "simple-peer"; import {mediaManager} from "./MediaManager"; -import {TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable"; +import {STUN_SERVER, TURN_PASSWORD, TURN_SERVER, TURN_USER} from "../Enum/EnvironmentVariable"; import {RoomConnection} from "../Connexion/RoomConnection"; const Peer: SimplePeerNamespace.SimplePeer = require('simple-peer'); @@ -21,7 +21,7 @@ export class VideoPeer extends Peer { config: { iceServers: [ { - urls: 'stun:stun.l.google.com:19302' + urls: STUN_SERVER.split(',') }, { urls: TURN_SERVER.split(','), @@ -38,7 +38,7 @@ export class VideoPeer extends Peer { config: { iceServers: [ { - urls: 'stun:stun.l.google.com:19302' + urls: STUN_SERVER.split(',') }, { urls: TURN_SERVER.split(','), diff --git a/front/src/index.ts b/front/src/index.ts index 4a3555b0..94b1df43 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -84,6 +84,11 @@ const config: GameConfig = { dom: { createContainer: true }, + render: { + pixelArt: true, + roundPixels: true, + antialias: false + }, physics: { default: "arcade", arcade: { diff --git a/front/webpack.config.js b/front/webpack.config.js index 3b97081c..69d507f2 100644 --- a/front/webpack.config.js +++ b/front/webpack.config.js @@ -45,7 +45,19 @@ 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', 'START_ROOM_URL']) + new webpack.EnvironmentPlugin([ + 'API_URL', + 'UPLOADER_URL', + 'ADMIN_URL', + 'DEBUG_MODE', + 'STUN_SERVER', + 'TURN_SERVER', + 'TURN_USER', + 'TURN_PASSWORD', + 'JITSI_URL', + 'JITSI_PRIVATE_MODE', + 'START_ROOM_URL' + ]) ], }; diff --git a/maps/Floor0/floor0.json b/maps/Floor0/floor0.json index 3bbd67cd..6a88f78d 100644 --- a/maps/Floor0/floor0.json +++ b/maps/Floor0/floor0.json @@ -278,7 +278,7 @@ { "name":"exitUrl", "type":"string", - "value":"\/@\/tcm\/workadventure\/floor1" + "value":"..\/Floor1\/floor1.json" }], "type":"tilelayer", "visible":true, diff --git a/maps/Floor1/floor1.json b/maps/Floor1/floor1.json index 6deb3bf5..1894ed42 100644 --- a/maps/Floor1/floor1.json +++ b/maps/Floor1/floor1.json @@ -83,9 +83,9 @@ "opacity":1, "properties":[ { - "name":"exitSceneUrl", + "name":"exitUrl", "type":"string", - "value":"\/@\/tcm\/workadventure\/floor0#down-the-stairs" + "value":"..\/Floor0\/floor0.json" }], "type":"tilelayer", "visible":true, @@ -264,7 +264,7 @@ "nextobjectid":1, "orientation":"orthogonal", "renderorder":"right-down", - "tiledversion":"1.4.2", + "tiledversion":"1.3.3", "tileheight":32, "tilesets":[ { @@ -1959,6 +1959,6 @@ }], "tilewidth":32, "type":"map", - "version":1.4, + "version":1.2, "width":46 } \ No newline at end of file diff --git a/maps/tests/Attribution-tilesets.txt b/maps/tests/Attribution-tilesets.txt new file mode 100644 index 00000000..a0e4224a --- /dev/null +++ b/maps/tests/Attribution-tilesets.txt @@ -0,0 +1,25 @@ +License +------- + +CC-BY-SA 3.0: + - http://creativecommons.org/licenses/by-sa/3.0/ + - See the file: cc-by-sa-3.0.txt +GNU GPL 3.0: + - http://www.gnu.org/licenses/gpl-3.0.html + - See the file: gpl-3.0.txt + +Assets from: workadventure@thecodingmachine.com + +BASE assets: +------------ + + - le-coq.png + - logotcm.png + - pin.png + - tileset1-repositioning.png + - tileset1.png + - tileset2.2.png + - tileset2.png + - tileset3.2.png + - tileset3.png + - walls2.png \ No newline at end of file diff --git a/maps/tests/jitsi_config.json b/maps/tests/jitsi_config.json new file mode 100644 index 00000000..1a831d97 --- /dev/null +++ b/maps/tests/jitsi_config.json @@ -0,0 +1,99 @@ +{ "compressionlevel":-1, + "editorsettings": + { + "export": + { + "target":"." + } + }, + "height":10, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":10, + "id":1, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 34, 34, 34, 34, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":4, + "name":"jitsi", + "opacity":1, + "properties":[ + { + "name":"jitsiConfig", + "type":"string", + "value":"{ \"startWithAudioMuted\": true }" + }, + { + "name":"jitsiInterfaceConfig", + "type":"string", + "value":"{ \"DEFAULT_BACKGROUND\": \"#77ee77\" }" + }, + { + "name":"jitsiRoom", + "type":"string", + "value":"myRoom avec espace \u00e9\u00e0$&'\"_ \ud83d\ude00" + }], + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":5, + "nextobjectid":1, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.3.3", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.2, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/tileset1.png b/maps/tests/tileset1.png new file mode 100644 index 00000000..6e7dafb4 Binary files /dev/null and b/maps/tests/tileset1.png differ diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 7bd50b32..713b8c7d 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -172,6 +172,7 @@ export class SocketManager implements ZoneEventListener { console.log('Calling joinRoom') const apiClient = await apiClientRepository.getClient(client.roomId); const streamToPusher = apiClient.joinRoom(); + clientEventsEmitter.emitClientJoin(client.userUuid, client.roomId); client.backConnection = streamToPusher;