From 11127db9f375d21c3b392d48308bf8c99708a91e Mon Sep 17 00:00:00 2001 From: Romain Lespinasse Date: Mon, 26 Oct 2020 20:10:59 +0100 Subject: [PATCH 01/13] ci: update to github-slug-action v1.1.1 --- .github/workflows/build-and-deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index e77fb133..790dbaaa 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -20,7 +20,7 @@ jobs: # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@master + - uses: rlespinasse/github-slug-action@1.1.1 - name: "Build and push front image" uses: docker/build-push-action@v1 From 67aa2f4b6c42c1784624d6dc510905c7ac07e97c Mon Sep 17 00:00:00 2001 From: kharhamel Date: Tue, 27 Oct 2020 16:59:12 +0100 Subject: [PATCH 02/13] opening and closing jitsi windows now trigger some transitions --- front/dist/index.html | 22 +----- front/dist/resources/style/style.css | 56 +++++++++------ front/src/Phaser/Game/GameScene.ts | 13 +--- front/src/WebRtc/CoWebsiteManager.ts | 101 +++++++++++++++++++-------- front/src/WebRtc/JitsiFactory.ts | 22 +++--- front/src/index.ts | 15 ++-- 6 files changed, 133 insertions(+), 96 deletions(-) diff --git a/front/dist/index.html b/front/dist/index.html index 8e957965..0e696622 100644 --- a/front/dist/index.html +++ b/front/dist/index.html @@ -42,30 +42,14 @@
-
-
+
+
-
- -
@@ -88,7 +72,7 @@
-
+
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index 23a4bba6..1acf6d2a 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -242,15 +242,10 @@ body { .main-container { height: 100vh; width: 100vw; - display: flex; - align-items: stretch; + position: absolute; } @media (min-aspect-ratio: 1/1) { - .main-container { - flex-direction: row; - } - .game-overlay { flex-direction: row; } @@ -266,12 +261,21 @@ body { .sidebar > div:hover { max-height: 25%; } + + #cowebsite { + right: 0; + top: 0; + width: 50%; + height: 100vh; + } + #cowebsite.loading { + transform: translateX(90%); + } + #cowebsite.hidden { + transform: translateX(100%); + } } @media (max-aspect-ratio: 1/1) { - .main-container { - flex-direction: column; - } - .game-overlay { flex-direction: column; } @@ -288,24 +292,36 @@ body { .sidebar > div:hover { max-width: 25%; } + + #cowebsite { + left: 0; + bottom: 0; + width: 100%; + height: 50%; + } + #cowebsite.loading { + transform: translateY(90%); + } + #cowebsite.hidden { + transform: translateY(100%); + } } -.game { - flex-basis: 100%; +#game { + width: 100%; position: relative; /* Position relative is needed for the game-overlay. */ } /* A potentially shared website could appear in an iframe in the cowebsite space. */ -.cowebsite { - flex-basis: 100%; - transition: flex-basis 0.5s; +#cowebsite { + position: fixed; + transition: transform 0.5s; +} +#cowebsite.loading { + background-color: gray; } -/*.cowebsite:hover { - flex-basis: 100%; -}*/ - -.cowebsite > iframe { +#cowebsite > iframe { width: 100%; height: 100%; } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index bfc6402e..96648255 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -38,7 +38,7 @@ import CanvasTexture = Phaser.Textures.CanvasTexture; import GameObject = Phaser.GameObjects.GameObject; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import {GameMap} from "./GameMap"; -import {CoWebsiteManager} from "../../WebRtc/CoWebsiteManager"; +import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {mediaManager} from "../../WebRtc/MediaManager"; import {FourOFourSceneName} from "../Reconnecting/FourOFourScene"; import {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; @@ -292,13 +292,6 @@ export class GameScene extends ResizableScene implements CenterListener { // }); // }); } - - // TEST: let's load a module dynamically! - /*let foo = "http://maps.workadventure.localhost/computer.js"; - import(/* webpackIgnore: true * / foo).then(result => { - console.log(result); - - });*/ } //hook initialisation @@ -476,9 +469,9 @@ export class GameScene extends ResizableScene implements CenterListener { this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { if (newValue === undefined) { - CoWebsiteManager.closeCoWebsite(); + coWebsiteManager.closeCoWebsite(); } else { - CoWebsiteManager.loadCoWebsite(newValue as string); + coWebsiteManager.loadCoWebsite(newValue as string); } }); this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 1793335b..ab63e60a 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -2,47 +2,90 @@ import {HtmlUtils} from "./HtmlUtils"; export type CoWebsiteStateChangedCallback = () => void; -export class CoWebsiteManager { +enum iframeStates { + closed = 1, + loading, // loading an iframe can be slow, so we show some placeholder until it is ready + opened, +} - private static observers = new Array(); +const cowebsiteDivId = "cowebsite"; // the id of the parent div of the iframe. +const animationTime = 500; //time used by the css transitions, in ms. - public static loadCoWebsite(url: string): void { - const cowebsiteDiv = HtmlUtils.getElementByIdOrFail("cowebsite"); +class CoWebsiteManager { + + private opened: iframeStates = iframeStates.closed; + + private observers = new Array(); + + private close(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('loaded'); //edit the css class to trigger the transition + cowebsiteDiv.classList.add('hidden'); + this.opened = iframeStates.closed; + return cowebsiteDiv; + } + private load(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('hidden'); //edit the css class to trigger the transition + cowebsiteDiv.classList.add('loading'); + this.opened = iframeStates.loading; + return cowebsiteDiv; + } + private open(): HTMLDivElement { + const cowebsiteDiv = HtmlUtils.getElementByIdOrFail(cowebsiteDivId); + cowebsiteDiv.classList.remove('loading', 'hidden'); //edit the css class to trigger the transition + this.opened = iframeStates.opened; + return cowebsiteDiv; + } + + public loadCoWebsite(url: string): void { + const cowebsiteDiv = this.load(); cowebsiteDiv.innerHTML = ''; const iframe = document.createElement('iframe'); iframe.id = 'cowebsite-iframe'; iframe.src = url; + const onloadPromise = new Promise((resolve) => { + iframe.onload = () => resolve(); + }); cowebsiteDiv.appendChild(iframe); - //iframe.onload = () => { - // onload can be long to trigger. Maybe we should display the website, whatever happens, after 1 second? - CoWebsiteManager.fire(); - //} + const onTimeoutPromise = new Promise((resolve) => { + setTimeout(() => resolve(), 2000); + }); + Promise.race([onloadPromise, onTimeoutPromise]).then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime) + }); } /** * Just like loadCoWebsite but the div can be filled by the user. */ - public static insertCoWebsite(callback: (cowebsite: HTMLDivElement) => void): void { - const cowebsiteDiv = HtmlUtils.getElementByIdOrFail("cowebsite"); - cowebsiteDiv.innerHTML = ''; - - callback(cowebsiteDiv); - //iframe.onload = () => { - // onload can be long to trigger. Maybe we should display the website, whatever happens, after 1 second? - CoWebsiteManager.fire(); - //} + public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise): void { + const cowebsiteDiv = this.load(); + callback(cowebsiteDiv).then(() => { + this.open(); + setTimeout(() => { + this.fire(); + }, animationTime) + }); } - public static closeCoWebsite(): void { - const cowebsiteDiv = HtmlUtils.getElementByIdOrFail("cowebsite"); - cowebsiteDiv.innerHTML = ''; - CoWebsiteManager.fire(); + public closeCoWebsite(): Promise { + return new Promise((resolve, reject) => { + const cowebsiteDiv = this.close(); + this.fire(); + setTimeout(() => { + resolve(); + setTimeout(() => cowebsiteDiv.innerHTML = '', 500) + }, animationTime) + }); } - public static getGameSize(): {width: number, height: number} { - const hasChildren = HtmlUtils.getElementByIdOrFail("cowebsite").children.length > 0; - if (hasChildren === false) { + public getGameSize(): {width: number, height: number} { + if (this.opened !== iframeStates.opened) { return { width: window.innerWidth, height: window.innerHeight @@ -61,13 +104,15 @@ export class CoWebsiteManager { } } - public static onStateChange(observer: CoWebsiteStateChangedCallback) { - CoWebsiteManager.observers.push(observer); + public onStateChange(observer: CoWebsiteStateChangedCallback) { + this.observers.push(observer); } - private static fire(): void { - for (const callback of CoWebsiteManager.observers) { + private fire(): void { + for (const callback of this.observers) { callback(); } } } + +export const coWebsiteManager = new CoWebsiteManager(); \ No newline at end of file diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 191642fb..45b9b3cf 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -1,6 +1,6 @@ -import {CoWebsiteManager} from "./CoWebsiteManager"; import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {mediaManager} from "./MediaManager"; +import {coWebsiteManager} from "./CoWebsiteManager"; declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any const interfaceConfig = { @@ -31,9 +31,9 @@ class JitsiFactory { private videoCallback = this.onVideoChange.bind(this); public start(roomName: string, playerName:string, jwt?: string): void { - CoWebsiteManager.insertCoWebsite((cowebsiteDiv => { + coWebsiteManager.insertCoWebsite((cowebsiteDiv => { const domain = JITSI_URL; - const options = { + const options: any = { // eslint-disable-line @typescript-eslint/no-explicit-any roomName: roomName, jwt: jwt, width: "100%", @@ -49,19 +49,23 @@ class JitsiFactory { if (!options.jwt) { delete options.jwt; } - this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options); - this.jitsiApi.executeCommand('displayName', playerName); + + return new Promise((resolve) => { + options.onload = () => resolve(); //we want for the iframe to be loaded before triggering animations. + this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options); + this.jitsiApi.executeCommand('displayName', playerName); - this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback); - this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback); + this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback); + this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback); + }); })); } - public stop(): void { + public async stop(): Promise { + await coWebsiteManager.closeCoWebsite(); this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback); this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback); this.jitsiApi?.dispose(); - CoWebsiteManager.closeCoWebsite(); } private onAudioChange({muted}: {muted: boolean}): void { diff --git a/front/src/index.ts b/front/src/index.ts index e12d8707..fe7ceb34 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -10,12 +10,9 @@ import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene"; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; -import {CoWebsiteManager} from "./WebRtc/CoWebsiteManager"; -import {gameManager} from "./Phaser/Game/GameManager"; import {ResizableScene} from "./Phaser/Login/ResizableScene"; import {EntryScene} from "./Phaser/Login/EntryScene"; - -//CoWebsiteManager.loadCoWebsite('https://thecodingmachine.com'); +import {coWebsiteManager} from "./WebRtc/CoWebsiteManager"; // Load Jitsi if the environment variable is set. if (JITSI_URL) { @@ -24,7 +21,7 @@ if (JITSI_URL) { document.head.appendChild(jitsiScript); } -const {width, height} = CoWebsiteManager.getGameSize(); +const {width, height} = coWebsiteManager.getGameSize(); const config: GameConfig = { title: "WorkAdventure", @@ -53,8 +50,7 @@ cypressAsserter.gameStarted(); const game = new Phaser.Game(config); window.addEventListener('resize', function (event) { - const {width, height} = CoWebsiteManager.getGameSize(); - + const {width, height} = coWebsiteManager.getGameSize(); game.scale.resize(width / RESOLUTION, height / RESOLUTION); // Let's trigger the onResize method of any active scene that is a ResizableScene @@ -64,8 +60,7 @@ window.addEventListener('resize', function (event) { } } }); -CoWebsiteManager.onStateChange(() => { - const {width, height} = CoWebsiteManager.getGameSize(); - +coWebsiteManager.onStateChange(() => { + const {width, height} = coWebsiteManager.getGameSize(); game.scale.resize(width / RESOLUTION, height / RESOLUTION); }); From d472f984b5388166121a4fbf128ce11c0def5a57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2020 20:55:36 +0000 Subject: [PATCH 03/13] Bump systeminformation from 4.27.5 to 4.27.11 in /back Bumps [systeminformation](https://github.com/sebhildebrandt/systeminformation) from 4.27.5 to 4.27.11. - [Release notes](https://github.com/sebhildebrandt/systeminformation/releases) - [Changelog](https://github.com/sebhildebrandt/systeminformation/blob/master/CHANGELOG.md) - [Commits](https://github.com/sebhildebrandt/systeminformation/commits) Signed-off-by: dependabot[bot] --- back/package.json | 2 +- back/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/back/package.json b/back/package.json index d989fb1e..bb34e186 100644 --- a/back/package.json +++ b/back/package.json @@ -49,7 +49,7 @@ "multer": "^1.4.2", "prom-client": "^12.0.0", "query-string": "^6.13.3", - "systeminformation": "^4.26.5", + "systeminformation": "^4.27.11", "ts-node-dev": "^1.0.0-pre.44", "typescript": "^3.8.3", "uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0", diff --git a/back/yarn.lock b/back/yarn.lock index fea8516e..1bd7c802 100644 --- a/back/yarn.lock +++ b/back/yarn.lock @@ -2345,10 +2345,10 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" -systeminformation@^4.26.5: - version "4.27.5" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.27.5.tgz#af304fbfd0e7ba51c87512333691b58b4ad90e43" - integrity sha512-EysogxKqREk54ZYDEFcsCODv8GymKZcyiSfegYit8dKhPjzuQr+KX4GFHjssWjYrWFEIM2bYNsFrZX5eufeAXg== +systeminformation@^4.27.11: + version "4.27.11" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.27.11.tgz#6dbe96e48091444f80dab6c05ee1901286826b60" + integrity sha512-U7bigXbOnsB8k1vNHS0Y13RCsRz5/UohiUmND+3mMUL6vfzrpbe/h4ZqewowB+B+tJNnmGFDj08Z8xGfYo45dQ== table@^5.2.3: version "5.4.6" From b1d2543631f3136af60c24b65f60afbc33692449 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Fri, 30 Oct 2020 15:23:50 +0100 Subject: [PATCH 04/13] improvment: added prometheus metrics for the number of groups in a room --- back/src/Controller/PrometheusController.ts | 1 - back/src/Model/GameRoom.ts | 4 +- back/src/Model/Group.ts | 16 +++++- back/src/Services/GaugeManager.ts | 54 +++++++++++++++++++++ back/src/Services/SocketManager.ts | 37 ++++---------- 5 files changed, 80 insertions(+), 32 deletions(-) create mode 100644 back/src/Services/GaugeManager.ts diff --git a/back/src/Controller/PrometheusController.ts b/back/src/Controller/PrometheusController.ts index 05570466..e854cf43 100644 --- a/back/src/Controller/PrometheusController.ts +++ b/back/src/Controller/PrometheusController.ts @@ -1,5 +1,4 @@ import {App} from "../Server/sifrr.server"; -import {IoSocketController} from "_Controller/IoSocketController"; import {HttpRequest, HttpResponse} from "uWebSockets.js"; const register = require('prom-client').register; const collectDefaultMetrics = require('prom-client').collectDefaultMetrics; diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 5b42f418..eaad701a 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -152,7 +152,7 @@ export class GameRoom { closestItem.join(user); } else { const closestUser : User = closestItem; - const group: Group = new Group([ + const group: Group = new Group(this.roomId,[ user, closestUser ], this.connectCallback, this.disconnectCallback, this.positionNotifier); @@ -200,7 +200,6 @@ export class GameRoom { if (group === undefined) { throw new Error("The user is part of no group"); } - const oldPosition = group.getPosition(); group.leave(user); if (group.isEmpty()) { this.positionNotifier.leave(group); @@ -209,6 +208,7 @@ export class GameRoom { throw new Error("Could not find group "+group.getId()+" referenced by user "+user.id+" in World."); } this.groups.delete(group); + //todo: is the group garbage collected? } else { group.updatePosition(); //this.positionNotifier.updatePosition(group, group.getPosition(), oldPosition); diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index d3b042a6..f26a0e0d 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -3,6 +3,7 @@ import { User } from "./User"; import {PositionInterface} from "_Model/PositionInterface"; import {Movable} from "_Model/Movable"; import {PositionNotifier} from "_Model/PositionNotifier"; +import {gaugeManager} from "../Services/GaugeManager"; export class Group implements Movable { static readonly MAX_PER_GROUP = 4; @@ -13,12 +14,23 @@ export class Group implements Movable { private users: Set; private x!: number; private y!: number; + private hasEditedGauge: boolean = false; + private wasDestroyed: boolean = false; + private roomId: string; - constructor(users: User[], private connectCallback: ConnectCallback, private disconnectCallback: DisconnectCallback, private positionNotifier: PositionNotifier) { + constructor(roomId: string, users: User[], private connectCallback: ConnectCallback, private disconnectCallback: DisconnectCallback, private positionNotifier: PositionNotifier) { + this.roomId = roomId; this.users = new Set(); this.id = Group.nextId; Group.nextId++; + //we only send a event for prometheus metrics if the group lives more than 5 seconds + setTimeout(() => { + if (!this.wasDestroyed) { + this.hasEditedGauge = true; + gaugeManager.incNbGroupsPerRoomGauge(roomId); + } + }, 5000); users.forEach((user: User) => { this.join(user); @@ -113,9 +125,11 @@ export class Group implements Movable { */ destroy(): void { + if (this.hasEditedGauge) gaugeManager.decNbGroupsPerRoomGauge(this.roomId); for (const user of this.users) { this.leave(user); } + this.wasDestroyed = true; } get getSize(){ diff --git a/back/src/Services/GaugeManager.ts b/back/src/Services/GaugeManager.ts new file mode 100644 index 00000000..f8af822b --- /dev/null +++ b/back/src/Services/GaugeManager.ts @@ -0,0 +1,54 @@ +import {Counter, Gauge} from "prom-client"; + +//this class should manage all the custom metrics used by prometheus +class GaugeManager { + private nbClientsGauge: Gauge; + private nbClientsPerRoomGauge: Gauge; + private nbGroupsPerRoomGauge: Gauge; + private nbGroupsPerRoomCounter: Counter; + + constructor() { + this.nbClientsGauge = new Gauge({ + name: 'workadventure_nb_sockets', + help: 'Number of connected sockets', + labelNames: [ ] + }); + this.nbClientsPerRoomGauge = new Gauge({ + name: 'workadventure_nb_clients_per_room', + help: 'Number of clients per room', + labelNames: [ 'room' ] + }); + + this.nbGroupsPerRoomCounter = new Counter({ + name: 'workadventure_counter_groups_per_room', + help: 'Counter of groups per room', + labelNames: [ 'room' ] + }); + this.nbGroupsPerRoomGauge = new Gauge({ + name: 'workadventure_nb_groups_per_room', + help: 'Number of groups per room', + labelNames: [ 'room' ] + }); + } + + incNbClientPerRoomGauge(roomId: string): void { + this.nbClientsGauge.inc(); + this.nbClientsPerRoomGauge.inc({ room: roomId }); + } + + decNbClientPerRoomGauge(roomId: string): void { + this.nbClientsGauge.dec(); + this.nbClientsPerRoomGauge.dec({ room: roomId }); + } + + incNbGroupsPerRoomGauge(roomId: string): void { + this.nbGroupsPerRoomCounter.inc({ room: roomId }) + this.nbGroupsPerRoomGauge.inc({ room: roomId }) + } + + decNbGroupsPerRoomGauge(roomId: string): void { + this.nbGroupsPerRoomGauge.dec({ room: roomId }) + } +} + +export const gaugeManager = new GaugeManager(); \ No newline at end of file diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 35f97c37..4bd26778 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -23,7 +23,6 @@ import { WebRtcStartMessage, QueryJitsiJwtMessage, SendJitsiJwtMessage, - CharacterLayerMessage, SendUserMessage } from "../Messages/generated/messages_pb"; import {PointInterface} from "../Model/Websocket/PointInterface"; @@ -37,11 +36,11 @@ import {Movable} from "../Model/Movable"; import {PositionInterface} from "../Model/PositionInterface"; import {adminApi, CharacterTexture} from "./AdminApi"; import Direction = PositionMessage.Direction; -import {Gauge} from "prom-client"; import {emitError, emitInBatch} from "./IoSocketHelpers"; import Jwt from "jsonwebtoken"; import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {clientEventsEmitter} from "./ClientEventsEmitter"; +import {gaugeManager} from "./GaugeManager"; interface AdminSocketRoomsList { [index: string]: number; @@ -58,30 +57,13 @@ export interface AdminSocketData { export class SocketManager { private Worlds: Map = new Map(); private sockets: Map = new Map(); - private nbClientsGauge: Gauge; - private nbClientsPerRoomGauge: Gauge; - + constructor() { - this.nbClientsGauge = new Gauge({ - name: 'workadventure_nb_sockets', - help: 'Number of connected sockets', - labelNames: [ ] + clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => { + gaugeManager.incNbClientPerRoomGauge(roomId); }); - this.nbClientsPerRoomGauge = new Gauge({ - name: 'workadventure_nb_clients_per_room', - help: 'Number of clients per room', - labelNames: [ 'room' ] - }); - - clientEventsEmitter.registerToClientJoin((clientUUid, roomId) => { - this.nbClientsGauge.inc(); - // Let's log server load when a user joins - console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)'); - }); - clientEventsEmitter.registerToClientLeave((clientUUid, roomId) => { - this.nbClientsGauge.dec(); - // Let's log server load when a user leaves - console.log('A user left (', this.sockets.size, ' connected users)'); + clientEventsEmitter.registerToClientLeave((clientUUid: string, roomId: string) => { + gaugeManager.decNbClientPerRoomGauge(roomId); }); } @@ -107,7 +89,6 @@ export class SocketManager { const viewport = client.viewport; try { this.sockets.set(client.userId, client); //todo: should this be at the end of the function? - clientEventsEmitter.emitClientJoin(client.userUuid, client.roomId); //join new previous room const gameRoom = this.joinRoom(client, position); @@ -377,8 +358,8 @@ export class SocketManager { } finally { //delete Client.roomId; this.sockets.delete(Client.userId); - this.nbClientsPerRoomGauge.dec({ room: Client.roomId }); clientEventsEmitter.emitClientLeave(Client.userUuid, Client.roomId); + console.log('A user left (', this.sockets.size, ' connected users)'); } } } @@ -410,8 +391,6 @@ export class SocketManager { private joinRoom(client : ExSocketInterface, position: PointInterface): GameRoom { const roomId = client.roomId; - //join user in room - this.nbClientsPerRoomGauge.inc({ room: roomId }); client.position = position; const world = this.Worlds.get(roomId) @@ -425,6 +404,8 @@ export class SocketManager { }); //join world world.join(client, client.position); + clientEventsEmitter.emitClientJoin(client.userUuid, client.roomId); + console.log(new Date().toISOString() + ' A user joined (', this.sockets.size, ' connected users)'); return world; } From 3fc7886b42455f045cc8524646d9770f57ae8608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 4 Nov 2020 12:15:01 +0100 Subject: [PATCH 05/13] Fixing rlespinasse master branch deleted --- .github/workflows/build-and-deploy.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index 790dbaaa..e8c683b9 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -43,7 +43,7 @@ jobs: uses: actions/checkout@v2 # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@master + - uses: rlespinasse/github-slug-action@1.1.1 - name: "Build and push back image" uses: docker/build-push-action@v1 @@ -66,7 +66,7 @@ jobs: uses: actions/checkout@v2 # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@master + - uses: rlespinasse/github-slug-action@1.1.1 - name: "Build and push back image" uses: docker/build-push-action@v1 @@ -90,7 +90,7 @@ jobs: # Create a slugified value of the branch - - uses: rlespinasse/github-slug-action@master + - uses: rlespinasse/github-slug-action@1.1.1 - name: "Build and push front image" uses: docker/build-push-action@v1 From fcef89aa70dd8f32d0875d70c175073fdb83c66a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 5 Nov 2020 11:25:35 +0100 Subject: [PATCH 06/13] Fixing deployment of WA without admin The open-source version must be installable without an admin backend. While working on AFUP's meeting, we introduced regression preventing WA from working without an admin. This commit fixes this behaviour and makes WA installable again! --- back/src/Controller/IoSocketController.ts | 59 ++++++++++++----------- back/src/Enum/EnvironmentVariable.ts | 2 +- back/src/Services/JWTTokenManager.ts | 32 ++++++------ 3 files changed, 51 insertions(+), 42 deletions(-) diff --git a/back/src/Controller/IoSocketController.ts b/back/src/Controller/IoSocketController.ts index 0ae6465f..a27d91df 100644 --- a/back/src/Controller/IoSocketController.ts +++ b/back/src/Controller/IoSocketController.ts @@ -22,7 +22,7 @@ import {adminApi, CharacterTexture, FetchMemberDataByUuidResponse} from "../Serv import {SocketManager, socketManager} from "../Services/SocketManager"; import {emitInBatch, resetPing} from "../Services/IoSocketHelpers"; import {clientEventsEmitter} from "../Services/ClientEventsEmitter"; -import {ADMIN_API_TOKEN} from "../Enum/EnvironmentVariable"; +import {ADMIN_API_TOKEN, ADMIN_API_URL} from "../Enum/EnvironmentVariable"; export class IoSocketController { private nextUserId: number = 1; @@ -166,18 +166,21 @@ export class IoSocketController { if(room.isFull){ throw new Error('Room is full'); } - try { - const userData = await adminApi.fetchMemberDataByUuid(userUuid); - //console.log('USERDATA', userData) - memberTags = userData.tags; - memberTextures = userData.textures; - if (!room.anonymous && room.policyType === GameRoomPolicyTypes.USE_TAGS_POLICY && !room.canAccess(memberTags)) { - throw new Error('No correct tags') + if (ADMIN_API_URL) { + try { + const userData = await adminApi.fetchMemberDataByUuid(userUuid); + //console.log('USERDATA', userData) + memberTags = userData.tags; + memberTextures = userData.textures; + if (!room.anonymous && room.policyType === GameRoomPolicyTypes.USE_TAGS_POLICY && !room.canAccess(memberTags)) { + throw new Error('No correct tags') + } + //console.log('access granted for user '+userUuid+' and room '+roomId); + } catch (e) { + console.log('access not granted for user '+userUuid+' and room '+roomId); + console.error(e); + throw new Error('Client cannot acces this ressource.') } - //console.log('access granted for user '+userUuid+' and room '+roomId); - } catch (e) { - console.log('access not granted for user '+userUuid+' and room '+roomId); - throw new Error('Client cannot acces this ressource.') } // Generate characterLayers objects from characterLayers string[] @@ -238,22 +241,24 @@ export class IoSocketController { socketManager.handleJoinRoom(client); resetPing(client); - //get data information and shwo messages - adminApi.fetchMemberDataByUuid(client.userUuid).then((res: FetchMemberDataByUuidResponse) => { - if (!res.messages) { - return; - } - res.messages.forEach((c: unknown) => { - const messageToSend = c as { type: string, message: string }; - socketManager.emitSendUserMessage({ - userUuid: client.userUuid, - type: messageToSend.type, - message: messageToSend.message - }) + //get data information and show messages + if (ADMIN_API_URL) { + adminApi.fetchMemberDataByUuid(client.userUuid).then((res: FetchMemberDataByUuidResponse) => { + if (!res.messages) { + return; + } + res.messages.forEach((c: unknown) => { + const messageToSend = c as { type: string, message: string }; + socketManager.emitSendUserMessage({ + userUuid: client.userUuid, + type: messageToSend.type, + message: messageToSend.message + }) + }); + }).catch((err) => { + console.error('fetchMemberDataByUuid => err', err); }); - }).catch((err) => { - console.error('fetchMemberDataByUuid => err', err); - }); + } }, message: (ws, arrayBuffer, isBinary): void => { const client = ws as ExSocketInterface; diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index 56c49284..55fd1bb7 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -3,7 +3,7 @@ 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; -const ADMIN_API_URL = process.env.ADMIN_API_URL || 'http://admin'; +const ADMIN_API_URL = process.env.ADMIN_API_URL || ''; const ADMIN_API_TOKEN = process.env.ADMIN_API_TOKEN || 'myapitoken'; const MAX_USERS_PER_ROOM = parseInt(process.env.MAX_USERS_PER_ROOM || '') || 600; const CPU_OVERHEAT_THRESHOLD = Number(process.env.CPU_OVERHEAT_THRESHOLD) || 80; diff --git a/back/src/Services/JWTTokenManager.ts b/back/src/Services/JWTTokenManager.ts index f82fa001..8abb0e45 100644 --- a/back/src/Services/JWTTokenManager.ts +++ b/back/src/Services/JWTTokenManager.ts @@ -1,11 +1,11 @@ -import {ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVariable"; +import {ADMIN_API_URL, ALLOW_ARTILLERY, SECRET_KEY} from "../Enum/EnvironmentVariable"; import {uuid} from "uuidv4"; import Jwt from "jsonwebtoken"; import {TokenInterface} from "../Controller/AuthenticateController"; import {adminApi, AdminApiData} from "../Services/AdminApi"; class JWTTokenManager { - + public createJWTToken(userUuid: string) { return Jwt.sign({userUuid: userUuid}, SECRET_KEY, {expiresIn: '200d'}); //todo: add a mechanic to refresh or recreate token } @@ -48,17 +48,21 @@ class JWTTokenManager { return; } - //verify user in admin - adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { - resolve(tokenInterface.userUuid); - }).catch((err) => { - //anonymous user - if(err.response && err.response.status && err.response.status === 404){ + if (ADMIN_API_URL) { + //verify user in admin + adminApi.fetchCheckUserByToken(tokenInterface.userUuid).then(() => { resolve(tokenInterface.userUuid); - return; - } - reject(new Error('Authentication error, invalid token structure. ' + err)); - }); + }).catch((err) => { + //anonymous user + if(err.response && err.response.status && err.response.status === 404){ + resolve(tokenInterface.userUuid); + return; + } + reject(err); + }); + } else { + resolve(tokenInterface.userUuid); + } }); }); } @@ -66,7 +70,7 @@ class JWTTokenManager { 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 +export const jwtTokenManager = new JWTTokenManager(); From e54061d464a281b8fc197071bdd7d52ad1a8943f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 5 Nov 2020 11:58:33 +0100 Subject: [PATCH 07/13] Deploying admin only on master / develop branch or if branch starts with "admin" --- deeployer.libsonnet | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/deeployer.libsonnet b/deeployer.libsonnet index 4edb4728..8953ed17 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -3,6 +3,7 @@ local namespace = env.GITHUB_REF_SLUG, local tag = namespace, local url = if namespace == "master" then "workadventu.re" else namespace+".workadventure.test.thecodingmachine.com", + local adminUrl = if namespace == "master" || namespace == "develop" || std.startsWith(namespace, "admin") then "https://admin."+url else null, "$schema": "https://raw.githubusercontent.com/thecodingmachine/deeployer/master/deeployer.schema.json", "version": "1.0", "containers": { @@ -20,7 +21,9 @@ "JITSI_ISS": env.JITSI_ISS, "JITSI_URL": env.JITSI_URL, "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, - } + } + if adminUrl != null then { + "ADMIN_API_URL": adminUrl, + } else {} }, "front": { "image": "thecodingmachine/workadventure-front:"+tag, From 2509f391c2cde4ecc644a4b765e7aaf81d25aa24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 5 Nov 2020 15:08:03 +0100 Subject: [PATCH 08/13] Fixing admin url deploy --- deeployer.libsonnet | 1 - 1 file changed, 1 deletion(-) diff --git a/deeployer.libsonnet b/deeployer.libsonnet index 8953ed17..db86fbb4 100644 --- a/deeployer.libsonnet +++ b/deeployer.libsonnet @@ -17,7 +17,6 @@ "env": { "SECRET_KEY": "tempSecretKeyNeedsToChange", "ADMIN_API_TOKEN": env.ADMIN_API_TOKEN, - "ADMIN_API_URL": "https://admin."+url, "JITSI_ISS": env.JITSI_ISS, "JITSI_URL": env.JITSI_URL, "SECRET_JITSI_KEY": env.SECRET_JITSI_KEY, From 8af99a4286ab8872a7c2aa7508fb5a09ecb590e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 5 Nov 2020 16:35:10 +0100 Subject: [PATCH 09/13] Upgrading cypress action version --- .github/workflows/build-and-deploy.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index e8c683b9..390f2556 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -137,7 +137,7 @@ jobs: check_for_duplicate_msg: true - name: Run Cypress tests - uses: cypress-io/github-action@v1 + uses: cypress-io/github-action@v2 if: ${{ env.GITHUB_REF_SLUG != 'master' }} env: CYPRESS_BASE_URL: https://play.${{ env.GITHUB_REF_SLUG }}.workadventure.test.thecodingmachine.com @@ -148,7 +148,7 @@ jobs: working-directory: e2e - name: Run Cypress tests in prod - uses: cypress-io/github-action@v1 + uses: cypress-io/github-action@v2 if: ${{ env.GITHUB_REF_SLUG == 'master' }} env: CYPRESS_BASE_URL: https://play.workadventu.re From 8e1e2596e51281521f2f02f1c78ee25d4c01bdbd Mon Sep 17 00:00:00 2001 From: kevin Date: Thu, 5 Nov 2020 23:17:58 +0100 Subject: [PATCH 10/13] changing typos on Home Page --- website/dist/index.html | 41 ++++++++++++++++++------------------ website/src/sass/styles.scss | 2 +- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/website/dist/index.html b/website/dist/index.html index 08805709..1e06204a 100644 --- a/website/dist/index.html +++ b/website/dist/index.html @@ -33,12 +33,12 @@ - + - + @@ -46,7 +46,7 @@ - + @@ -97,17 +97,17 @@

Your workplace
but better

-

You are impatient to discover this new world? Click on "Play online" and meet new people or share this adventure with your colleagues and friends by clicking on "Private mode"

+

You are impatient to discover this new world? Click on "Work online" and meet new people or share this adventure with your colleagues and friends by clicking on "Work in private"

@@ -165,10 +165,10 @@

You can also create a private room with your friends or your team !

-

To try, press button private mode

+

To try, press button work in private

- START IN PRIVATE MODE + START WORKING IN PRIVATE

Don’t forget to activate your mic and camera, let’s play @@ -287,23 +287,23 @@

-

How to play

+

HOW IT WORKS

-

Choose your map

+

CHOOSE YOUR WORKSPACE

-

Select your character

+

SELECT YOUR WOKA

-

Let's go explore and talk !

+

LET'S GO TO YOUR OFFICE

@@ -313,13 +313,14 @@
-

Future features

-

We have already thought of new features:

-

Share files with others

-

Lock group conversations

+

CURRENT FEATURES

+ +

Instant interaction with up to 4 people

Share screen with others

-

Interact with objects

-

And you, what would you want?

+

Organize a workshop and meeting with up to 60 people

+

Design your own digital space

+

... and infinite possibilities

+

You need more? Do not hesitate to tell us!

diff --git a/website/src/sass/styles.scss b/website/src/sass/styles.scss index c5095343..8a26df3e 100644 --- a/website/src/sass/styles.scss +++ b/website/src/sass/styles.scss @@ -148,7 +148,7 @@ header { left: 0; right: 0; bottom: 7px; - padding: 1.125rem; + padding: 1.125rem 0.5rem; text-align: center; z-index: 2; transition: all .1s cubic-bezier(0.000, -0.600, 1.000, 1.650); /* custom */ From 467cf3260f6c484f4117fbdd427951f39eeb963a Mon Sep 17 00:00:00 2001 From: kevin Date: Fri, 6 Nov 2020 06:57:56 +0100 Subject: [PATCH 11/13] change 'play private' to 'work in privtae', fix broken html, add homepage link in inner pages --- website/dist/choose-map.html | 9 +++++++++ website/dist/create-map.html | 11 ++++++++++- website/src/sass/styles.scss | 5 ++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/website/dist/choose-map.html b/website/dist/choose-map.html index a2366f1d..72877086 100644 --- a/website/dist/choose-map.html +++ b/website/dist/choose-map.html @@ -36,6 +36,15 @@ +

CHOOSE YOUR MAP !

diff --git a/website/dist/create-map.html b/website/dist/create-map.html index d34cea77..1369b92a 100644 --- a/website/dist/create-map.html +++ b/website/dist/create-map.html @@ -36,6 +36,15 @@ +

CREATE YOUR MAP !

@@ -44,7 +53,7 @@
- < class="col"> +

Tools you will need

In order to build your own map for WorkAdventure, you need:

    diff --git a/website/src/sass/styles.scss b/website/src/sass/styles.scss index 8a26df3e..7cc85608 100644 --- a/website/src/sass/styles.scss +++ b/website/src/sass/styles.scss @@ -431,7 +431,7 @@ img{ margin: 0; min-height: 6rem; font-family: 'Karmatic Arcade'; - font-size: 22px; + font-size: 24px; margin-bottom: 1.25rem; } } @@ -503,6 +503,9 @@ img{ .pixel-title{ font-family: "Karmatic Arcade" !important; } +.pixel-text{ + font-family: "VCR OSD Mono" !important; +} h3 { font-weight: 800; From d0f643cfa622c7aeafccaa66df4959fff765ac5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 6 Nov 2020 16:46:22 +0100 Subject: [PATCH 12/13] Update website/dist/choose-map.html --- website/dist/choose-map.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/dist/choose-map.html b/website/dist/choose-map.html index 72877086..5fb1d8f7 100644 --- a/website/dist/choose-map.html +++ b/website/dist/choose-map.html @@ -40,7 +40,7 @@ From 71e2c92f7bd008d3137a64eeed16da60485ef374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 6 Nov 2020 16:46:28 +0100 Subject: [PATCH 13/13] Update website/dist/create-map.html --- website/dist/create-map.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/dist/create-map.html b/website/dist/create-map.html index 1369b92a..0a398f45 100644 --- a/website/dist/create-map.html +++ b/website/dist/create-map.html @@ -40,7 +40,7 @@