diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index faf50c7a..ecd7ffef 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -50,6 +50,7 @@ jobs: run: yarn run build env: PUSHER_URL: "//localhost:8080" + ADMIN_URL: "//localhost:80" working-directory: "front" - name: "Svelte check" diff --git a/.github/workflows/push-to-npm.yml b/.github/workflows/push-to-npm.yml index fd247b11..1208e0c0 100644 --- a/.github/workflows/push-to-npm.yml +++ b/.github/workflows/push-to-npm.yml @@ -47,6 +47,7 @@ jobs: run: yarn run build-typings env: PUSHER_URL: "//localhost:8080" + ADMIN_URL: "//localhost:80" working-directory: "front" # We build the front to generate the typings of iframe_api, then we copy those typings in a separate package. diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 178eacc0..9fec90f3 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -790,7 +790,7 @@ export class SocketManager { if (!room) { //todo: this should cause the http call to return a 500 console.error( - "In sendAdminRoomMessage, could not find room with id '" + + "In dispatchWorldFullWarning, could not find room with id '" + roomId + "'. Maybe the room was closed a few milliseconds ago and there was a race condition?" ); diff --git a/front/dist/resources/html/warningContainer.html b/front/dist/resources/html/warningContainer.html deleted file mode 100644 index 832ac4da..00000000 --- a/front/dist/resources/html/warningContainer.html +++ /dev/null @@ -1,18 +0,0 @@ - - -
-

Warning!

-

This world is close to its limit!

-
\ No newline at end of file diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index 0f808074..1e846185 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -27,6 +27,8 @@ import {gameOverlayVisibilityStore} from "../Stores/GameOverlayStoreVisibility"; import {consoleGlobalMessageManagerVisibleStore} from "../Stores/ConsoleGlobalMessageManagerStore"; import ConsoleGlobalMessageManager from "./ConsoleGlobalMessageManager/ConsoleGlobalMessageManager.svelte"; + import {warningContainerStore} from "../Stores/MenuStore"; + import WarningContainer from "./WarningContainer/WarningContainer.svelte"; export let game: Game; @@ -91,4 +93,7 @@ {#if $chatVisibilityStore} {/if} + {#if $warningContainerStore} + + {/if} diff --git a/front/src/Components/WarningContainer/WarningContainer.svelte b/front/src/Components/WarningContainer/WarningContainer.svelte new file mode 100644 index 00000000..57b956e2 --- /dev/null +++ b/front/src/Components/WarningContainer/WarningContainer.svelte @@ -0,0 +1,37 @@ + + +
+

Warning!

+ {#if $userIsAdminStore} +

This world is close to its limit!. You can upgrade its capacity here

+ {:else} +

This world is close to its limit!

+ {/if} + +
+ + \ No newline at end of file diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 521a8473..f37c59d2 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -32,7 +32,8 @@ import { EmotePromptMessage, SendUserMessage, BanUserMessage, - VariableMessage, ErrorMessage, + VariableMessage, + ErrorMessage, } from "../Messages/generated/messages_pb"; import type { UserSimplePeerInterface } from "../WebRtc/SimplePeer"; @@ -54,9 +55,9 @@ import { import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; import { adminMessagesService } from "./AdminMessagesService"; import { worldFullMessageStream } from "./WorldFullMessageStream"; -import { worldFullWarningStream } from "./WorldFullWarningStream"; import { connectionManager } from "./ConnectionManager"; import { emoteEventStream } from "./EmoteEventStream"; +import { warningContainerStore } from "../Stores/MenuStore"; const manualPingDelay = 20000; @@ -167,7 +168,7 @@ export class RoomConnection implements RoomConnection { emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote()); } else if (subMessage.hasErrormessage()) { const errorMessage = subMessage.getErrormessage() as ErrorMessage; - console.error('An error occurred server side: '+errorMessage.getMessage()); + console.error("An error occurred server side: " + errorMessage.getMessage()); } else if (subMessage.hasVariablemessage()) { event = EventMessage.SET_VARIABLE; payload = subMessage.getVariablemessage(); @@ -192,7 +193,14 @@ export class RoomConnection implements RoomConnection { try { variables.set(variable.getName(), JSON.parse(variable.getValue())); } catch (e) { - console.error('Unable to unserialize value received from server for variable "'+variable.getName()+'". Value received: "'+variable.getValue()+'". Error: ', e); + console.error( + 'Unable to unserialize value received from server for variable "' + + variable.getName() + + '". Value received: "' + + variable.getValue() + + '". Error: ', + e + ); } } @@ -236,7 +244,7 @@ export class RoomConnection implements RoomConnection { } else if (message.hasBanusermessage()) { adminMessagesService.onSendusermessage(message.getBanusermessage() as BanUserMessage); } else if (message.hasWorldfullwarningmessage()) { - worldFullWarningStream.onMessage(); + warningContainerStore.activateWarningContainer(); } else if (message.hasRefreshroommessage()) { //todo: implement a way to notify the user the room was refreshed. } else { @@ -659,7 +667,14 @@ export class RoomConnection implements RoomConnection { try { value = JSON.parse(serializedValue); } catch (e) { - console.error('Unable to unserialize value received from server for variable "'+name+'". Value received: "'+serializedValue+'". Error: ', e); + console.error( + 'Unable to unserialize value received from server for variable "' + + name + + '". Value received: "' + + serializedValue + + '". Error: ', + e + ); } } callback(name, value); diff --git a/front/src/Connexion/WorldFullWarningStream.ts b/front/src/Connexion/WorldFullWarningStream.ts deleted file mode 100644 index 5e552830..00000000 --- a/front/src/Connexion/WorldFullWarningStream.ts +++ /dev/null @@ -1,14 +0,0 @@ -import {Subject} from "rxjs"; - -class WorldFullWarningStream { - - private _stream:Subject = new Subject(); - public stream = this._stream.asObservable(); - - - onMessage() { - this._stream.next(); - } -} - -export const worldFullWarningStream = new WorldFullWarningStream(); \ No newline at end of file diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index 73f6427c..b8e0904c 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -1,22 +1,24 @@ const DEBUG_MODE: boolean = process.env.DEBUG_MODE == "true"; -const START_ROOM_URL : string = process.env.START_ROOM_URL || '/_/global/maps.workadventure.localhost/Floor0/floor0.json'; -const PUSHER_URL = process.env.PUSHER_URL || '//pusher.workadventure.localhost'; -const UPLOADER_URL = process.env.UPLOADER_URL || '//uploader.workadventure.localhost'; +const START_ROOM_URL: string = + process.env.START_ROOM_URL || "/_/global/maps.workadventure.localhost/Floor0/floor0.json"; +const PUSHER_URL = process.env.PUSHER_URL || "//pusher.workadventure.localhost"; +export const ADMIN_URL = process.env.ADMIN_URL || "//workadventu.re"; +const UPLOADER_URL = process.env.UPLOADER_URL || "//uploader.workadventure.localhost"; const STUN_SERVER: string = process.env.STUN_SERVER || "stun:stun.l.google.com:19302"; const TURN_SERVER: string = process.env.TURN_SERVER || ""; const SKIP_RENDER_OPTIMIZATIONS: boolean = process.env.SKIP_RENDER_OPTIMIZATIONS == "true"; const DISABLE_NOTIFICATIONS: boolean = process.env.DISABLE_NOTIFICATIONS == "true"; -const TURN_USER: string = process.env.TURN_USER || ''; -const TURN_PASSWORD: string = process.env.TURN_PASSWORD || ''; -const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined : process.env.JITSI_URL; -const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true"; +const TURN_USER: string = process.env.TURN_USER || ""; +const TURN_PASSWORD: string = process.env.TURN_PASSWORD || ""; +const JITSI_URL: string | undefined = process.env.JITSI_URL === "" ? undefined : process.env.JITSI_URL; +const JITSI_PRIVATE_MODE: boolean = process.env.JITSI_PRIVATE_MODE == "true"; const POSITION_DELAY = 200; // Wait 200ms between sending position events const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player -export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8; -export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4'); -export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == 'true'; +export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || "") || 8; +export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || "4"); +export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == "true"; -export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window.innerHeight <= 600 ) ); +export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600; export { DEBUG_MODE, @@ -32,5 +34,5 @@ export { TURN_USER, TURN_PASSWORD, JITSI_URL, - JITSI_PRIVATE_MODE -} + JITSI_PRIVATE_MODE, +}; diff --git a/front/src/Phaser/Components/WarningContainer.ts b/front/src/Phaser/Components/WarningContainer.ts deleted file mode 100644 index 97e97660..00000000 --- a/front/src/Phaser/Components/WarningContainer.ts +++ /dev/null @@ -1,14 +0,0 @@ - -export const warningContainerKey = 'warningContainer'; -export const warningContainerHtml = 'resources/html/warningContainer.html'; - -export class WarningContainer extends Phaser.GameObjects.DOMElement { - - constructor(scene: Phaser.Scene) { - super(scene, 100, 0); - this.setOrigin(0, 0); - this.createFromCache(warningContainerKey); - this.scene.add.existing(this); - } - -} \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index e7738265..31acb83a 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -88,6 +88,7 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor import { SharedVariablesManager } from "./SharedVariablesManager"; import { playersStore } from "../../Stores/PlayersStore"; import { chatVisibilityStore } from "../../Stores/ChatStore"; +import { userIsAdminStore } from "../../Stores/GameStore"; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -604,6 +605,8 @@ export class GameScene extends DirtyScene { playersStore.connectToRoomConnection(this.connection); + userIsAdminStore.set(this.connection.hasTag("admin")); + this.connection.onUserJoins((message: MessageUserJoined) => { const userMessage: AddPlayerInterface = { userId: message.userId, diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index 4e9297b6..d41a5033 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -6,8 +6,6 @@ import { localUserStore } from "../../Connexion/LocalUserStore"; import { gameReportKey, gameReportRessource, ReportMenu } from "./ReportMenu"; import { connectionManager } from "../../Connexion/ConnectionManager"; import { GameConnexionTypes } from "../../Url/UrlManager"; -import { WarningContainer, warningContainerHtml, warningContainerKey } from "../Components/WarningContainer"; -import { worldFullWarningStream } from "../../Connexion/WorldFullWarningStream"; import { menuIconVisible } from "../../Stores/MenuStore"; import { videoConstraintStore } from "../../Stores/MediaStore"; import { showReportScreenStore } from "../../Stores/ShowReportScreenStore"; @@ -21,6 +19,7 @@ import { get } from "svelte/store"; import { playersStore } from "../../Stores/PlayersStore"; import { mediaManager } from "../../WebRtc/MediaManager"; import { chatVisibilityStore } from "../../Stores/ChatStore"; +import { ADMIN_URL } from "../../Enum/EnvironmentVariable"; export const MenuSceneName = "MenuScene"; const gameMenuKey = "gameMenu"; @@ -45,8 +44,6 @@ export class MenuScene extends Phaser.Scene { private gameQualityValue: number; private videoQualityValue: number; private menuButton!: Phaser.GameObjects.DOMElement; - private warningContainer: WarningContainer | null = null; - private warningContainerTimeout: NodeJS.Timeout | null = null; private subscriptions = new Subscription(); constructor() { super({ key: MenuSceneName }); @@ -91,7 +88,6 @@ export class MenuScene extends Phaser.Scene { this.load.html(gameSettingsMenuKey, "resources/html/gameQualityMenu.html"); this.load.html(gameShare, "resources/html/gameShare.html"); this.load.html(gameReportKey, gameReportRessource); - this.load.html(warningContainerKey, warningContainerHtml); } create() { @@ -147,7 +143,6 @@ export class MenuScene extends Phaser.Scene { this.menuElement.addListener("click"); this.menuElement.on("click", this.onMenuClick.bind(this)); - worldFullWarningStream.stream.subscribe(() => this.showWorldCapacityWarning()); chatVisibilityStore.subscribe((v) => { this.menuButton.setVisible(!v); }); @@ -194,20 +189,6 @@ export class MenuScene extends Phaser.Scene { }); } - private showWorldCapacityWarning() { - if (!this.warningContainer) { - this.warningContainer = new WarningContainer(this); - } - if (this.warningContainerTimeout) { - clearTimeout(this.warningContainerTimeout); - } - this.warningContainerTimeout = setTimeout(() => { - this.warningContainer?.destroy(); - this.warningContainer = null; - this.warningContainerTimeout = null; - }, 120000); - } - private closeSideMenu(): void { if (!this.sideMenuOpened) return; this.sideMenuOpened = false; @@ -403,7 +384,7 @@ export class MenuScene extends Phaser.Scene { private gotToCreateMapPage() { //const sparkHost = 'https://'+window.location.host.replace('play.', '')+'/choose-map.html'; //TODO fix me: this button can to send us on WorkAdventure BO. - const sparkHost = "https://workadventu.re/getting-started"; + const sparkHost = ADMIN_URL + "/getting-started"; window.open(sparkHost, "_blank"); } diff --git a/front/src/Stores/GameStore.ts b/front/src/Stores/GameStore.ts index 8899aa12..eada6d26 100644 --- a/front/src/Stores/GameStore.ts +++ b/front/src/Stores/GameStore.ts @@ -2,4 +2,6 @@ import { writable } from "svelte/store"; export const userMovingStore = writable(false); -export const requestVisitCardsStore = writable(null); +export const requestVisitCardsStore = writable(null); + +export const userIsAdminStore = writable(false); diff --git a/front/src/Stores/MenuStore.ts b/front/src/Stores/MenuStore.ts index c7c02130..084e8ce8 100644 --- a/front/src/Stores/MenuStore.ts +++ b/front/src/Stores/MenuStore.ts @@ -1,3 +1,23 @@ -import { derived, writable, Writable } from "svelte/store"; +import { writable } from "svelte/store"; +import Timeout = NodeJS.Timeout; export const menuIconVisible = writable(false); + +let warningContainerTimeout: Timeout | null = null; +function createWarningContainerStore() { + const { subscribe, set } = writable(false); + + return { + subscribe, + activateWarningContainer() { + set(true); + if (warningContainerTimeout) clearTimeout(warningContainerTimeout); + warningContainerTimeout = setTimeout(() => { + set(false); + warningContainerTimeout = null; + }, 120000); + }, + }; +} + +export const warningContainerStore = createWarningContainerStore(); diff --git a/front/webpack.config.ts b/front/webpack.config.ts index 37362baf..13647b30 100644 --- a/front/webpack.config.ts +++ b/front/webpack.config.ts @@ -189,7 +189,7 @@ module.exports = { DISABLE_NOTIFICATIONS: false, PUSHER_URL: undefined, UPLOADER_URL: null, - ADMIN_URL: null, + ADMIN_URL: undefined, DEBUG_MODE: null, STUN_SERVER: null, TURN_SERVER: null,