Merge branch 'develop' into feature-discussion

This commit is contained in:
Gregoire Parant 2020-10-30 20:04:12 +01:00
commit 724bb7f288
15 changed files with 246 additions and 136 deletions

View File

@ -20,7 +20,7 @@ jobs:
# Create a slugified value of the branch # 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" - name: "Build and push front image"
uses: docker/build-push-action@v1 uses: docker/build-push-action@v1

View File

@ -49,7 +49,7 @@
"multer": "^1.4.2", "multer": "^1.4.2",
"prom-client": "^12.0.0", "prom-client": "^12.0.0",
"query-string": "^6.13.3", "query-string": "^6.13.3",
"systeminformation": "^4.26.5", "systeminformation": "^4.27.11",
"ts-node-dev": "^1.0.0-pre.44", "ts-node-dev": "^1.0.0-pre.44",
"typescript": "^3.8.3", "typescript": "^3.8.3",
"uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0", "uWebSockets.js": "uNetworking/uWebSockets.js#v18.5.0",

View File

@ -1,5 +1,4 @@
import {App} from "../Server/sifrr.server"; import {App} from "../Server/sifrr.server";
import {IoSocketController} from "_Controller/IoSocketController";
import {HttpRequest, HttpResponse} from "uWebSockets.js"; import {HttpRequest, HttpResponse} from "uWebSockets.js";
const register = require('prom-client').register; const register = require('prom-client').register;
const collectDefaultMetrics = require('prom-client').collectDefaultMetrics; const collectDefaultMetrics = require('prom-client').collectDefaultMetrics;

View File

@ -152,7 +152,7 @@ export class GameRoom {
closestItem.join(user); closestItem.join(user);
} else { } else {
const closestUser : User = closestItem; const closestUser : User = closestItem;
const group: Group = new Group([ const group: Group = new Group(this.roomId,[
user, user,
closestUser closestUser
], this.connectCallback, this.disconnectCallback, this.positionNotifier); ], this.connectCallback, this.disconnectCallback, this.positionNotifier);
@ -200,7 +200,6 @@ export class GameRoom {
if (group === undefined) { if (group === undefined) {
throw new Error("The user is part of no group"); throw new Error("The user is part of no group");
} }
const oldPosition = group.getPosition();
group.leave(user); group.leave(user);
if (group.isEmpty()) { if (group.isEmpty()) {
this.positionNotifier.leave(group); 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."); throw new Error("Could not find group "+group.getId()+" referenced by user "+user.id+" in World.");
} }
this.groups.delete(group); this.groups.delete(group);
//todo: is the group garbage collected?
} else { } else {
group.updatePosition(); group.updatePosition();
//this.positionNotifier.updatePosition(group, group.getPosition(), oldPosition); //this.positionNotifier.updatePosition(group, group.getPosition(), oldPosition);

View File

@ -3,6 +3,7 @@ import { User } from "./User";
import {PositionInterface} from "_Model/PositionInterface"; import {PositionInterface} from "_Model/PositionInterface";
import {Movable} from "_Model/Movable"; import {Movable} from "_Model/Movable";
import {PositionNotifier} from "_Model/PositionNotifier"; import {PositionNotifier} from "_Model/PositionNotifier";
import {gaugeManager} from "../Services/GaugeManager";
export class Group implements Movable { export class Group implements Movable {
static readonly MAX_PER_GROUP = 4; static readonly MAX_PER_GROUP = 4;
@ -13,12 +14,23 @@ export class Group implements Movable {
private users: Set<User>; private users: Set<User>;
private x!: number; private x!: number;
private y!: 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<User>(); this.users = new Set<User>();
this.id = Group.nextId; this.id = Group.nextId;
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) => { users.forEach((user: User) => {
this.join(user); this.join(user);
@ -113,9 +125,11 @@ export class Group implements Movable {
*/ */
destroy(): void destroy(): void
{ {
if (this.hasEditedGauge) gaugeManager.decNbGroupsPerRoomGauge(this.roomId);
for (const user of this.users) { for (const user of this.users) {
this.leave(user); this.leave(user);
} }
this.wasDestroyed = true;
} }
get getSize(){ get getSize(){

View File

@ -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<string>;
private nbClientsPerRoomGauge: Gauge<string>;
private nbGroupsPerRoomGauge: Gauge<string>;
private nbGroupsPerRoomCounter: Counter<string>;
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();

View File

@ -23,7 +23,6 @@ import {
WebRtcStartMessage, WebRtcStartMessage,
QueryJitsiJwtMessage, QueryJitsiJwtMessage,
SendJitsiJwtMessage, SendJitsiJwtMessage,
CharacterLayerMessage,
SendUserMessage SendUserMessage
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import {PointInterface} from "../Model/Websocket/PointInterface"; import {PointInterface} from "../Model/Websocket/PointInterface";
@ -37,11 +36,11 @@ import {Movable} from "../Model/Movable";
import {PositionInterface} from "../Model/PositionInterface"; import {PositionInterface} from "../Model/PositionInterface";
import {adminApi, CharacterTexture} from "./AdminApi"; import {adminApi, CharacterTexture} from "./AdminApi";
import Direction = PositionMessage.Direction; import Direction = PositionMessage.Direction;
import {Gauge} from "prom-client";
import {emitError, emitInBatch} from "./IoSocketHelpers"; import {emitError, emitInBatch} from "./IoSocketHelpers";
import Jwt from "jsonwebtoken"; import Jwt from "jsonwebtoken";
import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {JITSI_URL} from "../Enum/EnvironmentVariable";
import {clientEventsEmitter} from "./ClientEventsEmitter"; import {clientEventsEmitter} from "./ClientEventsEmitter";
import {gaugeManager} from "./GaugeManager";
interface AdminSocketRoomsList { interface AdminSocketRoomsList {
[index: string]: number; [index: string]: number;
@ -58,30 +57,13 @@ export interface AdminSocketData {
export class SocketManager { export class SocketManager {
private Worlds: Map<string, GameRoom> = new Map<string, GameRoom>(); private Worlds: Map<string, GameRoom> = new Map<string, GameRoom>();
private sockets: Map<number, ExSocketInterface> = new Map<number, ExSocketInterface>(); private sockets: Map<number, ExSocketInterface> = new Map<number, ExSocketInterface>();
private nbClientsGauge: Gauge<string>;
private nbClientsPerRoomGauge: Gauge<string>;
constructor() { constructor() {
this.nbClientsGauge = new Gauge({ clientEventsEmitter.registerToClientJoin((clientUUid: string, roomId: string) => {
name: 'workadventure_nb_sockets', gaugeManager.incNbClientPerRoomGauge(roomId);
help: 'Number of connected sockets',
labelNames: [ ]
}); });
this.nbClientsPerRoomGauge = new Gauge({ clientEventsEmitter.registerToClientLeave((clientUUid: string, roomId: string) => {
name: 'workadventure_nb_clients_per_room', gaugeManager.decNbClientPerRoomGauge(roomId);
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)');
}); });
} }
@ -107,7 +89,6 @@ export class SocketManager {
const viewport = client.viewport; const viewport = client.viewport;
try { try {
this.sockets.set(client.userId, client); //todo: should this be at the end of the function? 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 //join new previous room
const gameRoom = this.joinRoom(client, position); const gameRoom = this.joinRoom(client, position);
@ -377,8 +358,8 @@ export class SocketManager {
} finally { } finally {
//delete Client.roomId; //delete Client.roomId;
this.sockets.delete(Client.userId); this.sockets.delete(Client.userId);
this.nbClientsPerRoomGauge.dec({ room: Client.roomId });
clientEventsEmitter.emitClientLeave(Client.userUuid, 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 { private joinRoom(client : ExSocketInterface, position: PointInterface): GameRoom {
const roomId = client.roomId; const roomId = client.roomId;
//join user in room
this.nbClientsPerRoomGauge.inc({ room: roomId });
client.position = position; client.position = position;
const world = this.Worlds.get(roomId) const world = this.Worlds.get(roomId)
@ -425,6 +404,8 @@ export class SocketManager {
}); });
//join world //join world
world.join(client, client.position); 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; return world;
} }

View File

@ -2345,10 +2345,10 @@ supports-color@^7.1.0:
dependencies: dependencies:
has-flag "^4.0.0" has-flag "^4.0.0"
systeminformation@^4.26.5: systeminformation@^4.27.11:
version "4.27.5" version "4.27.11"
resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.27.5.tgz#af304fbfd0e7ba51c87512333691b58b4ad90e43" resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-4.27.11.tgz#6dbe96e48091444f80dab6c05ee1901286826b60"
integrity sha512-EysogxKqREk54ZYDEFcsCODv8GymKZcyiSfegYit8dKhPjzuQr+KX4GFHjssWjYrWFEIM2bYNsFrZX5eufeAXg== integrity sha512-U7bigXbOnsB8k1vNHS0Y13RCsRz5/UohiUmND+3mMUL6vfzrpbe/h4ZqewowB+B+tJNnmGFDj08Z8xGfYo45dQ==
table@^5.2.3: table@^5.2.3:
version "5.4.6" version "5.4.6"

22
front/dist/index.html vendored
View File

@ -42,30 +42,14 @@
<body id="body" style="margin: 0"> <body id="body" style="margin: 0">
<div class="main-container" id="main-container"> <div class="main-container" id="main-container">
<!-- Create the editor container --> <!-- Create the editor container -->
<div id="game" class="game" style="/*background: red;*/"> <div id="game" class="game">
<div id="game-overlay" class="game-overlay" style="/*background: violet*/;"> <div id="game-overlay" class="game-overlay">
<div id="main-section" class="main-section"> <div id="main-section" class="main-section">
<!--<div style="background: lightpink;">a</div>
<div style="background: lightpink;">a</div> -->
</div> </div>
<aside id="sidebar" class="sidebar"> <aside id="sidebar" class="sidebar">
<!--<div style="background: lightgreen;">a</div>
<div style="background: green;">b</div>
<div style="background: darkgreen;">c</div>
<div style="background: darkgreen;">d</div>-->
</aside> </aside>
<div id="chat-mode" class="chat-mode three-col" style="display: none;"> <div id="chat-mode" class="chat-mode three-col" style="display: none;">
<!--<div style="background: lightgreen;">a</div>
<div style="background: green;">b</div>
<div style="background: darkgreen;">c</div>
<div style="background: darkolivegreen;">d</div>
<div style="background: darkolivegreen;">d</div>
<div style="background: darkgreen;">c</div>
<div style="background: green;">b</div>
<div style="background: lightgreen;">last elem for game</div>-->
</div> </div>
<div id="activeCam" class="activeCam"> <div id="activeCam" class="activeCam">
<div id="div-myCamVideo" class="video-container"> <div id="div-myCamVideo" class="video-container">
<video id="myCamVideo" autoplay muted></video> <video id="myCamVideo" autoplay muted></video>
@ -88,7 +72,7 @@
</div> </div>
</div> </div>
<div id="cowebsite" class="cowebsite"></div> <div id="cowebsite" class="cowebsite hidden"></div>
<div class="audio-playing"> <div class="audio-playing">
<img src="/resources/logos/megaphone.svg"/> <img src="/resources/logos/megaphone.svg"/>
</div> </div>

View File

@ -252,15 +252,10 @@ body {
.main-container { .main-container {
height: 100vh; height: 100vh;
width: 100vw; width: 100vw;
display: flex; position: absolute;
align-items: stretch;
} }
@media (min-aspect-ratio: 1/1) { @media (min-aspect-ratio: 1/1) {
.main-container {
flex-direction: row;
}
.game-overlay { .game-overlay {
flex-direction: row; flex-direction: row;
} }
@ -276,12 +271,21 @@ body {
.sidebar > div:hover { .sidebar > div:hover {
max-height: 25%; 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) { @media (max-aspect-ratio: 1/1) {
.main-container {
flex-direction: column;
}
.game-overlay { .game-overlay {
flex-direction: column; flex-direction: column;
} }
@ -298,24 +302,36 @@ body {
.sidebar > div:hover { .sidebar > div:hover {
max-width: 25%; max-width: 25%;
} }
#cowebsite {
left: 0;
bottom: 0;
width: 100%;
height: 50%;
}
#cowebsite.loading {
transform: translateY(90%);
}
#cowebsite.hidden {
transform: translateY(100%);
}
} }
.game { #game {
flex-basis: 100%; width: 100%;
position: relative; /* Position relative is needed for the game-overlay. */ position: relative; /* Position relative is needed for the game-overlay. */
} }
/* A potentially shared website could appear in an iframe in the cowebsite space. */ /* A potentially shared website could appear in an iframe in the cowebsite space. */
.cowebsite { #cowebsite {
flex-basis: 100%; position: fixed;
transition: flex-basis 0.5s; transition: transform 0.5s;
}
#cowebsite.loading {
background-color: gray;
} }
/*.cowebsite:hover { #cowebsite > iframe {
flex-basis: 100%;
}*/
.cowebsite > iframe {
width: 100%; width: 100%;
height: 100%; height: 100%;
} }

View File

@ -38,7 +38,7 @@ import CanvasTexture = Phaser.Textures.CanvasTexture;
import GameObject = Phaser.GameObjects.GameObject; import GameObject = Phaser.GameObjects.GameObject;
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
import {GameMap} from "./GameMap"; import {GameMap} from "./GameMap";
import {CoWebsiteManager} from "../../WebRtc/CoWebsiteManager"; import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import {mediaManager} from "../../WebRtc/MediaManager"; import {mediaManager} from "../../WebRtc/MediaManager";
import {FourOFourSceneName} from "../Reconnecting/FourOFourScene"; import {FourOFourSceneName} from "../Reconnecting/FourOFourScene";
import {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; 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 //hook initialisation
@ -476,9 +469,9 @@ export class GameScene extends ResizableScene implements CenterListener {
this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => { this.gameMap.onPropertyChange('openWebsite', (newValue, oldValue) => {
if (newValue === undefined) { if (newValue === undefined) {
CoWebsiteManager.closeCoWebsite(); coWebsiteManager.closeCoWebsite();
} else { } else {
CoWebsiteManager.loadCoWebsite(newValue as string); coWebsiteManager.loadCoWebsite(newValue as string);
} }
}); });
this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => { this.gameMap.onPropertyChange('jitsiRoom', (newValue, oldValue, allProps) => {

View File

@ -2,47 +2,90 @@ import {HtmlUtils} from "./HtmlUtils";
export type CoWebsiteStateChangedCallback = () => void; 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<CoWebsiteStateChangedCallback>(); 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 { class CoWebsiteManager {
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite");
private opened: iframeStates = iframeStates.closed;
private observers = new Array<CoWebsiteStateChangedCallback>();
private close(): HTMLDivElement {
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>(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<HTMLDivElement>(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<HTMLDivElement>(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 = ''; cowebsiteDiv.innerHTML = '';
const iframe = document.createElement('iframe'); const iframe = document.createElement('iframe');
iframe.id = 'cowebsite-iframe'; iframe.id = 'cowebsite-iframe';
iframe.src = url; iframe.src = url;
const onloadPromise = new Promise((resolve) => {
iframe.onload = () => resolve();
});
cowebsiteDiv.appendChild(iframe); cowebsiteDiv.appendChild(iframe);
//iframe.onload = () => { const onTimeoutPromise = new Promise((resolve) => {
// onload can be long to trigger. Maybe we should display the website, whatever happens, after 1 second? setTimeout(() => resolve(), 2000);
CoWebsiteManager.fire(); });
//} Promise.race([onloadPromise, onTimeoutPromise]).then(() => {
this.open();
setTimeout(() => {
this.fire();
}, animationTime)
});
} }
/** /**
* Just like loadCoWebsite but the div can be filled by the user. * Just like loadCoWebsite but the div can be filled by the user.
*/ */
public static insertCoWebsite(callback: (cowebsite: HTMLDivElement) => void): void { public insertCoWebsite(callback: (cowebsite: HTMLDivElement) => Promise<void>): void {
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite"); const cowebsiteDiv = this.load();
cowebsiteDiv.innerHTML = ''; callback(cowebsiteDiv).then(() => {
this.open();
callback(cowebsiteDiv); setTimeout(() => {
//iframe.onload = () => { this.fire();
// onload can be long to trigger. Maybe we should display the website, whatever happens, after 1 second? }, animationTime)
CoWebsiteManager.fire(); });
//}
} }
public static closeCoWebsite(): void { public closeCoWebsite(): Promise<void> {
const cowebsiteDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite"); return new Promise((resolve, reject) => {
cowebsiteDiv.innerHTML = ''; const cowebsiteDiv = this.close();
CoWebsiteManager.fire(); this.fire();
setTimeout(() => {
resolve();
setTimeout(() => cowebsiteDiv.innerHTML = '', 500)
}, animationTime)
});
} }
public static getGameSize(): {width: number, height: number} { public getGameSize(): {width: number, height: number} {
const hasChildren = HtmlUtils.getElementByIdOrFail<HTMLDivElement>("cowebsite").children.length > 0; if (this.opened !== iframeStates.opened) {
if (hasChildren === false) {
return { return {
width: window.innerWidth, width: window.innerWidth,
height: window.innerHeight height: window.innerHeight
@ -61,13 +104,15 @@ export class CoWebsiteManager {
} }
} }
public static onStateChange(observer: CoWebsiteStateChangedCallback) { public onStateChange(observer: CoWebsiteStateChangedCallback) {
CoWebsiteManager.observers.push(observer); this.observers.push(observer);
} }
private static fire(): void { private fire(): void {
for (const callback of CoWebsiteManager.observers) { for (const callback of this.observers) {
callback(); callback();
} }
} }
} }
export const coWebsiteManager = new CoWebsiteManager();

View File

@ -1,6 +1,6 @@
import {CoWebsiteManager} from "./CoWebsiteManager";
import {JITSI_URL} from "../Enum/EnvironmentVariable"; import {JITSI_URL} from "../Enum/EnvironmentVariable";
import {mediaManager} from "./MediaManager"; import {mediaManager} from "./MediaManager";
import {coWebsiteManager} from "./CoWebsiteManager";
declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any declare const window:any; // eslint-disable-line @typescript-eslint/no-explicit-any
const interfaceConfig = { const interfaceConfig = {
@ -27,10 +27,13 @@ const interfaceConfig = {
class JitsiFactory { class JitsiFactory {
private jitsiApi: any; // eslint-disable-line @typescript-eslint/no-explicit-any 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 { public start(roomName: string, playerName:string, jwt?: string): void {
CoWebsiteManager.insertCoWebsite((cowebsiteDiv => { coWebsiteManager.insertCoWebsite((cowebsiteDiv => {
const domain = JITSI_URL; const domain = JITSI_URL;
const options = { const options: any = { // eslint-disable-line @typescript-eslint/no-explicit-any
roomName: roomName, roomName: roomName,
jwt: jwt, jwt: jwt,
width: "100%", width: "100%",
@ -46,15 +49,41 @@ class JitsiFactory {
if (!options.jwt) { if (!options.jwt) {
delete options.jwt; delete options.jwt;
} }
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 = new window.JitsiMeetExternalAPI(domain, options);
this.jitsiApi.executeCommand('displayName', playerName); this.jitsiApi.executeCommand('displayName', playerName);
this.jitsiApi.addListener('audioMuteStatusChanged', this.audioCallback);
this.jitsiApi.addListener('videoMuteStatusChanged', this.videoCallback);
});
})); }));
} }
public stop(): void { public async stop(): Promise<void> {
await coWebsiteManager.closeCoWebsite();
this.jitsiApi.removeListener('audioMuteStatusChanged', this.audioCallback);
this.jitsiApi.removeListener('videoMuteStatusChanged', this.videoCallback);
this.jitsiApi?.dispose(); this.jitsiApi?.dispose();
CoWebsiteManager.closeCoWebsite(); }
private onAudioChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.audio === true) {
mediaManager.disableMicrophone();
} else if(!muted && mediaManager.constraintsMedia.audio === false) {
mediaManager.enableMicrophone();
} }
} }
private onVideoChange({muted}: {muted: boolean}): void {
if (muted && mediaManager.constraintsMedia.video !== false) {
mediaManager.disableCamera();
} else if(!muted && mediaManager.constraintsMedia.video === false) {
mediaManager.enableCamera();
}
}
}
export const jitsiFactory = new JitsiFactory(); export const jitsiFactory = new JitsiFactory();

View File

@ -140,7 +140,7 @@ export class MediaManager {
gameOverlay.classList.remove('active'); gameOverlay.classList.remove('active');
} }
private enableCamera() { public enableCamera() {
this.cinemaClose.style.display = "none"; this.cinemaClose.style.display = "none";
this.cinemaBtn.classList.remove("disabled"); this.cinemaBtn.classList.remove("disabled");
this.cinema.style.display = "block"; this.cinema.style.display = "block";
@ -150,7 +150,7 @@ export class MediaManager {
}); });
} }
private async disableCamera() { public async disableCamera() {
this.cinemaClose.style.display = "block"; this.cinemaClose.style.display = "block";
this.cinema.style.display = "none"; this.cinema.style.display = "none";
this.cinemaBtn.classList.add("disabled"); this.cinemaBtn.classList.add("disabled");
@ -166,7 +166,7 @@ export class MediaManager {
} }
} }
private enableMicrophone() { public enableMicrophone() {
this.microphoneClose.style.display = "none"; this.microphoneClose.style.display = "none";
this.microphone.style.display = "block"; this.microphone.style.display = "block";
this.microphoneBtn.classList.remove("disabled"); this.microphoneBtn.classList.remove("disabled");
@ -177,7 +177,7 @@ export class MediaManager {
}); });
} }
private async disableMicrophone() { public async disableMicrophone() {
this.microphoneClose.style.display = "block"; this.microphoneClose.style.display = "block";
this.microphone.style.display = "none"; this.microphone.style.display = "none";
this.microphoneBtn.classList.add("disabled"); this.microphoneBtn.classList.add("disabled");

View File

@ -10,12 +10,9 @@ import {FourOFourScene} from "./Phaser/Reconnecting/FourOFourScene";
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer; import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline"; import {OutlinePipeline} from "./Phaser/Shaders/OutlinePipeline";
import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene";
import {CoWebsiteManager} from "./WebRtc/CoWebsiteManager";
import {gameManager} from "./Phaser/Game/GameManager";
import {ResizableScene} from "./Phaser/Login/ResizableScene"; import {ResizableScene} from "./Phaser/Login/ResizableScene";
import {EntryScene} from "./Phaser/Login/EntryScene"; import {EntryScene} from "./Phaser/Login/EntryScene";
import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
//CoWebsiteManager.loadCoWebsite('https://thecodingmachine.com');
// Load Jitsi if the environment variable is set. // Load Jitsi if the environment variable is set.
if (JITSI_URL) { if (JITSI_URL) {
@ -24,7 +21,7 @@ if (JITSI_URL) {
document.head.appendChild(jitsiScript); document.head.appendChild(jitsiScript);
} }
const {width, height} = CoWebsiteManager.getGameSize(); const {width, height} = coWebsiteManager.getGameSize();
const config: GameConfig = { const config: GameConfig = {
title: "WorkAdventure", title: "WorkAdventure",
@ -53,8 +50,7 @@ cypressAsserter.gameStarted();
const game = new Phaser.Game(config); const game = new Phaser.Game(config);
window.addEventListener('resize', function (event) { window.addEventListener('resize', function (event) {
const {width, height} = CoWebsiteManager.getGameSize(); const {width, height} = coWebsiteManager.getGameSize();
game.scale.resize(width / RESOLUTION, height / RESOLUTION); game.scale.resize(width / RESOLUTION, height / RESOLUTION);
// Let's trigger the onResize method of any active scene that is a ResizableScene // 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(() => { coWebsiteManager.onStateChange(() => {
const {width, height} = CoWebsiteManager.getGameSize(); const {width, height} = coWebsiteManager.getGameSize();
game.scale.resize(width / RESOLUTION, height / RESOLUTION); game.scale.resize(width / RESOLUTION, height / RESOLUTION);
}); });