From a5d4d163e18d2d9f36082e938df7674b4afeb2e5 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Fri, 10 Dec 2021 01:51:40 +0100 Subject: [PATCH 01/46] Add test mode `*` char permit to defined test mode with warning message Signed-off-by: Gregoire Parant --- .../WarningContainer/WarningContainer.svelte | 9 ++++++++- front/src/Connexion/ConnectionManager.ts | 10 +++++++++- front/src/Connexion/Room.ts | 4 ++-- front/src/Stores/GameStore.ts | 2 ++ front/src/Url/UrlManager.ts | 3 +++ 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/front/src/Components/WarningContainer/WarningContainer.svelte b/front/src/Components/WarningContainer/WarningContainer.svelte index dd61d8fc..2f832fbd 100644 --- a/front/src/Components/WarningContainer/WarningContainer.svelte +++ b/front/src/Components/WarningContainer/WarningContainer.svelte @@ -1,9 +1,10 @@
@@ -14,6 +15,12 @@ >here

+ {:else if $limitMap} +

+ Your are une test mode. This map will be opened during 2 days. You can register your domain here! +

{:else}

This world is close to its limit!

{/if} diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 05d84367..aae3f386 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -8,9 +8,10 @@ import { CharacterTexture, LocalUser } from "./LocalUser"; import { Room } from "./Room"; import { _ServiceWorker } from "../Network/ServiceWorker"; import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore"; -import { userIsConnected } from "../Stores/MenuStore"; +import { userIsConnected, warningContainerStore } from "../Stores/MenuStore"; import { analyticsClient } from "../Administration/AnalyticsClient"; import { axiosWithRetry } from "./AxiosUtils"; +import { limitMap } from "../Stores/GameStore"; class ConnectionManager { private localUser!: LocalUser; @@ -148,6 +149,7 @@ class ConnectionManager { } else if ( connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || + connexionType === GameConnexionTypes.limit || connexionType === GameConnexionTypes.empty ) { this.authToken = localUserStore.getAuthToken(); @@ -228,6 +230,12 @@ class ConnectionManager { analyticsClient.identifyUser(this.localUser.uuid, this.localUser.email); } + //if limit room active test headband + if (connexionType === GameConnexionTypes.limit) { + warningContainerStore.activateWarningContainer(); + limitMap.set(true); + } + this.serviceWorker = new _ServiceWorker(); return Promise.resolve(this._currentRoom); } diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 044d8d67..3e36f84c 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -32,7 +32,7 @@ export class Room { if (this.id.startsWith("/")) { this.id = this.id.substr(1); } - if (this.id.startsWith("_/")) { + if (this.id.startsWith("_/") || this.id.startsWith("*/")) { this.isPublic = true; } else if (this.id.startsWith("@/")) { this.isPublic = false; @@ -138,7 +138,7 @@ export class Room { } if (this.isPublic) { - const match = /_\/([^/]+)\/.+/.exec(this.id); + const match = /[_*]\/([^/]+)\/.+/.exec(this.id); if (!match) throw new Error('Could not extract instance from "' + this.id + '"'); this.instance = match[1]; return this.instance; diff --git a/front/src/Stores/GameStore.ts b/front/src/Stores/GameStore.ts index eada6d26..1ad975ee 100644 --- a/front/src/Stores/GameStore.ts +++ b/front/src/Stores/GameStore.ts @@ -5,3 +5,5 @@ export const userMovingStore = writable(false); export const requestVisitCardsStore = writable(null); export const userIsAdminStore = writable(false); + +export const limitMap = writable(false); diff --git a/front/src/Url/UrlManager.ts b/front/src/Url/UrlManager.ts index 50dbedc9..8a39354d 100644 --- a/front/src/Url/UrlManager.ts +++ b/front/src/Url/UrlManager.ts @@ -9,6 +9,7 @@ export enum GameConnexionTypes { unknown, jwt, login, + limit, } //this class is responsible with analysing and editing the game's url @@ -19,6 +20,8 @@ class UrlManager { return GameConnexionTypes.login; } else if (url === "/jwt") { return GameConnexionTypes.jwt; + } else if (url.includes("*/")) { + return GameConnexionTypes.limit; } else if (url.includes("_/")) { return GameConnexionTypes.anonymous; } else if (url.includes("@/")) { From 9357afd13f9a63554bf191e16b1d0ff8b408bf2a Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Fri, 10 Dec 2021 01:54:48 +0100 Subject: [PATCH 02/46] Update warning container Signed-off-by: Gregoire Parant --- front/src/Components/WarningContainer/WarningContainer.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/src/Components/WarningContainer/WarningContainer.svelte b/front/src/Components/WarningContainer/WarningContainer.svelte index 2f832fbd..29a6740f 100644 --- a/front/src/Components/WarningContainer/WarningContainer.svelte +++ b/front/src/Components/WarningContainer/WarningContainer.svelte @@ -8,8 +8,8 @@
-

Warning!

{#if $userIsAdminStore} +

Warning!

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

{:else} +

Warning!

This world is close to its limit!

{/if}
From fd64fc43a4c062e529bbde8559bf6791cc5a4ba1 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Thu, 23 Dec 2021 13:09:28 +0100 Subject: [PATCH 03/46] Finish 2 days room limit - Create modal to register when limit is past - Create modal to share the link - Use UrlManager to check if limit room is active Signed-off-by: Gregoire Parant --- front/src/Components/App.svelte | 13 +++ front/src/Components/Menu/GuestSubMenu.svelte | 4 +- front/src/Components/Menu/MenuIcon.svelte | 21 +++- .../Components/Modal/LimitRoomModal.svelte | 45 +++++++++ .../Components/Modal/ShareLinkMapModal.svelte | 90 ++++++++++++++++++ .../WarningContainer/WarningContainer.svelte | 7 +- .../Components/images/logo-invite-pixel.png | Bin 0 -> 1104 bytes .../Components/images/logo-register-pixel.png | Bin 0 -> 977 bytes front/src/Connexion/ConnectionManager.ts | 10 +- front/src/Stores/GameStore.ts | 2 +- front/src/Stores/ModalStore.ts | 4 + front/src/Url/UrlManager.ts | 9 ++ 12 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 front/src/Components/Modal/LimitRoomModal.svelte create mode 100644 front/src/Components/Modal/ShareLinkMapModal.svelte create mode 100644 front/src/Components/images/logo-invite-pixel.png create mode 100644 front/src/Components/images/logo-register-pixel.png create mode 100644 front/src/Stores/ModalStore.ts diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index 4886cc4e..cee819e1 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -23,6 +23,9 @@ import { chatVisibilityStore } from "../Stores/ChatStore"; import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore"; import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte"; + import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore"; + import LimitRoomModal from "./Modal/LimitRoomModal.svelte"; + import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte"; import AudioPlaying from "./UI/AudioPlaying.svelte"; import { soundPlayingStore } from "../Stores/SoundPlayingStore"; import ErrorDialog from "./UI/ErrorDialog.svelte"; @@ -129,6 +132,16 @@ {/if} + {#if $showLimitRoomModalStore} +
+ +
+ {/if} + {#if $showShareLinkMapModalStore} +
+ +
+ {/if} {#if $requestVisitCardsStore} {/if} diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte index 0ae25b75..408dcbce 100644 --- a/front/src/Components/Menu/GuestSubMenu.svelte +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -21,12 +21,12 @@
-

Share the link of the room !

+

Share the link of the room!

diff --git a/front/src/Components/Menu/MenuIcon.svelte b/front/src/Components/Menu/MenuIcon.svelte index bb5a2df2..90248115 100644 --- a/front/src/Components/Menu/MenuIcon.svelte +++ b/front/src/Components/Menu/MenuIcon.svelte @@ -1,9 +1,14 @@
- open menu - open menu + {#if $limitMapStore} + open menu + open menu + {:else} + open menu + open menu + {/if}
diff --git a/front/src/Components/Modal/ShareLinkMapModal.svelte b/front/src/Components/Modal/ShareLinkMapModal.svelte new file mode 100644 index 00000000..5f84e791 --- /dev/null +++ b/front/src/Components/Modal/ShareLinkMapModal.svelte @@ -0,0 +1,90 @@ + + + + + diff --git a/front/src/Components/WarningContainer/WarningContainer.svelte b/front/src/Components/WarningContainer/WarningContainer.svelte index 29a6740f..1c5f7793 100644 --- a/front/src/Components/WarningContainer/WarningContainer.svelte +++ b/front/src/Components/WarningContainer/WarningContainer.svelte @@ -1,6 +1,6 @@ -
+

Limit of your room

Register your account!

-

This map is limited in the time and to continue to use WorkAdventure, you must register your account in our back office.

+

+ This map is limited in the time and to continue to use WorkAdventure, you must register your account in our + back office. +

@@ -20,26 +22,26 @@
diff --git a/front/src/Components/Modal/ShareLinkMapModal.svelte b/front/src/Components/Modal/ShareLinkMapModal.svelte index 25cde5bd..42ac1294 100644 --- a/front/src/Components/Modal/ShareLinkMapModal.svelte +++ b/front/src/Components/Modal/ShareLinkMapModal.svelte @@ -1,15 +1,16 @@ -
From c55cad6227468285baf069894e82acb4f30eb38c Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Fri, 31 Dec 2021 15:27:08 +0100 Subject: [PATCH 08/46] Disable user selection of UI elements --- front/src/Components/Chat/ChatElement.svelte | 1 + front/style/style.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/front/src/Components/Chat/ChatElement.svelte b/front/src/Components/Chat/ChatElement.svelte index 231e7260..4ac9c275 100644 --- a/front/src/Components/Chat/ChatElement.svelte +++ b/front/src/Components/Chat/ChatElement.svelte @@ -67,6 +67,7 @@ .messagePart { flex-grow: 1; max-width: 100%; + user-select: text; span.date { font-size: 80%; diff --git a/front/style/style.scss b/front/style/style.scss index 89437a99..32d95f57 100644 --- a/front/style/style.scss +++ b/front/style/style.scss @@ -1066,6 +1066,7 @@ div.action.danger p.action-body{ width: 100%; height: 100%; pointer-events: none; + user-select: none; & > div { position: relative; From 5460bd49be1f53a9b26d1d72abe3390d6da04447 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Mon, 27 Dec 2021 15:55:00 +0100 Subject: [PATCH 09/46] Fix follow requests via keyboard --- .../Components/FollowMenu/FollowMenu.svelte | 31 ++++++------------- front/src/Phaser/Player/Player.ts | 18 +++++++---- 2 files changed, 22 insertions(+), 27 deletions(-) diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 264b27ed..aaa23b3e 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -14,14 +14,11 @@ vim: ft=typescript } function sendFollowRequest() { - gameScene.connection?.emitFollowRequest(); - followRoleStore.set("leader"); - followStateStore.set("active"); + gameScene.CurrentPlayer.sendFollowRequest(); } function acceptFollowRequest() { - gameScene.CurrentPlayer.enableFollowing(); - gameScene.connection?.emitFollowConfirmation(); + gameScene.CurrentPlayer.startFollowing(); } function abortEnding() { @@ -42,23 +39,15 @@ vim: ft=typescript -{#if $followStateStore === "requesting"} +{#if $followStateStore === "requesting" && $followRoleStore === "follower"}
- {#if $followRoleStore === "follower"} -
-

Do you want to follow {name($followUsersStore[0])}?

-
-
- - -
- {:else if $followRoleStore === "leader"} -
-

Should never be displayed

-
- {/if} +
+

Do you want to follow {name($followUsersStore[0])}?

+
+
+ + +
{/if} diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 946bb6c4..e41b3237 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -100,10 +100,6 @@ export class Player extends Character { return [xMovement, yMovement]; } - public enableFollowing() { - followStateStore.set("active"); - } - public moveUser(delta: number): void { const activeEvents = this.userInputManager.getEventListForGameTick(); const state = get(followStateStore); @@ -111,8 +107,7 @@ export class Player extends Character { if (activeEvents.get(UserInputEvent.Follow)) { if (state === "off" && this.scene.groups.size > 0) { - followStateStore.set("requesting"); - followRoleStore.set("leader"); + this.sendFollowRequest(); } else if (state === "active") { followStateStore.set("ending"); } @@ -125,4 +120,15 @@ export class Player extends Character { } this.inputStep(activeEvents, x, y); } + + public sendFollowRequest() { + this.scene.connection?.emitFollowRequest(); + followRoleStore.set("leader"); + followStateStore.set("active"); + } + + public startFollowing() { + followStateStore.set("active"); + this.scene.connection?.emitFollowConfirmation(); + } } From bda46be466463a2888885abe37bbb0276a1c4fec Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 28 Dec 2021 17:41:35 +0100 Subject: [PATCH 10/46] Remove left-over debug message --- front/src/Stores/FollowStore.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/front/src/Stores/FollowStore.ts b/front/src/Stores/FollowStore.ts index ab1e61d1..eb9753d9 100644 --- a/front/src/Stores/FollowStore.ts +++ b/front/src/Stores/FollowStore.ts @@ -58,7 +58,6 @@ export const followUsersStore = createFollowUsersStore(); export const followUsersColorStore = derived( [followStateStore, followRoleStore, followUsersStore], ([$followStateStore, $followRoleStore, $followUsersStore]) => { - console.log($followStateStore); if ($followStateStore !== "active") { return undefined; } From 8cdbf382ad97910deac997f3f6921f697b3c9768 Mon Sep 17 00:00:00 2001 From: jonnytest1 Date: Fri, 31 Dec 2021 15:55:17 +0100 Subject: [PATCH 11/46] added columns type definition --- front/src/Phaser/Map/ITiledMap.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/Phaser/Map/ITiledMap.ts b/front/src/Phaser/Map/ITiledMap.ts index 57bb13c9..74810df4 100644 --- a/front/src/Phaser/Map/ITiledMap.ts +++ b/front/src/Phaser/Map/ITiledMap.ts @@ -162,6 +162,7 @@ export interface ITiledTileSet { imageheight: number; imagewidth: number; + columns: number; margin: number; name: string; properties?: ITiledMapProperty[]; From 0603d0857237deb2193a9424f3421669cfebd347 Mon Sep 17 00:00:00 2001 From: Tobias Tefke Date: Mon, 3 Jan 2022 09:07:09 +0100 Subject: [PATCH 12/46] ScaleManager: do not apply new size if width is zero If the game width reaches zero (e.g by resizing a cowebsite manually), a division by zero happens when calculating the new zoom. This results in NaN, which causes phaser to reach an invalid state when rezooming. --- front/src/Phaser/Services/WaScaleManager.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/front/src/Phaser/Services/WaScaleManager.ts b/front/src/Phaser/Services/WaScaleManager.ts index 447b6a1f..c0c01402 100644 --- a/front/src/Phaser/Services/WaScaleManager.ts +++ b/front/src/Phaser/Services/WaScaleManager.ts @@ -31,6 +31,10 @@ export class WaScaleManager { height: height * devicePixelRatio, }); + if (gameSize.width == 0) { + return; + } + this.actualZoom = realSize.width / gameSize.width / devicePixelRatio; this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio); From e025c1dc8e8587fe13c403f63fe80e01a1135170 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Fri, 15 Oct 2021 17:01:38 +0200 Subject: [PATCH 13/46] Allows to read and write "Player properties" from LocalStorage --- front/src/Api/Events/IframeEvent.ts | 11 ++++++++++- front/src/Api/Events/PlayerPropertyEvent.ts | 13 +++++++++++++ front/src/Api/iframe/player.ts | 15 +++++++++++++++ front/src/Connexion/LocalUserStore.ts | 9 ++++++++- front/src/Phaser/Game/GameScene.ts | 13 +++++++++++++ 5 files changed, 59 insertions(+), 2 deletions(-) create mode 100644 front/src/Api/Events/PlayerPropertyEvent.ts diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 2871b93c..6b57bb48 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -28,6 +28,7 @@ import type { MessageReferenceEvent } from "./ui/TriggerActionMessageEvent"; import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent"; import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent"; import type { ChangeLayerEvent } from "./ChangeLayerEvent"; +import { isPlayerPropertyEvent } from "./PlayerPropertyEvent"; import type { ChangeZoneEvent } from "./ChangeZoneEvent"; import { isColorEvent } from "./ColorEvent"; @@ -61,7 +62,7 @@ export type IframeEventMap = { registerMenu: MenuRegisterEvent; unregisterMenu: UnregisterMenuEvent; setTiles: SetTilesEvent; - modifyEmbeddedWebsite: Partial; // Note: name should be compulsory in fact + modifyEmbeddedWebsite: Partial; // Note: name should be compulsory in fact; }; export interface IframeEvent { type: T; @@ -153,6 +154,14 @@ export const iframeQueryMapTypeGuards = { query: isCreateEmbeddedWebsiteEvent, answer: tg.isUndefined, }, + getPlayerProperty: { + query: tg.isString, + answer: isPlayerPropertyEvent, + }, + setPlayerProperty: { + query: isPlayerPropertyEvent, + answer: tg.isUndefined, + }, setPlayerOutline: { query: isColorEvent, answer: tg.isUndefined, diff --git a/front/src/Api/Events/PlayerPropertyEvent.ts b/front/src/Api/Events/PlayerPropertyEvent.ts new file mode 100644 index 00000000..fe85d9ea --- /dev/null +++ b/front/src/Api/Events/PlayerPropertyEvent.ts @@ -0,0 +1,13 @@ +import * as tg from "generic-type-guard"; + +export const isPlayerPropertyEvent = new tg.IsInterface() + .withProperties({ + propertyName: tg.isString, + propertyValue: tg.isUnknown, + }) + .get(); + +/** + * A message sent from the iFrame to set player-related properties. + */ +export type PlayerPropertyEvent = tg.GuardedType; diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index 2d187bf5..d74c4aa3 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -3,6 +3,7 @@ import type { HasPlayerMovedEvent, HasPlayerMovedEventCallback } from "../Events import { Subject } from "rxjs"; import { apiCallback } from "./registeredCallbacks"; import { isHasPlayerMovedEvent } from "../Events/HasPlayerMovedEvent"; +import type { PlayerPropertyEvent } from "../Events/PlayerPropertyEvent"; const moveStream = new Subject(); @@ -100,6 +101,20 @@ export class WorkadventurePlayerCommands extends IframeApiContribution { + return queryWorkadventure({ + type: "getPlayerProperty", + data: name, + }); + } + + setPlayerProperty(property: PlayerPropertyEvent) { + queryWorkadventure({ + type: "setPlayerProperty", + data: property, + }).catch((e) => console.error(e)); + } } export default new WorkadventurePlayerCommands(); diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 4dce6924..4e445aa8 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -22,8 +22,8 @@ const nonce = "nonce"; const notification = "notificationPermission"; const code = "code"; const cameraSetup = "cameraSetup"; - const cacheAPIIndex = "workavdenture-cache"; +const userProperties = "user-properties"; class LocalUserStore { saveUser(localUser: LocalUser) { @@ -220,6 +220,13 @@ class LocalUserStore { const cameraSetupValues = localStorage.getItem(cameraSetup); return cameraSetupValues != undefined ? JSON.parse(cameraSetupValues) : undefined; } + getUserProperty(name: string): string | null { + return localStorage.getItem(userProperties + "_" + name); + } + + setUserProperty(name: string, value: string): void { + localStorage.setItem(userProperties + "_" + name, value); + } } export const localUserStore = new LocalUserStore(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 4800e259..4bb08faa 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1223,6 +1223,19 @@ ${escapedMessage} }; }); + //TODO : move Player Properties related-code + iframeListener.registerAnswerer("setPlayerProperty", (event) => { + localUserStore.setUserProperty(event.propertyName, event.propertyValue as string); + }); + + iframeListener.registerAnswerer("getPlayerProperty", (event) => { + return { + propertyName: event, + propertyValue: localUserStore.getUserProperty(event), + }; + }); + //END TODO + iframeListener.registerAnswerer("getState", async () => { // The sharedVariablesManager is not instantiated before the connection is established. So we need to wait // for the connection to send back the answer. From 3490daed6b0c594fda0b3972bd1e508bcd51ab16 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Mon, 25 Oct 2021 14:43:36 +0200 Subject: [PATCH 14/46] Creates player state and uses it to get and set values from local storage --- front/src/Api/Events/GameStateEvent.ts | 1 + front/src/Api/Events/PlayerPropertyEvent.ts | 13 -- front/src/Api/Events/SetVariableEvent.ts | 1 + front/src/Api/iframe/player.ts | 4 +- front/src/Api/iframe/state.ts | 118 ++++++++++-------- front/src/Connexion/LocalUserStore.ts | 22 +++- front/src/Phaser/Game/GameScene.ts | 31 +++-- .../src/Phaser/Game/SharedVariablesManager.ts | 75 ++++++----- front/src/iframe_api.ts | 25 ++-- 9 files changed, 153 insertions(+), 137 deletions(-) delete mode 100644 front/src/Api/Events/PlayerPropertyEvent.ts diff --git a/front/src/Api/Events/GameStateEvent.ts b/front/src/Api/Events/GameStateEvent.ts index 1f0f36ed..9755ba9e 100644 --- a/front/src/Api/Events/GameStateEvent.ts +++ b/front/src/Api/Events/GameStateEvent.ts @@ -10,6 +10,7 @@ export const isGameStateEvent = new tg.IsInterface() tags: tg.isArray(tg.isString), variables: tg.isObject, userRoomToken: tg.isUnion(tg.isString, tg.isUndefined), + playerVariables: tg.isObject, }) .get(); /** diff --git a/front/src/Api/Events/PlayerPropertyEvent.ts b/front/src/Api/Events/PlayerPropertyEvent.ts deleted file mode 100644 index fe85d9ea..00000000 --- a/front/src/Api/Events/PlayerPropertyEvent.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as tg from "generic-type-guard"; - -export const isPlayerPropertyEvent = new tg.IsInterface() - .withProperties({ - propertyName: tg.isString, - propertyValue: tg.isUnknown, - }) - .get(); - -/** - * A message sent from the iFrame to set player-related properties. - */ -export type PlayerPropertyEvent = tg.GuardedType; diff --git a/front/src/Api/Events/SetVariableEvent.ts b/front/src/Api/Events/SetVariableEvent.ts index 3e2303b3..80ac6f6e 100644 --- a/front/src/Api/Events/SetVariableEvent.ts +++ b/front/src/Api/Events/SetVariableEvent.ts @@ -4,6 +4,7 @@ export const isSetVariableEvent = new tg.IsInterface() .withProperties({ key: tg.isString, value: tg.isUnknown, + target: tg.isSingletonStringUnion("global", "player"), }) .get(); /** diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index d74c4aa3..ce865642 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -3,7 +3,7 @@ import type { HasPlayerMovedEvent, HasPlayerMovedEventCallback } from "../Events import { Subject } from "rxjs"; import { apiCallback } from "./registeredCallbacks"; import { isHasPlayerMovedEvent } from "../Events/HasPlayerMovedEvent"; -import type { PlayerPropertyEvent } from "../Events/PlayerPropertyEvent"; +import { createState } from "./state"; const moveStream = new Subject(); @@ -32,6 +32,8 @@ export const setUuid = (_uuid: string | undefined) => { }; export class WorkadventurePlayerCommands extends IframeApiContribution { + readonly state = createState("player"); + callbacks = [ apiCallback({ type: "hasPlayerMoved", diff --git a/front/src/Api/iframe/state.ts b/front/src/Api/iframe/state.ts index a875f3e0..7021b251 100644 --- a/front/src/Api/iframe/state.ts +++ b/front/src/Api/iframe/state.ts @@ -8,93 +8,101 @@ import { isSetVariableEvent, SetVariableEvent } from "../Events/SetVariableEvent import type { ITiledMap } from "../../Phaser/Map/ITiledMap"; -const setVariableResolvers = new Subject(); -const variables = new Map(); -const variableSubscribers = new Map>(); - -export const initVariables = (_variables: Map): void => { - for (const [name, value] of _variables.entries()) { - // In case the user already decided to put values in the variables (before onInit), let's make sure onInit does not override this. - if (!variables.has(name)) { - variables.set(name, value); - } - } -}; - -setVariableResolvers.subscribe((event) => { - const oldValue = variables.get(event.key); - // If we are setting the same value, no need to do anything. - // No need to do this check since it is already performed in SharedVariablesManager - /*if (JSON.stringify(oldValue) === JSON.stringify(event.value)) { - return; - }*/ - - variables.set(event.key, event.value); - const subject = variableSubscribers.get(event.key); - if (subject !== undefined) { - subject.next(event.value); - } -}); - export class WorkadventureStateCommands extends IframeApiContribution { + private setVariableResolvers = new Subject(); + private variables = new Map(); + private variableSubscribers = new Map>(); + + constructor(private target: "global" | "player") { + super(); + + this.setVariableResolvers.subscribe((event) => { + const oldValue = this.variables.get(event.key); + // If we are setting the same value, no need to do anything. + // No need to do this check since it is already performed in SharedVariablesManager + /*if (JSON.stringify(oldValue) === JSON.stringify(event.value)) { + return; + }*/ + + this.variables.set(event.key, event.value); + const subject = this.variableSubscribers.get(event.key); + if (subject !== undefined) { + subject.next(event.value); + } + }); + } + callbacks = [ apiCallback({ type: "setVariable", typeChecker: isSetVariableEvent, callback: (payloadData) => { - setVariableResolvers.next(payloadData); + if (payloadData.target === this.target) { + this.setVariableResolvers.next(payloadData); + } }, }), ]; + // TODO: see how we can remove this method from types exposed to WA.state object + initVariables(_variables: Map): void { + for (const [name, value] of _variables.entries()) { + // In case the user already decided to put values in the variables (before onInit), let's make sure onInit does not override this. + if (!this.variables.has(name)) { + this.variables.set(name, value); + } + } + } + saveVariable(key: string, value: unknown): Promise { - variables.set(key, value); + this.variables.set(key, value); return queryWorkadventure({ type: "setVariable", data: { key, value, + target: this.target, }, }); } loadVariable(key: string): unknown { - return variables.get(key); + return this.variables.get(key); } hasVariable(key: string): boolean { - return variables.has(key); + return this.variables.has(key); } onVariableChange(key: string): Observable { - let subject = variableSubscribers.get(key); + let subject = this.variableSubscribers.get(key); if (subject === undefined) { subject = new Subject(); - variableSubscribers.set(key, subject); + this.variableSubscribers.set(key, subject); } return subject.asObservable(); } } -const proxyCommand = new Proxy(new WorkadventureStateCommands(), { - get(target: WorkadventureStateCommands, p: PropertyKey, receiver: unknown): unknown { - if (p in target) { - return Reflect.get(target, p, receiver); - } - return target.loadVariable(p.toString()); - }, - set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean { - // Note: when using "set", there is no way to wait, so we ignore the return of the promise. - // User must use WA.state.saveVariable to have error message. - target.saveVariable(p.toString(), value); - return true; - }, - has(target: WorkadventureStateCommands, p: PropertyKey): boolean { - if (p in target) { +export function createState(target: "global" | "player"): WorkadventureStateCommands & { [key: string]: unknown } { + return new Proxy(new WorkadventureStateCommands(target), { + get(target: WorkadventureStateCommands, p: PropertyKey, receiver: unknown): unknown { + if (p in target) { + return Reflect.get(target, p, receiver); + } + return target.loadVariable(p.toString()); + }, + set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean { + // Note: when using "set", there is no way to wait, so we ignore the return of the promise. + // User must use WA.state.saveVariable to have error message. + target.saveVariable(p.toString(), value); return true; - } - return target.hasVariable(p.toString()); - }, -}) as WorkadventureStateCommands & { [key: string]: unknown }; - -export default proxyCommand; + }, + has(target: WorkadventureStateCommands, p: PropertyKey): boolean { + if (p in target) { + return true; + } + return target.hasVariable(p.toString()); + }, + }) as WorkadventureStateCommands & { [key: string]: unknown }; +} diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 4e445aa8..4f03a546 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -220,12 +220,26 @@ class LocalUserStore { const cameraSetupValues = localStorage.getItem(cameraSetup); return cameraSetupValues != undefined ? JSON.parse(cameraSetupValues) : undefined; } - getUserProperty(name: string): string | null { - return localStorage.getItem(userProperties + "_" + name); + + getAllUserProperties(): Map { + const result = new Map(); + for (let i = 0; i < localStorage.length; i++) { + const key = localStorage.key(i); + if (key) { + if (key.startsWith(userProperties + "_")) { + const value = localStorage.getItem(key); + if (value) { + const userKey = key.substr((userProperties + "_").length); + result.set(userKey, JSON.parse(value)); + } + } + } + } + return result; } - setUserProperty(name: string, value: string): void { - localStorage.setItem(userProperties + "_" + name, value); + setUserProperty(name: string, value: unknown): void { + localStorage.setItem(userProperties + "_" + name, JSON.stringify(value)); } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 4bb08faa..9fbb255b 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1223,19 +1223,6 @@ ${escapedMessage} }; }); - //TODO : move Player Properties related-code - iframeListener.registerAnswerer("setPlayerProperty", (event) => { - localUserStore.setUserProperty(event.propertyName, event.propertyValue as string); - }); - - iframeListener.registerAnswerer("getPlayerProperty", (event) => { - return { - propertyName: event, - propertyValue: localUserStore.getUserProperty(event), - }; - }); - //END TODO - iframeListener.registerAnswerer("getState", async () => { // The sharedVariablesManager is not instantiated before the connection is established. So we need to wait // for the connection to send back the answer. @@ -1248,6 +1235,7 @@ ${escapedMessage} roomId: this.roomUrl, tags: this.connection ? this.connection.getAllTags() : [], variables: this.sharedVariablesManager.variables, + playerVariables: localUserStore.getAllUserProperties(), userRoomToken: this.connection ? this.connection.userRoomToken : "", }; }); @@ -1338,6 +1326,22 @@ ${escapedMessage} }) ); + iframeListener.registerAnswerer("setVariable", (event, source) => { + switch (event.target) { + case "global": { + this.sharedVariablesManager.setVariable(event, source); + break; + } + case "player": { + localUserStore.setUserProperty(event.key, event.value); + break; + } + default: { + const _exhaustiveCheck: never = event.target; + } + } + }); + iframeListener.registerAnswerer("removeActionMessage", (message) => { layoutManagerActionStore.removeAction(message.uuid); }); @@ -1480,6 +1484,7 @@ ${escapedMessage} iframeListener.unregisterAnswerer("openCoWebsite"); iframeListener.unregisterAnswerer("getCoWebsites"); iframeListener.unregisterAnswerer("setPlayerOutline"); + iframeListener.unregisterAnswerer("setVariable"); this.sharedVariablesManager?.close(); this.embeddedWebsiteManager?.close(); diff --git a/front/src/Phaser/Game/SharedVariablesManager.ts b/front/src/Phaser/Game/SharedVariablesManager.ts index 5b5867dc..0619b6cc 100644 --- a/front/src/Phaser/Game/SharedVariablesManager.ts +++ b/front/src/Phaser/Game/SharedVariablesManager.ts @@ -3,6 +3,7 @@ import { iframeListener } from "../../Api/IframeListener"; import type { GameMap } from "./GameMap"; import type { ITiledMapLayer, ITiledMapObject } from "../Map/ITiledMap"; import { GameMapProperties } from "./GameMapProperties"; +import type { SetVariableEvent } from "../../Api/Events/SetVariableEvent"; interface Variable { defaultValue: unknown; @@ -48,51 +49,51 @@ export class SharedVariablesManager { iframeListener.setVariable({ key: name, value: value, + target: "global", }); }); + } - // When a variable is modified from an iFrame - iframeListener.registerAnswerer("setVariable", (event, source) => { - const key = event.key; + public setVariable(event: SetVariableEvent, source: MessageEventSource | null): void { + const key = event.key; - const object = this.variableObjects.get(key); + const object = this.variableObjects.get(key); - if (object === undefined) { - const errMsg = - 'A script is trying to modify variable "' + - key + - '" but this variable is not defined in the map.' + - 'There should be an object in the map whose name is "' + - key + - '" and whose type is "variable"'; - console.error(errMsg); - throw new Error(errMsg); - } + if (object === undefined) { + const errMsg = + 'A script is trying to modify variable "' + + key + + '" but this variable is not defined in the map.' + + 'There should be an object in the map whose name is "' + + key + + '" and whose type is "variable"'; + console.error(errMsg); + throw new Error(errMsg); + } - if (object.writableBy && !this.roomConnection.hasTag(object.writableBy)) { - const errMsg = - 'A script is trying to modify variable "' + - key + - '" but this variable is only writable for users with tag "' + - object.writableBy + - '".'; - console.error(errMsg); - throw new Error(errMsg); - } + if (object.writableBy && !this.roomConnection.hasTag(object.writableBy)) { + const errMsg = + 'A script is trying to modify variable "' + + key + + '" but this variable is only writable for users with tag "' + + object.writableBy + + '".'; + console.error(errMsg); + throw new Error(errMsg); + } - // Let's stop any propagation of the value we set is the same as the existing value. - if (JSON.stringify(event.value) === JSON.stringify(this._variables.get(key))) { - return; - } + // Let's stop any propagation of the value we set is the same as the existing value. + if (JSON.stringify(event.value) === JSON.stringify(this._variables.get(key))) { + return; + } - this._variables.set(key, event.value); + this._variables.set(key, event.value); - // Dispatch to the room connection. - this.roomConnection.emitSetVariableEvent(key, event.value); + // Dispatch to the room connection. + this.roomConnection.emitSetVariableEvent(key, event.value); - // Dispatch to other iframes - iframeListener.dispatchVariableToOtherIframes(key, event.value, source); - }); + // Dispatch to other iframes + iframeListener.dispatchVariableToOtherIframes(key, event.value, source); } private static findVariablesInMap(gameMap: GameMap): Map { @@ -164,10 +165,6 @@ export class SharedVariablesManager { return variable; } - public close(): void { - iframeListener.unregisterAnswerer("setVariable"); - } - get variables(): Map { return this._variables; } diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 93415b0d..f33bc8d7 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -14,25 +14,28 @@ import controls from "./Api/iframe/controls"; import ui from "./Api/iframe/ui"; import sound from "./Api/iframe/sound"; import room, { setMapURL, setRoomId } from "./Api/iframe/room"; -import state, { initVariables } from "./Api/iframe/state"; +import { createState } from "./Api/iframe/state"; import player, { setPlayerName, setTags, setUserRoomToken, setUuid } from "./Api/iframe/player"; import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor"; import type { Popup } from "./Api/iframe/Ui/Popup"; import type { Sound } from "./Api/iframe/Sound/Sound"; import { answerPromises, queryWorkadventure } from "./Api/iframe/IframeApiContribution"; +const globalState = createState("global"); + // Notify WorkAdventure that we are ready to receive data const initPromise = queryWorkadventure({ type: "getState", data: undefined, -}).then((state) => { - setPlayerName(state.nickname); - setRoomId(state.roomId); - setMapURL(state.mapUrl); - setTags(state.tags); - setUuid(state.uuid); - initVariables(state.variables as Map); - setUserRoomToken(state.userRoomToken); +}).then((gameState) => { + setPlayerName(gameState.nickname); + setRoomId(gameState.roomId); + setMapURL(gameState.mapUrl); + setTags(gameState.tags); + setUuid(gameState.uuid); + globalState.initVariables(gameState.variables as Map); + player.state.initVariables(gameState.playerVariables as Map); + setUserRoomToken(gameState.userRoomToken); }); const wa = { @@ -43,7 +46,7 @@ const wa = { sound, room, player, - state, + state: globalState, onInit(): Promise { return initPromise; @@ -225,7 +228,5 @@ window.addEventListener( callback?.callback(payloadData); } } - - // ... } ); From e39e341ab704d6612c3ff7896706d7c495c2a665 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Mon, 25 Oct 2021 17:23:02 +0200 Subject: [PATCH 15/46] Adds documentation on player properties --- docs/maps/api-player.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index 35d5f464..904a3000 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -107,6 +107,27 @@ Example : WA.player.onPlayerMove(console.log); ``` +## Player specific properties +Similarly to maps (see [API state related functions](api-state.md)), it is possible to store data **related to a specific player** in a "state". Such data will be stored using the local storage from the user's browser. + +Any value that is serializable in JSON can be stored. + +### Setting a property +A player property can be set simply by assigning a value. + +Example: +```javascript +WA.player.state.toto = "value" //will set the "toto" key to "value" +``` + +### Reading a property +A player property can be read by calling its key from the player's state. + +Example: +```javascript +WA.player.state.toto //will retrieve the property +``` + ### Set the outline color of the player ``` WA.player.setOutlineColor(red: number, green: number, blue: number): Promise; From 516d756db131e4e31492826499ef237aa3f5aefe Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Fri, 29 Oct 2021 12:01:07 +0200 Subject: [PATCH 16/46] Uses the current player position rather than the starting one to position iframe --- front/src/Api/Events/IframeEvent.ts | 5 +++++ front/src/Api/Events/PlayerPosition.ts | 10 ++++++++++ front/src/Api/iframe/player.ts | 13 +++++++++++++ front/src/Phaser/Game/GameScene.ts | 8 ++++++++ 4 files changed, 36 insertions(+) create mode 100644 front/src/Api/Events/PlayerPosition.ts diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 6b57bb48..e7031894 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -31,6 +31,7 @@ import type { ChangeLayerEvent } from "./ChangeLayerEvent"; import { isPlayerPropertyEvent } from "./PlayerPropertyEvent"; import type { ChangeZoneEvent } from "./ChangeZoneEvent"; import { isColorEvent } from "./ColorEvent"; +import { isPlayerPosition } from "./PlayerPosition"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -170,6 +171,10 @@ export const iframeQueryMapTypeGuards = { query: tg.isUndefined, answer: tg.isUndefined, }, + getPlayerPosition: { + query: tg.isUndefined, + answer: isPlayerPosition, + }, }; type GuardedType = T extends (x: unknown) => x is infer T ? T : never; diff --git a/front/src/Api/Events/PlayerPosition.ts b/front/src/Api/Events/PlayerPosition.ts new file mode 100644 index 00000000..54fac6fe --- /dev/null +++ b/front/src/Api/Events/PlayerPosition.ts @@ -0,0 +1,10 @@ +import * as tg from "generic-type-guard"; + +export const isPlayerPosition = new tg.IsInterface() + .withProperties({ + x: tg.isNumber, + y: tg.isNumber, + }) + .get(); + +export type PlayerPosition = tg.GuardedType; diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index ce865642..001ceb18 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -77,6 +77,13 @@ export class WorkadventurePlayerCommands extends IframeApiContribution { + return await queryWorkadventure({ + type: "getPlayerPosition", + data: undefined, + }); + } + get userRoomToken(): string | undefined { if (userRoomToken === undefined) { throw new Error( @@ -119,4 +126,10 @@ export class WorkadventurePlayerCommands extends IframeApiContribution { + return { + x: this.CurrentPlayer.x, + y: this.CurrentPlayer.y, + }; + }); } private setPropertyLayer( From 8bc75e1ccbf2a364fd60f29f0b7785967ae6b41f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Fri, 29 Oct 2021 18:34:54 +0200 Subject: [PATCH 17/46] Update docs/maps/api-player.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- docs/maps/api-player.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index 904a3000..ef91ee42 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -110,6 +110,9 @@ WA.player.onPlayerMove(console.log); ## Player specific properties Similarly to maps (see [API state related functions](api-state.md)), it is possible to store data **related to a specific player** in a "state". Such data will be stored using the local storage from the user's browser. +{.alert.alert-info} +In the future, player-related variables will be stored on the WorkAdventure server if the current player is logged. + Any value that is serializable in JSON can be stored. ### Setting a property From cbf7cdfe29c6aaa60958658ac40ae230fd68d554 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Fri, 29 Oct 2021 18:53:55 +0200 Subject: [PATCH 18/46] Cleans forgotten useless commentaries --- front/src/Api/iframe/player.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index 001ceb18..2a642d75 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -126,8 +126,7 @@ export class WorkadventurePlayerCommands extends IframeApiContribution Date: Fri, 29 Oct 2021 18:56:04 +0200 Subject: [PATCH 19/46] Updates documentation --- docs/maps/api-player.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index ef91ee42..4375dca6 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -86,6 +86,22 @@ WA.onInit().then(() => { }) ``` +### Get the position of the player +``` +WA.player.getPosition(): Promise +``` +The player's current position is available using the `WA.player.getPosition()` function. + +{.alert.alert-info} +You need to wait for the end of the initialization before calling `WA.player.getPosition()` + +```typescript +WA.onInit().then(() => { + console.log('Tags: ', WA.player.getPosition()); +}) +``` + + ### Listen to player movement ``` WA.player.onPlayerMove(callback: HasPlayerMovedEventCallback): void; @@ -107,8 +123,8 @@ Example : WA.player.onPlayerMove(console.log); ``` -## Player specific properties -Similarly to maps (see [API state related functions](api-state.md)), it is possible to store data **related to a specific player** in a "state". Such data will be stored using the local storage from the user's browser. +## Player specific variables +Similarly to maps (see [API state related functions](api-state.md)), it is possible to store data **related to a specific player** in a "state". Such data will be stored using the local storage from the user's browser. Any value that is serializable in JSON can be stored. {.alert.alert-info} In the future, player-related variables will be stored on the WorkAdventure server if the current player is logged. @@ -123,12 +139,12 @@ Example: WA.player.state.toto = "value" //will set the "toto" key to "value" ``` -### Reading a property -A player property can be read by calling its key from the player's state. +### Reading a variable +A player variable can be read by calling its key from the player's state. Example: ```javascript -WA.player.state.toto //will retrieve the property +WA.player.state.toto //will retrieve the variable ``` ### Set the outline color of the player From c53f0c6c8cf95d20051ef5e6f0c009ee93a331ad Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Fri, 29 Oct 2021 19:27:14 +0200 Subject: [PATCH 20/46] Fixes deleting SharedVariablesManager's close() function --- front/src/Phaser/Game/SharedVariablesManager.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/front/src/Phaser/Game/SharedVariablesManager.ts b/front/src/Phaser/Game/SharedVariablesManager.ts index 0619b6cc..72149473 100644 --- a/front/src/Phaser/Game/SharedVariablesManager.ts +++ b/front/src/Phaser/Game/SharedVariablesManager.ts @@ -165,6 +165,10 @@ export class SharedVariablesManager { return variable; } + public close(): void { + iframeListener.unregisterAnswerer("setVariable"); + } + get variables(): Map { return this._variables; } From d672a2dead4d87b94f776112312b8bc809eb8ec7 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Wed, 10 Nov 2021 19:04:06 +0100 Subject: [PATCH 21/46] WIP API updates for tutorial and more --- front/src/Api/Events/EmbeddedWebsiteEvent.ts | 4 ++ front/src/Api/Events/IframeEvent.ts | 1 + front/src/Api/iframe/Room/EmbeddedWebsite.ts | 43 ++++++++++++++ front/src/Api/iframe/website.ts | 4 -- .../src/Phaser/Game/EmbeddedWebsiteManager.ts | 58 +++++++++++++++---- front/src/Phaser/Game/GameScene.ts | 4 +- .../EmbeddedWebsite/website_in_map_script.php | 16 +++++ 7 files changed, 115 insertions(+), 15 deletions(-) diff --git a/front/src/Api/Events/EmbeddedWebsiteEvent.ts b/front/src/Api/Events/EmbeddedWebsiteEvent.ts index 42630be1..57c24853 100644 --- a/front/src/Api/Events/EmbeddedWebsiteEvent.ts +++ b/front/src/Api/Events/EmbeddedWebsiteEvent.ts @@ -22,6 +22,8 @@ export const isEmbeddedWebsiteEvent = new tg.IsInterface() y: tg.isNumber, width: tg.isNumber, height: tg.isNumber, + origin: tg.isSingletonStringUnion("player", "map"), + scale: tg.isNumber, }) .get(); @@ -35,6 +37,8 @@ export const isCreateEmbeddedWebsiteEvent = new tg.IsInterface() visible: tg.isBoolean, allowApi: tg.isBoolean, allow: tg.isString, + origin: tg.isSingletonStringUnion("player", "map"), + scale: tg.isNumber, }) .get(); diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index e7031894..2744eaa9 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -215,6 +215,7 @@ export const isIframeQuery = (event: any): event is IframeQuery { console.log('CREATING NEW EMBEDDED IFRAME'); @@ -28,6 +30,8 @@ height: parseInt(heightField.value), }, visible: !!visibleField.value, + origin: originField.value, + scale: parseFloat(scaleField.value), }); }); @@ -61,6 +65,16 @@ const website = await WA.room.website.get('test'); website.visible = this.checked; }); + + originField.addEventListener('change', async function() { + const website = await WA.room.website.get('test'); + website.origin = this.value; + }); + + scaleField.addEventListener('change', async function() { + const website = await WA.room.website.get('test'); + website.scale = parseFloat(this.value); + }); }); }) @@ -72,6 +86,8 @@ width:
height:
URL:
Visible:
+Origin:
+Scale:
From 435676773990fdf31cc8c2cbf655937fd49fe11c Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Tue, 23 Nov 2021 16:51:39 +0100 Subject: [PATCH 22/46] Adds the camera to available APIs with retrieving of the worldView --- docs/maps/api-player.md | 2 +- front/src/Api/Events/HasCameraMovedEvent.ts | 18 ++++++++++++ front/src/Api/Events/IframeEvent.ts | 3 ++ front/src/Api/IframeListener.ts | 13 +++++++++ front/src/Api/iframe/Room/EmbeddedWebsite.ts | 6 ++-- front/src/Api/iframe/camera.ts | 29 +++++++++++++++++++ .../src/Phaser/Game/EmbeddedWebsiteManager.ts | 7 +++-- front/src/Phaser/Game/GameScene.ts | 13 +++++++++ front/src/iframe_api.ts | 2 ++ 9 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 front/src/Api/Events/HasCameraMovedEvent.ts create mode 100644 front/src/Api/iframe/camera.ts diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index 4375dca6..ad08448b 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -97,7 +97,7 @@ You need to wait for the end of the initialization before calling `WA.player.get ```typescript WA.onInit().then(() => { - console.log('Tags: ', WA.player.getPosition()); + console.log('Position: ', WA.player.getPosition()); }) ``` diff --git a/front/src/Api/Events/HasCameraMovedEvent.ts b/front/src/Api/Events/HasCameraMovedEvent.ts new file mode 100644 index 00000000..23f85385 --- /dev/null +++ b/front/src/Api/Events/HasCameraMovedEvent.ts @@ -0,0 +1,18 @@ +import * as tg from "generic-type-guard"; + +export const isHasCameraMovedEvent = new tg.IsInterface() + .withProperties({ + x: tg.isNumber, + y: tg.isNumber, + width: tg.isNumber, + height: tg.isNumber, + }) + .get(); + +/** + * A message sent from the game to the iFrame to notify a movement from the camera. + */ + +export type HasCameraMovedEvent = tg.GuardedType; + +export type HasCameraMovedEventCallback = (event: HasCameraMovedEvent) => void; diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 2744eaa9..5b1c0f02 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -32,6 +32,7 @@ import { isPlayerPropertyEvent } from "./PlayerPropertyEvent"; import type { ChangeZoneEvent } from "./ChangeZoneEvent"; import { isColorEvent } from "./ColorEvent"; import { isPlayerPosition } from "./PlayerPosition"; +import type { HasCameraMovedEvent } from "./HasCameraMovedEvent"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -52,6 +53,7 @@ export type IframeEventMap = { displayBubble: null; removeBubble: null; onPlayerMove: undefined; + onCameraMove: undefined; showLayer: LayerEvent; hideLayer: LayerEvent; setProperty: SetPropertyEvent; @@ -84,6 +86,7 @@ export interface IframeResponseEventMap { leaveZoneEvent: ChangeZoneEvent; buttonClickedEvent: ButtonClickedEvent; hasPlayerMoved: HasPlayerMovedEvent; + hasCameraMoved: HasCameraMovedEvent; menuItemClicked: MenuItemClickedEvent; setVariable: SetVariableEvent; messageTriggered: MessageReferenceEvent; diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 67b49344..093cacc5 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -31,6 +31,7 @@ import type { SetVariableEvent } from "./Events/SetVariableEvent"; import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent"; +import type { HasCameraMovedEvent } from "./Events/HasCameraMovedEvent"; import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent"; type AnswererCallback = ( @@ -95,6 +96,7 @@ class IframeListener { private readonly iframeCloseCallbacks = new Map void)[]>(); private readonly scripts = new Map(); private sendPlayerMove: boolean = false; + private sendCameraMove: boolean = false; // Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904 private answerers: { @@ -226,6 +228,8 @@ class IframeListener { this._removeBubbleStream.next(); } else if (payload.type == "onPlayerMove") { this.sendPlayerMove = true; + } else if (payload.type == "onCameraMove") { + this.sendCameraMove = true; } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { this._setTilesStream.next(payload.data); } else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) { @@ -442,6 +446,15 @@ class IframeListener { } } + hasCameraMoved(event: HasCameraMovedEvent) { + if (this.sendCameraMove) { + this.postMessage({ + type: "hasCameraMoved", + data: event, + }); + } + } + sendButtonClickedEvent(popupId: number, buttonId: number): void { this.postMessage({ type: "buttonClickedEvent", diff --git a/front/src/Api/iframe/Room/EmbeddedWebsite.ts b/front/src/Api/iframe/Room/EmbeddedWebsite.ts index 1a2761bd..d9c2d986 100644 --- a/front/src/Api/iframe/Room/EmbeddedWebsite.ts +++ b/front/src/Api/iframe/Room/EmbeddedWebsite.ts @@ -13,7 +13,7 @@ export class EmbeddedWebsite { private _allowApi: boolean; private _position: Rectangle; private readonly origin: "map" | "player" | undefined; - private _scale: number | undefined; + private _scale: number; constructor(private config: CreateEmbeddedWebsiteEvent) { this.name = config.name; @@ -23,7 +23,7 @@ export class EmbeddedWebsite { this._allowApi = config.allowApi ?? false; this._position = config.position; this.origin = config.origin; - this._scale = config.scale; + this._scale = config.scale ?? 1; } public get url() { @@ -116,7 +116,7 @@ export class EmbeddedWebsite { }); } - public get scale() { + public get scale(): number { return this._scale; } diff --git a/front/src/Api/iframe/camera.ts b/front/src/Api/iframe/camera.ts new file mode 100644 index 00000000..e2fb258e --- /dev/null +++ b/front/src/Api/iframe/camera.ts @@ -0,0 +1,29 @@ +import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; +import { Subject } from "rxjs"; +import type { HasCameraMovedEvent, HasCameraMovedEventCallback } from "../Events/HasCameraMovedEvent"; +import { apiCallback } from "./registeredCallbacks"; +import { isHasCameraMovedEvent } from "../Events/HasCameraMovedEvent"; + +const moveStream = new Subject(); + +export class WorkAdventureCameraCommands extends IframeApiContribution { + callbacks = [ + apiCallback({ + type: "hasCameraMoved", + typeChecker: isHasCameraMovedEvent, + callback: (payloadData) => { + moveStream.next(payloadData); + }, + }), + ]; + + onCameraMove(callback: HasCameraMovedEventCallback): void { + moveStream.subscribe(callback); + sendToWorkadventure({ + type: "onCameraMove", + data: null, + }); + } +} + +export default new WorkAdventureCameraCommands(); diff --git a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts index 99c4bf5f..36e6b305 100644 --- a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts +++ b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts @@ -30,6 +30,7 @@ export class EmbeddedWebsiteManager { height: rect["height"], }, origin: website.origin, + scale: website.scale, }; }); @@ -144,9 +145,9 @@ export class EmbeddedWebsiteManager { name, url, /*x, - y, - width, - height,*/ + y, + width, + height,*/ allow, allowApi, visible, diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 417fab75..9febe432 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -93,6 +93,8 @@ import { MapStore } from "../../Stores/Utils/MapStore"; import { SetPlayerDetailsMessage } from "../../Messages/generated/messages_pb"; import { followUsersColorStore, followUsersStore } from "../../Stores/FollowStore"; import { getColorRgbFromHue } from "../../WebRtc/ColorGenerator"; +import Camera = Phaser.Cameras.Scene2D.Camera; +import type { HasCameraMovedEvent } from "../../Api/Events/HasCameraMovedEvent"; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -783,6 +785,17 @@ export class GameScene extends DirtyScene { this.gameMap.setPosition(event.x, event.y); }); + //listen event to share the actual worldView when the camera is updated + this.cameras.main.on("followupdate", (camera: Camera) => { + const worldView: HasCameraMovedEvent = { + x: camera.worldView.x, + y: camera.worldView.y, + width: camera.worldView.width, + height: camera.worldView.height, + }; + iframeListener.hasCameraMoved(worldView); + }); + // Set up variables manager this.sharedVariablesManager = new SharedVariablesManager( this.connection, diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index f33bc8d7..6b3ec8c3 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -20,6 +20,7 @@ import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor"; import type { Popup } from "./Api/iframe/Ui/Popup"; import type { Sound } from "./Api/iframe/Sound/Sound"; import { answerPromises, queryWorkadventure } from "./Api/iframe/IframeApiContribution"; +import camera from "./Api/iframe/camera"; const globalState = createState("global"); @@ -46,6 +47,7 @@ const wa = { sound, room, player, + camera, state: globalState, onInit(): Promise { From 1e073d8a0e5294923fe79d6343fa29a6233b6ab0 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Tue, 23 Nov 2021 17:39:45 +0100 Subject: [PATCH 23/46] Refactoring and documentation update --- docs/maps/api-camera.md | 23 +++++++++++++++++++ docs/maps/api-reference.md | 1 + front/src/Api/Events/IframeEvent.ts | 6 ++--- ...MovedEvent.ts => WasCameraUpdatedEvent.ts} | 6 ++--- front/src/Api/IframeListener.ts | 13 ++++++----- front/src/Api/iframe/camera.ts | 14 +++++------ front/src/Phaser/Game/GameScene.ts | 6 ++--- 7 files changed, 47 insertions(+), 22 deletions(-) create mode 100644 docs/maps/api-camera.md rename front/src/Api/Events/{HasCameraMovedEvent.ts => WasCameraUpdatedEvent.ts} (55%) diff --git a/docs/maps/api-camera.md b/docs/maps/api-camera.md new file mode 100644 index 00000000..b5b85b64 --- /dev/null +++ b/docs/maps/api-camera.md @@ -0,0 +1,23 @@ +{.section-title.accent.text-primary} +# API Camera functions Reference + +### Listen to the camera update + +``` +WA.camera.onCameraUpdate(callback: WasCameraUpdatedEventCallback): void +``` + +Listens to the updating of the camera linked to the player. It will trigger for every update of the camera's properties (position or scale for instance) or of the Game Object it is linked to (undestand: if the player moves). An event will then be sent. + +The event has the following attributes : +* **x (number):** coordinate X of the camera's world view (the area looked at by the camera). +* **y (number):** coordinate Y of the camera's world view. +* **width (number):** the width of the camera's world view. +* **height (number):** the height of the camera's world view. + +**callback:** the function that will be called when the camera is updated. + +Example : +```javascript +WA.camera.onCameraUpdate((worldView) => console.log(worldView)); +``` \ No newline at end of file diff --git a/docs/maps/api-reference.md b/docs/maps/api-reference.md index d044668f..a0869075 100644 --- a/docs/maps/api-reference.md +++ b/docs/maps/api-reference.md @@ -10,5 +10,6 @@ - [UI functions](api-ui.md) - [Sound functions](api-sound.md) - [Controls functions](api-controls.md) +- [Camera functions](api-camera.md) - [List of deprecated functions](api-deprecated.md) diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 5b1c0f02..ee7b0f64 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -32,7 +32,7 @@ import { isPlayerPropertyEvent } from "./PlayerPropertyEvent"; import type { ChangeZoneEvent } from "./ChangeZoneEvent"; import { isColorEvent } from "./ColorEvent"; import { isPlayerPosition } from "./PlayerPosition"; -import type { HasCameraMovedEvent } from "./HasCameraMovedEvent"; +import type { WasCameraUpdatedEvent } from "./WasCameraUpdatedEvent"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -53,7 +53,7 @@ export type IframeEventMap = { displayBubble: null; removeBubble: null; onPlayerMove: undefined; - onCameraMove: undefined; + onCameraUpdate: undefined; showLayer: LayerEvent; hideLayer: LayerEvent; setProperty: SetPropertyEvent; @@ -86,7 +86,7 @@ export interface IframeResponseEventMap { leaveZoneEvent: ChangeZoneEvent; buttonClickedEvent: ButtonClickedEvent; hasPlayerMoved: HasPlayerMovedEvent; - hasCameraMoved: HasCameraMovedEvent; + wasCameraUpdated: WasCameraUpdatedEvent; menuItemClicked: MenuItemClickedEvent; setVariable: SetVariableEvent; messageTriggered: MessageReferenceEvent; diff --git a/front/src/Api/Events/HasCameraMovedEvent.ts b/front/src/Api/Events/WasCameraUpdatedEvent.ts similarity index 55% rename from front/src/Api/Events/HasCameraMovedEvent.ts rename to front/src/Api/Events/WasCameraUpdatedEvent.ts index 23f85385..8f37753c 100644 --- a/front/src/Api/Events/HasCameraMovedEvent.ts +++ b/front/src/Api/Events/WasCameraUpdatedEvent.ts @@ -1,6 +1,6 @@ import * as tg from "generic-type-guard"; -export const isHasCameraMovedEvent = new tg.IsInterface() +export const isWasCameraUpdatedEvent = new tg.IsInterface() .withProperties({ x: tg.isNumber, y: tg.isNumber, @@ -13,6 +13,6 @@ export const isHasCameraMovedEvent = new tg.IsInterface() * A message sent from the game to the iFrame to notify a movement from the camera. */ -export type HasCameraMovedEvent = tg.GuardedType; +export type WasCameraUpdatedEvent = tg.GuardedType; -export type HasCameraMovedEventCallback = (event: HasCameraMovedEvent) => void; +export type WasCameraUpdatedEventCallback = (event: WasCameraUpdatedEvent) => void; diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 093cacc5..b790c0ca 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -31,6 +31,7 @@ import type { SetVariableEvent } from "./Events/SetVariableEvent"; import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent"; +import type { WasCameraUpdatedEvent } from "./Events/WasCameraUpdatedEvent"; import type { HasCameraMovedEvent } from "./Events/HasCameraMovedEvent"; import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent"; @@ -96,7 +97,7 @@ class IframeListener { private readonly iframeCloseCallbacks = new Map void)[]>(); private readonly scripts = new Map(); private sendPlayerMove: boolean = false; - private sendCameraMove: boolean = false; + private sendCameraUpdate: boolean = false; // Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904 private answerers: { @@ -228,8 +229,8 @@ class IframeListener { this._removeBubbleStream.next(); } else if (payload.type == "onPlayerMove") { this.sendPlayerMove = true; - } else if (payload.type == "onCameraMove") { - this.sendCameraMove = true; + } else if (payload.type == "onCameraUpdate") { + this.sendCameraUpdate = true; } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { this._setTilesStream.next(payload.data); } else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) { @@ -446,10 +447,10 @@ class IframeListener { } } - hasCameraMoved(event: HasCameraMovedEvent) { - if (this.sendCameraMove) { + sendCameraUpdated(event: WasCameraUpdatedEvent) { + if (this.sendCameraUpdate) { this.postMessage({ - type: "hasCameraMoved", + type: "wasCameraUpdated", data: event, }); } diff --git a/front/src/Api/iframe/camera.ts b/front/src/Api/iframe/camera.ts index e2fb258e..4f62b94c 100644 --- a/front/src/Api/iframe/camera.ts +++ b/front/src/Api/iframe/camera.ts @@ -1,26 +1,26 @@ import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; import { Subject } from "rxjs"; -import type { HasCameraMovedEvent, HasCameraMovedEventCallback } from "../Events/HasCameraMovedEvent"; +import type { WasCameraUpdatedEvent, WasCameraUpdatedEventCallback } from "../Events/WasCameraUpdatedEvent"; import { apiCallback } from "./registeredCallbacks"; -import { isHasCameraMovedEvent } from "../Events/HasCameraMovedEvent"; +import { isWasCameraUpdatedEvent } from "../Events/WasCameraUpdatedEvent"; -const moveStream = new Subject(); +const moveStream = new Subject(); export class WorkAdventureCameraCommands extends IframeApiContribution { callbacks = [ apiCallback({ - type: "hasCameraMoved", - typeChecker: isHasCameraMovedEvent, + type: "wasCameraUpdated", + typeChecker: isWasCameraUpdatedEvent, callback: (payloadData) => { moveStream.next(payloadData); }, }), ]; - onCameraMove(callback: HasCameraMovedEventCallback): void { + onCameraUpdate(callback: WasCameraUpdatedEventCallback): void { moveStream.subscribe(callback); sendToWorkadventure({ - type: "onCameraMove", + type: "onCameraUpdate", data: null, }); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9febe432..7ae2707f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -94,7 +94,7 @@ import { SetPlayerDetailsMessage } from "../../Messages/generated/messages_pb"; import { followUsersColorStore, followUsersStore } from "../../Stores/FollowStore"; import { getColorRgbFromHue } from "../../WebRtc/ColorGenerator"; import Camera = Phaser.Cameras.Scene2D.Camera; -import type { HasCameraMovedEvent } from "../../Api/Events/HasCameraMovedEvent"; +import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent"; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -787,13 +787,13 @@ export class GameScene extends DirtyScene { //listen event to share the actual worldView when the camera is updated this.cameras.main.on("followupdate", (camera: Camera) => { - const worldView: HasCameraMovedEvent = { + const worldView: WasCameraUpdatedEvent = { x: camera.worldView.x, y: camera.worldView.y, width: camera.worldView.width, height: camera.worldView.height, }; - iframeListener.hasCameraMoved(worldView); + iframeListener.sendCameraUpdated(worldView); }); // Set up variables manager From 2a7c8f3786d54838caff63afd70dda53141650a6 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Fri, 26 Nov 2021 14:46:02 +0100 Subject: [PATCH 24/46] Reverts adding scale to the camera updated event and uses it directly from the website manager --- .../src/Phaser/Game/EmbeddedWebsiteManager.ts | 68 +++++++++---------- front/src/Phaser/Game/GameScene.ts | 4 +- 2 files changed, 35 insertions(+), 37 deletions(-) diff --git a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts index 36e6b305..d58d7eaf 100644 --- a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts +++ b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts @@ -26,8 +26,8 @@ export class EmbeddedWebsiteManager { position: { x: website.phaserObject.x, y: website.phaserObject.y, - width: rect["width"], - height: rect["height"], + width: website.phaserObject.width, + height: website.phaserObject.height, }, origin: website.origin, scale: website.scale, @@ -111,14 +111,18 @@ export class EmbeddedWebsiteManager { website.phaserObject.y = embeddedWebsiteEvent.y; } if (embeddedWebsiteEvent?.width !== undefined) { - website.iframe.style.width = embeddedWebsiteEvent.width + "px"; + website.position.width = embeddedWebsiteEvent.width; + website.iframe.style.width = embeddedWebsiteEvent.width / website.phaserObject.scale + "px"; } if (embeddedWebsiteEvent?.height !== undefined) { - website.iframe.style.height = embeddedWebsiteEvent.height + "px"; + website.position.height = embeddedWebsiteEvent.height; + website.iframe.style.height = embeddedWebsiteEvent.height / website.phaserObject.scale + "px"; } if (embeddedWebsiteEvent?.scale !== undefined) { website.phaserObject.scale = embeddedWebsiteEvent.scale; + website.iframe.style.width = website.position.width / embeddedWebsiteEvent.scale + "px"; + website.iframe.style.height = website.position.height / embeddedWebsiteEvent.scale + "px"; } } ); @@ -145,9 +149,9 @@ export class EmbeddedWebsiteManager { name, url, /*x, - y, - width, - height,*/ +y, +width, +height,*/ allow, allowApi, visible, @@ -173,49 +177,43 @@ export class EmbeddedWebsiteManager { const absoluteUrl = new URL(embeddedWebsiteEvent.url, this.gameScene.MapUrlFile).toString(); const iframe = document.createElement("iframe"); + const scale = embeddedWebsiteEvent.scale ?? 1; + iframe.src = absoluteUrl; iframe.tabIndex = -1; - iframe.style.width = embeddedWebsiteEvent.position.width + "px"; - iframe.style.height = embeddedWebsiteEvent.position.height + "px"; + iframe.style.width = embeddedWebsiteEvent.position.width / scale + "px"; + iframe.style.height = embeddedWebsiteEvent.position.height / scale + "px"; iframe.style.margin = "0"; iframe.style.padding = "0"; iframe.style.border = "none"; - let embeddedWebsite; - let domElement; + const domElement = new DOMElement( + this.gameScene, + embeddedWebsiteEvent.position.x, + embeddedWebsiteEvent.position.y, + iframe + ); + domElement.setOrigin(0, 0); + if (embeddedWebsiteEvent.scale) { + domElement.scale = embeddedWebsiteEvent.scale; + } + domElement.setVisible(visible); switch (embeddedWebsiteEvent.origin) { case "player": - domElement = new DOMElement( - this.gameScene, - embeddedWebsiteEvent.position.x, - embeddedWebsiteEvent.position.y, - iframe - ); - if (embeddedWebsiteEvent.scale) { - domElement.scale = embeddedWebsiteEvent.scale; - } this.gameScene.CurrentPlayer.add(domElement); - - embeddedWebsite = { - ...embeddedWebsiteEvent, - phaserObject: domElement, - iframe: iframe, - }; - break; case "map": default: - embeddedWebsite = { - ...embeddedWebsiteEvent, - phaserObject: this.gameScene.add - .dom(embeddedWebsiteEvent.position.x, embeddedWebsiteEvent.position.y, iframe) - .setVisible(visible) - .setOrigin(0, 0), - iframe: iframe, - }; + this.gameScene.add.existing(domElement); } + const embeddedWebsite = { + ...embeddedWebsiteEvent, + phaserObject: domElement, + iframe: iframe, + }; + if (embeddedWebsiteEvent.allowApi) { iframeListener.registerIframe(iframe); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 7ae2707f..55599510 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -787,13 +787,13 @@ export class GameScene extends DirtyScene { //listen event to share the actual worldView when the camera is updated this.cameras.main.on("followupdate", (camera: Camera) => { - const worldView: WasCameraUpdatedEvent = { + const cameraEvent: WasCameraUpdatedEvent = { x: camera.worldView.x, y: camera.worldView.y, width: camera.worldView.width, height: camera.worldView.height, }; - iframeListener.sendCameraUpdated(worldView); + iframeListener.sendCameraUpdated(cameraEvent); }); // Set up variables manager From 3abc571e791fb24787336f8f5c8e86ecc81a67e9 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Mon, 6 Dec 2021 18:44:37 +0100 Subject: [PATCH 25/46] Corrects scale managing and camera event listening --- front/src/Api/Events/WasCameraUpdatedEvent.ts | 1 + front/src/Api/IframeListener.ts | 16 +++--- .../src/Phaser/Game/EmbeddedWebsiteManager.ts | 7 +-- front/src/Phaser/Game/GameScene.ts | 51 ++++++++++++++----- 4 files changed, 50 insertions(+), 25 deletions(-) diff --git a/front/src/Api/Events/WasCameraUpdatedEvent.ts b/front/src/Api/Events/WasCameraUpdatedEvent.ts index 8f37753c..34e39a84 100644 --- a/front/src/Api/Events/WasCameraUpdatedEvent.ts +++ b/front/src/Api/Events/WasCameraUpdatedEvent.ts @@ -6,6 +6,7 @@ export const isWasCameraUpdatedEvent = new tg.IsInterface() y: tg.isNumber, width: tg.isNumber, height: tg.isNumber, + zoom: tg.isNumber, }) .get(); diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index b790c0ca..3c79bbe2 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -87,6 +87,9 @@ class IframeListener { private readonly _loadSoundStream: Subject = new Subject(); public readonly loadSoundStream = this._loadSoundStream.asObservable(); + private readonly _trackCameraUpdateStream: Subject = new Subject(); + public readonly trackCameraUpdateStream = this._trackCameraUpdateStream.asObservable(); + private readonly _setTilesStream: Subject = new Subject(); public readonly setTilesStream = this._setTilesStream.asObservable(); @@ -97,7 +100,6 @@ class IframeListener { private readonly iframeCloseCallbacks = new Map void)[]>(); private readonly scripts = new Map(); private sendPlayerMove: boolean = false; - private sendCameraUpdate: boolean = false; // Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904 private answerers: { @@ -230,7 +232,7 @@ class IframeListener { } else if (payload.type == "onPlayerMove") { this.sendPlayerMove = true; } else if (payload.type == "onCameraUpdate") { - this.sendCameraUpdate = true; + this._trackCameraUpdateStream.next(); } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { this._setTilesStream.next(payload.data); } else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) { @@ -448,12 +450,10 @@ class IframeListener { } sendCameraUpdated(event: WasCameraUpdatedEvent) { - if (this.sendCameraUpdate) { - this.postMessage({ - type: "wasCameraUpdated", - data: event, - }); - } + this.postMessage({ + type: "wasCameraUpdated", + data: event, + }); } sendButtonClickedEvent(popupId: number, buttonId: number): void { diff --git a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts index d58d7eaf..387940c7 100644 --- a/front/src/Phaser/Game/EmbeddedWebsiteManager.ts +++ b/front/src/Phaser/Game/EmbeddedWebsiteManager.ts @@ -16,7 +16,8 @@ export class EmbeddedWebsiteManager { if (website === undefined) { throw new Error('Cannot find embedded website with name "' + name + '"'); } - const rect = website.iframe.getBoundingClientRect(); + + const scale = website.scale ?? 1; return { url: website.url, name: website.name, @@ -26,8 +27,8 @@ export class EmbeddedWebsiteManager { position: { x: website.phaserObject.x, y: website.phaserObject.y, - width: website.phaserObject.width, - height: website.phaserObject.height, + width: website.phaserObject.width * scale, + height: website.phaserObject.height * scale, }, origin: website.origin, scale: website.scale, diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 55599510..ec12aae8 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -212,6 +212,8 @@ export class GameScene extends DirtyScene { private objectsByType = new Map(); private embeddedWebsiteManager!: EmbeddedWebsiteManager; private loader: Loader; + private lastCameraEvent: WasCameraUpdatedEvent | undefined; + private firstCameraUpdateSent: boolean = false; constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) { super({ @@ -785,17 +787,6 @@ export class GameScene extends DirtyScene { this.gameMap.setPosition(event.x, event.y); }); - //listen event to share the actual worldView when the camera is updated - this.cameras.main.on("followupdate", (camera: Camera) => { - const cameraEvent: WasCameraUpdatedEvent = { - x: camera.worldView.x, - y: camera.worldView.y, - width: camera.worldView.width, - height: camera.worldView.height, - }; - iframeListener.sendCameraUpdated(cameraEvent); - }); - // Set up variables manager this.sharedVariablesManager = new SharedVariablesManager( this.connection, @@ -1115,9 +1106,33 @@ ${escapedMessage} ); this.iframeSubscriptionList.push( - iframeListener.stopSoundStream.subscribe((stopSoundEvent) => { - const url = new URL(stopSoundEvent.url, this.MapUrlFile); - soundManager.stopSound(this.sound, url.toString()); + iframeListener.trackCameraUpdateStream.subscribe(() => { + if (!this.firstCameraUpdateSent) { + this.cameras.main.on("followupdate", (camera: Camera) => { + const cameraEvent: WasCameraUpdatedEvent = { + x: camera.worldView.x, + y: camera.worldView.y, + width: camera.worldView.width, + height: camera.worldView.height, + zoom: camera.scaleManager.zoom, + }; + if ( + this.lastCameraEvent?.x == cameraEvent.x && + this.lastCameraEvent?.y == cameraEvent.y && + this.lastCameraEvent?.width == cameraEvent.width && + this.lastCameraEvent?.height == cameraEvent.height && + this.lastCameraEvent?.zoom == cameraEvent.zoom + ) { + return; + } + + this.lastCameraEvent = cameraEvent; + iframeListener.sendCameraUpdated(cameraEvent); + this.firstCameraUpdateSent = true; + }); + + iframeListener.sendCameraUpdated(this.cameras.main); + } }) ); @@ -1180,6 +1195,12 @@ ${escapedMessage} }) ); + this.iframeSubscriptionList.push( + iframeListener.setPropertyStream.subscribe((setProperty) => { + this.setPropertyLayer(setProperty.layerName, setProperty.propertyName, setProperty.propertyValue); + }) + ); + iframeListener.registerAnswerer("openCoWebsite", async (openCoWebsite, source) => { if (!source) { throw new Error("Unknown query source"); @@ -1986,6 +2007,7 @@ ${escapedMessage} this.loader.resize(); } + private getObjectLayerData(objectName: string): ITiledMapObject | undefined { for (const layer of this.mapFile.layers) { if (layer.type === "objectgroup" && layer.name === "floorLayer") { @@ -1998,6 +2020,7 @@ ${escapedMessage} } return undefined; } + private reposition(): void { // Recompute camera offset if needed biggestAvailableAreaStore.recompute(); From ddb4ae88239f456970b7682aacf624935f84402a Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Tue, 7 Dec 2021 18:47:40 +0100 Subject: [PATCH 26/46] Documentation --- docs/maps/api-camera.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/api-camera.md b/docs/maps/api-camera.md index b5b85b64..7ae9ce2b 100644 --- a/docs/maps/api-camera.md +++ b/docs/maps/api-camera.md @@ -7,7 +7,7 @@ WA.camera.onCameraUpdate(callback: WasCameraUpdatedEventCallback): void ``` -Listens to the updating of the camera linked to the player. It will trigger for every update of the camera's properties (position or scale for instance) or of the Game Object it is linked to (undestand: if the player moves). An event will then be sent. +Listens to the updating of the camera linked to the player. It will trigger for every update of the camera's properties (position or scale for instance). An event will then be sent. The event has the following attributes : * **x (number):** coordinate X of the camera's world view (the area looked at by the camera). From 9cf64c3c765a9c97cad55c8049172cc164755569 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Wed, 8 Dec 2021 11:48:16 +0100 Subject: [PATCH 27/46] Documentation on type Position --- docs/maps/api-player.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index ad08448b..2bfad463 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -92,6 +92,11 @@ WA.player.getPosition(): Promise ``` The player's current position is available using the `WA.player.getPosition()` function. +`Position` has the following attributes : +* **x (number) :** The coordinate x of the current player's position. +* **y (number) :** The coordinate y of the current player's position. + + {.alert.alert-info} You need to wait for the end of the initialization before calling `WA.player.getPosition()` From 864ff49af58e0540ff37a86be4a6d5a0cfea8454 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Wed, 8 Dec 2021 10:05:46 +0100 Subject: [PATCH 28/46] Update docs/maps/api-camera.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- docs/maps/api-camera.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/api-camera.md b/docs/maps/api-camera.md index 7ae9ce2b..405de418 100644 --- a/docs/maps/api-camera.md +++ b/docs/maps/api-camera.md @@ -1,7 +1,7 @@ {.section-title.accent.text-primary} # API Camera functions Reference -### Listen to the camera update +### Listen to camera updates ``` WA.camera.onCameraUpdate(callback: WasCameraUpdatedEventCallback): void From 73efdab52f5307a5a45fcd0db5e216d993a09897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Wed, 8 Dec 2021 10:06:07 +0100 Subject: [PATCH 29/46] Update docs/maps/api-camera.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- docs/maps/api-camera.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/api-camera.md b/docs/maps/api-camera.md index 405de418..e0b5d7f7 100644 --- a/docs/maps/api-camera.md +++ b/docs/maps/api-camera.md @@ -7,7 +7,7 @@ WA.camera.onCameraUpdate(callback: WasCameraUpdatedEventCallback): void ``` -Listens to the updating of the camera linked to the player. It will trigger for every update of the camera's properties (position or scale for instance). An event will then be sent. +Listens to updates of the camera viewport. It will trigger for every update of the camera's properties (position or scale for instance). An event will be sent. The event has the following attributes : * **x (number):** coordinate X of the camera's world view (the area looked at by the camera). From adc71b5695977452b666435c616b5748b9e11749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Wed, 8 Dec 2021 10:06:34 +0100 Subject: [PATCH 30/46] Update front/src/Api/Events/IframeEvent.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- front/src/Api/Events/IframeEvent.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index ee7b0f64..4e87d4d4 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -218,7 +218,6 @@ export const isIframeQuery = (event: any): event is IframeQuery Date: Wed, 8 Dec 2021 10:14:31 +0100 Subject: [PATCH 31/46] Update front/src/Api/Events/IframeEvent.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- front/src/Api/Events/IframeEvent.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 4e87d4d4..dac8509d 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -65,7 +65,7 @@ export type IframeEventMap = { registerMenu: MenuRegisterEvent; unregisterMenu: UnregisterMenuEvent; setTiles: SetTilesEvent; - modifyEmbeddedWebsite: Partial; // Note: name should be compulsory in fact; + modifyEmbeddedWebsite: Partial; // Note: name should be compulsory in fact }; export interface IframeEvent { type: T; From 99f9d56c5ce27be73c7f4d1e840f6d3b1b454a97 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Tue, 28 Dec 2021 15:05:58 +0100 Subject: [PATCH 32/46] Updates room documentation for embeddedWebsite properties --- docs/maps/api-room.md | 140 ++++++++++++++++------------ front/src/Api/Events/IframeEvent.ts | 9 -- front/src/Api/IframeListener.ts | 1 - front/src/Api/iframe/player.ts | 14 --- 4 files changed, 78 insertions(+), 86 deletions(-) diff --git a/docs/maps/api-room.md b/docs/maps/api-room.md index 72947df8..bcc53332 100644 --- a/docs/maps/api-room.md +++ b/docs/maps/api-room.md @@ -1,8 +1,11 @@ {.section-title.accent.text-primary} + # API Room functions Reference ### Working with group layers -If you use group layers in your map, to reference a layer in a group you will need to use a `/` to join layer names together. + +If you use group layers in your map, to reference a layer in a group you will need to use a `/` to join layer names +together. Example :
@@ -12,6 +15,7 @@ Example :
The name of the layers of this map are : + * `entries/start` * `bottom/ground/under` * `bottom/build/carpet` @@ -26,29 +30,32 @@ WA.room.onLeaveLayer(name: string): Subscription Listens to the position of the current user. The event is triggered when the user enters or leaves a given layer. -* **name**: the name of the layer who as defined in Tiled. +* **name**: the name of the layer who as defined in Tiled. Example: ```javascript WA.room.onEnterLayer('myLayer').subscribe(() => { - WA.chat.sendChatMessage("Hello!", 'Mr Robot'); + WA.chat.sendChatMessage("Hello!", 'Mr Robot'); }); WA.room.onLeaveLayer('myLayer').subscribe(() => { - WA.chat.sendChatMessage("Goodbye!", 'Mr Robot'); + WA.chat.sendChatMessage("Goodbye!", 'Mr Robot'); }); ``` ### Show / Hide a layer + ``` WA.room.showLayer(layerName : string): void WA.room.hideLayer(layerName : string) : void ``` -These 2 methods can be used to show and hide a layer. -if `layerName` is the name of a group layer, show/hide all the layer in that group layer. + +These 2 methods can be used to show and hide a layer. if `layerName` is the name of a group layer, show/hide all the +layer in that group layer. Example : + ```javascript WA.room.showLayer('bottom'); //... @@ -61,12 +68,14 @@ WA.room.hideLayer('bottom'); WA.room.setProperty(layerName : string, propertyName : string, propertyValue : string | number | boolean | undefined) : void; ``` -Set the value of the `propertyName` property of the layer `layerName` at `propertyValue`. If the property doesn't exist, create the property `propertyName` and set the value of the property at `propertyValue`. +Set the value of the `propertyName` property of the layer `layerName` at `propertyValue`. If the property doesn't exist, +create the property `propertyName` and set the value of the property at `propertyValue`. Note : To unset a property from a layer, use `setProperty` with `propertyValue` set to `undefined`. Example : + ```javascript WA.room.setProperty('wikiLayer', 'openWebsite', 'https://www.wikipedia.org/'); ``` @@ -79,13 +88,12 @@ WA.room.id: string; The ID of the current room is available from the `WA.room.id` property. -{.alert.alert-info} -You need to wait for the end of the initialization before accessing `WA.room.id` +{.alert.alert-info} You need to wait for the end of the initialization before accessing `WA.room.id` ```typescript WA.onInit().then(() => { - console.log('Room id: ', WA.room.id); - // Will output something like: 'https://play.workadventu.re/@/myorg/myworld/myroom', or 'https://play.workadventu.re/_/global/mymap.org/map.json" + console.log('Room id: ', WA.room.id); + // Will output something like: 'https://play.workadventu.re/@/myorg/myworld/myroom', or 'https://play.workadventu.re/_/global/mymap.org/map.json" }) ``` @@ -97,19 +105,17 @@ WA.room.mapURL: string; The URL of the map is available from the `WA.room.mapURL` property. -{.alert.alert-info} -You need to wait for the end of the initialization before accessing `WA.room.mapURL` +{.alert.alert-info} You need to wait for the end of the initialization before accessing `WA.room.mapURL` ```typescript WA.onInit().then(() => { - console.log('Map URL: ', WA.room.mapURL); - // Will output something like: 'https://mymap.org/map.json" + console.log('Map URL: ', WA.room.mapURL); + // Will output something like: 'https://mymap.org/map.json" }) ``` - - ### Getting map data + ``` WA.room.getTiledMap(): Promise ``` @@ -121,12 +127,16 @@ const map = await WA.room.getTiledMap(); console.log("Map generated with Tiled version ", map.tiledversion); ``` -Check the [Tiled documentation to learn more about the format of the JSON map](https://doc.mapeditor.org/en/stable/reference/json-map-format/). +Check +the [Tiled documentation to learn more about the format of the JSON map](https://doc.mapeditor.org/en/stable/reference/json-map-format/) +. ### Changing tiles + ``` WA.room.setTiles(tiles: TileDescriptor[]): void ``` + Replace the tile at the `x` and `y` coordinates in the layer named `layer` by the tile with the id `tile`. If `tile` is a string, it's not the id of the tile but the value of the property `name`. @@ -137,43 +147,48 @@ If `tile` is a string, it's not the id of the tile but the value of the property
`TileDescriptor` has the following attributes : + * **x (number) :** The coordinate x of the tile that you want to replace. * **y (number) :** The coordinate y of the tile that you want to replace. * **tile (number | string) :** The id of the tile that will be placed in the map. * **layer (string) :** The name of the layer where the tile will be placed. -**Important !** : If you use `tile` as a number, be sure to add the `firstgid` of the tileset of the tile that you want to the id of the tile in Tiled Editor. +**Important !** : If you use `tile` as a number, be sure to add the `firstgid` of the tileset of the tile that you want +to the id of the tile in Tiled Editor. Note: If you want to unset a tile, use `setTiles` with `tile` set to `null`. Example : + ```javascript WA.room.setTiles([ - {x: 6, y: 4, tile: 'blue', layer: 'setTiles'}, - {x: 7, y: 4, tile: 109, layer: 'setTiles'}, - {x: 8, y: 4, tile: 109, layer: 'setTiles'}, - {x: 9, y: 4, tile: 'blue', layer: 'setTiles'} - ]); + { x: 6, y: 4, tile: 'blue', layer: 'setTiles' }, + { x: 7, y: 4, tile: 109, layer: 'setTiles' }, + { x: 8, y: 4, tile: 109, layer: 'setTiles' }, + { x: 9, y: 4, tile: 'blue', layer: 'setTiles' } +]); ``` ### Loading a tileset + ``` WA.room.loadTileset(url: string): Promise ``` + Load a tileset in JSON format from an url and return the id of the first tile of the loaded tileset. You can create a tileset file in Tile Editor. ```javascript WA.room.loadTileset("Assets/Tileset.json").then((firstId) => { - WA.room.setTiles([{x: 4, y: 4, tile: firstId, layer: 'bottom'}]); + WA.room.setTiles([{ x: 4, y: 4, tile: firstId, layer: 'bottom' }]); }) ``` - ## Embedding websites in a map -You can use the scripting API to embed websites in a map, or to edit websites that are already embedded (using the ["website" objects](website-in-map.md)). +You can use the scripting API to embed websites in a map, or to edit websites that are already embedded (using +the ["website" objects](website-in-map.md)). ### Getting an instance of a website already embedded in the map @@ -181,8 +196,8 @@ You can use the scripting API to embed websites in a map, or to edit websites th WA.room.website.get(objectName: string): Promise ``` -You can get an instance of an embedded website by using the `WA.room.website.get()` method. -It returns a promise of an `EmbeddedWebsite` instance. +You can get an instance of an embedded website by using the `WA.room.website.get()` method. It returns a promise of +an `EmbeddedWebsite` instance. ```javascript // Get an existing website object where 'my_website' is the name of the object (on any layer object of the map) @@ -191,7 +206,6 @@ website.url = 'https://example.com'; website.visible = true; ``` - ### Adding a new website in a map ``` @@ -201,34 +215,38 @@ interface CreateEmbeddedWebsiteEvent { name: string; // A unique name for this iframe url: string; // The URL the iframe points to. position: { - x: number, // In pixels, relative to the map coordinates - y: number, // In pixels, relative to the map coordinates - width: number, // In pixels, sensitive to zoom level - height: number, // In pixels, sensitive to zoom level + x: number, // relative to the map or player coordinates, depending on origin + y: number, // relative to the map or player coordinates, depending on origin + width: number, // In pixels, sensitive to zoom level and scale + height: number, // In pixels, sensitive to zoom level and scale }, visible?: boolean, // Whether to display the iframe or not allowApi?: boolean, // Whether the scripting API should be available to the iframe allow?: string, // The list of feature policies allowed + origin: "player" | "map" // The origin used to place the x and y coordinates of the iframe's top-left corner + scale: number, // A ratio used to resize the iframe (will affect the iframe's width and height when it is rendered } ``` -You can create an instance of an embedded website by using the `WA.room.website.create()` method. -It returns an `EmbeddedWebsite` instance. +You can create an instance of an embedded website by using the `WA.room.website.create()` method. It returns +an `EmbeddedWebsite` instance. ```javascript // Create a new website object const website = WA.room.website.create({ - name: "my_website", - url: "https://example.com", - position: { - x: 64, - y: 128, - width: 320, - height: 240, - }, - visible: true, - allowApi: true, - allow: "fullscreen", + name: "my_website", + url: "https://example.com", + position: { + x: 64, + y: 128, + width: 320, + height: 240, + }, + visible: true, + allowApi: true, + allow: "fullscreen", + origin: "map", + scale: 1, }); ``` @@ -240,30 +258,28 @@ WA.room.website.delete(name: string): Promise Use `WA.room.website.delete` to completely remove an embedded website from your map. - ### The EmbeddedWebsite class Instances of the `EmbeddedWebsite` class represent the website displayed on the map. ```typescript class EmbeddedWebsite { - readonly name: string; - url: string; - visible: boolean; - allow: string; - allowApi: boolean; - x: number; // In pixels, relative to the map coordinates - y: number; // In pixels, relative to the map coordinates - width: number; // In pixels, sensitive to zoom level - height: number; // In pixels, sensitive to zoom level + readonly name: string; + url: string; + visible: boolean; + allow: string; + allowApi: boolean; + x: number; // In pixels, relative to the map or player coordinates, depending on origin + y: number; // In pixels, relative to the map or player coordinates, depending on origin + width: number; // In pixels, sensitive to zoom level and scale + height: number; // In pixels, sensitive to zoom level and scale + origin: "player" | "map"; + scale: number; } ``` When you modify a property of an `EmbeddedWebsite` instance, the iframe is automatically modified in the map. - -{.alert.alert-warning} -The websites you add/edit/delete via the scripting API are only shown locally. If you want them -to be displayed for every player, you can use [variables](api-start.md) to share a common state -between all users. +{.alert.alert-warning} The websites you add/edit/delete via the scripting API are only shown locally. If you want them +to be displayed for every player, you can use [variables](api-start.md) to share a common state between all users. diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index dac8509d..8fb488dc 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -28,7 +28,6 @@ import type { MessageReferenceEvent } from "./ui/TriggerActionMessageEvent"; import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent"; import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent"; import type { ChangeLayerEvent } from "./ChangeLayerEvent"; -import { isPlayerPropertyEvent } from "./PlayerPropertyEvent"; import type { ChangeZoneEvent } from "./ChangeZoneEvent"; import { isColorEvent } from "./ColorEvent"; import { isPlayerPosition } from "./PlayerPosition"; @@ -158,14 +157,6 @@ export const iframeQueryMapTypeGuards = { query: isCreateEmbeddedWebsiteEvent, answer: tg.isUndefined, }, - getPlayerProperty: { - query: tg.isString, - answer: isPlayerPropertyEvent, - }, - setPlayerProperty: { - query: isPlayerPropertyEvent, - answer: tg.isUndefined, - }, setPlayerOutline: { query: isColorEvent, answer: tg.isUndefined, diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 3c79bbe2..216a9510 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -32,7 +32,6 @@ import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/Emb import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent"; import type { WasCameraUpdatedEvent } from "./Events/WasCameraUpdatedEvent"; -import type { HasCameraMovedEvent } from "./Events/HasCameraMovedEvent"; import type { ChangeZoneEvent } from "./Events/ChangeZoneEvent"; type AnswererCallback = ( diff --git a/front/src/Api/iframe/player.ts b/front/src/Api/iframe/player.ts index 2a642d75..0c71ae33 100644 --- a/front/src/Api/iframe/player.ts +++ b/front/src/Api/iframe/player.ts @@ -110,20 +110,6 @@ export class WorkadventurePlayerCommands extends IframeApiContribution { - return queryWorkadventure({ - type: "getPlayerProperty", - data: name, - }); - } - - setPlayerProperty(property: PlayerPropertyEvent) { - queryWorkadventure({ - type: "setPlayerProperty", - data: property, - }).catch((e) => console.error(e)); - } } export type Position = { From d9482d484b575c656808de696beb3f249094beee Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Wed, 29 Dec 2021 17:46:46 +0100 Subject: [PATCH 33/46] WIP enable/disable tutorial according to the map 'tutorial' property --- front/src/Phaser/Game/GameMapProperties.ts | 1 + front/src/Phaser/Game/GameScene.ts | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Game/GameMapProperties.ts b/front/src/Phaser/Game/GameMapProperties.ts index 65ce6ab8..2da56ef8 100644 --- a/front/src/Phaser/Game/GameMapProperties.ts +++ b/front/src/Phaser/Game/GameMapProperties.ts @@ -31,6 +31,7 @@ export enum GameMapProperties { SILENT = "silent", START = "start", START_LAYER = "startLayer", + TUTORIAL = "tutorial", URL = "url", WRITABLE_BY = "writableBy", ZONE = "zone", diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index ec12aae8..e32fa98a 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -603,6 +603,10 @@ export class GameScene extends DirtyScene { for (const script of scripts) { scriptPromises.push(iframeListener.registerScript(script)); } + if (this.isTutorialEnabled()) { + //TODO: split tutorial and bundle scripts + scriptPromises.push(iframeListener.registerScript(new URL("bundle.js", this.MapUrlFile).toString())); + } this.userInputManager.spaceEvent(() => { this.outlinedItem?.activate(); @@ -1397,7 +1401,6 @@ ${escapedMessage} this.connection?.emitPlayerOutlineColor(null); }); - iframeListener.registerAnswerer("getPlayerPosition", () => { return { x: this.CurrentPlayer.x, @@ -1569,6 +1572,10 @@ ${escapedMessage} ); } + private isTutorialEnabled(): boolean { + return this.getProperty(this.mapFile, GameMapProperties.TUTORIAL) as boolean; + } + private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined { const properties: ITiledMapProperty[] | undefined = layer.properties; if (!properties) { From 85d45071fa3a78ac79f43c70dc8fb41a5daf8c83 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Thu, 30 Dec 2021 15:50:52 +0100 Subject: [PATCH 34/46] Makes onCameraUpdate subscribe-able --- front/src/Api/iframe/camera.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/front/src/Api/iframe/camera.ts b/front/src/Api/iframe/camera.ts index 4f62b94c..a832290e 100644 --- a/front/src/Api/iframe/camera.ts +++ b/front/src/Api/iframe/camera.ts @@ -1,6 +1,6 @@ import { IframeApiContribution, sendToWorkadventure } from "./IframeApiContribution"; import { Subject } from "rxjs"; -import type { WasCameraUpdatedEvent, WasCameraUpdatedEventCallback } from "../Events/WasCameraUpdatedEvent"; +import type { WasCameraUpdatedEvent } from "../Events/WasCameraUpdatedEvent"; import { apiCallback } from "./registeredCallbacks"; import { isWasCameraUpdatedEvent } from "../Events/WasCameraUpdatedEvent"; @@ -17,12 +17,12 @@ export class WorkAdventureCameraCommands extends IframeApiContribution { sendToWorkadventure({ type: "onCameraUpdate", data: null, }); + return moveStream; } } From 8157ee4603df3db2db3a5a37b537fe2b92557c06 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Mon, 3 Jan 2022 10:36:23 +0100 Subject: [PATCH 35/46] Completes documentation --- docs/maps/api-camera.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/maps/api-camera.md b/docs/maps/api-camera.md index e0b5d7f7..f78df51f 100644 --- a/docs/maps/api-camera.md +++ b/docs/maps/api-camera.md @@ -4,7 +4,7 @@ ### Listen to camera updates ``` -WA.camera.onCameraUpdate(callback: WasCameraUpdatedEventCallback): void +WA.camera.onCameraUpdate: Subscription ``` Listens to updates of the camera viewport. It will trigger for every update of the camera's properties (position or scale for instance). An event will be sent. @@ -19,5 +19,7 @@ The event has the following attributes : Example : ```javascript -WA.camera.onCameraUpdate((worldView) => console.log(worldView)); +WA.camera.onCameraUpdate.subscribe((worldView) => console.log(worldView)); +//later... +WA.camera.onCameraUpdate().unsubscribe(); ``` \ No newline at end of file From ac27ab7e3e91febbbfef4907d6cd2d066fe6fd81 Mon Sep 17 00:00:00 2001 From: Benedicte Quimbert Date: Mon, 3 Jan 2022 11:22:01 +0100 Subject: [PATCH 36/46] Revert "WIP enable/disable tutorial according to the map 'tutorial' property" This reverts commit 47a6710b60e3856bf57c0700fe33bec95c6fc6dd. --- front/src/Phaser/Game/GameMapProperties.ts | 1 - front/src/Phaser/Game/GameScene.ts | 8 -------- 2 files changed, 9 deletions(-) diff --git a/front/src/Phaser/Game/GameMapProperties.ts b/front/src/Phaser/Game/GameMapProperties.ts index 2da56ef8..65ce6ab8 100644 --- a/front/src/Phaser/Game/GameMapProperties.ts +++ b/front/src/Phaser/Game/GameMapProperties.ts @@ -31,7 +31,6 @@ export enum GameMapProperties { SILENT = "silent", START = "start", START_LAYER = "startLayer", - TUTORIAL = "tutorial", URL = "url", WRITABLE_BY = "writableBy", ZONE = "zone", diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index e32fa98a..69683e25 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -603,10 +603,6 @@ export class GameScene extends DirtyScene { for (const script of scripts) { scriptPromises.push(iframeListener.registerScript(script)); } - if (this.isTutorialEnabled()) { - //TODO: split tutorial and bundle scripts - scriptPromises.push(iframeListener.registerScript(new URL("bundle.js", this.MapUrlFile).toString())); - } this.userInputManager.spaceEvent(() => { this.outlinedItem?.activate(); @@ -1572,10 +1568,6 @@ ${escapedMessage} ); } - private isTutorialEnabled(): boolean { - return this.getProperty(this.mapFile, GameMapProperties.TUTORIAL) as boolean; - } - private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined { const properties: ITiledMapProperty[] | undefined = layer.properties; if (!properties) { From 7c34e0a435240ed08e1f4576446844a2b33a7971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Mon, 3 Jan 2022 14:19:55 +0100 Subject: [PATCH 37/46] Update docs/maps/api-camera.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- docs/maps/api-camera.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/api-camera.md b/docs/maps/api-camera.md index f78df51f..c9dc413f 100644 --- a/docs/maps/api-camera.md +++ b/docs/maps/api-camera.md @@ -4,7 +4,7 @@ ### Listen to camera updates ``` -WA.camera.onCameraUpdate: Subscription +WA.camera.onCameraUpdate(): Subscription ``` Listens to updates of the camera viewport. It will trigger for every update of the camera's properties (position or scale for instance). An event will be sent. From 5d0aa835a2c5d55a870a36743ffe48f3877869f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A9n=C3=A9dicte=20Q?= <37311765+HimeShaman@users.noreply.github.com> Date: Mon, 3 Jan 2022 14:21:59 +0100 Subject: [PATCH 38/46] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: David Négrier --- docs/maps/api-camera.md | 5 ++--- docs/maps/api-player.md | 4 ++-- docs/maps/api-room.md | 20 ++++++++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/docs/maps/api-camera.md b/docs/maps/api-camera.md index c9dc413f..cb1fe72d 100644 --- a/docs/maps/api-camera.md +++ b/docs/maps/api-camera.md @@ -19,7 +19,6 @@ The event has the following attributes : Example : ```javascript -WA.camera.onCameraUpdate.subscribe((worldView) => console.log(worldView)); +const subscription = WA.camera.onCameraUpdate().subscribe((worldView) => console.log(worldView)); //later... -WA.camera.onCameraUpdate().unsubscribe(); -``` \ No newline at end of file +subscription.unsubscribe(); \ No newline at end of file diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index 2bfad463..58d5701a 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -101,8 +101,8 @@ The player's current position is available using the `WA.player.getPosition()` f You need to wait for the end of the initialization before calling `WA.player.getPosition()` ```typescript -WA.onInit().then(() => { - console.log('Position: ', WA.player.getPosition()); +WA.onInit().then(async () => { + console.log('Position: ', await WA.player.getPosition()); }) ``` diff --git a/docs/maps/api-room.md b/docs/maps/api-room.md index bcc53332..7d438a1f 100644 --- a/docs/maps/api-room.md +++ b/docs/maps/api-room.md @@ -215,16 +215,16 @@ interface CreateEmbeddedWebsiteEvent { name: string; // A unique name for this iframe url: string; // The URL the iframe points to. position: { - x: number, // relative to the map or player coordinates, depending on origin - y: number, // relative to the map or player coordinates, depending on origin - width: number, // In pixels, sensitive to zoom level and scale - height: number, // In pixels, sensitive to zoom level and scale + x: number, // In "game" pixels, relative to the map or player coordinates, depending on origin + y: number, // In "game" pixels, relative to the map or player coordinates, depending on origin + width: number, // In "game" pixels + height: number, // In "game" pixels }, visible?: boolean, // Whether to display the iframe or not allowApi?: boolean, // Whether the scripting API should be available to the iframe allow?: string, // The list of feature policies allowed - origin: "player" | "map" // The origin used to place the x and y coordinates of the iframe's top-left corner - scale: number, // A ratio used to resize the iframe (will affect the iframe's width and height when it is rendered + origin: "player" | "map" // The origin used to place the x and y coordinates of the iframe's top-left corner, defaults to "map" + scale: number, // A ratio used to resize the iframe } ``` @@ -269,10 +269,10 @@ class EmbeddedWebsite { visible: boolean; allow: string; allowApi: boolean; - x: number; // In pixels, relative to the map or player coordinates, depending on origin - y: number; // In pixels, relative to the map or player coordinates, depending on origin - width: number; // In pixels, sensitive to zoom level and scale - height: number; // In pixels, sensitive to zoom level and scale + x: number; // In "game" pixels, relative to the map or player coordinates, depending on origin + y: number; // In "game" pixels, relative to the map or player coordinates, depending on origin + width: number; // In "game" pixels + height: number; // In "game" pixels origin: "player" | "map"; scale: number; } From bf070c33b5421cfbbdefa5f26306fb2f55bb3c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 3 Jan 2022 15:23:28 +0100 Subject: [PATCH 39/46] Migrating front protobuf encode/decode to ts-proto lib The ts-proto lib has the huge advantage of producing code the "Typescript" way and not the "Java" way. In particular, for "oneof" types in protobuf, it is generating "ADT" that can be used to check if we forgot or not to deal with a type. --- .github/workflows/continuous_integration.yml | 2 +- .github/workflows/push-to-npm.yml | 2 +- front/Dockerfile | 5 +- front/package.json | 1 + front/src/Connexion/AdminMessagesService.ts | 6 +- front/src/Connexion/ConnectionManager.ts | 4 +- front/src/Connexion/ConnexionModels.ts | 36 +- front/src/Connexion/EmoteEventStream.ts | 17 - front/src/Connexion/RoomConnection.ts | 1068 +++++++++-------- front/src/Connexion/WorldFullMessageStream.ts | 12 - front/src/Messages/.gitignore | 1 - .../Messages/ts-proto-generated/.gitignore | 1 + front/src/Network/ProtobufClientUtils.ts | 20 +- front/src/Phaser/Game/EmoteManager.ts | 8 +- front/src/Phaser/Game/GameScene.ts | 56 +- .../src/Phaser/Game/SharedVariablesManager.ts | 2 +- front/src/Stores/PlayersStore.ts | 8 +- front/src/WebRtc/SimplePeer.ts | 14 +- front/yarn.lock | 143 ++- messages/package.json | 8 +- messages/ts-proto-generated/.gitignore | 2 + messages/yarn.lock | 75 +- 22 files changed, 865 insertions(+), 626 deletions(-) delete mode 100644 front/src/Connexion/EmoteEventStream.ts delete mode 100644 front/src/Connexion/WorldFullMessageStream.ts delete mode 100644 front/src/Messages/.gitignore create mode 100644 front/src/Messages/ts-proto-generated/.gitignore create mode 100644 messages/ts-proto-generated/.gitignore diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index 1ad73027..ea67c3c1 100644 --- a/.github/workflows/continuous_integration.yml +++ b/.github/workflows/continuous_integration.yml @@ -39,7 +39,7 @@ jobs: working-directory: "messages" - name: "Build proto messages" - run: yarn run proto && yarn run copy-to-front && yarn run json-copy-to-front + run: yarn run ts-proto && yarn run copy-to-front-ts-proto && yarn run json-copy-to-front working-directory: "messages" - name: "Create index.html" diff --git a/.github/workflows/push-to-npm.yml b/.github/workflows/push-to-npm.yml index 71f2824f..571a16e6 100644 --- a/.github/workflows/push-to-npm.yml +++ b/.github/workflows/push-to-npm.yml @@ -36,7 +36,7 @@ jobs: working-directory: "messages" - name: "Build proto messages" - run: yarn run proto && yarn run copy-to-front && yarn run json-copy-to-front + run: yarn run ts-proto && yarn run copy-to-front-ts-proto && yarn run json-copy-to-front working-directory: "messages" - name: "Create index.html" diff --git a/front/Dockerfile b/front/Dockerfile index f781a37c..49cf6046 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -1,13 +1,14 @@ FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder WORKDIR /usr/src COPY messages . -RUN yarn install && yarn proto +RUN yarn install && yarn ts-proto # we are rebuilding on each deploy to cope with the PUSHER_URL environment URL FROM thecodingmachine/nodejs:14-apache COPY --chown=docker:docker front . -COPY --from=builder --chown=docker:docker /usr/src/generated /var/www/html/src/Messages/generated +COPY --from=builder --chown=docker:docker /usr/src/ts-proto-generated/protos /var/www/html/src/Messages/ts-proto-generated +RUN sed -i 's/import { Observable } from "rxjs";/import type { Observable } from "rxjs";/g' /var/www/html/src/Messages/ts-proto-generated/messages.ts COPY --from=builder --chown=docker:docker /usr/src/JsonMessages /var/www/html/src/Messages/JsonMessages # Removing the iframe.html file from the final image as this adds a XSS attack. diff --git a/front/package.json b/front/package.json index eae92cd2..935c254f 100644 --- a/front/package.json +++ b/front/package.json @@ -62,6 +62,7 @@ "simple-peer": "^9.11.0", "socket.io-client": "^2.3.0", "standardized-audio-context": "^25.2.4", + "ts-proto": "^1.96.0", "uuidv4": "^6.2.10" }, "scripts": { diff --git a/front/src/Connexion/AdminMessagesService.ts b/front/src/Connexion/AdminMessagesService.ts index 0b217760..4b7030ed 100644 --- a/front/src/Connexion/AdminMessagesService.ts +++ b/front/src/Connexion/AdminMessagesService.ts @@ -1,5 +1,5 @@ import { Subject } from "rxjs"; -import type { BanUserMessage, SendUserMessage } from "../Messages/generated/messages_pb"; +import type { BanUserMessage, SendUserMessage } from "../Messages/ts-proto-generated/messages"; export enum AdminMessageEventTypes { admin = "message", @@ -26,8 +26,8 @@ class AdminMessagesService { onSendusermessage(message: SendUserMessage | BanUserMessage) { this._messageStream.next({ - type: message.getType() as unknown as AdminMessageEventTypes, - text: message.getMessage(), + type: message.type as unknown as AdminMessageEventTypes, + text: message.message, }); } } diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 026cc20a..19750ee8 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -280,7 +280,7 @@ class ConnectionManager { reject(error); }); - connection.onConnectingError((event: CloseEvent) => { + connection.connectionErrorStream.subscribe((event: CloseEvent) => { console.log("An error occurred while connecting to socket server. Retrying"); reject( new Error( @@ -292,7 +292,7 @@ class ConnectionManager { ); }); - connection.onConnect((connect: OnConnectInterface) => { + connection.roomJoinedMessageStream.subscribe((connect: OnConnectInterface) => { resolve(connect); }); }).catch((err) => { diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 6200e0c9..bf834a02 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -1,44 +1,12 @@ import type { SignalData } from "simple-peer"; import type { RoomConnection } from "./RoomConnection"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; - -export enum EventMessage { - CONNECT = "connect", - WEBRTC_SIGNAL = "webrtc-signal", - WEBRTC_SCREEN_SHARING_SIGNAL = "webrtc-screen-sharing-signal", - WEBRTC_START = "webrtc-start", - //START_ROOM = "start-room", // From server to client: list of all room users/groups/items - JOIN_ROOM = "join-room", // bi-directional - USER_POSITION = "user-position", // From client to server - USER_MOVED = "user-moved", // From server to client - USER_LEFT = "user-left", // From server to client - MESSAGE_ERROR = "message-error", - WEBRTC_DISCONNECT = "webrtc-disconect", - GROUP_CREATE_UPDATE = "group-create-update", - GROUP_DELETE = "group-delete", - SET_PLAYER_DETAILS = "set-player-details", // Send the name and character to the server (on connect), receive back the id. - ITEM_EVENT = "item-event", - USER_DETAILS_UPDATED = "user-details-updated", - - CONNECT_ERROR = "connect_error", - CONNECTING_ERROR = "connecting_error", - SET_SILENT = "set_silent", // Set or unset the silent mode for this user. - SET_VIEWPORT = "set-viewport", - BATCH = "batch", - - PLAY_GLOBAL_MESSAGE = "play-global-message", - STOP_GLOBAL_MESSAGE = "stop-global-message", - - TELEPORT = "teleport", - USER_MESSAGE = "user-message", - START_JITSI_ROOM = "start-jitsi-room", - SET_VARIABLE = "set-variable", -} +import { PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; export interface PointInterface { x: number; y: number; - direction: string; + direction: string; // TODO: modify this to the enum from ts-proto moving: boolean; } diff --git a/front/src/Connexion/EmoteEventStream.ts b/front/src/Connexion/EmoteEventStream.ts deleted file mode 100644 index 32f1daa0..00000000 --- a/front/src/Connexion/EmoteEventStream.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Subject } from "rxjs"; - -interface EmoteEvent { - userId: number; - emote: string; -} - -class EmoteEventStream { - private _stream: Subject = new Subject(); - public stream = this._stream.asObservable(); - - fire(userId: number, emote: string) { - this._stream.next({ userId, emote }); - } -} - -export const emoteEventStream = new EmoteEventStream(); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 328f1aec..b7aa30ce 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -1,50 +1,9 @@ import { PUSHER_URL, UPLOADER_URL } from "../Enum/EnvironmentVariable"; import Axios from "axios"; -import { - BatchMessage, - ClientToServerMessage, - GroupDeleteMessage, - GroupUpdateMessage, - ItemEventMessage, - PlayGlobalMessage, - PositionMessage, - RoomJoinedMessage, - ServerToClientMessage, - SetPlayerDetailsMessage, - SilentMessage, - StopGlobalMessage, - UserJoinedMessage, - UserLeftMessage, - UserMovedMessage, - UserMovesMessage, - ViewportMessage, - WebRtcDisconnectMessage, - WebRtcSignalToClientMessage, - WebRtcSignalToServerMessage, - WebRtcStartMessage, - ReportPlayerMessage, - TeleportMessageMessage, - QueryJitsiJwtMessage, - SendJitsiJwtMessage, - CharacterLayerMessage, - PingMessage, - EmoteEventMessage, - EmotePromptMessage, - FollowRequestMessage, - FollowConfirmationMessage, - FollowAbortMessage, - SendUserMessage, - BanUserMessage, - VariableMessage, - ErrorMessage, - PlayerDetailsUpdatedMessage, -} from "../Messages/generated/messages_pb"; import type { UserSimplePeerInterface } from "../WebRtc/SimplePeer"; -import Direction = PositionMessage.Direction; import { ProtobufClientUtils } from "../Network/ProtobufClientUtils"; -import { - EventMessage, +import type { GroupCreatedUpdatedMessageInterface, ItemEventMessageInterface, MessageUserJoined, @@ -59,13 +18,44 @@ import { } from "./ConnexionModels"; import type { BodyResourceDescriptionInterface } from "../Phaser/Entity/PlayerTextures"; import { adminMessagesService } from "./AdminMessagesService"; -import { worldFullMessageStream } from "./WorldFullMessageStream"; import { connectionManager } from "./ConnectionManager"; -import { emoteEventStream } from "./EmoteEventStream"; import { get } from "svelte/store"; import { warningContainerStore } from "../Stores/MenuStore"; import { followStateStore, followRoleStore, followUsersStore } from "../Stores/FollowStore"; import { localUserStore } from "./LocalUserStore"; +import { + RefreshRoomMessage, + ServerToClientMessage as ServerToClientMessageTsProto, + TokenExpiredMessage, + WorldConnexionMessage, + WorldFullMessage, + ErrorMessage as ErrorMessageTsProto, + UserMovedMessage as UserMovedMessageTsProto, + GroupUpdateMessage as GroupUpdateMessageTsProto, + GroupDeleteMessage as GroupDeleteMessageTsProto, + UserJoinedMessage as UserJoinedMessageTsProto, + UserLeftMessage as UserLeftMessageTsProto, + ItemEventMessage as ItemEventMessageTsProto, + EmoteEventMessage as EmoteEventMessageTsProto, + VariableMessage as VariableMessageTsProto, + PlayerDetailsUpdatedMessage as PlayerDetailsUpdatedMessageTsProto, + WorldFullWarningMessage, + WebRtcDisconnectMessage as WebRtcDisconnectMessageTsProto, + PlayGlobalMessage as PlayGlobalMessageTsProto, + StopGlobalMessage as StopGlobalMessageTsProto, + SendJitsiJwtMessage as SendJitsiJwtMessageTsProto, + SendUserMessage as SendUserMessageTsProto, + BanUserMessage as BanUserMessageTsProto, + ClientToServerMessage as ClientToServerMessageTsProto, + PositionMessage as PositionMessageTsProto, + ViewportMessage as ViewportMessageTsProto, + PositionMessage_Direction, + SetPlayerDetailsMessage as SetPlayerDetailsMessageTsProto, + PingMessage as PingMessageTsProto, +} from "../Messages/ts-proto-generated/messages"; +import { Subject } from "rxjs"; +import { OpenPopupEvent } from "../Api/Events/OpenPopupEvent"; +import { match } from "assert"; const manualPingDelay = 20000; @@ -78,6 +68,85 @@ export class RoomConnection implements RoomConnection { private tags: string[] = []; private _userRoomToken: string | undefined; + private readonly _errorMessageStream = new Subject(); + public readonly errorMessageStream = this._errorMessageStream.asObservable(); + + private readonly _roomJoinedMessageStream = new Subject<{ + connection: RoomConnection; + room: RoomJoinedMessageInterface; + }>(); + public readonly roomJoinedMessageStream = this._roomJoinedMessageStream.asObservable(); + + private readonly _webRtcStartMessageStream = new Subject(); + public readonly webRtcStartMessageStream = this._webRtcStartMessageStream.asObservable(); + + private readonly _webRtcSignalToClientMessageStream = new Subject(); + public readonly webRtcSignalToClientMessageStream = this._webRtcSignalToClientMessageStream.asObservable(); + + private readonly _webRtcScreenSharingSignalToClientMessageStream = + new Subject(); + public readonly webRtcScreenSharingSignalToClientMessageStream = + this._webRtcScreenSharingSignalToClientMessageStream.asObservable(); + + private readonly _webRtcDisconnectMessageStream = new Subject(); + public readonly webRtcDisconnectMessageStream = this._webRtcDisconnectMessageStream.asObservable(); + + private readonly _playGlobalMessageStream = new Subject(); + public readonly playGlobalMessageStream = this._playGlobalMessageStream.asObservable(); + + private readonly _stopGlobalMessageStream = new Subject(); + public readonly stopGlobalMessageStream = this._stopGlobalMessageStream.asObservable(); + + private readonly _teleportMessageMessageStream = new Subject(); + public readonly teleportMessageMessageStream = this._teleportMessageMessageStream.asObservable(); + + private readonly _sendJitsiJwtMessageStream = new Subject(); + public readonly sendJitsiJwtMessageStream = this._sendJitsiJwtMessageStream.asObservable(); + + private readonly _worldFullMessageStream = new Subject(); + public readonly worldFullMessageStream = this._worldFullMessageStream.asObservable(); + + private readonly _worldConnexionMessageStream = new Subject(); + public readonly worldConnexionMessageStream = this._worldConnexionMessageStream.asObservable(); + + private readonly _tokenExpiredMessageStream = new Subject(); + public readonly tokenExpiredMessageStream = this._tokenExpiredMessageStream.asObservable(); + + private readonly _userMovedMessageStream = new Subject(); + public readonly userMovedMessageStream = this._userMovedMessageStream.asObservable(); + + private readonly _groupUpdateMessageStream = new Subject(); + public readonly groupUpdateMessageStream = this._groupUpdateMessageStream.asObservable(); + + private readonly _groupDeleteMessageStream = new Subject(); + public readonly groupDeleteMessageStream = this._groupDeleteMessageStream.asObservable(); + + private readonly _userJoinedMessageStream = new Subject(); + public readonly userJoinedMessageStream = this._userJoinedMessageStream.asObservable(); + + private readonly _userLeftMessageStream = new Subject(); + public readonly userLeftMessageStream = this._userLeftMessageStream.asObservable(); + + private readonly _itemEventMessageStream = new Subject<{ + itemId: number; + event: string; + parameters: unknown; + state: unknown; + }>(); + public readonly itemEventMessageStream = this._itemEventMessageStream.asObservable(); + + private readonly _emoteEventMessageStream = new Subject(); + public readonly emoteEventMessageStream = this._emoteEventMessageStream.asObservable(); + + private readonly _variableMessageStream = new Subject<{ name: string; value: unknown }>(); + public readonly variableMessageStream = this._variableMessageStream.asObservable(); + + private readonly _playerDetailsUpdatedMessageStream = new Subject(); + public readonly playerDetailsUpdatedMessageStream = this._playerDetailsUpdatedMessageStream.asObservable(); + + private readonly _connectionErrorStream = new Subject(); + public readonly connectionErrorStream = this._connectionErrorStream.asObservable(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any public static setWebsocketFactory(websocketFactory: (url: string) => any): void { RoomConnection.websocketFactory = websocketFactory; @@ -136,8 +205,8 @@ export class RoomConnection implements RoomConnection { this.socket.onopen = (ev) => { //we manually ping every 20s to not be logged out by the server, even when the game is in background. - const pingMessage = new PingMessage(); - interval = setInterval(() => this.socket.send(pingMessage.serializeBinary().buffer), manualPingDelay); + const pingMessage = PingMessageTsProto.encode({}).finish(); + interval = setInterval(() => this.socket.send(pingMessage), manualPingDelay); }; this.socket.addEventListener("close", (event) => { @@ -147,147 +216,264 @@ export class RoomConnection implements RoomConnection { // If we are not connected yet (if a JoinRoomMessage was not sent), we need to retry. if (this.userId === null && !this.closed) { - this.dispatch(EventMessage.CONNECTING_ERROR, event); + this._connectionErrorStream.next(event); } }); this.socket.onmessage = (messageEvent) => { const arrayBuffer: ArrayBuffer = messageEvent.data; - const message = ServerToClientMessage.deserializeBinary(new Uint8Array(arrayBuffer)); - if (message.hasBatchmessage()) { - for (const subMessage of (message.getBatchmessage() as BatchMessage).getPayloadList()) { - let event: string | null = null; - let payload; - if (subMessage.hasUsermovedmessage()) { - event = EventMessage.USER_MOVED; - payload = subMessage.getUsermovedmessage(); - } else if (subMessage.hasGroupupdatemessage()) { - event = EventMessage.GROUP_CREATE_UPDATE; - payload = subMessage.getGroupupdatemessage(); - } else if (subMessage.hasGroupdeletemessage()) { - event = EventMessage.GROUP_DELETE; - payload = subMessage.getGroupdeletemessage(); - } else if (subMessage.hasUserjoinedmessage()) { - event = EventMessage.JOIN_ROOM; - payload = subMessage.getUserjoinedmessage(); - } else if (subMessage.hasUserleftmessage()) { - event = EventMessage.USER_LEFT; - payload = subMessage.getUserleftmessage(); - } else if (subMessage.hasItemeventmessage()) { - event = EventMessage.ITEM_EVENT; - payload = subMessage.getItemeventmessage(); - } else if (subMessage.hasEmoteeventmessage()) { - const emoteMessage = subMessage.getEmoteeventmessage() as EmoteEventMessage; - emoteEventStream.fire(emoteMessage.getActoruserid(), emoteMessage.getEmote()); - } else if (subMessage.hasPlayerdetailsupdatedmessage()) { - event = EventMessage.USER_DETAILS_UPDATED; - payload = subMessage.getPlayerdetailsupdatedmessage(); - } else if (subMessage.hasErrormessage()) { - const errorMessage = subMessage.getErrormessage() as ErrorMessage; - console.error("An error occurred server side: " + errorMessage.getMessage()); - } else if (subMessage.hasVariablemessage()) { - event = EventMessage.SET_VARIABLE; - payload = subMessage.getVariablemessage(); + const serverToClientMessage = ServerToClientMessageTsProto.decode(new Uint8Array(arrayBuffer)); + //const message = ServerToClientMessage.deserializeBinary(new Uint8Array(arrayBuffer)); + + const message = serverToClientMessage.message; + if (message === undefined) { + return; + } + + switch (message.$case) { + case "batchMessage": { + for (const subMessageWrapper of message.batchMessage.payload) { + const subMessage = subMessageWrapper.message; + if (subMessage === undefined) { + return; + } + switch (subMessage.$case) { + case "errorMessage": { + this._errorMessageStream.next(subMessage.errorMessage); + console.error("An error occurred server side: " + subMessage.errorMessage.message); + break; + } + case "userJoinedMessage": { + this._userJoinedMessageStream.next( + this.toMessageUserJoined(subMessage.userJoinedMessage) + ); + break; + } + case "userLeftMessage": { + this._userLeftMessageStream.next(subMessage.userLeftMessage); + break; + } + case "userMovedMessage": { + this._userMovedMessageStream.next(subMessage.userMovedMessage); + break; + } + case "groupUpdateMessage": { + this._groupUpdateMessageStream.next( + this.toGroupCreatedUpdatedMessage(subMessage.groupUpdateMessage) + ); + break; + } + case "groupDeleteMessage": { + this._groupDeleteMessageStream.next(subMessage.groupDeleteMessage); + break; + } + case "itemEventMessage": { + this._itemEventMessageStream.next({ + itemId: subMessage.itemEventMessage.itemId, + event: subMessage.itemEventMessage.event, + parameters: JSON.parse(subMessage.itemEventMessage.parametersJson), + state: JSON.parse(subMessage.itemEventMessage.stateJson), + }); + break; + } + case "emoteEventMessage": { + this._emoteEventMessageStream.next(subMessage.emoteEventMessage); + break; + } + case "playerDetailsUpdatedMessage": { + this._playerDetailsUpdatedMessageStream.next(subMessage.playerDetailsUpdatedMessage); + break; + } + case "variableMessage": { + const name = subMessage.variableMessage.name; + const serializedValue = subMessage.variableMessage.value; + let value: unknown = undefined; + if (serializedValue) { + try { + value = JSON.parse(serializedValue); + } catch (e) { + console.error( + 'Unable to unserialize value received from server for variable "' + + name + + '". Value received: "' + + serializedValue + + '". Error: ', + e + ); + } + } + + this._variableMessageStream.next({ name, value }); + break; + } + default: { + // Security check: if we forget a "case", the line below will catch the error at compile-time. + const tmp: never = subMessage; + } + } + } + break; + } + case "roomJoinedMessage": { + const roomJoinedMessage = message.roomJoinedMessage; + + const items: { [itemId: number]: unknown } = {}; + for (const item of roomJoinedMessage.item) { + items[item.itemId] = JSON.parse(item.stateJson); + } + + const variables = new Map(); + for (const variable of roomJoinedMessage.variable) { + try { + variables.set(variable.name, JSON.parse(variable.value)); + } catch (e) { + console.error( + 'Unable to unserialize value received from server for variable "' + + variable.name + + '". Value received: "' + + variable.value + + '". Error: ', + e + ); + } + } + + this.userId = roomJoinedMessage.currentUserId; + this.tags = roomJoinedMessage.tag; + this._userRoomToken = roomJoinedMessage.userRoomToken; + + this._roomJoinedMessageStream.next({ + connection: this, + room: { + items, + variables, + } as RoomJoinedMessageInterface, + }); + break; + } + case "worldFullMessage": { + this._worldFullMessageStream.next(null); + this.closed = true; + break; + } + case "tokenExpiredMessage": { + connectionManager.logout(); + this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency + break; + } + case "worldConnexionMessage": { + this._worldFullMessageStream.next(message.worldConnexionMessage.message); + this.closed = true; + break; + } + case "webRtcSignalToClientMessage": { + this._webRtcSignalToClientMessageStream.next({ + userId: message.webRtcSignalToClientMessage.userId, + signal: JSON.parse(message.webRtcSignalToClientMessage.signal), + webRtcUser: message.webRtcSignalToClientMessage.webrtcUserName + ? message.webRtcSignalToClientMessage.webrtcUserName + : undefined, + webRtcPassword: message.webRtcSignalToClientMessage.webrtcPassword + ? message.webRtcSignalToClientMessage.webrtcPassword + : undefined, + }); + break; + } + case "webRtcScreenSharingSignalToClientMessage": { + this._webRtcScreenSharingSignalToClientMessageStream.next({ + userId: message.webRtcScreenSharingSignalToClientMessage.userId, + signal: JSON.parse(message.webRtcScreenSharingSignalToClientMessage.signal), + webRtcUser: message.webRtcScreenSharingSignalToClientMessage.webrtcUserName + ? message.webRtcScreenSharingSignalToClientMessage.webrtcUserName + : undefined, + webRtcPassword: message.webRtcScreenSharingSignalToClientMessage.webrtcPassword + ? message.webRtcScreenSharingSignalToClientMessage.webrtcPassword + : undefined, + }); + break; + } + case "webRtcStartMessage": { + this._webRtcStartMessageStream.next({ + userId: message.webRtcStartMessage.userId, + initiator: message.webRtcStartMessage.initiator, + webRtcUser: message.webRtcStartMessage.webrtcUserName + ? message.webRtcStartMessage.webrtcUserName + : undefined, + webRtcPassword: message.webRtcStartMessage.webrtcPassword + ? message.webRtcStartMessage.webrtcPassword + : undefined, + }); + break; + } + case "webRtcDisconnectMessage": { + this._webRtcDisconnectMessageStream.next(message.webRtcDisconnectMessage); + break; + } + case "playGlobalMessage": { + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + this._playGlobalMessageStream.next(message.playGlobalMessage); + break; + } + case "stopGlobalMessage": { + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + this._stopGlobalMessageStream.next(message.stopGlobalMessage); + break; + } + case "teleportMessageMessage": { + // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? + this._teleportMessageMessageStream.next(message.teleportMessageMessage.map); + break; + } + case "sendJitsiJwtMessage": { + this._sendJitsiJwtMessageStream.next(message.sendJitsiJwtMessage); + break; + } + case "sendUserMessage": { + adminMessagesService.onSendusermessage(message.sendUserMessage); + break; + } + case "banUserMessage": { + adminMessagesService.onSendusermessage(message.banUserMessage); + break; + } + case "worldFullWarningMessage": { + warningContainerStore.activateWarningContainer(); + break; + } + case "refreshRoomMessage": { + //todo: implement a way to notify the user the room was refreshed. + break; + } + case "followRequestMessage": { + if (!localUserStore.getIgnoreFollowRequests()) { + followUsersStore.addFollowRequest(message.followRequestMessage.leader); + } + break; + } + case "followConfirmationMessage": { + followUsersStore.addFollower(message.followConfirmationMessage.follower); + break; + } + case "followAbortMessage": { + if (get(followRoleStore) === "follower") { + followUsersStore.stopFollowing(); } else { - throw new Error("Unexpected batch message type"); - } - - if (event) { - this.dispatch(event, payload); + followUsersStore.removeFollower(message.followAbortMessage.follower); } + break; } - } else if (message.hasRoomjoinedmessage()) { - const roomJoinedMessage = message.getRoomjoinedmessage() as RoomJoinedMessage; - - const items: { [itemId: number]: unknown } = {}; - for (const item of roomJoinedMessage.getItemList()) { - items[item.getItemid()] = JSON.parse(item.getStatejson()); + case "errorMessage": { + this._errorMessageStream.next(message.errorMessage); + console.error("An error occurred server side: " + message.errorMessage.message); + break; } - - const variables = new Map(); - for (const variable of roomJoinedMessage.getVariableList()) { - 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 - ); - } + default: { + // Security check: if we forget a "case", the line below will catch the error at compile-time. + const tmp: never = message; } - - this.userId = roomJoinedMessage.getCurrentuserid(); - this.tags = roomJoinedMessage.getTagList(); - this._userRoomToken = roomJoinedMessage.getUserroomtoken(); - - this.dispatch(EventMessage.CONNECT, { - connection: this, - room: { - items, - variables, - } as RoomJoinedMessageInterface, - }); - } else if (message.hasWorldfullmessage()) { - worldFullMessageStream.onMessage(); - this.closed = true; - } else if (message.hasTokenexpiredmessage()) { - connectionManager.logout(); - this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency - } else if (message.hasWorldconnexionmessage()) { - worldFullMessageStream.onMessage(message.getWorldconnexionmessage()?.getMessage()); - this.closed = true; - } else if (message.hasWebrtcsignaltoclientmessage()) { - this.dispatch(EventMessage.WEBRTC_SIGNAL, message.getWebrtcsignaltoclientmessage()); - } else if (message.hasWebrtcscreensharingsignaltoclientmessage()) { - this.dispatch( - EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, - message.getWebrtcscreensharingsignaltoclientmessage() - ); - } else if (message.hasWebrtcstartmessage()) { - this.dispatch(EventMessage.WEBRTC_START, message.getWebrtcstartmessage()); - } else if (message.hasWebrtcdisconnectmessage()) { - this.dispatch(EventMessage.WEBRTC_DISCONNECT, message.getWebrtcdisconnectmessage()); - } else if (message.hasPlayglobalmessage()) { - this.dispatch(EventMessage.PLAY_GLOBAL_MESSAGE, message.getPlayglobalmessage()); - } else if (message.hasStopglobalmessage()) { - this.dispatch(EventMessage.STOP_GLOBAL_MESSAGE, message.getStopglobalmessage()); - } else if (message.hasTeleportmessagemessage()) { - this.dispatch(EventMessage.TELEPORT, message.getTeleportmessagemessage()); - } else if (message.hasSendjitsijwtmessage()) { - this.dispatch(EventMessage.START_JITSI_ROOM, message.getSendjitsijwtmessage()); - } else if (message.hasSendusermessage()) { - adminMessagesService.onSendusermessage(message.getSendusermessage() as SendUserMessage); - } else if (message.hasBanusermessage()) { - adminMessagesService.onSendusermessage(message.getBanusermessage() as BanUserMessage); - } else if (message.hasWorldfullwarningmessage()) { - warningContainerStore.activateWarningContainer(); - } else if (message.hasRefreshroommessage()) { - //todo: implement a way to notify the user the room was refreshed. - } else if (message.hasFollowrequestmessage()) { - const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; - if (!localUserStore.getIgnoreFollowRequests()) { - followUsersStore.addFollowRequest(requestMessage.getLeader()); - } - } else if (message.hasFollowconfirmationmessage()) { - const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; - followUsersStore.addFollower(responseMessage.getFollower()); - } else if (message.hasFollowabortmessage()) { - const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; - if (get(followRoleStore) === "follower") { - followUsersStore.stopFollowing(); - } else { - followUsersStore.removeFollower(abortMessage.getFollower()); - } - } else if (message.hasErrormessage()) { - const errorMessage = message.getErrormessage() as ErrorMessage; - console.error("An error occurred server side: " + errorMessage.getMessage()); - } else { - throw new Error("Unknown message received"); } }; } @@ -314,17 +500,25 @@ export class RoomConnection implements RoomConnection { }*/ public emitPlayerOutlineColor(color: number | null) { - const message = new SetPlayerDetailsMessage(); + let message: SetPlayerDetailsMessageTsProto; if (color === null) { - message.setRemoveoutlinecolor(true); + message = SetPlayerDetailsMessageTsProto.fromPartial({ + removeOutlineColor: true, + }); } else { - message.setOutlinecolor(color); + message = SetPlayerDetailsMessageTsProto.fromPartial({ + outlineColor: color, + }); } - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setSetplayerdetailsmessage(message); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "setPlayerDetailsMessage", + setPlayerDetailsMessage: message, + }, + }).finish(); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public closeConnection(): void { @@ -332,41 +526,35 @@ export class RoomConnection implements RoomConnection { this.closed = true; } - private toPositionMessage(x: number, y: number, direction: string, moving: boolean): PositionMessage { - const positionMessage = new PositionMessage(); - positionMessage.setX(Math.floor(x)); - positionMessage.setY(Math.floor(y)); - let directionEnum: Direction; - switch (direction) { - case "up": - directionEnum = Direction.UP; - break; - case "down": - directionEnum = Direction.DOWN; - break; - case "left": - directionEnum = Direction.LEFT; - break; - case "right": - directionEnum = Direction.RIGHT; - break; - default: - throw new Error("Unexpected direction"); - } - positionMessage.setDirection(directionEnum); - positionMessage.setMoving(moving); - - return positionMessage; + private toPositionMessage(x: number, y: number, direction: string, moving: boolean): PositionMessageTsProto { + return { + x: Math.floor(x), + y: Math.floor(y), + moving, + direction: (() => { + switch (direction) { + case "up": + return PositionMessage_Direction.UP; + case "down": + return PositionMessage_Direction.DOWN; + case "left": + return PositionMessage_Direction.LEFT; + case "right": + return PositionMessage_Direction.RIGHT; + default: + throw new Error("Unexpected direction"); + } + })(), + }; } - private toViewportMessage(viewport: ViewportInterface): ViewportMessage { - const viewportMessage = new ViewportMessage(); - viewportMessage.setLeft(Math.floor(viewport.left)); - viewportMessage.setRight(Math.floor(viewport.right)); - viewportMessage.setTop(Math.floor(viewport.top)); - viewportMessage.setBottom(Math.floor(viewport.bottom)); - - return viewportMessage; + private toViewportMessage(viewport: ViewportInterface): ViewportMessageTsProto { + return { + left: Math.floor(viewport.left), + right: Math.floor(viewport.right), + top: Math.floor(viewport.top), + bottom: Math.floor(viewport.bottom), + }; } public sharePosition(x: number, y: number, direction: string, moving: boolean, viewport: ViewportInterface): void { @@ -378,81 +566,77 @@ export class RoomConnection implements RoomConnection { const viewportMessage = this.toViewportMessage(viewport); - const userMovesMessage = new UserMovesMessage(); - userMovesMessage.setPosition(positionMessage); - userMovesMessage.setViewport(viewportMessage); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "userMovesMessage", + userMovesMessage: { + position: positionMessage, + viewport: viewportMessage, + }, + }, + }).finish(); - //console.log('Sending position ', positionMessage.getX(), positionMessage.getY()); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setUsermovesmessage(userMovesMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public setSilent(silent: boolean): void { - const silentMessage = new SilentMessage(); - silentMessage.setSilent(silent); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "silentMessage", + silentMessage: { + silent, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setSilentmessage(silentMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public setViewport(viewport: ViewportInterface): void { - const viewportMessage = new ViewportMessage(); - viewportMessage.setTop(Math.round(viewport.top)); - viewportMessage.setBottom(Math.round(viewport.bottom)); - viewportMessage.setLeft(Math.round(viewport.left)); - viewportMessage.setRight(Math.round(viewport.right)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "viewportMessage", + viewportMessage: this.toViewportMessage(viewport), + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setViewportmessage(viewportMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } - public onUserJoins(callback: (message: MessageUserJoined) => void): void { + /* public onUserJoins(callback: (message: MessageUserJoined) => void): void { this.onMessage(EventMessage.JOIN_ROOM, (message: UserJoinedMessage) => { callback(this.toMessageUserJoined(message)); }); - } + }*/ // TODO: move this to protobuf utils - private toMessageUserJoined(message: UserJoinedMessage): MessageUserJoined { - const position = message.getPosition(); + private toMessageUserJoined(message: UserJoinedMessageTsProto): MessageUserJoined { + const position = message.position; if (position === undefined) { throw new Error("Invalid JOIN_ROOM message"); } - const characterLayers = message - .getCharacterlayersList() - .map((characterLayer: CharacterLayerMessage): BodyResourceDescriptionInterface => { - return { - name: characterLayer.getName(), - img: characterLayer.getUrl(), - }; - }); + const characterLayers = message.characterLayers.map((characterLayer): BodyResourceDescriptionInterface => { + return { + name: characterLayer.name, + img: characterLayer.url, + }; + }); - const companion = message.getCompanion(); + const companion = message.companion; return { - userId: message.getUserid(), - name: message.getName(), + userId: message.userId, + name: message.name, characterLayers, - visitCardUrl: message.getVisitcardurl(), + visitCardUrl: message.visitCardUrl, position: ProtobufClientUtils.toPointInterface(position), - companion: companion ? companion.getName() : null, - userUuid: message.getUseruuid(), - outlineColor: message.getHasoutline() ? message.getOutlinecolor() : undefined, + companion: companion ? companion.name : null, + userUuid: message.userUuid, + outlineColor: message.hasOutline ? message.outlineColor : undefined, }; } - public onUserMoved(callback: (message: UserMovedMessage) => void): void { - this.onMessage(EventMessage.USER_MOVED, callback); - //this.socket.on(EventMessage.USER_MOVED, callback); - } - /** * Registers a listener on a message that is part of a batch */ @@ -465,114 +649,49 @@ export class RoomConnection implements RoomConnection { callbacks.push(callback); } - public onUserLeft(callback: (userId: number) => void): void { - this.onMessage(EventMessage.USER_LEFT, (message: UserLeftMessage) => { - callback(message.getUserid()); - }); - } - - public onGroupUpdatedOrCreated( - callback: (groupCreateUpdateMessage: GroupCreatedUpdatedMessageInterface) => void - ): void { - this.onMessage(EventMessage.GROUP_CREATE_UPDATE, (message: GroupUpdateMessage) => { - callback(this.toGroupCreatedUpdatedMessage(message)); - }); - } - - private toGroupCreatedUpdatedMessage(message: GroupUpdateMessage): GroupCreatedUpdatedMessageInterface { - const position = message.getPosition(); + private toGroupCreatedUpdatedMessage(message: GroupUpdateMessageTsProto): GroupCreatedUpdatedMessageInterface { + const position = message.position; if (position === undefined) { throw new Error("Missing position in GROUP_CREATE_UPDATE"); } return { - groupId: message.getGroupid(), - position: position.toObject(), - groupSize: message.getGroupsize(), + groupId: message.groupId, + position: position, + groupSize: message.groupSize, }; } - public onGroupDeleted(callback: (groupId: number) => void): void { - this.onMessage(EventMessage.GROUP_DELETE, (message: GroupDeleteMessage) => { - callback(message.getGroupid()); - }); - } - - public onConnectingError(callback: (event: CloseEvent) => void): void { - this.onMessage(EventMessage.CONNECTING_ERROR, (event: CloseEvent) => { - callback(event); - }); - } - public onConnectError(callback: (error: Event) => void): void { this.socket.addEventListener("error", callback); } - public onConnect(callback: (roomConnection: OnConnectInterface) => void): void { - //this.socket.addEventListener('open', callback) - this.onMessage(EventMessage.CONNECT, callback); - } - - /** - * Triggered when we receive all the details of a room (users, groups, ...) - */ - /*public onStartRoom(callback: (event: RoomJoinedMessageInterface) => void): void { - this.onMessage(EventMessage.START_ROOM, callback); - }*/ - public sendWebrtcSignal(signal: unknown, receiverId: number) { - const webRtcSignal = new WebRtcSignalToServerMessage(); - webRtcSignal.setReceiverid(receiverId); - webRtcSignal.setSignal(JSON.stringify(signal)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "webRtcSignalToServerMessage", + webRtcSignalToServerMessage: { + receiverId, + signal: JSON.stringify(signal), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setWebrtcsignaltoservermessage(webRtcSignal); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public sendWebrtcScreenSharingSignal(signal: unknown, receiverId: number) { - const webRtcSignal = new WebRtcSignalToServerMessage(); - webRtcSignal.setReceiverid(receiverId); - webRtcSignal.setSignal(JSON.stringify(signal)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "webRtcScreenSharingSignalToServerMessage", + webRtcScreenSharingSignalToServerMessage: { + receiverId, + signal: JSON.stringify(signal), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setWebrtcscreensharingsignaltoservermessage(webRtcSignal); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); - } - - public receiveWebrtcStart(callback: (message: UserSimplePeerInterface) => void) { - this.onMessage(EventMessage.WEBRTC_START, (message: WebRtcStartMessage) => { - callback({ - userId: message.getUserid(), - initiator: message.getInitiator(), - webRtcUser: message.getWebrtcusername() ?? undefined, - webRtcPassword: message.getWebrtcpassword() ?? undefined, - }); - }); - } - - public receiveWebrtcSignal(callback: (message: WebRtcSignalReceivedMessageInterface) => void) { - this.onMessage(EventMessage.WEBRTC_SIGNAL, (message: WebRtcSignalToClientMessage) => { - callback({ - userId: message.getUserid(), - signal: JSON.parse(message.getSignal()), - webRtcUser: message.getWebrtcusername() ?? undefined, - webRtcPassword: message.getWebrtcpassword() ?? undefined, - }); - }); - } - - public receiveWebrtcScreenSharingSignal(callback: (message: WebRtcSignalReceivedMessageInterface) => void) { - this.onMessage(EventMessage.WEBRTC_SCREEN_SHARING_SIGNAL, (message: WebRtcSignalToClientMessage) => { - callback({ - userId: message.getUserid(), - signal: JSON.parse(message.getSignal()), - webRtcUser: message.getWebrtcusername() ?? undefined, - webRtcPassword: message.getWebrtcpassword() ?? undefined, - }); - }); + this.socket.send(bytes); } public onServerDisconnected(callback: () => void): void { @@ -594,61 +713,34 @@ export class RoomConnection implements RoomConnection { return this.userId; } - disconnectMessage(callback: (message: WebRtcDisconnectMessageInterface) => void): void { - this.onMessage(EventMessage.WEBRTC_DISCONNECT, (message: WebRtcDisconnectMessage) => { - callback({ - userId: message.getUserid(), - }); - }); - } - emitActionableEvent(itemId: number, event: string, state: unknown, parameters: unknown): void { - const itemEventMessage = new ItemEventMessage(); - itemEventMessage.setItemid(itemId); - itemEventMessage.setEvent(event); - itemEventMessage.setStatejson(JSON.stringify(state)); - itemEventMessage.setParametersjson(JSON.stringify(parameters)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "itemEventMessage", + itemEventMessage: { + itemId, + event, + stateJson: JSON.stringify(state), + parametersJson: JSON.stringify(parameters), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setItemeventmessage(itemEventMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } emitSetVariableEvent(name: string, value: unknown): void { - const variableMessage = new VariableMessage(); - variableMessage.setName(name); - variableMessage.setValue(JSON.stringify(value)); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "variableMessage", + variableMessage: { + name, + value: JSON.stringify(value), + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setVariablemessage(variableMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); - } - - onActionableEvent(callback: (message: ItemEventMessageInterface) => void): void { - this.onMessage(EventMessage.ITEM_EVENT, (message: ItemEventMessage) => { - callback({ - itemId: message.getItemid(), - event: message.getEvent(), - parameters: JSON.parse(message.getParametersjson()), - state: JSON.parse(message.getStatejson()), - }); - }); - } - - onPlayerDetailsUpdated(callback: (message: PlayerDetailsUpdatedMessageInterface) => void): void { - this.onMessage(EventMessage.USER_DETAILS_UPDATED, (message: PlayerDetailsUpdatedMessage) => { - const details = message.getDetails(); - if (details === undefined) { - throw new Error("Malformed message. Missing details in PlayerDetailsUpdatedMessage"); - } - callback({ - userId: message.getUserid(), - outlineColor: details.getOutlinecolor(), - removeOutlineColor: details.getRemoveoutlinecolor(), - }); - }); + this.socket.send(bytes); } public uploadAudio(file: FormData) { @@ -662,91 +754,48 @@ export class RoomConnection implements RoomConnection { }); } - /* public receivePlayGlobalMessage(callback: (message: PlayGlobalMessageInterface) => void) { - return this.onMessage(EventMessage.PLAY_GLOBAL_MESSAGE, (message: PlayGlobalMessage) => { - callback({ - id: message.getId(), - type: message.getType(), - message: message.getMessage(), - }); - }); - }*/ - - public receiveStopGlobalMessage(callback: (messageId: string) => void) { - return this.onMessage(EventMessage.STOP_GLOBAL_MESSAGE, (message: StopGlobalMessage) => { - callback(message.getId()); - }); - } - - public receiveTeleportMessage(callback: (messageId: string) => void) { - return this.onMessage(EventMessage.TELEPORT, (message: TeleportMessageMessage) => { - callback(message.getMap()); - }); - } - public emitGlobalMessage(message: PlayGlobalMessageInterface): void { - const playGlobalMessage = new PlayGlobalMessage(); - playGlobalMessage.setType(message.type); - playGlobalMessage.setContent(message.content); - playGlobalMessage.setBroadcasttoworld(message.broadcastToWorld); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "playGlobalMessage", + playGlobalMessage: { + type: message.type, + content: message.content, + broadcastToWorld: message.broadcastToWorld, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setPlayglobalmessage(playGlobalMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public emitReportPlayerMessage(reportedUserUuid: string, reportComment: string): void { - const reportPlayerMessage = new ReportPlayerMessage(); - reportPlayerMessage.setReporteduseruuid(reportedUserUuid); - reportPlayerMessage.setReportcomment(reportComment); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "reportPlayerMessage", + reportPlayerMessage: { + reportedUserUuid, + reportComment, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setReportplayermessage(reportPlayerMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public emitQueryJitsiJwtMessage(jitsiRoom: string, tag: string | undefined): void { - const queryJitsiJwtMessage = new QueryJitsiJwtMessage(); - queryJitsiJwtMessage.setJitsiroom(jitsiRoom); - if (tag !== undefined) { - queryJitsiJwtMessage.setTag(tag); - } + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "queryJitsiJwtMessage", + queryJitsiJwtMessage: { + jitsiRoom, + tag: tag ?? "", // empty string is sent as "undefined" by ts-proto + // TODO: when we migrated "pusher" to ts-proto, migrate this to a StringValue + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setQueryjitsijwtmessage(queryJitsiJwtMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); - } - - public onStartJitsiRoom(callback: (jwt: string, room: string) => void): void { - this.onMessage(EventMessage.START_JITSI_ROOM, (message: SendJitsiJwtMessage) => { - callback(message.getJwt(), message.getJitsiroom()); - }); - } - - public onSetVariable(callback: (name: string, value: unknown) => void): void { - this.onMessage(EventMessage.SET_VARIABLE, (message: VariableMessage) => { - const name = message.getName(); - const serializedValue = message.getValue(); - let value: unknown = undefined; - if (serializedValue) { - try { - value = JSON.parse(serializedValue); - } catch (e) { - console.error( - 'Unable to unserialize value received from server for variable "' + - name + - '". Value received: "' + - serializedValue + - '". Error: ', - e - ); - } - } - callback(name, value); - }); + this.socket.send(bytes); } public hasTag(tag: string): boolean { @@ -758,36 +807,51 @@ export class RoomConnection implements RoomConnection { } public emitEmoteEvent(emoteName: string): void { - const emoteMessage = new EmotePromptMessage(); - emoteMessage.setEmote(emoteName); + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "emotePromptMessage", + emotePromptMessage: { + emote: emoteName, + }, + }, + }).finish(); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setEmotepromptmessage(emoteMessage); - - this.socket.send(clientToServerMessage.serializeBinary().buffer); + this.socket.send(bytes); } public emitFollowRequest(): void { if (!this.userId) { return; } - const message = new FollowRequestMessage(); - message.setLeader(this.userId); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowrequestmessage(message); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "followRequestMessage", + followRequestMessage: { + leader: this.userId, + }, + }, + }).finish(); + + this.socket.send(bytes); } public emitFollowConfirmation(): void { if (!this.userId) { return; } - const message = new FollowConfirmationMessage(); - message.setLeader(get(followUsersStore)[0]); - message.setFollower(this.userId); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowconfirmationmessage(message); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "followConfirmationMessage", + followConfirmationMessage: { + leader: get(followUsersStore)[0], + follower: this.userId, + }, + }, + }).finish(); + + this.socket.send(bytes); } public emitFollowAbort(): void { @@ -796,12 +860,18 @@ export class RoomConnection implements RoomConnection { if (!this.userId || (isLeader && !hasFollowers)) { return; } - const message = new FollowAbortMessage(); - message.setLeader(isLeader ? this.userId : get(followUsersStore)[0]); - message.setFollower(isLeader ? 0 : this.userId); - const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowabortmessage(message); - this.socket.send(clientToServerMessage.serializeBinary().buffer); + + const bytes = ClientToServerMessageTsProto.encode({ + message: { + $case: "followAbortMessage", + followAbortMessage: { + leader: isLeader ? this.userId : get(followUsersStore)[0], + follower: isLeader ? 0 : this.userId, + }, + }, + }).finish(); + + this.socket.send(bytes); } public getAllTags(): string[] { diff --git a/front/src/Connexion/WorldFullMessageStream.ts b/front/src/Connexion/WorldFullMessageStream.ts deleted file mode 100644 index 01ce6f20..00000000 --- a/front/src/Connexion/WorldFullMessageStream.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Subject } from "rxjs"; - -class WorldFullMessageStream { - private _stream: Subject = new Subject(); - public stream = this._stream.asObservable(); - - onMessage(message?: string) { - this._stream.next(message); - } -} - -export const worldFullMessageStream = new WorldFullMessageStream(); diff --git a/front/src/Messages/.gitignore b/front/src/Messages/.gitignore deleted file mode 100644 index 9e0adcc1..00000000 --- a/front/src/Messages/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/generated/ diff --git a/front/src/Messages/ts-proto-generated/.gitignore b/front/src/Messages/ts-proto-generated/.gitignore new file mode 100644 index 00000000..72e8ffc0 --- /dev/null +++ b/front/src/Messages/ts-proto-generated/.gitignore @@ -0,0 +1 @@ +* diff --git a/front/src/Network/ProtobufClientUtils.ts b/front/src/Network/ProtobufClientUtils.ts index 9ba0f40b..3e172d0f 100644 --- a/front/src/Network/ProtobufClientUtils.ts +++ b/front/src/Network/ProtobufClientUtils.ts @@ -1,21 +1,21 @@ -import { PositionMessage } from "../Messages/generated/messages_pb"; -import Direction = PositionMessage.Direction; +import { PositionMessage, PositionMessage_Direction } from "../Messages/ts-proto-generated/messages"; + import type { PointInterface } from "../Connexion/ConnexionModels"; export class ProtobufClientUtils { public static toPointInterface(position: PositionMessage): PointInterface { let direction: string; - switch (position.getDirection()) { - case Direction.UP: + switch (position.direction) { + case PositionMessage_Direction.UP: direction = "up"; break; - case Direction.DOWN: + case PositionMessage_Direction.DOWN: direction = "down"; break; - case Direction.LEFT: + case PositionMessage_Direction.LEFT: direction = "left"; break; - case Direction.RIGHT: + case PositionMessage_Direction.RIGHT: direction = "right"; break; default: @@ -24,10 +24,10 @@ export class ProtobufClientUtils { // sending to all clients in room except sender return { - x: position.getX(), - y: position.getY(), + x: position.x, + y: position.y, direction, - moving: position.getMoving(), + moving: position.moving, }; } } diff --git a/front/src/Phaser/Game/EmoteManager.ts b/front/src/Phaser/Game/EmoteManager.ts index 06e8b099..097ebf45 100644 --- a/front/src/Phaser/Game/EmoteManager.ts +++ b/front/src/Phaser/Game/EmoteManager.ts @@ -1,13 +1,13 @@ -import { emoteEventStream } from "../../Connexion/EmoteEventStream"; import type { GameScene } from "./GameScene"; import type { Subscription } from "rxjs"; +import type { RoomConnection } from "../../Connexion/RoomConnection"; export class EmoteManager { private subscription: Subscription; - constructor(private scene: GameScene) { - this.subscription = emoteEventStream.stream.subscribe((event) => { - const actor = this.scene.MapPlayersByKey.get(event.userId); + constructor(private scene: GameScene, private connection: RoomConnection) { + this.subscription = connection.emoteEventMessageStream.subscribe((event) => { + const actor = this.scene.MapPlayersByKey.get(event.actorUserId); if (actor) { actor.playEmote(event.emote); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 4800e259..bab589e1 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -40,7 +40,6 @@ import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene"; import { GameMap } from "./GameMap"; import { PlayerMovement } from "./PlayerMovement"; import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator"; -import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream"; import { DirtyScene } from "./DirtyScene"; import { TextUtils } from "../Components/TextUtils"; import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick"; @@ -60,7 +59,6 @@ import type { PositionInterface, RoomJoinedMessageInterface, } from "../../Connexion/ConnexionModels"; -import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; import type { RoomConnection } from "../../Connexion/RoomConnection"; import type { ActionableItem } from "../Items/ActionableItem"; import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; @@ -90,7 +88,6 @@ import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile; import { deepCopy } from "deep-copy-ts"; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import { MapStore } from "../../Stores/Utils/MapStore"; -import { SetPlayerDetailsMessage } from "../../Messages/generated/messages_pb"; import { followUsersColorStore, followUsersStore } from "../../Stores/FollowStore"; import { getColorRgbFromHue } from "../../WebRtc/ColorGenerator"; @@ -448,10 +445,6 @@ export class GameScene extends DirtyScene { this.pinchManager = new PinchManager(this); } - this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => - this.showWorldFullError(message) - ); - const playerName = gameManager.getPlayerName(); if (!playerName) { throw "playerName is not set"; @@ -617,8 +610,6 @@ export class GameScene extends DirtyScene { this.connect(); } - this.emoteManager = new EmoteManager(this); - let oldPeerNumber = 0; this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { const newPeerNumber = peers.size; @@ -693,7 +684,7 @@ export class GameScene extends DirtyScene { playersStore.connectToRoomConnection(this.connection); userIsAdminStore.set(this.connection.hasTag("admin")); - this.connection.onUserJoins((message: MessageUserJoined) => { + this.connection.userJoinedMessageStream.subscribe((message) => { const userMessage: AddPlayerInterface = { userId: message.userId, characterLayers: message.characterLayers, @@ -707,31 +698,33 @@ export class GameScene extends DirtyScene { this.addPlayer(userMessage); }); - this.connection.onUserMoved((message: UserMovedMessage) => { - const position = message.getPosition(); + this.connection.userMovedMessageStream.subscribe((message) => { + const position = message.position; if (position === undefined) { throw new Error("Position missing from UserMovedMessage"); } const messageUserMoved: MessageUserMovedInterface = { - userId: message.getUserid(), + userId: message.userId, position: ProtobufClientUtils.toPointInterface(position), }; this.updatePlayerPosition(messageUserMoved); }); - this.connection.onUserLeft((userId: number) => { - this.removePlayer(userId); + this.connection.userLeftMessageStream.subscribe((message) => { + this.removePlayer(message.userId); }); - this.connection.onGroupUpdatedOrCreated((groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { - this.shareGroupPosition(groupPositionMessage); - }); + this.connection.groupUpdateMessageStream.subscribe( + (groupPositionMessage: GroupCreatedUpdatedMessageInterface) => { + this.shareGroupPosition(groupPositionMessage); + } + ); - this.connection.onGroupDeleted((groupId: number) => { + this.connection.groupDeleteMessageStream.subscribe((message) => { try { - this.deleteGroup(groupId); + this.deleteGroup(message.groupId); } catch (e) { console.error(e); } @@ -743,7 +736,7 @@ export class GameScene extends DirtyScene { this.createSuccessorGameScene(true, true); }); - this.connection.onActionableEvent((message) => { + this.connection.itemEventMessageStream.subscribe((message) => { const item = this.actionableItems.get(message.itemId); if (item === undefined) { console.warn( @@ -756,18 +749,29 @@ export class GameScene extends DirtyScene { item.fire(message.event, message.state, message.parameters); }); - this.connection.onPlayerDetailsUpdated((message) => { + this.connection.playerDetailsUpdatedMessageStream.subscribe((message) => { + if (message.details === undefined) { + throw new Error("Malformed message. Missing details in PlayerDetailsUpdatedMessage"); + } this.pendingEvents.enqueue({ type: "PlayerDetailsUpdated", - details: message, + details: { + userId: message.userId, + outlineColor: message.details.outlineColor, + removeOutlineColor: message.details.removeOutlineColor, + }, }); }); /** * Triggered when we receive the JWT token to connect to Jitsi */ - this.connection.onStartJitsiRoom((jwt, room) => { - this.startJitsi(room, jwt); + this.connection.sendJitsiJwtMessageStream.subscribe((message) => { + this.startJitsi(message.jitsiRoom, message.jwt); + }); + + this.messageSubscription = this.connection.worldFullMessageStream.subscribe((message) => { + this.showWorldFullError(message); }); // When connection is performed, let's connect SimplePeer @@ -842,6 +846,8 @@ export class GameScene extends DirtyScene { }); }); + this.emoteManager = new EmoteManager(this, this.connection); + // this.gameMap.onLeaveLayer((layers) => { // layers.forEach((layer) => { // iframeListener.sendLeaveLayerEvent(layer.name); diff --git a/front/src/Phaser/Game/SharedVariablesManager.ts b/front/src/Phaser/Game/SharedVariablesManager.ts index 5b5867dc..62d39d1a 100644 --- a/front/src/Phaser/Game/SharedVariablesManager.ts +++ b/front/src/Phaser/Game/SharedVariablesManager.ts @@ -41,7 +41,7 @@ export class SharedVariablesManager { this._variables.set(name, value); } - roomConnection.onSetVariable((name, value) => { + roomConnection.variableMessageStream.subscribe(({ name, value }) => { this._variables.set(name, value); // On server change, let's notify the iframes diff --git a/front/src/Stores/PlayersStore.ts b/front/src/Stores/PlayersStore.ts index 07c18b96..0676235a 100644 --- a/front/src/Stores/PlayersStore.ts +++ b/front/src/Stores/PlayersStore.ts @@ -3,6 +3,7 @@ import type { PlayerInterface } from "../Phaser/Game/PlayerInterface"; import type { RoomConnection } from "../Connexion/RoomConnection"; import { getRandomColor } from "../WebRtc/ColorGenerator"; import { localUserStore } from "../Connexion/LocalUserStore"; +import room from "../Api/iframe/room"; let idCount = 0; @@ -19,7 +20,8 @@ function createPlayersStore() { connectToRoomConnection: (roomConnection: RoomConnection) => { players = new Map(); set(players); - roomConnection.onUserJoins((message) => { + // TODO: it would be cool to unsubscribe properly here + roomConnection.userJoinedMessageStream.subscribe((message) => { update((users) => { users.set(message.userId, { userId: message.userId, @@ -33,9 +35,9 @@ function createPlayersStore() { return users; }); }); - roomConnection.onUserLeft((userId) => { + roomConnection.userLeftMessageStream.subscribe((message) => { update((users) => { - users.delete(userId); + users.delete(message.userId); return users; }); }); diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts index ccbd0012..f4016015 100644 --- a/front/src/WebRtc/SimplePeer.ts +++ b/front/src/WebRtc/SimplePeer.ts @@ -75,23 +75,25 @@ export class SimplePeer { */ private initialise() { //receive signal by gemer - this.Connection.receiveWebrtcSignal((message: WebRtcSignalReceivedMessageInterface) => { + this.Connection.webRtcSignalToClientMessageStream.subscribe((message: WebRtcSignalReceivedMessageInterface) => { this.receiveWebrtcSignal(message); }); //receive signal by gemer - this.Connection.receiveWebrtcScreenSharingSignal((message: WebRtcSignalReceivedMessageInterface) => { - this.receiveWebrtcScreenSharingSignal(message); - }); + this.Connection.webRtcScreenSharingSignalToClientMessageStream.subscribe( + (message: WebRtcSignalReceivedMessageInterface) => { + this.receiveWebrtcScreenSharingSignal(message); + } + ); mediaManager.showGameOverlay(); //receive message start - this.Connection.receiveWebrtcStart((message: UserSimplePeerInterface) => { + this.Connection.webRtcStartMessageStream.subscribe((message: UserSimplePeerInterface) => { this.receiveWebrtcStart(message); }); - this.Connection.disconnectMessage((data: WebRtcDisconnectMessageInterface): void => { + this.Connection.webRtcDisconnectMessageStream.subscribe((data: WebRtcDisconnectMessageInterface): void => { this.closeConnection(data.userId); }); } diff --git a/front/yarn.lock b/front/yarn.lock index 1557a08e..faf5e681 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -150,6 +150,59 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353" integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q== +"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" + integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= + +"@protobufjs/base64@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" + integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== + +"@protobufjs/codegen@^2.0.4": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" + integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== + +"@protobufjs/eventemitter@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" + integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= + +"@protobufjs/fetch@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" + integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= + dependencies: + "@protobufjs/aspromise" "^1.1.1" + "@protobufjs/inquire" "^1.1.0" + +"@protobufjs/float@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" + integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= + +"@protobufjs/inquire@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" + integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= + +"@protobufjs/path@^1.1.2": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" + integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= + +"@protobufjs/pool@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" + integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= + +"@protobufjs/utf8@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" + integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= + "@sentry/types@^6.11.0": version "6.12.0" resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.12.0.tgz#b7395688a79403c6df8d8bb8d81deb8222519853" @@ -293,6 +346,11 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= +"@types/long@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" + integrity sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w== + "@types/mime@^1": version "1.3.2" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" @@ -317,11 +375,26 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-15.3.0.tgz#d6fed7d6bc6854306da3dea1af9f874b00783e26" integrity sha512-8/bnjSZD86ZfpBsDlCIkNXIvm+h6wi9g7IqL+kmFkQ+Wvu3JrasgLElfiPgoo8V8vVfnEi0QVS12gbl94h9YsQ== +"@types/node@>=13.7.0": + version "17.0.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.5.tgz#57ca67ec4e57ad9e4ef5a6bab48a15387a1c83e0" + integrity sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw== + +"@types/object-hash@^1.3.0": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" + integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== + "@types/pug@^2.0.4": version "2.0.4" resolved "https://registry.yarnpkg.com/@types/pug/-/pug-2.0.4.tgz#8772fcd0418e3cd2cc171555d73007415051f4b2" @@ -1656,6 +1729,11 @@ cssesc@^3.0.0: resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== + debug@2.6.9, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -3662,7 +3740,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.20: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3695,6 +3773,11 @@ lokijs@^1.5.12: resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.12.tgz#cb55b37009bdf09ee7952a6adddd555b893653a0" integrity sha512-Q5ALD6JiS6xAUWCwX3taQmgwxyveCtIIuL08+ml0nHwT3k0S/GIFJN+Hd38b1qYIMaE5X++iqsqWVksz7SYW+Q== +long@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== + lower-case@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -4098,6 +4181,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + object-inspect@^1.9.0: version "1.10.3" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" @@ -4590,6 +4678,11 @@ prettier-plugin-svelte@^2.5.0: resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-2.5.0.tgz#7922534729f7febe59b4c56c3f5360539f0d8ab1" integrity sha512-+iHY2uGChOngrgKielJUnqo74gIL/EO5oeWm8MftFWjEi213lq9QYTOwm1pv4lI1nA61tdgf80CF2i5zMcu1kw== +prettier@^2.0.2: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + prettier@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.1.tgz#76903c3f8c4449bc9ac597acefa24dc5ad4cbea6" @@ -4618,6 +4711,25 @@ progress@^2.0.0: resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +protobufjs@^6.8.8: + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + proxy-addr@~2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" @@ -5819,6 +5931,35 @@ ts-node@^10.4.0: make-error "^1.1.1" yn "3.1.1" +ts-poet@^4.5.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-4.6.1.tgz#015dc823d726655af9f095c900f84ed7c60e2dd3" + integrity sha512-DXJ+mBJIDp+jiaUgB4N5I/sczHHDU2FWacdbDNVAVS4Mh4hb7ckpvUWVW7m7/nAOcjR0r4Wt+7AoO7FeJKExfA== + dependencies: + "@types/prettier" "^1.19.0" + lodash "^4.17.15" + prettier "^2.0.2" + +ts-proto-descriptors@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.3.1.tgz#760ebaaa19475b03662f7b358ffea45b9c5348f5" + integrity sha512-Cybb3fqceMwA6JzHdC32dIo8eVGVmXrM6TWhdk1XQVVHT/6OQqk0ioyX1dIdu3rCIBhRmWUhUE4HsyK+olmgMw== + dependencies: + long "^4.0.0" + protobufjs "^6.8.8" + +ts-proto@^1.96.0: + version "1.96.0" + resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.96.0.tgz#63768d7da533b337aee84db065dd66773bd4cac9" + integrity sha512-fKwaGzi8EOCU9xwmcXK917jj1WhFdLbFkPRawQ+5CAZM9eSXr/mpkz/yEctXCiuei364z6jAB2Odb64KCDFTPQ== + dependencies: + "@types/object-hash" "^1.3.0" + dataloader "^1.4.0" + object-hash "^1.3.1" + protobufjs "^6.8.8" + ts-poet "^4.5.0" + ts-proto-descriptors "^1.2.1" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" diff --git a/messages/package.json b/messages/package.json index 6095a626..e6f04ba5 100644 --- a/messages/package.json +++ b/messages/package.json @@ -4,13 +4,14 @@ "description": "", "scripts": { "proto": "grpc_tools_node_protoc --plugin=protoc-gen-ts=./node_modules/.bin/protoc-gen-ts --grpc_out=generated --js_out=\"import_style=commonjs,binary:generated\" --ts_out=generated -I ./protos protos/*.proto", + "ts-proto": "grpc_tools_node_protoc --plugin=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=ts-proto-generated --ts_proto_opt=oneof=unions --ts_proto_opt=esModuleInterop=true protos/*.proto", "copy-to-back": "rm -rf ../back/src/Messages/generated && cp -rf generated/ ../back/src/Messages/generated", - "copy-to-front": "rm -rf ../front/src/Messages/generated && cp -rf generated/ ../front/src/Messages/generated", + "copy-to-front-ts-proto": "sed 's/import { Observable } from \"rxjs\";/import type { Observable } from \"rxjs\";/g' ts-proto-generated/protos/messages.ts > ../front/src/Messages/ts-proto-generated/messages.ts", "copy-to-pusher": "rm -rf ../pusher/src/Messages/generated && cp -rf generated/ ../pusher/src/Messages/generated", "json-copy-to-pusher": "rm -rf ../pusher/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../pusher/src/Messages/JsonMessages/", "json-copy-to-front": "rm -rf ../front/src/Messages/JsonMessages/* && cp -rf JsonMessages/* ../front/src/Messages/JsonMessages/", "precommit": "lint-staged", - "proto-all": "yarn run proto && yarn run copy-to-back && yarn run copy-to-front && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-front", + "proto-all": "yarn run proto && yarn run ts-proto && yarn run copy-to-back && yarn run copy-to-front-ts-proto && yarn run copy-to-pusher && yarn run json-copy-to-pusher && yarn run json-copy-to-front", "proto:watch": "yarn run proto-all; inotifywait -q -m -e close_write protos/messages.proto JsonMessages/ | while read -r filename event; do yarn run proto-all; done", "pretty": "yarn prettier --write 'JsonMessages/**/*.ts'", "pretty-check": "yarn prettier --check 'JsonMessages/**/*.ts'" @@ -18,7 +19,8 @@ "dependencies": { "generic-type-guard": "^3.5.0", "google-protobuf": "^3.13.0", - "grpc": "^1.24.4" + "grpc": "^1.24.4", + "ts-proto": "^1.96.0" }, "devDependencies": { "@types/google-protobuf": "^3.7.4", diff --git a/messages/ts-proto-generated/.gitignore b/messages/ts-proto-generated/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/messages/ts-proto-generated/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/messages/yarn.lock b/messages/yarn.lock index 5d040c6f..8ba2fe90 100644 --- a/messages/yarn.lock +++ b/messages/yarn.lock @@ -174,6 +174,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.7.tgz#8ea1e8f8eae2430cf440564b98c6dfce1ec5945d" integrity sha512-Zw1vhUSQZYw+7u5dAwNbIA9TuTotpzY/OF7sJM9FqPOF3SPjKnxrjoTktXDZgUjybf4cWVBP7O8wvKdSaGHweg== +"@types/node@>=13.7.0": + version "17.0.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.5.tgz#57ca67ec4e57ad9e4ef5a6bab48a15387a1c83e0" + integrity sha512-w3mrvNXLeDYV1GKTZorGJQivK6XLCoGwpnyJFbJVK/aTBQUxOCaa/GlFAAN3OTDFcb7h5tiFG+YXCO2By+riZw== + "@types/node@^12.12.29": version "12.19.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.4.tgz#cdfbb62e26c7435ed9aab9c941393cc3598e9b46" @@ -184,6 +189,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.30.tgz#1ed6e01e4ca576d5aec9cc802cc3bcf94c274192" integrity sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA== +"@types/object-hash@^1.3.0": + version "1.3.4" + resolved "https://registry.yarnpkg.com/@types/object-hash/-/object-hash-1.3.4.tgz#079ba142be65833293673254831b5e3e847fe58b" + integrity sha512-xFdpkAkikBgqBdG9vIlsqffDV8GpvnPEzs0IUtr1v3BEB97ijsFQ4RXVbUZwjFThhB4MDSTUfvmxUD5PGx0wXA== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -194,6 +204,11 @@ resolved "https://registry.yarnpkg.com/@types/parsimmon/-/parsimmon-1.10.4.tgz#7639e16015440d9baf622f83c12dae47787226b7" integrity sha512-M56NfQHfaWuaj6daSgCVs7jh8fXLI3LmxjRoQxmOvYesgIkI+9HPsDLO0vd7wX7cwA0D0ZWFEJdp0VPwLdS+bQ== +"@types/prettier@^1.19.0": + version "1.19.1" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-1.19.1.tgz#33509849f8e679e4add158959fdb086440e9553f" + integrity sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ== + "@typescript-eslint/eslint-plugin@^4.7.0": version "4.7.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.7.0.tgz#85c9bbda00c0cb604d3c241f7bc7fb171a2d3479" @@ -1156,6 +1171,11 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +dataloader@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/dataloader/-/dataloader-1.4.0.tgz#bca11d867f5d3f1b9ed9f737bd15970c65dff5c8" + integrity sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw== + date.js@^0.3.1: version "0.3.3" resolved "https://registry.yarnpkg.com/date.js/-/date.js-0.3.3.tgz#ef1e92332f507a638795dbb985e951882e50bbda" @@ -3154,6 +3174,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" +object-hash@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" + integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== + object-inspect@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" @@ -3423,7 +3448,7 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prettier@^2.3.1: +prettier@^2.0.2, prettier@^2.3.1: version "2.5.1" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== @@ -3467,6 +3492,25 @@ protobufjs@^6.10.1: "@types/node" "^13.7.0" long "^4.0.0" +protobufjs@^6.8.8: + version "6.11.2" + resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" + integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== + dependencies: + "@protobufjs/aspromise" "^1.1.2" + "@protobufjs/base64" "^1.1.2" + "@protobufjs/codegen" "^2.0.4" + "@protobufjs/eventemitter" "^1.1.0" + "@protobufjs/fetch" "^1.1.0" + "@protobufjs/float" "^1.0.2" + "@protobufjs/inquire" "^1.1.0" + "@protobufjs/path" "^1.1.2" + "@protobufjs/pool" "^1.1.0" + "@protobufjs/utf8" "^1.1.0" + "@types/long" "^4.0.1" + "@types/node" ">=13.7.0" + long "^4.0.0" + psl@^1.1.28: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -4245,6 +4289,35 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +ts-poet@^4.5.0: + version "4.6.1" + resolved "https://registry.yarnpkg.com/ts-poet/-/ts-poet-4.6.1.tgz#015dc823d726655af9f095c900f84ed7c60e2dd3" + integrity sha512-DXJ+mBJIDp+jiaUgB4N5I/sczHHDU2FWacdbDNVAVS4Mh4hb7ckpvUWVW7m7/nAOcjR0r4Wt+7AoO7FeJKExfA== + dependencies: + "@types/prettier" "^1.19.0" + lodash "^4.17.15" + prettier "^2.0.2" + +ts-proto-descriptors@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/ts-proto-descriptors/-/ts-proto-descriptors-1.3.1.tgz#760ebaaa19475b03662f7b358ffea45b9c5348f5" + integrity sha512-Cybb3fqceMwA6JzHdC32dIo8eVGVmXrM6TWhdk1XQVVHT/6OQqk0ioyX1dIdu3rCIBhRmWUhUE4HsyK+olmgMw== + dependencies: + long "^4.0.0" + protobufjs "^6.8.8" + +ts-proto@^1.96.0: + version "1.96.0" + resolved "https://registry.yarnpkg.com/ts-proto/-/ts-proto-1.96.0.tgz#63768d7da533b337aee84db065dd66773bd4cac9" + integrity sha512-fKwaGzi8EOCU9xwmcXK917jj1WhFdLbFkPRawQ+5CAZM9eSXr/mpkz/yEctXCiuei364z6jAB2Odb64KCDFTPQ== + dependencies: + "@types/object-hash" "^1.3.0" + dataloader "^1.4.0" + object-hash "^1.3.1" + protobufjs "^6.8.8" + ts-poet "^4.5.0" + ts-proto-descriptors "^1.2.1" + tsconfig-paths@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz#098547a6c4448807e8fcb8eae081064ee9a3c90b" From 6bd9a998f97b6ad1bc40b29b50fd52a4c2cebbcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 3 Jan 2022 16:16:32 +0100 Subject: [PATCH 40/46] Removing unused messages --- back/src/RoomManager.ts | 5 ----- back/src/Services/SocketManager.ts | 9 --------- front/src/Connexion/RoomConnection.ts | 18 ------------------ messages/protos/messages.proto | 4 ---- 4 files changed, 36 deletions(-) diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 3bb425b7..d375fbd8 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -106,11 +106,6 @@ const roomManager: IRoomManagerServer = { user, message.getWebrtcscreensharingsignaltoservermessage() as WebRtcSignalToServerMessage ); - } else if (message.hasPlayglobalmessage()) { - socketManager.emitPlayGlobalMessage( - room, - message.getPlayglobalmessage() as PlayGlobalMessage - ); } else if (message.hasQueryjitsijwtmessage()) { socketManager.handleQueryJitsiJwtMessage( user, diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index c9da7c96..9233811b 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -531,15 +531,6 @@ export class SocketManager { } } - emitPlayGlobalMessage(room: GameRoom, playGlobalMessage: PlayGlobalMessage) { - const serverToClientMessage = new ServerToClientMessage(); - serverToClientMessage.setPlayglobalmessage(playGlobalMessage); - - for (const [id, user] of room.getUsers().entries()) { - user.socket.write(serverToClientMessage); - } - } - public getWorlds(): Map> { return this.roomsPromises; } diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index b7aa30ce..96c6dd23 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -91,12 +91,6 @@ export class RoomConnection implements RoomConnection { private readonly _webRtcDisconnectMessageStream = new Subject(); public readonly webRtcDisconnectMessageStream = this._webRtcDisconnectMessageStream.asObservable(); - private readonly _playGlobalMessageStream = new Subject(); - public readonly playGlobalMessageStream = this._playGlobalMessageStream.asObservable(); - - private readonly _stopGlobalMessageStream = new Subject(); - public readonly stopGlobalMessageStream = this._stopGlobalMessageStream.asObservable(); - private readonly _teleportMessageMessageStream = new Subject(); public readonly teleportMessageMessageStream = this._teleportMessageMessageStream.asObservable(); @@ -410,18 +404,6 @@ export class RoomConnection implements RoomConnection { this._webRtcDisconnectMessageStream.next(message.webRtcDisconnectMessage); break; } - case "playGlobalMessage": { - // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? - // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? - this._playGlobalMessageStream.next(message.playGlobalMessage); - break; - } - case "stopGlobalMessage": { - // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? - // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? - this._stopGlobalMessageStream.next(message.stopGlobalMessage); - break; - } case "teleportMessageMessage": { // FIXME: WHY IS THIS UNUSED? CAN WE REMOVE THIS??? this._teleportMessageMessageStream.next(message.teleportMessageMessage.map); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 3c05037a..8ac7bbf0 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -296,8 +296,6 @@ message ServerToClientMessage { WebRtcSignalToClientMessage webRtcSignalToClientMessage = 5; WebRtcSignalToClientMessage webRtcScreenSharingSignalToClientMessage = 6; WebRtcDisconnectMessage webRtcDisconnectMessage = 7; - PlayGlobalMessage playGlobalMessage = 8; - StopGlobalMessage stopGlobalMessage = 9; TeleportMessageMessage teleportMessageMessage = 10; SendJitsiJwtMessage sendJitsiJwtMessage = 11; SendUserMessage sendUserMessage = 12; @@ -390,8 +388,6 @@ message PusherToBackMessage { SetPlayerDetailsMessage setPlayerDetailsMessage = 5; WebRtcSignalToServerMessage webRtcSignalToServerMessage = 6; WebRtcSignalToServerMessage webRtcScreenSharingSignalToServerMessage = 7; - PlayGlobalMessage playGlobalMessage = 8; - StopGlobalMessage stopGlobalMessage = 9; ReportPlayerMessage reportPlayerMessage = 10; QueryJitsiJwtMessage queryJitsiJwtMessage = 11; SendUserMessage sendUserMessage = 12; From 7b239e8ebbe28613a0c0587715bd6ca3b78f059b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 3 Jan 2022 16:28:49 +0100 Subject: [PATCH 41/46] Disabling running end to end tests for forks Github Actions does not provide big enough VMs for E2E tests. So we need to spin our own instances on AWS. But we cannot share the credentials with the forks for obvious security reasons. We therefore need to completely disable E2E tests for forks :( --- .github/workflows/end_to_end_tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/end_to_end_tests.yml b/.github/workflows/end_to_end_tests.yml index a1cb5e5d..ea9ba41c 100644 --- a/.github/workflows/end_to_end_tests.yml +++ b/.github/workflows/end_to_end_tests.yml @@ -12,6 +12,7 @@ on: jobs: start-runner: + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) name: Start self-hosted EC2 runner runs-on: ubuntu-latest outputs: @@ -109,12 +110,14 @@ jobs: if: ${{ always() }} # required to stop the runner even if the error happened in the previous jobs steps: - name: Configure AWS credentials + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ secrets.AWS_REGION }} - name: Stop EC2 runner + if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false) uses: machulav/ec2-github-runner@v2 with: mode: stop From 6e27ffb2d55ae4113458e25732adff07b192a038 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 18:18:55 +0100 Subject: [PATCH 42/46] Starting fixing unhandled promises --- front/.eslintrc.js | 1 - front/src/Administration/AnalyticsClient.ts | 20 +++++++++---------- front/src/Api/iframe/Ui/ActionMessage.ts | 2 +- front/src/Api/iframe/state.ts | 2 +- .../AudioManager/AudioManager.svelte | 2 +- .../Menu/GlobalMessagesSubMenu.svelte | 6 +++--- .../src/Components/Menu/ProfileSubMenu.svelte | 4 ++-- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/front/.eslintrc.js b/front/.eslintrc.js index 117cb7e6..dc2b6bd6 100644 --- a/front/.eslintrc.js +++ b/front/.eslintrc.js @@ -35,7 +35,6 @@ module.exports = { "no-unused-vars": "off", "@typescript-eslint/no-explicit-any": "error", // TODO: remove those ignored rules and write a stronger code! - "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/restrict-plus-operands": "off", "@typescript-eslint/no-unsafe-assignment": "off", diff --git a/front/src/Administration/AnalyticsClient.ts b/front/src/Administration/AnalyticsClient.ts index fb2b604b..4248339b 100644 --- a/front/src/Administration/AnalyticsClient.ts +++ b/front/src/Administration/AnalyticsClient.ts @@ -20,62 +20,62 @@ class AnalyticsClient { identifyUser(uuid: string, email: string | null) { this.posthogPromise?.then((posthog) => { posthog.identify(uuid, { uuid, email, wa: true }); - }); + }).catch(e => console.error(e)); } loggedWithSso() { this.posthogPromise?.then((posthog) => { posthog.capture("wa-logged-sso"); - }); + }).catch(e => console.error(e)); } loggedWithToken() { this.posthogPromise?.then((posthog) => { posthog.capture("wa-logged-token"); - }); + }).catch(e => console.error(e)); } enteredRoom(roomId: string, roomGroup: string | null) { this.posthogPromise?.then((posthog) => { posthog.capture("$pageView", { roomId, roomGroup }); posthog.capture("enteredRoom"); - }); + }).catch(e => console.error(e)); } openedMenu() { this.posthogPromise?.then((posthog) => { posthog.capture("wa-opened-menu"); - }); + }).catch(e => console.error(e)); } launchEmote(emote: string) { this.posthogPromise?.then((posthog) => { posthog.capture("wa-emote-launch", { emote }); - }); + }).catch(e => console.error(e)); } enteredJitsi(roomName: string, roomId: string) { this.posthogPromise?.then((posthog) => { posthog.capture("wa-entered-jitsi", { roomName, roomId }); - }); + }).catch(e => console.error(e)); } validationName() { this.posthogPromise?.then((posthog) => { posthog.capture("wa-name-validation"); - }); + }).catch(e => console.error(e)); } validationWoka(scene: string) { this.posthogPromise?.then((posthog) => { posthog.capture("wa-woka-validation", { scene }); - }); + }).catch(e => console.error(e)); } validationVideo() { this.posthogPromise?.then((posthog) => { posthog.capture("wa-video-validation"); - }); + }).catch(e => console.error(e)); } } export const analyticsClient = new AnalyticsClient(); diff --git a/front/src/Api/iframe/Ui/ActionMessage.ts b/front/src/Api/iframe/Ui/ActionMessage.ts index 912603b9..f4e6a937 100644 --- a/front/src/Api/iframe/Ui/ActionMessage.ts +++ b/front/src/Api/iframe/Ui/ActionMessage.ts @@ -26,7 +26,7 @@ export class ActionMessage { this.message = actionMessageOptions.message; this.type = actionMessageOptions.type ?? "message"; this.callback = actionMessageOptions.callback; - this.create(); + this.create().catch(e => console.error(e)); } private async create() { diff --git a/front/src/Api/iframe/state.ts b/front/src/Api/iframe/state.ts index 7021b251..ccc671f6 100644 --- a/front/src/Api/iframe/state.ts +++ b/front/src/Api/iframe/state.ts @@ -95,7 +95,7 @@ export function createState(target: "global" | "player"): WorkadventureStateComm set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean { // Note: when using "set", there is no way to wait, so we ignore the return of the promise. // User must use WA.state.saveVariable to have error message. - target.saveVariable(p.toString(), value); + target.saveVariable(p.toString(), value).catch(e => console.error(e)); return true; }, has(target: WorkadventureStateCommands, p: PropertyKey): boolean { diff --git a/front/src/Components/AudioManager/AudioManager.svelte b/front/src/Components/AudioManager/AudioManager.svelte index c4ca44f9..b62d8fbe 100644 --- a/front/src/Components/AudioManager/AudioManager.svelte +++ b/front/src/Components/AudioManager/AudioManager.svelte @@ -25,7 +25,7 @@ HTMLAudioPlayer.loop = get(audioManagerVolumeStore).loop; HTMLAudioPlayer.volume = get(audioManagerVolumeStore).volume; HTMLAudioPlayer.muted = get(audioManagerVolumeStore).muted; - HTMLAudioPlayer.play(); + void HTMLAudioPlayer.play(); }); unsubscriberVolumeStore = audioManagerVolumeStore.subscribe((audioManager: audioManagerVolume) => { const reduceVolume = audioManager.talking && audioManager.decreaseWhileTalking; diff --git a/front/src/Components/Menu/GlobalMessagesSubMenu.svelte b/front/src/Components/Menu/GlobalMessagesSubMenu.svelte index 524e5e50..e755a243 100644 --- a/front/src/Components/Menu/GlobalMessagesSubMenu.svelte +++ b/front/src/Components/Menu/GlobalMessagesSubMenu.svelte @@ -19,12 +19,12 @@ uploadAudioActive = true; } - function send() { + async function send(): Promise { if (inputSendTextActive) { - handleSendText.sendTextMessage(broadcastToWorld); + return handleSendText.sendTextMessage(broadcastToWorld); } if (uploadAudioActive) { - handleSendAudio.sendAudioMessage(broadcastToWorld); + return handleSendAudio.sendAudioMessage(broadcastToWorld); } } diff --git a/front/src/Components/Menu/ProfileSubMenu.svelte b/front/src/Components/Menu/ProfileSubMenu.svelte index 07356f6c..87bf57c9 100644 --- a/front/src/Components/Menu/ProfileSubMenu.svelte +++ b/front/src/Components/Menu/ProfileSubMenu.svelte @@ -41,10 +41,10 @@ gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); } - function logOut() { + async function logOut() { disableMenuStores(); loginSceneVisibleStore.set(true); - connectionManager.logout(); + return connectionManager.logout(); } function getProfileUrl() { From 24baf5664cf1cc04cdd70fb726b0e2ba667a5e4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 4 Jan 2022 16:48:47 +0100 Subject: [PATCH 43/46] All promises are now handled --- front/src/Administration/AnalyticsClient.ts | 82 +++++++----- front/src/Api/iframe/Ui/ActionMessage.ts | 2 +- front/src/Api/iframe/state.ts | 2 +- .../Components/Menu/SettingsSubMenu.svelte | 22 ++-- front/src/Components/UI/AudioPlaying.svelte | 2 +- front/src/Connexion/ConnectionManager.ts | 4 +- front/src/Connexion/LocalUserStore.ts | 9 +- front/src/Connexion/RoomConnection.ts | 2 +- front/src/Phaser/Companion/Companion.ts | 16 ++- .../CompanionTexturesLoadingManager.ts | 2 +- front/src/Phaser/Components/Loader.ts | 8 +- .../Phaser/Game/GameMapPropertiesListener.ts | 7 +- front/src/Phaser/Game/GameScene.ts | 97 ++++++++------ front/src/Phaser/Login/CustomizeScene.ts | 28 ++-- .../src/Phaser/Login/SelectCharacterScene.ts | 14 +- front/src/Stores/MediaStore.ts | 29 ++--- front/src/Stores/ScreenSharingStore.ts | 2 +- front/src/Url/UrlManager.ts | 2 +- front/src/WebRtc/CoWebsiteManager.ts | 122 +++++++++--------- front/src/WebRtc/JitsiFactory.ts | 8 +- front/src/iframe_api.ts | 10 +- 21 files changed, 262 insertions(+), 208 deletions(-) diff --git a/front/src/Administration/AnalyticsClient.ts b/front/src/Administration/AnalyticsClient.ts index 4248339b..4c1ca93a 100644 --- a/front/src/Administration/AnalyticsClient.ts +++ b/front/src/Administration/AnalyticsClient.ts @@ -18,64 +18,84 @@ class AnalyticsClient { } identifyUser(uuid: string, email: string | null) { - this.posthogPromise?.then((posthog) => { - posthog.identify(uuid, { uuid, email, wa: true }); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.identify(uuid, { uuid, email, wa: true }); + }) + .catch((e) => console.error(e)); } loggedWithSso() { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-logged-sso"); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-logged-sso"); + }) + .catch((e) => console.error(e)); } loggedWithToken() { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-logged-token"); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-logged-token"); + }) + .catch((e) => console.error(e)); } enteredRoom(roomId: string, roomGroup: string | null) { - this.posthogPromise?.then((posthog) => { - posthog.capture("$pageView", { roomId, roomGroup }); - posthog.capture("enteredRoom"); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("$pageView", { roomId, roomGroup }); + posthog.capture("enteredRoom"); + }) + .catch((e) => console.error(e)); } openedMenu() { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-opened-menu"); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-opened-menu"); + }) + .catch((e) => console.error(e)); } launchEmote(emote: string) { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-emote-launch", { emote }); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-emote-launch", { emote }); + }) + .catch((e) => console.error(e)); } enteredJitsi(roomName: string, roomId: string) { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-entered-jitsi", { roomName, roomId }); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-entered-jitsi", { roomName, roomId }); + }) + .catch((e) => console.error(e)); } validationName() { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-name-validation"); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-name-validation"); + }) + .catch((e) => console.error(e)); } validationWoka(scene: string) { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-woka-validation", { scene }); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-woka-validation", { scene }); + }) + .catch((e) => console.error(e)); } validationVideo() { - this.posthogPromise?.then((posthog) => { - posthog.capture("wa-video-validation"); - }).catch(e => console.error(e)); + this.posthogPromise + ?.then((posthog) => { + posthog.capture("wa-video-validation"); + }) + .catch((e) => console.error(e)); } } export const analyticsClient = new AnalyticsClient(); diff --git a/front/src/Api/iframe/Ui/ActionMessage.ts b/front/src/Api/iframe/Ui/ActionMessage.ts index f4e6a937..ff0908ff 100644 --- a/front/src/Api/iframe/Ui/ActionMessage.ts +++ b/front/src/Api/iframe/Ui/ActionMessage.ts @@ -26,7 +26,7 @@ export class ActionMessage { this.message = actionMessageOptions.message; this.type = actionMessageOptions.type ?? "message"; this.callback = actionMessageOptions.callback; - this.create().catch(e => console.error(e)); + this.create().catch((e) => console.error(e)); } private async create() { diff --git a/front/src/Api/iframe/state.ts b/front/src/Api/iframe/state.ts index ccc671f6..278b208e 100644 --- a/front/src/Api/iframe/state.ts +++ b/front/src/Api/iframe/state.ts @@ -95,7 +95,7 @@ export function createState(target: "global" | "player"): WorkadventureStateComm set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean { // Note: when using "set", there is no way to wait, so we ignore the return of the promise. // User must use WA.state.saveVariable to have error message. - target.saveVariable(p.toString(), value).catch(e => console.error(e)); + target.saveVariable(p.toString(), value).catch((e) => console.error(e)); return true; }, has(target: WorkadventureStateCommands, p: PropertyKey): boolean { diff --git a/front/src/Components/Menu/SettingsSubMenu.svelte b/front/src/Components/Menu/SettingsSubMenu.svelte index 1db14036..1ad1ac8b 100644 --- a/front/src/Components/Menu/SettingsSubMenu.svelte +++ b/front/src/Components/Menu/SettingsSubMenu.svelte @@ -33,9 +33,9 @@ const body = HtmlUtils.querySelectorOrFail("body"); if (body) { if (document.fullscreenElement !== null && !fullscreen) { - document.exitFullscreen(); + document.exitFullscreen().catch((e) => console.error(e)); } else { - body.requestFullscreen(); + body.requestFullscreen().catch((e) => console.error(e)); } localUserStore.setFullscreen(fullscreen); } @@ -45,14 +45,16 @@ if (Notification.permission === "granted") { localUserStore.setNotification(notification ? "granted" : "denied"); } else { - Notification.requestPermission().then((response) => { - if (response === "granted") { - localUserStore.setNotification(notification ? "granted" : "denied"); - } else { - localUserStore.setNotification("denied"); - notification = false; - } - }); + Notification.requestPermission() + .then((response) => { + if (response === "granted") { + localUserStore.setNotification(notification ? "granted" : "denied"); + } else { + localUserStore.setNotification("denied"); + notification = false; + } + }) + .catch((e) => console.error(e)); } } diff --git a/front/src/Components/UI/AudioPlaying.svelte b/front/src/Components/UI/AudioPlaying.svelte index 09ffd639..a8d12ec9 100644 --- a/front/src/Components/UI/AudioPlaying.svelte +++ b/front/src/Components/UI/AudioPlaying.svelte @@ -12,7 +12,7 @@ } afterUpdate(() => { - audio.play(); + audio.play().catch((e) => console.error(e)); }); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 19750ee8..20c4aae9 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -188,7 +188,7 @@ class ConnectionManager { //Set last room visited! (connected or nor, must to be saved in localstorage and cache API) //use href to keep # value - localUserStore.setLastRoomUrl(this._currentRoom.href); + await localUserStore.setLastRoomUrl(this._currentRoom.href); //todo: add here some kind of warning if authToken has expired. if (!this.authToken && !this._currentRoom.authenticationMandatory) { @@ -301,7 +301,7 @@ class ConnectionManager { this.reconnectingTimeout = setTimeout(() => { //todo: allow a way to break recursion? //todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely. - this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then( + void this.connectToRoomSocket(roomUrl, name, characterLayers, position, viewport, companion).then( (connection) => resolve(connection) ); }, 4000 + Math.floor(Math.random() * 2000)); diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 4f03a546..cc84f043 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -136,13 +136,12 @@ class LocalUserStore { return localStorage.getItem(ignoreFollowRequests) === "true"; } - setLastRoomUrl(roomUrl: string): void { + async setLastRoomUrl(roomUrl: string): Promise { localStorage.setItem(lastRoomUrl, roomUrl.toString()); if ("caches" in window) { - caches.open(cacheAPIIndex).then((cache) => { - const stringResponse = new Response(JSON.stringify({ roomUrl })); - cache.put(`/${lastRoomUrl}`, stringResponse); - }); + const cache = await caches.open(cacheAPIIndex); + const stringResponse = new Response(JSON.stringify({ roomUrl })); + await cache.put(`/${lastRoomUrl}`, stringResponse); } } getLastRoomUrl(): string { diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 96c6dd23..d360c705 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -352,7 +352,7 @@ export class RoomConnection implements RoomConnection { break; } case "tokenExpiredMessage": { - connectionManager.logout(); + connectionManager.logout().catch((e) => console.error(e)); this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency break; } diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 80b0236e..6157ebaa 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -41,13 +41,15 @@ export class Companion extends Container { this.companionName = name; this._pictureStore = writable(undefined); - texturePromise.then((resource) => { - this.addResource(resource); - this.invisible = false; - return this.getSnapshot().then((htmlImageElementSrc) => { - this._pictureStore.set(htmlImageElementSrc); - }); - }); + texturePromise + .then((resource) => { + this.addResource(resource); + this.invisible = false; + return this.getSnapshot().then((htmlImageElementSrc) => { + this._pictureStore.set(htmlImageElementSrc); + }); + }) + .catch((e) => console.error(e)); this.scene.physics.world.enableBody(this); diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index bd87ba75..98cceafa 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -3,7 +3,7 @@ import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./Co export const getAllCompanionResources = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => { COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => { - lazyLoadCompanionResource(loader, resource.name); + lazyLoadCompanionResource(loader, resource.name).catch((e) => console.error(e)); }); return COMPANION_RESOURCES; diff --git a/front/src/Phaser/Components/Loader.ts b/front/src/Phaser/Components/Loader.ts index e013e758..7eb08e6d 100644 --- a/front/src/Phaser/Components/Loader.ts +++ b/front/src/Phaser/Components/Loader.ts @@ -72,9 +72,11 @@ export class Loader { if (this.loadingText) { this.loadingText.destroy(); } - promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => { - resLoadingImage.destroy(); - }); + promiseLoadLogoTexture + .then((resLoadingImage: Phaser.GameObjects.Image) => { + resLoadingImage.destroy(); + }) + .catch((e) => console.error(e)); this.progress.destroy(); this.progressContainer.destroy(); if (this.scene instanceof DirtyScene) { diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 2dc36df8..f6c28862 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -123,7 +123,7 @@ export class GameMapPropertiesListener { .then((coWebsite) => { const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer); if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) { - coWebsiteManager.closeCoWebsite(coWebsite); + coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => console.error(e)); this.coWebsitesOpenByLayer.delete(layer); this.coWebsitesActionTriggerByLayer.delete(layer); } else { @@ -132,7 +132,8 @@ export class GameMapPropertiesListener { state: OpenCoWebsiteState.OPENED, }); } - }); + }) + .catch((e) => console.error(e)); layoutManagerActionStore.removeAction(actionUuid); }; @@ -198,7 +199,7 @@ export class GameMapPropertiesListener { } if (coWebsiteOpen.coWebsite !== undefined) { - coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite); + coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite).catch((e) => console.error(e)); } this.coWebsitesOpenByLayer.delete(layer); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 42b0c06e..b6d7274c 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -241,7 +241,7 @@ export class GameScene extends DirtyScene { const textures = localUser?.textures; if (textures) { for (const texture of textures) { - loadCustomTexture(this.load, texture); + loadCustomTexture(this.load, texture).catch((e) => console.error(e)); } } @@ -268,7 +268,7 @@ export class GameScene extends DirtyScene { this.load.on( "filecomplete-tilemapJSON-" + this.MapUrlFile, (key: string, type: string, data: unknown) => { - this.onMapLoad(data); + this.onMapLoad(data).catch((e) => console.error(e)); } ); return; @@ -292,14 +292,14 @@ export class GameScene extends DirtyScene { this.load.on( "filecomplete-tilemapJSON-" + this.MapUrlFile, (key: string, type: string, data: unknown) => { - this.onMapLoad(data); + this.onMapLoad(data).catch((e) => console.error(e)); } ); // If the map has already been loaded as part of another GameScene, the "on load" event will not be triggered. // In this case, we check in the cache to see if the map is here and trigger the event manually. if (this.cache.tilemap.exists(this.MapUrlFile)) { const data = this.cache.tilemap.get(this.MapUrlFile); - this.onMapLoad(data); + this.onMapLoad(data).catch((e) => console.error(e)); } return; } @@ -320,7 +320,7 @@ export class GameScene extends DirtyScene { }); this.load.scenePlugin("AnimatedTiles", AnimatedTiles, "animatedTiles", "animatedTiles"); this.load.on("filecomplete-tilemapJSON-" + this.MapUrlFile, (key: string, type: string, data: unknown) => { - this.onMapLoad(data); + this.onMapLoad(data).catch((e) => console.error(e)); }); //TODO strategy to add access token this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); @@ -328,7 +328,7 @@ export class GameScene extends DirtyScene { // In this case, we check in the cache to see if the map is here and trigger the event manually. if (this.cache.tilemap.exists(this.MapUrlFile)) { const data = this.cache.tilemap.get(this.MapUrlFile); - this.onMapLoad(data); + this.onMapLoad(data).catch((e) => console.error(e)); } //eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -406,21 +406,23 @@ export class GameScene extends DirtyScene { this.load.on("complete", () => { // FIXME: the factory might fail because the resources might not be loaded yet... // We would need to add a loader ended event in addition to the createPromise - this.createPromise.then(async () => { - itemFactory.create(this); + this.createPromise + .then(async () => { + itemFactory.create(this); - const roomJoinedAnswer = await this.connectionAnswerPromise; + const roomJoinedAnswer = await this.connectionAnswerPromise; - for (const object of objectsOfType) { - // TODO: we should pass here a factory to create sprites (maybe?) + for (const object of objectsOfType) { + // TODO: we should pass here a factory to create sprites (maybe?) - // Do we have a state for this object? - const state = roomJoinedAnswer.items[object.id]; + // Do we have a state for this object? + const state = roomJoinedAnswer.items[object.id]; - const actionableItem = itemFactory.factory(this, object, state); - this.actionableItems.set(actionableItem.getId(), actionableItem); - } - }); + const actionableItem = itemFactory.factory(this, object, state); + this.actionableItems.set(actionableItem.getId(), actionableItem); + } + }) + .catch((e) => console.error(e)); }); } } @@ -486,11 +488,11 @@ export class GameScene extends DirtyScene { if (exitSceneUrl !== undefined) { this.loadNextGame( Room.getRoomPathFromExitSceneUrl(exitSceneUrl, window.location.toString(), this.MapUrlFile) - ); + ).catch((e) => console.error(e)); } const exitUrl = this.getExitUrl(layer); if (exitUrl !== undefined) { - this.loadNextGameFromExitUrl(exitUrl); + this.loadNextGameFromExitUrl(exitUrl).catch((e) => console.error(e)); } } if (layer.type === "objectgroup") { @@ -530,7 +532,7 @@ export class GameScene extends DirtyScene { } this.gameMap.exitUrls.forEach((exitUrl) => { - this.loadNextGameFromExitUrl(exitUrl); + this.loadNextGameFromExitUrl(exitUrl).catch((e) => console.error(e)); }); this.startPositionCalculator = new StartPositionCalculator( @@ -551,7 +553,10 @@ export class GameScene extends DirtyScene { mediaManager.setUserInputManager(this.userInputManager); if (localUserStore.getFullscreen()) { - document.querySelector("body")?.requestFullscreen(); + document + .querySelector("body") + ?.requestFullscreen() + .catch((e) => console.error(e)); } //notify game manager can to create currentUser in map @@ -657,9 +662,16 @@ export class GameScene extends DirtyScene { } }); - Promise.all([this.connectionAnswerPromise as Promise, ...scriptPromises]).then(() => { - this.scene.wake(); - }); + Promise.all([this.connectionAnswerPromise as Promise, ...scriptPromises]) + .then(() => { + this.scene.wake(); + }) + .catch((e) => + console.error( + "Some scripts failed to load ot the connection failed to establish to WorkAdventure server", + e + ) + ); } /** @@ -859,7 +871,8 @@ export class GameScene extends DirtyScene { // iframeListener.sendLeaveLayerEvent(layer.name); // }); // }); - }); + }) + .catch((e) => console.error(e)); } //todo: into dedicated classes @@ -912,7 +925,7 @@ export class GameScene extends DirtyScene { if (newValue) { this.onMapExit( Room.getRoomPathFromExitSceneUrl(newValue as string, window.location.toString(), this.MapUrlFile) - ); + ).catch((e) => console.error(e)); } else { setTimeout(() => { layoutManagerActionStore.removeAction("roomAccessDenied"); @@ -921,7 +934,9 @@ export class GameScene extends DirtyScene { }); this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue, oldValue) => { if (newValue) { - this.onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())); + this.onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())).catch((e) => + console.error(e) + ); } else { setTimeout(() => { layoutManagerActionStore.removeAction("roomAccessDenied"); @@ -1107,7 +1122,9 @@ ${escapedMessage} this.iframeSubscriptionList.push( iframeListener.playSoundStream.subscribe((playSoundEvent) => { const url = new URL(playSoundEvent.url, this.MapUrlFile); - soundManager.playSound(this.load, this.sound, url.toString(), playSoundEvent.config); + soundManager + .playSound(this.load, this.sound, url.toString(), playSoundEvent.config) + .catch((e) => console.error(e)); }) ); @@ -1145,7 +1162,7 @@ ${escapedMessage} this.iframeSubscriptionList.push( iframeListener.loadSoundStream.subscribe((loadSoundEvent) => { const url = new URL(loadSoundEvent.url, this.MapUrlFile); - soundManager.loadSound(this.load, this.sound, url.toString()); + soundManager.loadSound(this.load, this.sound, url.toString()).catch((e) => console.error(e)); }) ); @@ -1156,11 +1173,15 @@ ${escapedMessage} ); this.iframeSubscriptionList.push( iframeListener.loadPageStream.subscribe((url: string) => { - this.loadNextGameFromExitUrl(url).then(() => { - this.events.once(EVENT_TYPE.POST_UPDATE, () => { - this.onMapExit(Room.getRoomPathFromExitUrl(url, window.location.toString())); - }); - }); + this.loadNextGameFromExitUrl(url) + .then(() => { + this.events.once(EVENT_TYPE.POST_UPDATE, () => { + this.onMapExit(Room.getRoomPathFromExitUrl(url, window.location.toString())).catch((e) => + console.error(e) + ); + }); + }) + .catch((e) => console.error(e)); }) ); let scriptedBubbleSprite: Sprite; @@ -1417,7 +1438,7 @@ ${escapedMessage} propertyValue: string | number | boolean | undefined ): void { if (propertyName === GameMapProperties.EXIT_URL && typeof propertyValue === "string") { - this.loadNextGameFromExitUrl(propertyValue); + this.loadNextGameFromExitUrl(propertyValue).catch((e) => console.error(e)); } this.gameMap.setLayerProperty(layerName, propertyName, propertyValue); } @@ -1502,7 +1523,7 @@ ${escapedMessage} public cleanupClosingScene(): void { // stop playing audio, close any open website, stop any open Jitsi - coWebsiteManager.closeCoWebsites(); + coWebsiteManager.closeCoWebsites().catch((e) => console.error(e)); // Stop the script, if any const scripts = this.getScriptUrls(this.mapFile); for (const script of scripts) { @@ -2044,7 +2065,9 @@ ${escapedMessage} const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; const jitsiWidth = allProps.get(GameMapProperties.JITSI_WIDTH) as number | undefined; - jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth); + jitsiFactory + .start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth) + .catch((e) => console.error(e)); this.connection?.setSilent(true); mediaManager.hideGameOverlay(); analyticsClient.enteredJitsi(roomName, this.room.id); diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index d5629c88..5c208edd 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -40,19 +40,21 @@ export class CustomizeScene extends AbstractCharacterScene { } preload() { - this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => { - bodyResourceDescriptions.forEach((bodyResourceDescription) => { - if ( - bodyResourceDescription.level == undefined || - bodyResourceDescription.level < 0 || - bodyResourceDescription.level > 5 - ) { - throw "Texture level is null"; - } - this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription); - }); - this.lazyloadingAttempt = true; - }); + this.loadCustomSceneSelectCharacters() + .then((bodyResourceDescriptions) => { + bodyResourceDescriptions.forEach((bodyResourceDescription) => { + if ( + bodyResourceDescription.level == undefined || + bodyResourceDescription.level < 0 || + bodyResourceDescription.level > 5 + ) { + throw "Texture level is null"; + } + this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription); + }); + this.lazyloadingAttempt = true; + }) + .catch((e) => console.error(e)); this.layers = loadAllLayers(this.load); this.lazyloadingAttempt = false; diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 64fa9791..4e372e0e 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -41,12 +41,14 @@ export class SelectCharacterScene extends AbstractCharacterScene { } preload() { - this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => { - bodyResourceDescriptions.forEach((bodyResourceDescription) => { - this.playerModels.push(bodyResourceDescription); - }); - this.lazyloadingAttempt = true; - }); + this.loadSelectSceneCharacters() + .then((bodyResourceDescriptions) => { + bodyResourceDescriptions.forEach((bodyResourceDescription) => { + this.playerModels.push(bodyResourceDescription); + }); + this.lazyloadingAttempt = true; + }) + .catch((e) => console.error(e)); this.playerModels = loadAllDefaultModels(this.load); this.lazyloadingAttempt = false; diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts index a0f1a92b..7eeac35d 100644 --- a/front/src/Stores/MediaStore.ts +++ b/front/src/Stores/MediaStore.ts @@ -360,32 +360,27 @@ const implementCorrectTrackBehavior = getNavigatorType() === NavigatorType.firef /** * Stops the camera from filming */ -function applyCameraConstraints(currentStream: MediaStream | null, constraints: MediaTrackConstraints | boolean): void { +async function applyCameraConstraints( + currentStream: MediaStream | null, + constraints: MediaTrackConstraints | boolean +): Promise { if (!currentStream) { - return; - } - for (const track of currentStream.getVideoTracks()) { - toggleConstraints(track, constraints).catch((e) => - console.error("Error while setting new camera constraints:", e) - ); + return []; } + return Promise.all(currentStream.getVideoTracks().map((track) => toggleConstraints(track, constraints))); } /** * Stops the microphone from listening */ -function applyMicrophoneConstraints( +async function applyMicrophoneConstraints( currentStream: MediaStream | null, constraints: MediaTrackConstraints | boolean -): void { +): Promise { if (!currentStream) { - return; - } - for (const track of currentStream.getAudioTracks()) { - toggleConstraints(track, constraints).catch((e) => - console.error("Error while setting new audio constraints:", e) - ); + return []; } + return Promise.all(currentStream.getAudioTracks().map((track) => toggleConstraints(track, constraints))); } async function toggleConstraints(track: MediaStreamTrack, constraints: MediaTrackConstraints | boolean): Promise { @@ -477,8 +472,8 @@ export const localStreamStore = derived, LocalS } } - applyMicrophoneConstraints(currentStream, constraints.audio || false); - applyCameraConstraints(currentStream, constraints.video || false); + applyMicrophoneConstraints(currentStream, constraints.audio || false).catch((e) => console.error(e)); + applyCameraConstraints(currentStream, constraints.video || false).catch((e) => console.error(e)); if (implementCorrectTrackBehavior) { //on good navigators like firefox, we can instantiate the stream once and simply disable or enable the tracks as needed diff --git a/front/src/Stores/ScreenSharingStore.ts b/front/src/Stores/ScreenSharingStore.ts index d68dbf8b..dc2c495c 100644 --- a/front/src/Stores/ScreenSharingStore.ts +++ b/front/src/Stores/ScreenSharingStore.ts @@ -156,7 +156,7 @@ export const screenSharingLocalStreamStore = derived console.error(e)); } ); diff --git a/front/src/Url/UrlManager.ts b/front/src/Url/UrlManager.ts index 50dbedc9..95ea12f2 100644 --- a/front/src/Url/UrlManager.ts +++ b/front/src/Url/UrlManager.ts @@ -41,7 +41,7 @@ class UrlManager { if (window.location.pathname === room.id) return; //Set last room visited! (connected or nor, must to be saved in localstorage and cache API) //use href to keep # value - localUserStore.setLastRoomUrl(room.href); + localUserStore.setLastRoomUrl(room.href).catch((e) => console.error(e)); const hash = window.location.hash; const search = room.search.toString(); history.pushState({}, "WorkAdventure", room.id + (search ? "?" + search : "") + hash); diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 7a003604..8bff2acb 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -149,7 +149,7 @@ class CoWebsiteManager { } buttonCloseCoWebsites.blur(); - this.closeCoWebsites(); + this.closeCoWebsites().catch((e) => console.error(e)); }); const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId); @@ -515,70 +515,72 @@ class CoWebsiteManager { throw new Error("Too many we"); } - Promise.resolve(callback(this.cowebsiteBufferDom)).then((iframe) => { - iframe?.classList.add("pixel"); + Promise.resolve(callback(this.cowebsiteBufferDom)) + .then((iframe) => { + iframe?.classList.add("pixel"); - if (!iframe.id) { - do { - iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7); - } while (this.getCoWebsiteById(iframe.id)); - } - - const onloadPromise = new Promise((resolve) => { - iframe.onload = () => resolve(); - }); - - const icon = this.generateCoWebsiteIcon(iframe); - - const coWebsite = { - iframe, - icon, - position: position ?? this.coWebsites.length, - }; - - // Iframe management on mobile - icon.addEventListener("click", () => { - if (this.isSmallScreen()) { - this.moveRightPreviousCoWebsite(coWebsite, 0); + if (!iframe.id) { + do { + iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7); + } while (this.getCoWebsiteById(iframe.id)); } - }); - this.coWebsites.push(coWebsite); - this.cowebsiteSubIconsDom.appendChild(icon); + const onloadPromise = new Promise((resolve) => { + iframe.onload = () => resolve(); + }); - const onTimeoutPromise = new Promise((resolve) => { - setTimeout(() => resolve(), 2000); - }); + const icon = this.generateCoWebsiteIcon(iframe); - this.currentOperationPromise = this.currentOperationPromise - .then(() => Promise.race([onloadPromise, onTimeoutPromise])) - .then(() => { - if (coWebsite.position === 0) { - this.openMain(); - if (widthPercent) { - this.widthPercent = widthPercent; - } + const coWebsite = { + iframe, + icon, + position: position ?? this.coWebsites.length, + }; - setTimeout(() => { - this.fire(); + // Iframe management on mobile + icon.addEventListener("click", () => { + if (this.isSmallScreen()) { + this.moveRightPreviousCoWebsite(coWebsite, 0); + } + }); + + this.coWebsites.push(coWebsite); + this.cowebsiteSubIconsDom.appendChild(icon); + + const onTimeoutPromise = new Promise((resolve) => { + setTimeout(() => resolve(), 2000); + }); + + this.currentOperationPromise = this.currentOperationPromise + .then(() => Promise.race([onloadPromise, onTimeoutPromise])) + .then(() => { + if (coWebsite.position === 0) { + this.openMain(); + if (widthPercent) { + this.widthPercent = widthPercent; + } + + setTimeout(() => { + this.fire(); + position !== undefined + ? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) + : this.moveCoWebsite(coWebsite, coWebsite.position); + }, animationTime); + } else { position !== undefined ? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) : this.moveCoWebsite(coWebsite, coWebsite.position); - }, animationTime); - } else { - position !== undefined - ? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) - : this.moveCoWebsite(coWebsite, coWebsite.position); - } + } - return resolve(coWebsite); - }) - .catch((err) => { - console.error("Error loadCoWebsite => ", err); - this.removeCoWebsiteFromStack(coWebsite); - return reject(); - }); - }); + return resolve(coWebsite); + }) + .catch((err) => { + console.error("Error loadCoWebsite => ", err); + this.removeCoWebsiteFromStack(coWebsite); + return reject(); + }); + }) + .catch((e) => console.error("Error loadCoWebsite >=> ", e)); }); } @@ -603,17 +605,21 @@ class CoWebsiteManager { return this.currentOperationPromise; } - public closeJitsi() { + public async closeJitsi() { const jitsi = this.searchJitsi(); if (jitsi) { - this.closeCoWebsite(jitsi); + return this.closeCoWebsite(jitsi); } } public closeCoWebsites(): Promise { this.currentOperationPromise = this.currentOperationPromise.then(() => { + const promises: Promise[] = []; this.coWebsites.forEach((coWebsite: CoWebsite) => { - this.closeCoWebsite(coWebsite); + promises.push(this.closeCoWebsite(coWebsite)); + }); + return Promise.all(promises).then(() => { + return; }); }); return this.currentOperationPromise; diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 0f205f47..c067a255 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -1,5 +1,5 @@ import { JITSI_URL } from "../Enum/EnvironmentVariable"; -import { coWebsiteManager } from "./CoWebsiteManager"; +import { CoWebsite, coWebsiteManager } from "./CoWebsiteManager"; import { requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore"; import { get } from "svelte/store"; @@ -140,8 +140,8 @@ class JitsiFactory { interfaceConfig?: object, jitsiUrl?: string, jitsiWidth?: number - ): void { - coWebsiteManager.addCoWebsite( + ): Promise { + return coWebsiteManager.addCoWebsite( async (cowebsiteDiv) => { // Jitsi meet external API maintains some data in local storage // which is sent via the appData URL parameter when joining a @@ -200,7 +200,7 @@ class JitsiFactory { const jitsiCoWebsite = coWebsiteManager.searchJitsi(); if (jitsiCoWebsite) { - coWebsiteManager.closeJitsi(); + coWebsiteManager.closeJitsi().catch((e) => console.error(e)); } this.jitsiApi.removeListener("audioMuteStatusChanged", this.audioCallback); diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 6b3ec8c3..66ee77c0 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -9,7 +9,7 @@ import { } from "./Api/Events/IframeEvent"; import chat from "./Api/iframe/chat"; import type { IframeCallback } from "./Api/iframe/IframeApiContribution"; -import nav from "./Api/iframe/nav"; +import nav, { CoWebsite } from "./Api/iframe/nav"; import controls from "./Api/iframe/controls"; import ui from "./Api/iframe/ui"; import sound from "./Api/iframe/sound"; @@ -136,17 +136,17 @@ const wa = { /** * @deprecated Use WA.nav.openCoWebSite instead */ - openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void { + openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): Promise { console.warn("Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead"); - nav.openCoWebSite(url, allowApi, allowPolicy); + return nav.openCoWebSite(url, allowApi, allowPolicy); }, /** * @deprecated Use WA.nav.closeCoWebSite instead */ - closeCoWebSite(): void { + closeCoWebSite(): Promise { console.warn("Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead"); - nav.closeCoWebSite(); + return nav.closeCoWebSite(); }, /** From ae122261d482f4bcb9f4bfb3af1a34b936622199 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 5 Jan 2022 11:49:01 +0100 Subject: [PATCH 44/46] Fix prettier update Signed-off-by: Gregoire Parant --- front/src/Components/WarningContainer/WarningContainer.svelte | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/front/src/Components/WarningContainer/WarningContainer.svelte b/front/src/Components/WarningContainer/WarningContainer.svelte index 86b2d972..f75050fc 100644 --- a/front/src/Components/WarningContainer/WarningContainer.svelte +++ b/front/src/Components/WarningContainer/WarningContainer.svelte @@ -17,9 +17,7 @@

{:else if $limitMapStore}

- This map is available for 2 days. You can register your domain here! + This map is available for 2 days. You can register your domain here!

{:else}

Warning!

From 968e71cbca28df16cdfd6e13060c997d42707b10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 5 Jan 2022 14:32:02 +0100 Subject: [PATCH 45/46] Removing completely any analysis of the URL from the front. Instead, data related to room is sent from the admin, via the pusher. --- .../Components/ReportMenu/ReportMenu.svelte | 7 +++---- .../WarningContainer/WarningContainer.svelte | 4 +--- front/src/Connexion/ConnectionManager.ts | 11 +++------- front/src/Connexion/Room.ts | 14 +++++++++++++ front/src/Url/UrlManager.ts | 21 +++---------------- messages/JsonMessages/MapDetailsData.ts | 4 ++++ 6 files changed, 28 insertions(+), 33 deletions(-) diff --git a/front/src/Components/ReportMenu/ReportMenu.svelte b/front/src/Components/ReportMenu/ReportMenu.svelte index 8479a5b0..92601774 100644 --- a/front/src/Components/ReportMenu/ReportMenu.svelte +++ b/front/src/Components/ReportMenu/ReportMenu.svelte @@ -6,12 +6,11 @@ import type { Unsubscriber } from "svelte/store"; import { playersStore } from "../../Stores/PlayersStore"; import { connectionManager } from "../../Connexion/ConnectionManager"; - import { GameConnexionTypes } from "../../Url/UrlManager"; import { get } from "svelte/store"; let blockActive = true; let reportActive = !blockActive; - let anonymous: boolean = false; + let disableReport: boolean = false; let userUUID: string | undefined = playersStore.getPlayerById(get(showReportScreenStore).userId)?.userUuid; let userName = "No name"; let unsubscriber: Unsubscriber; @@ -26,7 +25,7 @@ } } }); - anonymous = connectionManager.getConnexionType === GameConnexionTypes.anonymous; + disableReport = !connectionManager.currentRoom?.canReport ?? true; }); onDestroy(() => { @@ -65,7 +64,7 @@ -
+