From 0d5c31804076886db9e31052175808df8b40e4e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 7 Dec 2021 15:40:35 +0100 Subject: [PATCH 001/149] Adding a test case to test exitSceneUrl property this test case reproduces error #1631 --- maps/tests/Properties/exitSceneUrl1.json | 167 +++++++++++++++++++++++ maps/tests/Properties/exitSceneUrl2.json | 149 ++++++++++++++++++++ maps/tests/index.html | 8 ++ 3 files changed, 324 insertions(+) create mode 100644 maps/tests/Properties/exitSceneUrl1.json create mode 100644 maps/tests/Properties/exitSceneUrl2.json diff --git a/maps/tests/Properties/exitSceneUrl1.json b/maps/tests/Properties/exitSceneUrl1.json new file mode 100644 index 00000000..463bf686 --- /dev/null +++ b/maps/tests/Properties/exitSceneUrl1.json @@ -0,0 +1,167 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":10, + "id":1, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17], + "height":10, + "id":7, + "name":"walls", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":8, + "name":"exit", + "opacity":1, + "properties":[ + { + "name":"exitSceneUrl", + "type":"string", + "value":"exitSceneUrl2.json" + }], + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":9, + "name":"from_exit2", + "opacity":1, + "properties":[ + { + "name":"startLayer", + "type":"bool", + "value":true + }], + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":263.008397229317, + "id":1, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":13, + "text":"You are on Map 1\n\nTest:\nWalk through the exit.\n\nResult:\nYou should arrive to Map 2\n", + "wrap":true + }, + "type":"", + "visible":true, + "width":249.954975648686, + "x":35.2740564642832, + "y":34.4372323693377 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":10, + "nextobjectid":2, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"2021.03.23", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"../tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tiles":[ + { + "id":16, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":17, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":18, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":19, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":10 +} diff --git a/maps/tests/Properties/exitSceneUrl2.json b/maps/tests/Properties/exitSceneUrl2.json new file mode 100644 index 00000000..bc2aa4d3 --- /dev/null +++ b/maps/tests/Properties/exitSceneUrl2.json @@ -0,0 +1,149 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":10, + "id":1, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17], + "height":10, + "id":7, + "name":"walls", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":8, + "name":"exit", + "opacity":1, + "properties":[ + { + "name":"exitSceneUrl", + "type":"string", + "value":"exitSceneUrl1.json#from_exit2" + }], + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":263.008397229317, + "id":1, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":13, + "text":"You are on Map 2\n\nTest:\nWalk back through the exit.\n\nResult:\nYou should arrive back to Map 1\n", + "wrap":true + }, + "type":"", + "visible":true, + "width":249.954975648686, + "x":35.2740564642832, + "y":34.4372323693377 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":9, + "nextobjectid":2, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"2021.03.23", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"../tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tiles":[ + { + "id":16, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":17, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":18, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":19, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":10 +} diff --git a/maps/tests/index.html b/maps/tests/index.html index 068136ed..366f245f 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -64,6 +64,14 @@ Test exits + + + Success Failure Pending + + + Test the (deprecated) exitSceneUrl property + + Success Failure Pending From 1d2d60a67a9976d267dc19ca74fbe8477900df91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 7 Dec 2021 15:51:30 +0100 Subject: [PATCH 002/149] Fixing issue with "instance" part of the URL lost in exitSceneUrl URLs. --- front/src/Connexion/Room.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 4c5955cc..7d68185a 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -77,7 +77,7 @@ export class Room { const currentRoom = new Room(baseUrl); let instance: string = "global"; if (currentRoom.isPublic) { - instance = currentRoom.instance as string; + instance = currentRoom.getInstance(); } baseUrl.pathname = "/_/" + instance + "/" + absoluteExitSceneUrl.host + absoluteExitSceneUrl.pathname; From 7a6e250a588f937f44a900b4da9cb43c267eed5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Wed, 8 Dec 2021 14:17:13 +0100 Subject: [PATCH 003/149] Fixing map caching Maps in the GameScene can be modified by the scripting API. If so, previously, the changes to the maps were persisted in the ITiledMap object in the GameScene because this was cached by the Phaser loader, causing a series of problems, the most noticeable being that templating in the scripting API stopped working on a second visit of a page. We are now deep-copying the map on load to avoid these nasty effects. Closes https://github.com/workadventure/scripting-api-extra/issues/77 --- front/package.json | 1 + front/src/Phaser/Game/GameScene.ts | 6 +++++- front/yarn.lock | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/front/package.json b/front/package.json index ec81d8a7..8e616aae 100644 --- a/front/package.json +++ b/front/package.json @@ -45,6 +45,7 @@ "@types/socket.io-client": "^1.4.32", "axios": "^0.21.2", "cross-env": "^7.0.3", + "deep-copy-ts": "^0.5.0", "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "phaser": "^3.54.0", diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index d9bb8186..90c67437 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -89,6 +89,7 @@ import { get } from "svelte/store"; import { contactPageStore } from "../../Stores/MenuStore"; import { GameMapProperties } from "./GameMapProperties"; import SpriteSheetFile = Phaser.Loader.FileTypes.SpriteSheetFile; +import { deepCopy } from "deep-copy-ts"; export interface GameSceneInitInterface { initPosition: PointInterface | null; @@ -341,7 +342,10 @@ export class GameScene extends DirtyScene { private async onMapLoad(data: any): Promise { // Triggered when the map is loaded // Load tiles attached to the map recursively - this.mapFile = data.data; + + // The map file can be modified by the scripting API and we don't want to tamper the Phaser cache (in case we come back on the map after visiting other maps) + // So we are doing a deep copy + this.mapFile = deepCopy(data.data); const url = this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf("/")); this.mapFile.tilesets.forEach((tileset) => { if (typeof tileset.name === "undefined" || typeof tileset.image === "undefined") { diff --git a/front/yarn.lock b/front/yarn.lock index f94e659f..480de55c 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -1656,6 +1656,11 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= +deep-copy-ts@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/deep-copy-ts/-/deep-copy-ts-0.5.0.tgz#b9493d8e2bae85ef7d659c16eb707c13efb84499" + integrity sha512-/3cgBcMkznRf5BM8wu6YWz3SQUkHzgh/v1TZFjevztLj9sMjFvNFBtpN4uUtPzw/rA/TldyD6c6LRL1zno4+YA== + deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" From a5d4d163e18d2d9f36082e938df7674b4afeb2e5 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Fri, 10 Dec 2021 01:51:40 +0100 Subject: [PATCH 004/149] 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 005/149] 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 aad582df7076088e4b6fc8f1f66ea11b56a94555 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Mon, 6 Dec 2021 21:16:53 +0100 Subject: [PATCH 006/149] Display map link in map credits if provided --- docs/maps/wa-maps.md | 8 ++++---- front/src/Components/Menu/AboutRoomSubMenu.svelte | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/maps/wa-maps.md b/docs/maps/wa-maps.md index 70581a57..819d632e 100644 --- a/docs/maps/wa-maps.md +++ b/docs/maps/wa-maps.md @@ -98,13 +98,13 @@ The exception is the "collides" property that can only be set on tiles, but not By setting properties on the map itself, you can help visitors know more about the creators of the map. The following *map* properties are supported: -* `mapName` (string) -* `mapDescription` (string) -* `mapCopyright` (string) +* `mapName` (string): The name of your map +* `mapLink` (string): A link to your map, for example a repository +* `mapDescription` (string): A short description of your map +* `mapCopyright` (string): Copyright notice And *each tileset* can also have a property called `tilesetCopyright` (string). Resulting in a "credit" page in the menu looking like this: ![](images/mapProperties.png){.document-img} - diff --git a/front/src/Components/Menu/AboutRoomSubMenu.svelte b/front/src/Components/Menu/AboutRoomSubMenu.svelte index 666183e0..0fd51ce7 100644 --- a/front/src/Components/Menu/AboutRoomSubMenu.svelte +++ b/front/src/Components/Menu/AboutRoomSubMenu.svelte @@ -8,6 +8,7 @@ let expandedTilesetCopyright = false; let mapName: string = ""; + let mapLink: string = ""; let mapDescription: string = ""; let mapCopyright: string = "The map creator did not declare a copyright for the map."; let tilesetCopyright: string[] = []; @@ -18,6 +19,10 @@ if (propertyName !== undefined && typeof propertyName.value === "string") { mapName = propertyName.value; } + const propertyLink = gameScene.mapFile.properties.find((property) => property.name === "mapLink"); + if (propertyLink !== undefined && typeof propertyLink.value === "string") { + mapLink = propertyLink.value; + } const propertyDescription = gameScene.mapFile.properties.find( (property) => property.name === "mapDescription" ); @@ -48,6 +53,9 @@

{mapName}

{mapDescription}

+ {#if mapLink} +

> link to this map <

+ {/if}

(expandedMapCopyright = !expandedMapCopyright)}> Copyrights of the map

From 80794975aab5f9fbc476e0f8876b1465f56a52fb Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sat, 11 Dec 2021 22:24:28 +0100 Subject: [PATCH 007/149] Add new layer property `audioCopyright` --- docs/maps/wa-maps.md | 3 +- .../Components/Menu/AboutRoomSubMenu.svelte | 32 +++++++++++++++++-- maps/tests/Properties/mapProperties.json | 8 ++++- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/docs/maps/wa-maps.md b/docs/maps/wa-maps.md index 819d632e..6e84a251 100644 --- a/docs/maps/wa-maps.md +++ b/docs/maps/wa-maps.md @@ -103,7 +103,8 @@ The following *map* properties are supported: * `mapDescription` (string): A short description of your map * `mapCopyright` (string): Copyright notice -And *each tileset* can also have a property called `tilesetCopyright` (string). +Each *tileset* can also have a property called `tilesetCopyright` (string). +If you are using audio files in your map, you can declare a layer property `audioCopyright` (string). Resulting in a "credit" page in the menu looking like this: diff --git a/front/src/Components/Menu/AboutRoomSubMenu.svelte b/front/src/Components/Menu/AboutRoomSubMenu.svelte index 0fd51ce7..2bbb4d3c 100644 --- a/front/src/Components/Menu/AboutRoomSubMenu.svelte +++ b/front/src/Components/Menu/AboutRoomSubMenu.svelte @@ -6,12 +6,14 @@ let expandedMapCopyright = false; let expandedTilesetCopyright = false; + let expandedAudioCopyright = false; let mapName: string = ""; let mapLink: string = ""; let mapDescription: string = ""; let mapCopyright: string = "The map creator did not declare a copyright for the map."; let tilesetCopyright: string[] = []; + let audioCopyright: string[] = []; onMount(() => { if (gameScene.mapFile.properties !== undefined) { @@ -41,7 +43,18 @@ (property) => property.name === "tilesetCopyright" ); if (propertyTilesetCopyright !== undefined && typeof propertyTilesetCopyright.value === "string") { - tilesetCopyright = [...tilesetCopyright, propertyTilesetCopyright.value]; //Assignment needed to trigger Svelte's reactivity + // Assignment needed to trigger Svelte's reactivity + tilesetCopyright = [...tilesetCopyright, propertyTilesetCopyright.value]; + } + } + } + + for (const layer of gameScene.mapFile.layers) { + if (layer.type && layer.type === "tilelayer" && layer.properties) { + const propertyAudioCopyright = layer.properties.find((property) => property.name === "audioCopyright"); + if (propertyAudioCopyright !== undefined && typeof propertyAudioCopyright.value === "string") { + // Assignment needed to trigger Svelte's reactivity + audioCopyright = [...audioCopyright, propertyAudioCopyright.value]; } } } @@ -68,8 +81,21 @@

{copyright}

{:else}

- The map creator did not declare a copyright for the tilesets. Warning, This doesn't mean that those - tilesets have no license. + The map creator did not declare a copyright for the tilesets. This doesn't mean that those tilesets + have no license. +

+ {/each} +
+

(expandedAudioCopyright = !expandedAudioCopyright)}> + Copyrights of audio files +

+ diff --git a/maps/tests/Properties/mapProperties.json b/maps/tests/Properties/mapProperties.json index a58c002f..34178e38 100644 --- a/maps/tests/Properties/mapProperties.json +++ b/maps/tests/Properties/mapProperties.json @@ -8,6 +8,12 @@ "id":1, "name":"start", "opacity":1, + "properties":[ + { + "name":"audioCopyright", + "type":"string", + "value":"Copyright 2021 John Doe" + }], "type":"tilelayer", "visible":true, "width":10, @@ -124,4 +130,4 @@ "type":"map", "version":1.4, "width":10 -} \ No newline at end of file +} From bbef3b3eaf49c9da41196a868f8c03ff4ee7f17b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 13 Dec 2021 16:45:16 +0100 Subject: [PATCH 008/149] Upgrade Typescript to 4.5 This commit contains the new Typescript version but NOT the fixes needed to have the project compiling. --- front/package.json | 14 +- front/yarn.lock | 484 +++++++++++++++++++++++++-------------------- 2 files changed, 275 insertions(+), 223 deletions(-) diff --git a/front/package.json b/front/package.json index 4e81cd3c..cc977cdc 100644 --- a/front/package.json +++ b/front/package.json @@ -12,12 +12,12 @@ "@types/quill": "^1.3.7", "@types/uuidv4": "^5.0.0", "@types/webpack-dev-server": "^3.11.4", - "@typescript-eslint/eslint-plugin": "^4.23.0", - "@typescript-eslint/parser": "^4.23.0", + "@typescript-eslint/eslint-plugin": "^5.6.0", + "@typescript-eslint/parser": "^5.6.0", "css-loader": "^5.2.4", - "eslint": "^7.26.0", + "eslint": "^8.4.1", "eslint-plugin-svelte3": "^3.2.1", - "fork-ts-checker-webpack-plugin": "^6.2.9", + "fork-ts-checker-webpack-plugin": "^6.5.0", "html-webpack-plugin": "^5.3.1", "jasmine": "^3.5.0", "lint-staged": "^11.0.0", @@ -32,10 +32,10 @@ "svelte-check": "^2.1.0", "svelte-loader": "^3.1.1", "svelte-preprocess": "^4.7.3", - "ts-loader": "^9.1.2", - "ts-node": "^9.1.1", + "ts-loader": "^9.2.6", + "ts-node": "^10.4.0", "tsconfig-paths": "^3.9.0", - "typescript": "^4.2.4", + "typescript": "^4.5.3", "webpack": "^5.37.0", "webpack-cli": "^4.7.0", "webpack-dev-server": "^3.11.2" diff --git a/front/yarn.lock b/front/yarn.lock index 2af31d13..37834b34 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -2,13 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" - integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== - dependencies: - "@babel/highlight" "^7.10.4" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": version "7.12.13" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" @@ -21,7 +14,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz#d26cad8a47c65286b15df1547319a5d0bcf27288" integrity sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A== -"@babel/highlight@^7.10.4", "@babel/highlight@^7.12.13": +"@babel/highlight@^7.12.13": version "7.14.0" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.14.0.tgz#3197e375711ef6bf834e67d0daec88e4f46113cf" integrity sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg== @@ -37,23 +30,35 @@ dependencies: regenerator-runtime "^0.13.4" +"@cspotcode/source-map-consumer@0.8.0": + version "0.8.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz#33bf4b7b39c178821606f669bbc447a6a629786b" + integrity sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg== + +"@cspotcode/source-map-support@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz#4789840aa859e46d2f3173727ab707c66bf344f5" + integrity sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA== + dependencies: + "@cspotcode/source-map-consumer" "0.8.0" + "@discoveryjs/json-ext@^0.5.0": version "0.5.3" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d" integrity sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g== -"@eslint/eslintrc@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.4.1.tgz#442763b88cecbe3ee0ec7ca6d6dd6168550cbf14" - integrity sha512-5v7TDE9plVhvxQeWLXDTvFvJBdH6pEsdnl2g/dAptmuFEPedQ4Erq5rsDsX+mvAM610IhNaO2W5V1dOOnDKxkQ== +"@eslint/eslintrc@^1.0.5": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.0.5.tgz#33f1b838dbf1f923bfa517e008362b78ddbbf318" + integrity sha512-BLxsnmK3KyPunz5wmCCpqy0YelEoxxGmH73Is+Z74oOTMtExcjkr3dDR6quwrjh1YspA8DH9gnX1o069KiS9AQ== dependencies: ajv "^6.12.4" - debug "^4.1.1" - espree "^7.3.0" - globals "^12.1.0" + debug "^4.3.2" + espree "^9.2.0" + globals "^13.9.0" ignore "^4.0.6" import-fresh "^3.2.1" - js-yaml "^3.13.1" + js-yaml "^4.1.0" minimatch "^3.0.4" strip-json-comments "^3.1.1" @@ -88,6 +93,20 @@ dependencies: "@fortawesome/fontawesome-common-types" "^0.2.35" +"@humanwhocodes/config-array@^0.9.2": + version "0.9.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.2.tgz#68be55c737023009dfc5fe245d51181bb6476914" + integrity sha512-UXOuFCGcwciWckOpmfKDq/GyhlTf9pN/BzG//x8p8zTOFEcGuA68ANXheFS0AGvy3qgZqLBUkMs7hqzqCKOVwA== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.4" + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + "@joeattardi/emoji-button@^4.6.2": version "4.6.2" resolved "https://registry.yarnpkg.com/@joeattardi/emoji-button/-/emoji-button-4.6.2.tgz#75baf4ce27324e4d6fb90292f8b248235f638ad0" @@ -136,6 +155,26 @@ resolved "https://registry.yarnpkg.com/@sentry/types/-/types-6.12.0.tgz#b7395688a79403c6df8d8bb8d81deb8222519853" integrity sha512-urtgLzE4EDMAYQHYdkgC0Ei9QvLajodK1ntg71bGn0Pm84QUpaqpPDfHRU+i6jLeteyC7kWwa5O5W1m/jrjGXA== +"@tsconfig/node10@^1.0.7": + version "1.0.8" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.8.tgz#c1e4e80d6f964fbecb3359c43bd48b40f7cadad9" + integrity sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg== + +"@tsconfig/node12@^1.0.7": + version "1.0.9" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.9.tgz#62c1f6dee2ebd9aead80dc3afa56810e58e1a04c" + integrity sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw== + +"@tsconfig/node14@^1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.1.tgz#95f2d167ffb9b8d2068b0b235302fafd4df711f2" + integrity sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg== + +"@tsconfig/node16@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" + integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== + "@tsconfig/svelte@^1.0.10": version "1.0.10" resolved "https://registry.yarnpkg.com/@tsconfig/svelte/-/svelte-1.0.10.tgz#30ec7feeee0bdf38b12a50f0686f8a2e7b6b9dc0" @@ -239,11 +278,16 @@ resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.7.4.tgz#99a49aa9a5f8dc86fc249ed13ed59552c6ce862d" integrity sha512-L3FKeEwMm8e8hqGvt7cSesVmGtavpRyHV1FNDq+Pm5pS4x5eFmE70ZERXCSBWAiLQqXBcZRUrwV59FZLQl/GxQ== -"@types/json-schema@*", "@types/json-schema@^7.0.3", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": +"@types/json-schema@*", "@types/json-schema@^7.0.4", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": version "7.0.7" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/json-schema@^7.0.9": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + "@types/json5@^0.0.29": version "0.0.29" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" @@ -393,75 +437,75 @@ "@types/webpack-sources" "*" source-map "^0.6.0" -"@typescript-eslint/eslint-plugin@^4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.23.0.tgz#29d3c9c81f6200b1fd6d8454cfb007ba176cde80" - integrity sha512-tGK1y3KIvdsQEEgq6xNn1DjiFJtl+wn8JJQiETtCbdQxw1vzjXyAaIkEmO2l6Nq24iy3uZBMFQjZ6ECf1QdgGw== +"@typescript-eslint/eslint-plugin@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.6.0.tgz#efd8668b3d6627c46ce722c2afe813928fe120a0" + integrity sha512-MIbeMy5qfLqtgs1hWd088k1hOuRsN9JrHUPwVVKCD99EOUqScd7SrwoZl4Gso05EAP9w1kvLWUVGJOVpRPkDPA== dependencies: - "@typescript-eslint/experimental-utils" "4.23.0" - "@typescript-eslint/scope-manager" "4.23.0" - debug "^4.1.1" + "@typescript-eslint/experimental-utils" "5.6.0" + "@typescript-eslint/scope-manager" "5.6.0" + debug "^4.3.2" functional-red-black-tree "^1.0.1" - lodash "^4.17.15" - regexpp "^3.0.0" - semver "^7.3.2" - tsutils "^3.17.1" + ignore "^5.1.8" + regexpp "^3.2.0" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-4.23.0.tgz#f2059434cd6e5672bfeab2fb03b7c0a20622266f" - integrity sha512-WAFNiTDnQfrF3Z2fQ05nmCgPsO5o790vOhmWKXbbYQTO9erE1/YsFot5/LnOUizLzU2eeuz6+U/81KV5/hFTGA== +"@typescript-eslint/experimental-utils@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.6.0.tgz#f3a5960f2004abdcac7bb81412bafc1560841c23" + integrity sha512-VDoRf3Qj7+W3sS/ZBXZh3LBzp0snDLEgvp6qj0vOAIiAPM07bd5ojQ3CTzF/QFl5AKh7Bh1ycgj6lFBJHUt/DA== dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.6.0" + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/typescript-estree" "5.6.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" -"@typescript-eslint/parser@^4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-4.23.0.tgz#239315d38e42e852bef43a4b0b01bef78f78911c" - integrity sha512-wsvjksHBMOqySy/Pi2Q6UuIuHYbgAMwLczRl4YanEPKW5KVxI9ZzDYh3B5DtcZPQTGRWFJrfcbJ6L01Leybwug== +"@typescript-eslint/parser@^5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.6.0.tgz#11677324659641400d653253c03dcfbed468d199" + integrity sha512-YVK49NgdUPQ8SpCZaOpiq1kLkYRPMv9U5gcMrywzI8brtwZjr/tG3sZpuHyODt76W/A0SufNjYt9ZOgrC4tLIQ== dependencies: - "@typescript-eslint/scope-manager" "4.23.0" - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/typescript-estree" "4.23.0" - debug "^4.1.1" + "@typescript-eslint/scope-manager" "5.6.0" + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/typescript-estree" "5.6.0" + debug "^4.3.2" -"@typescript-eslint/scope-manager@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-4.23.0.tgz#8792ef7eacac122e2ec8fa2d30a59b8d9a1f1ce4" - integrity sha512-ZZ21PCFxPhI3n0wuqEJK9omkw51wi2bmeKJvlRZPH5YFkcawKOuRMQMnI8mH6Vo0/DoHSeZJnHiIx84LmVQY+w== +"@typescript-eslint/scope-manager@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.6.0.tgz#9dd7f007dc8f3a34cdff6f79f5eaab27ae05157e" + integrity sha512-1U1G77Hw2jsGWVsO2w6eVCbOg0HZ5WxL/cozVSTfqnL/eB9muhb8THsP0G3w+BB5xAHv9KptwdfYFAUfzcIh4A== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/visitor-keys" "5.6.0" -"@typescript-eslint/types@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.23.0.tgz#da1654c8a5332f4d1645b2d9a1c64193cae3aa3b" - integrity sha512-oqkNWyG2SLS7uTWLZf6Sr7Dm02gA5yxiz1RP87tvsmDsguVATdpVguHr4HoGOcFOpCvx9vtCSCyQUGfzq28YCw== +"@typescript-eslint/types@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.6.0.tgz#745cb1b59daadcc1f32f7be95f0f68accf38afdd" + integrity sha512-OIZffked7mXv4mXzWU5MgAEbCf9ecNJBKi+Si6/I9PpTaj+cf2x58h2oHW5/P/yTnPkKaayfjhLvx+crnl5ubA== -"@typescript-eslint/typescript-estree@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.23.0.tgz#0753b292097523852428a6f5a1aa8ccc1aae6cd9" - integrity sha512-5Sty6zPEVZF5fbvrZczfmLCOcby3sfrSPu30qKoY1U3mca5/jvU5cwsPb/CO6Q3ByRjixTMIVsDkqwIxCf/dMw== +"@typescript-eslint/typescript-estree@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.6.0.tgz#dfbb19c9307fdd81bd9c650c67e8397821d7faf0" + integrity sha512-92vK5tQaE81rK7fOmuWMrSQtK1IMonESR+RJR2Tlc7w4o0MeEdjgidY/uO2Gobh7z4Q1hhS94Cr7r021fMVEeA== dependencies: - "@typescript-eslint/types" "4.23.0" - "@typescript-eslint/visitor-keys" "4.23.0" - debug "^4.1.1" - globby "^11.0.1" - is-glob "^4.0.1" - semver "^7.3.2" - tsutils "^3.17.1" + "@typescript-eslint/types" "5.6.0" + "@typescript-eslint/visitor-keys" "5.6.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" -"@typescript-eslint/visitor-keys@4.23.0": - version "4.23.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-4.23.0.tgz#7215cc977bd3b4ef22467b9023594e32f9e4e455" - integrity sha512-5PNe5cmX9pSifit0H+nPoQBXdbNzi5tOEec+3riK+ku4e3er37pKxMKDH5Ct5Y4fhWxcD4spnlYjxi9vXbSpwg== +"@typescript-eslint/visitor-keys@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.6.0.tgz#3e36509e103fe9713d8f035ac977235fd63cb6e6" + integrity sha512-1p7hDp5cpRFUyE3+lvA74egs+RWSgumrBpzBCDzfTFv0aQ7lIeay80yU0hIxgAhwQ6PcasW35kaOCyDOv6O/Ng== dependencies: - "@typescript-eslint/types" "4.23.0" - eslint-visitor-keys "^2.0.0" + "@typescript-eslint/types" "5.6.0" + eslint-visitor-keys "^3.0.0" "@webassemblyjs/ast@1.11.0": version "1.11.0" @@ -624,16 +668,21 @@ acorn-jsx@^5.3.1: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== -acorn@^7.4.0: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== acorn@^8.2.1: version "8.2.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.2.4.tgz#caba24b08185c3b56e3168e97d15ed17f4d31fd0" integrity sha512-Ibt84YwBDDA890eDiDCEqcbwvHlBvzzDkU2cGBBDDI1QWT12jTiXIOn2CIw5KK4i6N5Z2HUxwYjzriDyqaqqZg== +acorn@^8.4.1, acorn@^8.6.0: + version "8.6.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" + integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -667,16 +716,6 @@ ajv@^6.1.0, ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.4.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.4.0.tgz#48984fdb2ce225cab15795f0772a8d85669075e4" - integrity sha512-7QD2l6+KBSLwf+7MuYocbWvRPdOu63/trReTLu2KFwkgctnub1auoF+Y1WYcm09CTM7quuscrzqmASaLHC/K4Q== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" @@ -714,6 +753,11 @@ ansi-regex@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -749,12 +793,10 @@ arg@^4.1.0: resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== arr-diff@^4.0.0: version "4.0.0" @@ -1628,13 +1670,20 @@ debug@^3.1.1, debug@^3.2.6: dependencies: ms "^2.1.1" -debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" +debug@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -2041,12 +2090,17 @@ escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + eslint-plugin-svelte3@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/eslint-plugin-svelte3/-/eslint-plugin-svelte3-3.2.1.tgz#f0f24150ecea3061c38c69e282bea26dc3e660c6" integrity sha512-YoBR9mLoKCjGghJ/gvpnFZKaMEu/VRcuxpSRS8KuozuEo7CdBH7bmBHa6FmMm0i4kJnOyx+PVsaptz96K6H/4Q== -eslint-scope@^5.0.0, eslint-scope@^5.1.1: +eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -2054,79 +2108,83 @@ eslint-scope@^5.0.0, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.0.0, eslint-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== +eslint-scope@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.0.tgz#c1f6ea30ac583031f203d65c73e723b01298f153" + integrity sha512-aWwkhnS0qAXqNOgKOK0dJ2nvzEbhEvpy8OlJ9kZ0FeZnA6zpjv1/Vei+puGFFX7zkPCkHHXb7IDX3A+7yPrRWg== dependencies: - eslint-visitor-keys "^1.1.0" + esrecurse "^4.3.0" + estraverse "^5.2.0" -eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" eslint-visitor-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^7.26.0: - version "7.26.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.26.0.tgz#d416fdcdcb3236cd8f282065312813f8c13982f6" - integrity sha512-4R1ieRf52/izcZE7AlLy56uIHHDLT74Yzz2Iv2l6kDaYvEu9x+wMB5dZArVL8SYGXSYV2YAg70FcW5Y5nGGNIg== +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.1.0.tgz#eee4acea891814cda67a7d8812d9647dd0179af2" + integrity sha512-yWJFpu4DtjsWKkt5GeNBBuZMlNcYVs6vRCLoCVEJrTjaSB6LC98gFipNK/erM2Heg/E8mIK+hXG/pJMLK+eRZA== + +eslint@^8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.4.1.tgz#d6531bbf3e598dffd7c0c7d35ec52a0b30fdfa2d" + integrity sha512-TxU/p7LB1KxQ6+7aztTnO7K0i+h0tDi81YRY9VzB6Id71kNz+fFYnf5HD5UOQmxkzcoa0TlVZf9dpMtUv0GpWg== dependencies: - "@babel/code-frame" "7.12.11" - "@eslint/eslintrc" "^0.4.1" + "@eslint/eslintrc" "^1.0.5" + "@humanwhocodes/config-array" "^0.9.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" - debug "^4.0.1" + debug "^4.3.2" doctrine "^3.0.0" enquirer "^2.3.5" - eslint-scope "^5.1.1" - eslint-utils "^2.1.0" - eslint-visitor-keys "^2.0.0" - espree "^7.3.1" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.0" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.1.0" + espree "^9.2.0" esquery "^1.4.0" esutils "^2.0.2" + fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" + glob-parent "^6.0.1" globals "^13.6.0" ignore "^4.0.6" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" - js-yaml "^3.13.1" + js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.21" + lodash.merge "^4.6.2" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" progress "^2.0.0" - regexpp "^3.1.0" + regexpp "^3.2.0" semver "^7.2.1" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" strip-json-comments "^3.1.0" - table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^7.3.0, espree@^7.3.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" - integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== +espree@^9.2.0: + version "9.2.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.2.0.tgz#c50814e01611c2d0f8bd4daa83c369eabba80dbc" + integrity sha512-oP3utRkynpZWF/F2x/HZJ+AGtnIclaR7z1pYPxy7NYM2fSO6LgK/Rkny8anRSPK/VwEA1eqm2squui0T7ZMOBg== dependencies: - acorn "^7.4.0" + acorn "^8.6.0" acorn-jsx "^5.3.1" - eslint-visitor-keys "^1.3.0" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + eslint-visitor-keys "^3.1.0" esquery@^1.4.0: version "1.4.0" @@ -2308,7 +2366,7 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -2462,10 +2520,10 @@ foreach@^2.0.5: resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= -fork-ts-checker-webpack-plugin@^6.2.9: - version "6.2.9" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.2.9.tgz#08f51b685a48b09ab3ec079a8501762422443120" - integrity sha512-D/KSb/2VeiOy3odDerrC16WiZ1t5TLwiFfZmuDeTXcf3Km79M+f8nTCIdKkokxybybrgMcStbx0QpGaseePxnA== +fork-ts-checker-webpack-plugin@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz#0282b335fa495a97e167f69018f566ea7d2a2b5e" + integrity sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw== dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" @@ -2609,13 +2667,20 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0, glob-parent@~5.1.2: +glob-parent@^5.1.0, glob-parent@~5.1.0, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -2633,13 +2698,6 @@ glob@^7.0.3, glob@^7.1.3, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -globals@^12.1.0: - version "12.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" - integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== - dependencies: - type-fest "^0.8.1" - globals@^13.6.0: version "13.8.0" resolved "https://registry.yarnpkg.com/globals/-/globals-13.8.0.tgz#3e20f504810ce87a8d72e55aecf8435b50f4c1b3" @@ -2647,10 +2705,17 @@ globals@^13.6.0: dependencies: type-fest "^0.20.2" -globby@^11.0.1: - version "11.0.3" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.3.tgz#9b1f0cb523e171dd1ad8c7b2a9fb4b644b9593cb" - integrity sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg== +globals@^13.9.0: + version "13.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.12.0.tgz#4d733760304230a0082ed96e21e5c565f898089e" + integrity sha512-uS8X6lSKN2JumVoXrbUz+uG4BYG+eiawqm3qFcT7ammfbUHeCBoJMlHcec/S3krSk73/AE/f0szYFmgAA3kYZg== + dependencies: + type-fest "^0.20.2" + +globby@^11.0.4: + version "11.0.4" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.4.tgz#2cbaff77c2f2a62e71e9b2813a67b97a3a3001a5" + integrity sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg== dependencies: array-union "^2.1.0" dir-glob "^3.0.1" @@ -2951,6 +3016,11 @@ ignore@^5.1.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +ignore@^5.1.8: + version "5.1.9" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.9.tgz#9ec1a5cbe8e1446ec60d4420060d43aa6e7382fb" + integrity sha512-2zeMQpbKz5dhZ9IwL0gbxSW5w0NK/MSAMtNuhgIHEPmaU3vPdKPL0UdvUCXs5SS4JAwsBxysK5sFMW8ocFiVjQ== + import-fresh@^3.0.0, import-fresh@^3.1.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -3193,6 +3263,13 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-nan@^1.2.1: version "1.3.2" resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.2.tgz#043a54adea31748b55b6cd4e09aadafa69bd9e1d" @@ -3374,13 +3451,12 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: - argparse "^1.0.7" - esprima "^4.0.0" + argparse "^2.0.1" json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" @@ -3397,11 +3473,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json-stable-stringify-without-jsonify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" @@ -3576,22 +3647,17 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= - lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM= +lodash.merge@^4.6.2: + version "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.15, lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.20: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -4762,10 +4828,10 @@ regexp.prototype.flags@^1.2.0: call-bind "^1.0.2" define-properties "^1.1.3" -regexpp@^3.0.0, regexpp@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== relateurl@^0.2.7: version "0.2.7" @@ -4803,11 +4869,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -5291,7 +5352,7 @@ source-map-resolve@^0.5.0: source-map-url "^0.4.0" urix "^0.1.0" -source-map-support@^0.5.17, source-map-support@~0.5.12, source-map-support@~0.5.19: +source-map-support@~0.5.12, source-map-support@~0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -5375,11 +5436,6 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - standardized-audio-context@^25.2.4: version "25.2.4" resolved "https://registry.yarnpkg.com/standardized-audio-context/-/standardized-audio-context-25.2.4.tgz#d64dbdd70615171ec90d1b7151a0d945af94cf3d" @@ -5512,6 +5568,13 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -5614,18 +5677,6 @@ tabbable@^4.0.0: resolved "https://registry.yarnpkg.com/tabbable/-/tabbable-4.0.0.tgz#5bff1d1135df1482cf0f0206434f15eadbeb9261" integrity sha512-H1XoH1URcBOa/rZZWxLxHCtOdVUEev+9vo5YdYhC9tCY4wnybX+VQrCYuy9ubkg69fCBxCONJOSLGfw0DWMffQ== -table@^6.0.4: - version "6.7.1" - resolved "https://registry.yarnpkg.com/table/-/table-6.7.1.tgz#ee05592b7143831a8c94f3cee6aae4c1ccef33e2" - integrity sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg== - dependencies: - ajv "^8.0.1" - lodash.clonedeep "^4.5.0" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.0" - strip-ansi "^6.0.0" - tapable@^1.0.0: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -5735,26 +5786,32 @@ toidentifier@1.0.0: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== -ts-loader@^9.1.2: - version "9.1.2" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.1.2.tgz#ba9b9abb05a514e8ff825791a3f6fcf793272728" - integrity sha512-ryMgATvLLl+z8zQvdlm6Pep0slmwxFWIEnq/5VdiLVjqQXnFJgO+qNLGIIP+d2N2jsFZ9MibZCVDb2bSp7OmEA== +ts-loader@^9.2.6: + version "9.2.6" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-9.2.6.tgz#9937c4dd0a1e3dbbb5e433f8102a6601c6615d74" + integrity sha512-QMTC4UFzHmu9wU2VHZEmWWE9cUajjfcdcws+Gh7FhiO+Dy0RnR1bNz0YCHqhI0yRowCE9arVnNxYHqELOy9Hjw== dependencies: chalk "^4.1.0" enhanced-resolve "^5.0.0" micromatch "^4.0.0" semver "^7.3.4" -ts-node@^9.1.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-9.1.1.tgz#51a9a450a3e959401bda5f004a72d54b936d376d" - integrity sha512-hPlt7ZACERQGf03M253ytLY3dHbGNGrAq9qIHWUY9XHYl1z7wYngSr3OQ5xmui8o2AaxsONxIzjafLUiWBo1Fg== +ts-node@^10.4.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.4.0.tgz#680f88945885f4e6cf450e7f0d6223dd404895f7" + integrity sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A== dependencies: + "@cspotcode/source-map-support" "0.7.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" arg "^4.1.0" create-require "^1.1.0" diff "^4.0.1" make-error "^1.1.1" - source-map-support "^0.5.17" yn "3.1.1" tsconfig-paths@^3.9.0: @@ -5777,7 +5834,7 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== -tsutils@^3.17.1: +tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== @@ -5821,11 +5878,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -5839,10 +5891,10 @@ typescript@*: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.3.2.tgz#399ab18aac45802d6f2498de5054fcbbe716a805" integrity sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw== -typescript@^4.2.4: - version "4.2.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" - integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== +typescript@^4.5.3: + version "4.5.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.3.tgz#afaa858e68c7103317d89eb90c5d8906268d353c" + integrity sha512-eVYaEHALSt+s9LbvgEv4Ef+Tdq7hBiIZgii12xXJnukryt3pMgJf6aKhoCZ3FWQsu6sydEnkg11fYXLzhLBjeQ== unbox-primitive@^1.0.0: version "1.0.1" From 2fff6ae41e0623fdb0d37565ba74a8488817fae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 14 Dec 2021 18:55:41 +0100 Subject: [PATCH 009/149] Improving code security by adding stricter typings --- front/src/Connexion/ConnectionManager.ts | 7 +++-- front/src/Connexion/Room.ts | 31 ++++++++++++------- front/src/Messages/JsonMessages/.gitignore | 2 ++ .../JsonMessages}/CharacterTexture.ts | 0 .../JsonMessages}/MapDetailsData.ts | 4 +-- .../JsonMessages}/RoomRedirect.ts | 0 messages/package.json | 7 +++-- messages/yarn.lock | 5 +++ .../src/Controller/AuthenticateController.ts | 4 +-- pusher/src/Controller/MapController.ts | 2 +- pusher/src/Messages/JsonMessages/.gitignore | 2 ++ pusher/src/Services/SocketManager.ts | 4 +-- 12 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 front/src/Messages/JsonMessages/.gitignore rename {pusher/src/Services/AdminApi => messages/JsonMessages}/CharacterTexture.ts (100%) rename {pusher/src/Services/AdminApi => messages/JsonMessages}/MapDetailsData.ts (87%) rename {pusher/src/Services/AdminApi => messages/JsonMessages}/RoomRedirect.ts (100%) create mode 100644 pusher/src/Messages/JsonMessages/.gitignore diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 05d84367..39bb079c 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -11,6 +11,7 @@ import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore"; import { userIsConnected } from "../Stores/MenuStore"; import { analyticsClient } from "../Administration/AnalyticsClient"; import { axiosWithRetry } from "./AxiosUtils"; +import axios from "axios"; class ConnectionManager { private localUser!: LocalUser; @@ -192,11 +193,11 @@ class ConnectionManager { analyticsClient.loggedWithSso(); } catch (err) { console.error(err); - //if user must to be connect in current room or pusher error is not openid provier access error - //try to connected with function loadOpenIDScreen + // if the user must be connected in the current room or if the pusher error is not openid provider access error + // try to connect with function loadOpenIDScreen if ( this._currentRoom.authenticationMandatory || - (err.response?.data && err.response.data !== "User cannot to be connected on openid provier") + (axios.isAxiosError(err) && err.response?.data && err.response.data !== "User cannot to be connected on openid provider") ) { this.loadOpenIDScreen(); return Promise.reject(new Error("You will be redirect on login page")); diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 044d8d67..15ac9502 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -5,6 +5,8 @@ import type { CharacterTexture } from "./LocalUser"; import { localUserStore } from "./LocalUserStore"; import axios from "axios"; import { axiosWithRetry } from "./AxiosUtils"; +import {isMapDetailsData} from "../../../pusher/src/Messages/JsonMessages/MapDetailsData"; +import {isRoomRedirect} from "../Messages/JsonMessages/RoomRedirect"; export class MapDetail { constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {} @@ -101,27 +103,34 @@ export class Room { }); const data = result.data; - if (data.redirectUrl) { + + if (isRoomRedirect(data.redirectUrl)) { return { redirectUrl: data.redirectUrl as string, }; + } else if (isMapDetailsData(data)) { + console.log("Map ", this.id, " resolves to URL ", data.mapUrl); + this._mapUrl = data.mapUrl; + this._textures = data.textures; + this._group = data.group; + this._authenticationMandatory = + data.authenticationMandatory != null ? data.authenticationMandatory : DISABLE_ANONYMOUS; + this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER; + this._contactPage = data.contactPage || CONTACT_URL; + return new MapDetail(data.mapUrl, data.textures); + } else { + throw new Error('Data received by the /map endpoint of the Pusher is not in a valid format.'); } - console.log("Map ", this.id, " resolves to URL ", data.mapUrl); - this._mapUrl = data.mapUrl; - this._textures = data.textures; - this._group = data.group; - this._authenticationMandatory = - data.authenticationMandatory != null ? data.authenticationMandatory : DISABLE_ANONYMOUS; - this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER; - this._contactPage = data.contactPage || CONTACT_URL; - return new MapDetail(data.mapUrl, data.textures); + } catch (e) { if (axios.isAxiosError(e) && e.response?.status == 401 && e.response?.data === "Token decrypted error") { console.warn("JWT token sent could not be decrypted. Maybe it expired?"); localUserStore.setAuthToken(null); window.location.assign("/login"); - } else { + } else if (axios.isAxiosError(e)) { console.error("Error => getMapDetail", e, e.response); + } else { + console.error("Error => getMapDetail", e); } throw e; } diff --git a/front/src/Messages/JsonMessages/.gitignore b/front/src/Messages/JsonMessages/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/front/src/Messages/JsonMessages/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/pusher/src/Services/AdminApi/CharacterTexture.ts b/messages/JsonMessages/CharacterTexture.ts similarity index 100% rename from pusher/src/Services/AdminApi/CharacterTexture.ts rename to messages/JsonMessages/CharacterTexture.ts diff --git a/pusher/src/Services/AdminApi/MapDetailsData.ts b/messages/JsonMessages/MapDetailsData.ts similarity index 87% rename from pusher/src/Services/AdminApi/MapDetailsData.ts rename to messages/JsonMessages/MapDetailsData.ts index 7a1f57ff..d359edf2 100644 --- a/pusher/src/Services/AdminApi/MapDetailsData.ts +++ b/messages/JsonMessages/MapDetailsData.ts @@ -1,7 +1,6 @@ import * as tg from "generic-type-guard"; -import { GameRoomPolicyTypes } from "_Model/PusherRoom"; import { isCharacterTexture } from "./CharacterTexture"; -import { isAny, isNumber } from "generic-type-guard"; +import { isNumber } from "generic-type-guard"; /*const isNumericEnum = (vs: T) => @@ -17,6 +16,7 @@ export const isMapDetailsData = new tg.IsInterface() textures: tg.isArray(isCharacterTexture), contactPage: tg.isUnion(tg.isString, tg.isUndefined), authenticationMandatory: tg.isUnion(tg.isBoolean, tg.isUndefined), + group: tg.isString, }) .get(); diff --git a/pusher/src/Services/AdminApi/RoomRedirect.ts b/messages/JsonMessages/RoomRedirect.ts similarity index 100% rename from pusher/src/Services/AdminApi/RoomRedirect.ts rename to messages/JsonMessages/RoomRedirect.ts diff --git a/messages/package.json b/messages/package.json index 636f4647..d4906977 100644 --- a/messages/package.json +++ b/messages/package.json @@ -7,10 +7,13 @@ "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-pusher": "rm -rf ../pusher/src/Messages/generated && cp -rf generated/ ../pusher/src/Messages/generated", - "proto-all": "yarn run proto && yarn run copy-to-back && yarn run copy-to-front && yarn run copy-to-pusher", - "proto:watch": "yarn run proto-all; inotifywait -q -m -e close_write protos/messages.proto | while read -r filename event; do yarn run proto-all; done" + "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/", + "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: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" }, "dependencies": { + "generic-type-guard": "^3.5.0", "google-protobuf": "^3.13.0", "grpc": "^1.24.4" }, diff --git a/messages/yarn.lock b/messages/yarn.lock index 2c9d3000..2f4a00bf 100644 --- a/messages/yarn.lock +++ b/messages/yarn.lock @@ -1788,6 +1788,11 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" +generic-type-guard@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/generic-type-guard/-/generic-type-guard-3.5.0.tgz#39de9f8fceee65d79e7540959f0e7b23210c07b6" + integrity sha512-OpgXv/sbRobhFboaSyN/Tsh97Sxt5pcfLLxCiYZgYIIWFFp+kn2EzAXiaQZKEVRlq1rOE/zh8cYhJXEwplbJiQ== + get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index 47d35fab..e2089c89 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -83,7 +83,7 @@ export class AuthenticateController extends BaseController { console.error("Token cannot to be check on OpenId provider"); res.writeStatus("500"); res.writeHeader("Access-Control-Allow-Origin", FRONT_URL); - res.end("User cannot to be connected on openid provier"); + res.end("User cannot to be connected on openid provider"); return; } @@ -105,7 +105,7 @@ export class AuthenticateController extends BaseController { console.error("User cannot to be connected on OpenId provider => ", err); res.writeStatus("500"); res.writeHeader("Access-Control-Allow-Origin", FRONT_URL); - res.end("User cannot to be connected on openid provier"); + res.end("User cannot to be connected on openid provider"); return; } const email = userInfo.email || userInfo.sub; diff --git a/pusher/src/Controller/MapController.ts b/pusher/src/Controller/MapController.ts index 23eef566..3277bd84 100644 --- a/pusher/src/Controller/MapController.ts +++ b/pusher/src/Controller/MapController.ts @@ -4,7 +4,7 @@ import { parse } from "query-string"; import { adminApi } from "../Services/AdminApi"; import { ADMIN_API_URL, DISABLE_ANONYMOUS, FRONT_URL } from "../Enum/EnvironmentVariable"; import { GameRoomPolicyTypes } from "../Model/PusherRoom"; -import { isMapDetailsData, MapDetailsData } from "../Services/AdminApi/MapDetailsData"; +import { isMapDetailsData, MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; import { socketManager } from "../Services/SocketManager"; import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { v4 } from "uuid"; diff --git a/pusher/src/Messages/JsonMessages/.gitignore b/pusher/src/Messages/JsonMessages/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/pusher/src/Messages/JsonMessages/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 083840e4..f66e20fc 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -47,8 +47,8 @@ import { GroupDescriptor, UserDescriptor, ZoneEventListener } from "_Model/Zone" import Debug from "debug"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; import { WebSocket } from "uWebSockets.js"; -import { isRoomRedirect } from "./AdminApi/RoomRedirect"; -import { CharacterTexture } from "./AdminApi/CharacterTexture"; +import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; +import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; const debug = Debug("socket"); From 10b7fee50260d1e1d86614ff14ba097011a72be7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 14 Dec 2021 19:03:02 +0100 Subject: [PATCH 010/149] Even better typing of messages in /map --- messages/JsonMessages/MapDetailsData.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/messages/JsonMessages/MapDetailsData.ts b/messages/JsonMessages/MapDetailsData.ts index d359edf2..899506b7 100644 --- a/messages/JsonMessages/MapDetailsData.ts +++ b/messages/JsonMessages/MapDetailsData.ts @@ -16,7 +16,10 @@ export const isMapDetailsData = new tg.IsInterface() textures: tg.isArray(isCharacterTexture), contactPage: tg.isUnion(tg.isString, tg.isUndefined), authenticationMandatory: tg.isUnion(tg.isBoolean, tg.isUndefined), - group: tg.isString, + group: tg.isNullable(tg.isString), + }) + .withOptionalProperties({ + iframeAuthentication: tg.isNullable(tg.isString), }) .get(); From 4385d5aff979f6d6d00df75d13394f779e9f2391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Tue, 14 Dec 2021 19:05:14 +0100 Subject: [PATCH 011/149] Adding warning messages regarding files copying --- messages/JsonMessages/CharacterTexture.ts | 5 +++++ messages/JsonMessages/MapDetailsData.ts | 8 ++++---- messages/JsonMessages/RoomRedirect.ts | 5 +++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/messages/JsonMessages/CharacterTexture.ts b/messages/JsonMessages/CharacterTexture.ts index 055b3033..eb2ec15e 100644 --- a/messages/JsonMessages/CharacterTexture.ts +++ b/messages/JsonMessages/CharacterTexture.ts @@ -1,5 +1,10 @@ import * as tg from "generic-type-guard"; +/* + * WARNING! The original file is in /messages/JsonMessages. + * All other files are automatically copied from this file on container startup / build + */ + export const isCharacterTexture = new tg.IsInterface() .withProperties({ id: tg.isNumber, diff --git a/messages/JsonMessages/MapDetailsData.ts b/messages/JsonMessages/MapDetailsData.ts index 899506b7..ff18acc3 100644 --- a/messages/JsonMessages/MapDetailsData.ts +++ b/messages/JsonMessages/MapDetailsData.ts @@ -2,10 +2,10 @@ import * as tg from "generic-type-guard"; import { isCharacterTexture } from "./CharacterTexture"; import { isNumber } from "generic-type-guard"; -/*const isNumericEnum = - (vs: T) => - (v: any): v is T => - typeof v === "number" && v in vs;*/ +/* + * WARNING! The original file is in /messages/JsonMessages. + * All other files are automatically copied from this file on container startup / build + */ export const isMapDetailsData = new tg.IsInterface() .withProperties({ diff --git a/messages/JsonMessages/RoomRedirect.ts b/messages/JsonMessages/RoomRedirect.ts index 7257ebd3..1eb09937 100644 --- a/messages/JsonMessages/RoomRedirect.ts +++ b/messages/JsonMessages/RoomRedirect.ts @@ -1,5 +1,10 @@ import * as tg from "generic-type-guard"; +/* + * WARNING! The original file is in /messages/JsonMessages. + * All other files are automatically copied from this file on container startup / build + */ + export const isRoomRedirect = new tg.IsInterface() .withProperties({ redirectUrl: tg.isString, From be0c70f9acb3af8cb53c94da7de77c67f2d48b9b Mon Sep 17 00:00:00 2001 From: Valdo Romao Date: Wed, 15 Dec 2021 11:11:37 +0000 Subject: [PATCH 012/149] Added some skins to test the woka related code --- maps/assets/skins/skin-blue.png | Bin 0 -> 12676 bytes maps/assets/skins/skin-green.png | Bin 0 -> 13572 bytes maps/assets/skins/skin-yellow.png | Bin 0 -> 14003 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 maps/assets/skins/skin-blue.png create mode 100644 maps/assets/skins/skin-green.png create mode 100644 maps/assets/skins/skin-yellow.png diff --git a/maps/assets/skins/skin-blue.png b/maps/assets/skins/skin-blue.png new file mode 100644 index 0000000000000000000000000000000000000000..ad6a976e81796c612f22d8109932e965845a77a4 GIT binary patch literal 12676 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 Nf{dzkjig!F{{c${J^}y$ literal 0 HcmV?d00001 diff --git a/maps/assets/skins/skin-yellow.png b/maps/assets/skins/skin-yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..f3214968441f8b5ba6c9b75329bac2d01db2416d GIT binary patch literal 14003 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ÿUC&zTvaO<#el;VcY=UvXyRB0s#}qO3H?V~zku z#B?OKUegADZdoG@DCAPiBDYk>db%moZjO4?Cv&~xB6{?L*V$^GeDkRRK)B(ar>-4? z6jr7WoYSq5Cb+l&Cti)SB5Gm{Qt!NXVR1pTbLJa0xQPmwIq=;0?t{G6{@TVwH3^}Y zE;mhmgBBPt5N6G9x9bJZ9#*Cyz}ok0_T|bRW=b7gmFs&uT3q6E<+nwa`=6$4AWjjbYZ%*7pX#3!Y4kN8aX?PZF8sh|tF+ zWHZn`n`v=zafuX(tGT*hHMuPBu3-l*bNrRj5pRAFumG4*aPgupWhl}Vqp%UnF!CZs=n{M`B(MD+f z6@~$Tcqrv_0@{zAVOR-j1welO(4{5w^0Km+@GdZ*pd+}wxbSg`FVR*|jV)J8r7E7g_g_Z^QPO~+Y8Q2DOnseE5bro|$S+0g4w2iQi_)oSr^ zaWhk9SC-!|RRR10qvmiM%#dl>V-+J$DRp+_N73r=**dddu$|kisfC2=zl9f|Q)FwI z+bsr9qqX1@wPtlZxF3v_OsA*}e$BAk Ks`%Sz!nXT50Hw*oF(v$`np&CGzT`4!2 zFSv?W3&sq%q386_sbW4VYUio&0u7P8qeNxWw*dFdU%UTa#(>>aYPt^q59+Ww070=6=mZLlA3vW@{@fP6SD8ah@yKM z9EGF(r7J%499|^pAY=upHqqst@?C~9bgUl}4T>`$C%n9zXMee!UUIl=?<}$4^^Rxr zW0IEzS+WxCX#k4Vi4xuX=4O*Jtenmw4;)Am5xR30L3ZL>$-0olStW*3{JWsv=xKK&3z@62zD? zn~{~(vpJZ$dgiNbVNv0~D(Z$VI100|aXsHR!Va}msfMeiczSv!Itt^;5#fn+;5A2q zPyt-^)OeUt<2xXAb^Nts?@xO~f44~dre;;!Q?DSs!0QW!YbrxSIhoIa7|YiLmll}8 zbA{KGwtJ{4d0sWotmZ16q8Ujr4Pv{FiUVV~;1idcZ@5KECwm=A%Ct{U4bVIG*j2iU z7zYnf2YlY|l5&WkAiRj0Eb9%@2>{Otw=b5puv!o#rl&>)n3k;N+iPBqoao)VfKojy zm(?e^tRc>sK+mCtA?lQBq~3uvUu~c~^7j|o>us$Au~gZC=1=)(wN6i}^2gZgolU3# z-1W{6nAZ*T`$|dC4z6v?0m?;vdr7?{!2%GT332mXc`**kB1W^Oyk@fZ^%WRpD||YS z3=vE>figx-uL6WK6ifE7YH2Mu$Ig`ItgO#YtF7>SBgz-o5d9$35_znC0F<~rwk6RFg}&5^~$n8rpix3zAZLDbb_?|w*} z6efU$uxf+R z(*V{yn@8v8qiH2PbK2VI*4NjupUH3h@)`Fz?s?#Pv?8}ZSBKlx)rF)*T4pB5!Xmq% z^KgHkSzTS-p0+#HB2ab7caIw|kiv3saDa`DisX6s#zy`X(@J1mqKhbA(Krr3rD%L% z^sO=u{;$uU@pvrSUe3-M4Wbf9f#TKJB`#>B{V4!0KThoii(0%TV-ezIb573An1J-i zwDfewW&iK^N=izI$D8VdBeAgr@*dv{iivYFnDq<|yEffAcVEzynIVl7jT`AS|9v9t zW%`U2RLo;9^zmq=ix=vTd=T*_0&&VsbUhOo*s87Wv|^tklyWuse)zhz{Vc)8g!Di& zz)${SS;j_baRW=}HB?;fZRhm6>>aCBhg`Bj9b%^{`PFSU6(@WkwbxruxYC7G?`t;SBsk7t$Mt6Y7#F@)xzmqTkMA#|0x!$EKT9O z$Zk8`>!$LEtY7LvSFy~M&EGnG{ma(%a(;C64H-TD@sTp#q`vCgeyZ!_wy*n{kwU6q z)!q=ga`K1+^r4i+jxhY;Ayb8=*_;LEWs?Lz%K*#5=>nVEw?g3KTl?jBevM(Yk$w*) zAJT(8x;(^=@4GH6QdM7`)Ud8qE$D7=b$L5+)cI{qfC7^&5qe}^@jwOC2s5WRk404; zxx$0A!`WV>Sm&_4h-bzjc#ei#-xHbpt4rs$eCQfYHJnn(&QXd3b*^U}J++@$8%=R* zPv79>D4Fs+Sa23WK3X^mXY6tfe`n+5)V&~2%g7M$x?CP+O?R4zooZWje%`hE6wzv= z6_oNrHSEAebj7oI)veL8W;T8ZHWYw~%vWDF-H4%sL@axUi_Ooku6!KlYH=D(2>`Og z(RsE}w)zehDNyDlRgGpiKngB*?hfeJx&HqB`%ERctNMI&a#Nie1u!x;7P{USx4zaB zy?uD7?BKu&(%#tJ-6dC7^gG|LV#Y@Dc6(>1^ZKwo?#&yz+S*zGz}Up(0WBO3ukZf# zM#_&tH8I`M!^49}F~PyfNiir$hR3`m5{dc#fd9sXXt6t_JUa-P7y%t4BSgy3c>tHm zR2B~{@~2uzice5s6pcrnoVZ=y1X}Y`o;A$k;o-T^($a=Y6PBPa?;!HZ%J2d4D$Ixn zHWc3g03_$-Ig)j7$=C{J1ac}W;_jo%WjWg_F9)$?`=3M=;8f1FF8gfJ>KPdDL=PJO ad?2%NrI4sR9)jF208|w Date: Mon, 6 Dec 2021 14:37:06 +0100 Subject: [PATCH 013/149] Remove unused import --- front/src/Connexion/ConnectionManager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 05d84367..883e705e 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -1,5 +1,5 @@ import Axios from "axios"; -import { PUSHER_URL, START_ROOM_URL } from "../Enum/EnvironmentVariable"; +import { PUSHER_URL } from "../Enum/EnvironmentVariable"; import { RoomConnection } from "./RoomConnection"; import type { OnConnectInterface, PositionInterface, ViewportInterface } from "./ConnexionModels"; import { GameConnexionTypes, urlManager } from "../Url/UrlManager"; From f340e8114da5cd8329c5dd5ae340d8ec96d6df21 Mon Sep 17 00:00:00 2001 From: danb Date: Tue, 19 Oct 2021 15:35:19 +0200 Subject: [PATCH 014/149] Implement automatic following of other players. * initiate following by reacting to the interact event * subscribe to remote player and update positions in relation to them instead of reacting to user input * cancel following if the user moves actively again --- front/src/Phaser/Player/Player.ts | 107 ++++++++++++++++++++++++++++-- 1 file changed, 103 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index a1924457..0e576235 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -1,15 +1,18 @@ import { PlayerAnimationDirections } from "./Animation"; import type { GameScene } from "../Game/GameScene"; -import { UserInputEvent, UserInputManager } from "../UserInput/UserInputManager"; +import { ActiveEventList, UserInputEvent, UserInputManager } from "../UserInput/UserInputManager"; import { Character } from "../Entity/Character"; +import type { RemotePlayer } from "../Entity/RemotePlayer"; import { userMovingStore } from "../../Stores/GameStore"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; export class Player extends Character { - private previousDirection: string = PlayerAnimationDirections.Down; + private previousDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; private wasMoving: boolean = false; + private timeCounter: number = 0; + private follow: { followPlayer: RemotePlayer; direction: PlayerAnimationDirections } | null = null; constructor( Scene: GameScene, @@ -29,17 +32,17 @@ export class Player extends Character { this.getBody().setImmovable(false); } - moveUser(delta: number): void { + private inputStep(activeEvents: ActiveEventList, delta: number) { //if user client on shift, camera and player speed let direction = null; let moving = false; - const activeEvents = this.userInputManager.getEventListForGameTick(); const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; const moveAmount = speedMultiplier * 20; let x = 0; let y = 0; + if (activeEvents.get(UserInputEvent.MoveUp)) { y = -moveAmount; direction = PlayerAnimationDirections.Up; @@ -49,6 +52,7 @@ export class Player extends Character { direction = PlayerAnimationDirections.Down; moving = true; } + if (activeEvents.get(UserInputEvent.MoveLeft)) { x = -moveAmount; direction = PlayerAnimationDirections.Left; @@ -58,6 +62,7 @@ export class Player extends Character { direction = PlayerAnimationDirections.Right; moving = true; } + moving = moving || activeEvents.get(UserInputEvent.JoystickMove); if (x !== 0 || y !== 0) { @@ -89,10 +94,104 @@ export class Player extends Character { if (direction !== null) { this.previousDirection = direction; } + this.wasMoving = moving; userMovingStore.set(moving); } + private followStep(activeEvents: ActiveEventList, delta: number) { + if (this.follow === null) { + return; + } + + this.timeCounter += delta; + if (this.timeCounter < 128) { + return; + } + this.timeCounter = 0; + + const xDist = this.follow.followPlayer.x - this.x; + const yDist = this.follow.followPlayer.y - this.y; + + const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); + + if (distance < 650) { + this.stop(); + } else { + const moveAmount = 9 * 20; + const xDir = xDist / Math.sqrt(distance); + const yDir = yDist / Math.sqrt(distance); + + this.move(xDir * moveAmount, yDir * moveAmount); + + if (Math.abs(xDist) > Math.abs(yDist)) { + if (xDist < 0) { + this.follow.direction = PlayerAnimationDirections.Left; + } else { + this.follow.direction = PlayerAnimationDirections.Right; + } + } else { + if (yDist < 0) { + this.follow.direction = PlayerAnimationDirections.Up; + } else { + this.follow.direction = PlayerAnimationDirections.Down; + } + } + } + + this.emit(hasMovedEventName, { + moving: true, + direction: this.follow.direction, + x: this.x, + y: this.y, + }); + + this.previousDirection = this.follow.direction; + + this.wasMoving = true; + userMovingStore.set(true); + } + + moveUser(delta: number): void { + const activeEvents = this.userInputManager.getEventListForGameTick(); + + if (activeEvents.get(UserInputEvent.Interact)) { + const sortedPlayers = Array.from(this.scene.MapPlayersByKey.values()).sort((p1, p2) => { + const distToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); + const distToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); + if (distToP1 > distToP2) { + return 1; + } else if (distToP1 < distToP2) { + return -1; + } else { + return 0; + } + }); + + if (typeof sortedPlayers !== "undefined" && sortedPlayers.length > 0) { + this.follow = { + followPlayer: sortedPlayers[0], + direction: this.previousDirection, + }; + } + } + + if ( + activeEvents.get(UserInputEvent.MoveUp) || + activeEvents.get(UserInputEvent.MoveDown) || + activeEvents.get(UserInputEvent.MoveLeft) || + activeEvents.get(UserInputEvent.MoveRight) + ) { + this.follow = null; + } + + if (this.follow === null) { + this.inputStep(activeEvents, delta); + } else { + this.followStep(activeEvents, delta); + } + } + public isMoving(): boolean { return this.wasMoving; } From 7c7144527c09a01d134b154a20bbe0282bd0854b Mon Sep 17 00:00:00 2001 From: danb Date: Thu, 28 Oct 2021 16:20:57 +0200 Subject: [PATCH 015/149] Add minimum distance to enable following --- front/src/Phaser/Player/Player.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 0e576235..5846c10e 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -157,22 +157,26 @@ export class Player extends Character { if (activeEvents.get(UserInputEvent.Interact)) { const sortedPlayers = Array.from(this.scene.MapPlayersByKey.values()).sort((p1, p2) => { - const distToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); - const distToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); - if (distToP1 > distToP2) { + const sdistToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); + const sdistToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); + if (sdistToP1 > sdistToP2) { return 1; - } else if (distToP1 < distToP2) { + } else if (sdistToP1 < sdistToP2) { return -1; } else { return 0; } }); + const minFollowDist = 10000; if (typeof sortedPlayers !== "undefined" && sortedPlayers.length > 0) { - this.follow = { - followPlayer: sortedPlayers[0], - direction: this.previousDirection, - }; + const sdist = Math.pow(sortedPlayers[0].x - this.x, 2) + Math.pow(sortedPlayers[0].y - this.y, 2); + if (sdist < minFollowDist) { + this.follow = { + followPlayer: sortedPlayers[0], + direction: this.previousDirection, + }; + } } } From 372dda792f115814107b1b05013d85425bcc00bf Mon Sep 17 00:00:00 2001 From: danb Date: Thu, 18 Nov 2021 12:23:10 +0100 Subject: [PATCH 016/149] Fix issue of interrupted conversation in follow-mode --- front/src/Phaser/Player/Player.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 5846c10e..17ad1cc1 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -100,6 +100,8 @@ export class Player extends Character { } private followStep(activeEvents: ActiveEventList, delta: number) { + let moving = false; + if (this.follow === null) { return; } @@ -137,10 +139,12 @@ export class Player extends Character { this.follow.direction = PlayerAnimationDirections.Down; } } + + moving = true; } this.emit(hasMovedEventName, { - moving: true, + moving: moving, direction: this.follow.direction, x: this.x, y: this.y, @@ -148,8 +152,8 @@ export class Player extends Character { this.previousDirection = this.follow.direction; - this.wasMoving = true; - userMovingStore.set(true); + this.wasMoving = moving; + userMovingStore.set(moving); } moveUser(delta: number): void { From e7f1395809dc8c088762235e3f0f1caecabc9e5b Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 30 Nov 2021 11:48:59 +0100 Subject: [PATCH 017/149] Stop before running into followed Avatar; stop sprite animation --- front/src/Phaser/Game/PlayerMovement.ts | 2 +- front/src/Phaser/Player/Player.ts | 2 +- front/tests/Phaser/Game/PlayerMovementTest.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Game/PlayerMovement.ts b/front/src/Phaser/Game/PlayerMovement.ts index 7758f010..274cbee1 100644 --- a/front/src/Phaser/Game/PlayerMovement.ts +++ b/front/src/Phaser/Game/PlayerMovement.ts @@ -41,7 +41,7 @@ export class PlayerMovement { oldX: this.startPosition.x, oldY: this.startPosition.y, direction: this.endPosition.direction, - moving: true, + moving: this.endPosition.moving, }; } } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 17ad1cc1..adf7a302 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -117,7 +117,7 @@ export class Player extends Character { const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); - if (distance < 650) { + if (distance < 2000) { this.stop(); } else { const moveAmount = 9 * 20; diff --git a/front/tests/Phaser/Game/PlayerMovementTest.ts b/front/tests/Phaser/Game/PlayerMovementTest.ts index 70f7b95d..bd5f40b4 100644 --- a/front/tests/Phaser/Game/PlayerMovementTest.ts +++ b/front/tests/Phaser/Game/PlayerMovementTest.ts @@ -74,7 +74,7 @@ describe("Interpolation / Extrapolation", () => { }); }); - it("should should keep moving until it stops", () => { + it("should keep moving until it stops", () => { const playerMovement = new PlayerMovement({ x: 100, y: 200 }, 42000, @@ -95,7 +95,7 @@ describe("Interpolation / Extrapolation", () => { oldX: 100, oldY: 200, direction: 'up', - moving: true + moving: false }); }); }) From b30d0989c86a45e9c438959759433291ecb9cb89 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 7 Dec 2021 22:14:51 +0100 Subject: [PATCH 018/149] Fix indentation in messages.proto --- messages/protos/messages.proto | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 117ab582..2dfd9aa2 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -67,12 +67,12 @@ message ReportPlayerMessage { } message EmotePromptMessage { - string emote = 2; + string emote = 2; } message EmoteEventMessage { - int32 actorUserId = 1; - string emote = 2; + int32 actorUserId = 1; + string emote = 2; } message QueryJitsiJwtMessage { @@ -236,14 +236,14 @@ message SendUserMessage{ message WorldFullWarningMessage{ } message WorldFullWarningToRoomMessage{ - string roomId = 1; + string roomId = 1; } message RefreshRoomPromptMessage{ - string roomId = 1; + string roomId = 1; } message RefreshRoomMessage{ - string roomId = 1; - int32 versionNumber = 2; + string roomId = 1; + int32 versionNumber = 2; } message WorldFullMessage{ From 1fca99c0d1317b97b6ace0bca3d892eb7363128b Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sun, 12 Dec 2021 00:24:39 +0100 Subject: [PATCH 019/149] Send follow me request to all players --- back/src/RoomManager.ts | 7 +++++++ back/src/Services/SocketManager.ts | 12 ++++++++++++ front/src/Connexion/RoomConnection.ts | 12 ++++++++++++ messages/protos/messages.proto | 15 +++++++++++++++ pusher/src/Controller/IoSocketController.ts | 6 ++++++ pusher/src/Services/SocketManager.ts | 7 +++++++ 6 files changed, 59 insertions(+) diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 8dbde018..16a9d023 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -8,6 +8,7 @@ import { BatchToPusherMessage, BatchToPusherRoomMessage, EmotePromptMessage, + FollowMeRequestMessage, EmptyMessage, ItemEventMessage, JoinRoomMessage, @@ -116,6 +117,12 @@ const roomManager: IRoomManagerServer = { user, message.getEmotepromptmessage() as EmotePromptMessage ); + } else if (message.hasFollowmerequestmessage()) { + socketManager.handleFollowMeRequestMessage( + room, + user, + message.getFollowmerequestmessage() as FollowMeRequestMessage + ); } else if (message.hasSendusermessage()) { const sendUserMessage = message.getSendusermessage(); if (sendUserMessage !== undefined) { diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 8989df75..069e298b 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -30,6 +30,7 @@ import { BanUserMessage, RefreshRoomMessage, EmotePromptMessage, + FollowMeRequestMessage, VariableMessage, BatchToPusherRoomMessage, SubToPusherRoomMessage, @@ -833,6 +834,17 @@ export class SocketManager { emoteEventMessage.setActoruserid(user.id); room.emitEmoteEvent(user, emoteEventMessage); } + + handleFollowMeRequestMessage(room: GameRoom, user: User, requestMessage: FollowMeRequestMessage) { + console.log("Handling follow me request message"); + console.log(user.name); + requestMessage.setPlayername(user.name); + room.getUsers().forEach((recipient) => { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowmerequestmessage(requestMessage); + recipient.socket.write(clientMessage); + }); + } } export const socketManager = new SocketManager(); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 9e4025b1..de7cfcc2 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -30,6 +30,7 @@ import { PingMessage, EmoteEventMessage, EmotePromptMessage, + FollowMeRequestMessage, SendUserMessage, BanUserMessage, VariableMessage, @@ -257,6 +258,9 @@ export class RoomConnection implements RoomConnection { warningContainerStore.activateWarningContainer(); } else if (message.hasRefreshroommessage()) { //todo: implement a way to notify the user the room was refreshed. + } else if (message.hasFollowmerequestmessage()) { + const requestMessage = message.getFollowmerequestmessage() as FollowMeRequestMessage; + console.log("Follow me request from " + requestMessage.getPlayername()); } else if (message.hasErrormessage()) { const errorMessage = message.getErrormessage() as ErrorMessage; console.error("An error occurred server side: " + errorMessage.getMessage()); @@ -712,6 +716,14 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } + public emitFollowMeRequest(): void { + console.log("Emitting follow me request"); + const message = new FollowMeRequestMessage(); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setFollowmerequestmessage(message); + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + public getAllTags(): string[] { return this.tags; } diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 2dfd9aa2..e9e75205 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -80,6 +80,15 @@ message QueryJitsiJwtMessage { string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map! } +message FollowMeRequestMessage { + string playerName = 1; +} + +message FollowMeResponseMessage { + string playerName = 1; + bool accepted = 2; +} + message ClientToServerMessage { oneof message { UserMovesMessage userMovesMessage = 2; @@ -95,6 +104,8 @@ message ClientToServerMessage { QueryJitsiJwtMessage queryJitsiJwtMessage = 12; EmotePromptMessage emotePromptMessage = 13; VariableMessage variableMessage = 14; + FollowMeRequestMessage followMeRequestMessage = 15; + FollowMeResponseMessage followMeResponseMessage = 16; } } @@ -285,6 +296,8 @@ message ServerToClientMessage { WorldConnexionMessage worldConnexionMessage = 18; //EmoteEventMessage emoteEventMessage = 19; TokenExpiredMessage tokenExpiredMessage = 20; + FollowMeRequestMessage followMeRequestMessage = 21; + FollowMeResponseMessage followMeResponseMessage = 22; } } @@ -365,6 +378,8 @@ message PusherToBackMessage { BanUserMessage banUserMessage = 13; EmotePromptMessage emotePromptMessage = 14; VariableMessage variableMessage = 15; + FollowMeRequestMessage followMeRequestMessage = 16; + FollowMeResponseMessage followMeResponseMessage = 17; } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index df29db57..5d66c4df 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -17,6 +17,7 @@ import { ServerToClientMessage, CompanionMessage, EmotePromptMessage, + FollowMeRequestMessage, VariableMessage, } from "../Messages/generated/messages_pb"; import { UserMovesMessage } from "../Messages/generated/messages_pb"; @@ -469,6 +470,11 @@ export class IoSocketController { client, message.getEmotepromptmessage() as EmotePromptMessage ); + } else if (message.hasFollowmerequestmessage()) { + socketManager.handleFollowMeRequest( + client, + message.getFollowmerequestmessage() as FollowMeRequestMessage + ); } /* Ok is false if backpressure was built up, wait for drain */ diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 4f4b086f..2df167bd 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -8,6 +8,7 @@ import { CharacterLayerMessage, EmoteEventMessage, EmotePromptMessage, + FollowMeRequestMessage, GroupDeleteMessage, ItemEventMessage, JoinRoomMessage, @@ -269,6 +270,12 @@ export class SocketManager implements ZoneEventListener { this.handleViewport(client, viewport.toObject()); } + handleFollowMeRequest(client: ExSocketInterface, requestMessage: FollowMeRequestMessage): void { + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setFollowmerequestmessage(requestMessage); + client.backConnection.write(pusherToBackMessage); + } + onEmote(emoteMessage: EmoteEventMessage, listener: ExSocketInterface): void { const subMessage = new SubMessage(); subMessage.setEmoteeventmessage(emoteMessage); From d6ef60a3d898238265f247d23951d78cf0365585 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sun, 12 Dec 2021 02:17:39 +0100 Subject: [PATCH 020/149] Send request to group members only --- back/src/Model/Group.ts | 10 ++++++++++ back/src/Services/SocketManager.ts | 21 +++++++++++++++------ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 931ddda5..570eaedf 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -106,6 +106,16 @@ export class Group implements Movable { return this.users.size <= 1; } + includes(user: User): boolean { + let found = false; + this.users.forEach((currentUser: User) => { + if (currentUser.name === user.name) { + found = true; + } + }); + return found; + } + join(user: User): void { // Broadcast on the right event this.connectCallback(user, this); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 069e298b..cc950163 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -836,13 +836,22 @@ export class SocketManager { } handleFollowMeRequestMessage(room: GameRoom, user: User, requestMessage: FollowMeRequestMessage) { - console.log("Handling follow me request message"); - console.log(user.name); + // Find group including the requesting user + let foundGroups = room.getGroups().filter((grp) => grp.includes(user)); + if (!foundGroups[0]) { + return; + } + let group = foundGroups[0]; + + // Send invitations to other group members requestMessage.setPlayername(user.name); - room.getUsers().forEach((recipient) => { - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowmerequestmessage(requestMessage); - recipient.socket.write(clientMessage); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowmerequestmessage(requestMessage); + group.getUsers().forEach((currentUser: User) => { + if (user.name !== currentUser.name) { + console.log("Inviting " + currentUser.name + " to follow " + user.name); + currentUser.socket.write(clientMessage); + } }); } } From 0a410d289d8f0fdf553f70dd58da0ec830306a13 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sun, 12 Dec 2021 16:56:26 +0100 Subject: [PATCH 021/149] Implement follow request / confirmation UI --- back/src/Model/GameRoom.ts | 25 ++ back/src/RoomManager.ts | 22 +- back/src/Services/SocketManager.ts | 44 ++-- front/src/Components/App.svelte | 7 + .../InteractMenu/InteractMenu.svelte | 238 ++++++++++++++++++ front/src/Connexion/RoomConnection.ts | 79 +++++- front/src/Phaser/Player/Player.ts | 63 +++-- front/src/Stores/InteractStore.ts | 17 ++ messages/protos/messages.proto | 28 ++- pusher/src/Controller/IoSocketController.ts | 17 +- pusher/src/Services/SocketManager.ts | 20 +- 11 files changed, 481 insertions(+), 79 deletions(-) create mode 100644 front/src/Components/InteractMenu/InteractMenu.svelte create mode 100644 front/src/Stores/InteractStore.ts diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 5c114f19..1b0db5eb 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -14,6 +14,7 @@ import { SubToPusherRoomMessage, VariableMessage, VariableWithTagMessage, + ServerToClientMessage, } from "../Messages/generated/messages_pb"; import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils"; import { RoomSocket, ZoneSocket } from "src/RoomManager"; @@ -95,10 +96,20 @@ export class GameRoom { return Array.from(this.groups.values()); } + public getGroupIncludingUser(user: User): Group | undefined { + const foundGroups = this.getGroups().filter((grp) => grp.includes(user)); + return foundGroups[0]; + } + public getUsers(): Map { return this.users; } + public getUserByName(name: string): User | undefined { + let foundUsers = Array.from(this.users.values()); + foundUsers = foundUsers.filter((user: User) => user.name === name); + return foundUsers[0]; + } public getUserByUuid(uuid: string): User | undefined { return this.usersByUuid.get(uuid); } @@ -226,6 +237,20 @@ export class GameRoom { } } + public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void { + this.getGroupIncludingUser(user) + ?.getUsers() + .forEach((currentUser: User) => { + if (currentUser.name !== user.name) { + currentUser.socket.write(message); + } + }); + } + + public sendToUserWithName(name: string, message: ServerToClientMessage): void { + this.getUserByName(name)?.socket.write(message); + } + setSilent(user: User, silent: boolean) { if (user.silent === silent) { return; diff --git a/back/src/RoomManager.ts b/back/src/RoomManager.ts index 16a9d023..9020c130 100644 --- a/back/src/RoomManager.ts +++ b/back/src/RoomManager.ts @@ -8,7 +8,9 @@ import { BatchToPusherMessage, BatchToPusherRoomMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, EmptyMessage, ItemEventMessage, JoinRoomMessage, @@ -117,11 +119,23 @@ const roomManager: IRoomManagerServer = { user, message.getEmotepromptmessage() as EmotePromptMessage ); - } else if (message.hasFollowmerequestmessage()) { - socketManager.handleFollowMeRequestMessage( + } else if (message.hasFollowrequestmessage()) { + socketManager.handleFollowRequestMessage( room, user, - message.getFollowmerequestmessage() as FollowMeRequestMessage + message.getFollowrequestmessage() as FollowRequestMessage + ); + } else if (message.hasFollowconfirmationmessage()) { + socketManager.handleFollowConfirmationMessage( + room, + user, + message.getFollowconfirmationmessage() as FollowConfirmationMessage + ); + } else if (message.hasFollowabortmessage()) { + socketManager.handleFollowAbortMessage( + room, + user, + message.getFollowabortmessage() as FollowAbortMessage ); } else if (message.hasSendusermessage()) { const sendUserMessage = message.getSendusermessage(); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index cc950163..3ab53719 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -30,7 +30,9 @@ import { BanUserMessage, RefreshRoomMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, VariableMessage, BatchToPusherRoomMessage, SubToPusherRoomMessage, @@ -835,24 +837,30 @@ export class SocketManager { room.emitEmoteEvent(user, emoteEventMessage); } - handleFollowMeRequestMessage(room: GameRoom, user: User, requestMessage: FollowMeRequestMessage) { - // Find group including the requesting user - let foundGroups = room.getGroups().filter((grp) => grp.includes(user)); - if (!foundGroups[0]) { - return; - } - let group = foundGroups[0]; - - // Send invitations to other group members - requestMessage.setPlayername(user.name); + handleFollowRequestMessage(room: GameRoom, user: User, message: FollowRequestMessage) { const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowmerequestmessage(requestMessage); - group.getUsers().forEach((currentUser: User) => { - if (user.name !== currentUser.name) { - console.log("Inviting " + currentUser.name + " to follow " + user.name); - currentUser.socket.write(clientMessage); - } - }); + clientMessage.setFollowrequestmessage(message); + room.sendToOthersInGroupIncludingUser(user, clientMessage); + } + + handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowconfirmationmessage(message); + room.sendToUserWithName(message.getLeader(), clientMessage); + } + + handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { + if (message.getRole() === "leader") { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + room.sendToOthersInGroupIncludingUser(user, clientMessage); + } else { + const recipient = message.getPlayername(); + message.setPlayername(user.name); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + room.sendToUserWithName(recipient, clientMessage); + } } } diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index 4886cc4e..b14801c9 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -42,6 +42,8 @@ import AudioManager from "./AudioManager/AudioManager.svelte"; import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore"; import ReportMenu from "./ReportMenu/ReportMenu.svelte"; + import { followStateStore, followStates } from "../Stores/InteractStore"; + import InteractMenu from "./InteractMenu/InteractMenu.svelte"; export let game: Game; @@ -102,6 +104,11 @@ {/if} + {#if $followStateStore !== followStates.off} +
+ +
+ {/if} {#if $menuIconVisiblilityStore}
diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/InteractMenu/InteractMenu.svelte new file mode 100644 index 00000000..cb78f5bc --- /dev/null +++ b/front/src/Components/InteractMenu/InteractMenu.svelte @@ -0,0 +1,238 @@ + + + + + +{#if followState === followStates.requesting} +
+
+

Interaction

+
+ {#if followRole === followRoles.follower} +
+

Do you want to follow {followUsers[0]}?

+
+
+ + +
+ {:else if followRole === followRoles.leader} +
+

Ask others to follow you?

+
+
+ + +
+ {/if} +
+{/if} + +{#if followState === followStates.ending} +
+
+

Interaction

+
+ {#if followRole === followRoles.follower} +
+

Do you want to stop following {followUsers[0]}?

+
+ {:else if followRole === followRoles.leader} +
+

Do you want to stop leading the way?

+
+ {/if} +
+ + +
+
+{/if} + +{#if followState === followStates.active || followState === followStates.ending} +
+
+ {#if followRole === followRoles.follower} +

Following {followUsers[0]}

+ {:else if followUsers.length === 0} +

Waiting for followers' confirmation

+ {:else if followUsers.length === 1} +

{followUsers[0]} is following you

+ {:else if followUsers.length === 2} +

{followUsers[0]} and {followUsers[1]} are following you

+ {:else} +

{followUsers[0]}, {followUsers[1]} and {followUsers[2]} are following you

+ {/if} +
+
+{/if} + + diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index de7cfcc2..e3f36cb7 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -30,7 +30,9 @@ import { PingMessage, EmoteEventMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, SendUserMessage, BanUserMessage, VariableMessage, @@ -58,7 +60,15 @@ 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, + followRoles, + followStates, +} from "../Stores/InteractStore"; const manualPingDelay = 20000; @@ -258,9 +268,32 @@ export class RoomConnection implements RoomConnection { warningContainerStore.activateWarningContainer(); } else if (message.hasRefreshroommessage()) { //todo: implement a way to notify the user the room was refreshed. - } else if (message.hasFollowmerequestmessage()) { - const requestMessage = message.getFollowmerequestmessage() as FollowMeRequestMessage; - console.log("Follow me request from " + requestMessage.getPlayername()); + } else if (message.hasFollowrequestmessage()) { + const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; + console.log("Got follow request from " + requestMessage.getPlayername()); + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.follower); + followUsersStore.set([requestMessage.getPlayername()]); + } else if (message.hasFollowconfirmationmessage()) { + const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; + console.log("Got follow response from " + responseMessage.getFollower()); + followUsersStore.set([...get(followUsersStore), responseMessage.getFollower()]); + } else if (message.hasFollowabortmessage()) { + const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; + console.log("Got follow abort message from", abortMessage.getRole()); + if (abortMessage.getRole() === followRoles.leader) { + followStateStore.set(followStates.off); + followRoleStore.set(followRoles.leader); + followUsersStore.set([]); + } else { + let followers = get(followUsersStore); + followers = followers.filter((name) => name !== abortMessage.getPlayername()); + followUsersStore.set(followers); + if (followers.length === 0) { + followStateStore.set(followStates.off); + followRoleStore.set(followRoles.leader); + } + } } else if (message.hasErrormessage()) { const errorMessage = message.getErrormessage() as ErrorMessage; console.error("An error occurred server side: " + errorMessage.getMessage()); @@ -716,11 +749,41 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowMeRequest(): void { - console.log("Emitting follow me request"); - const message = new FollowMeRequestMessage(); + public emitFollowRequest(user: string | null): void { + if (!user) { + return; + } + console.log("Emitting follow request"); + const message = new FollowRequestMessage(); + message.setPlayername(user); const clientToServerMessage = new ClientToServerMessage(); - clientToServerMessage.setFollowmerequestmessage(message); + clientToServerMessage.setFollowrequestmessage(message); + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public emitFollowConfirmation(leader: string, follower: string | null): void { + if (!follower) { + return; + } + console.log("Emitting follow confirmation"); + const message = new FollowConfirmationMessage(); + message.setLeader(leader); + message.setFollower(follower); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setFollowconfirmationmessage(message); + this.socket.send(clientToServerMessage.serializeBinary().buffer); + } + + public emitFollowAbort(role: string, user: string | null): void { + if (!user) { + return; + } + console.log("Emitting follow abort"); + const message = new FollowAbortMessage(); + message.setRole(role); + message.setPlayername(user); + const clientToServerMessage = new ClientToServerMessage(); + clientToServerMessage.setFollowabortmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index adf7a302..d8de9ab6 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -3,7 +3,16 @@ import type { GameScene } from "../Game/GameScene"; import { ActiveEventList, UserInputEvent, UserInputManager } from "../UserInput/UserInputManager"; import { Character } from "../Entity/Character"; import type { RemotePlayer } from "../Entity/RemotePlayer"; + +import { get } from "svelte/store"; import { userMovingStore } from "../../Stores/GameStore"; +import { + followStateStore, + followRoleStore, + followUsersStore, + followRoles, + followStates, +} from "../../Stores/InteractStore"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; @@ -156,44 +165,34 @@ export class Player extends Character { userMovingStore.set(moving); } - moveUser(delta: number): void { + public enableFollowing() { + Array.from(this.scene.MapPlayersByKey.values()).forEach((player) => { + if (player.PlayerValue !== get(followUsersStore)[0]) { + return; + } + this.follow = { + followPlayer: player, + direction: this.previousDirection, + }; + followStateStore.set(followStates.active); + }); + } + + public moveUser(delta: number): void { const activeEvents = this.userInputManager.getEventListForGameTick(); + const state = get(followStateStore); + const role = get(followRoleStore); if (activeEvents.get(UserInputEvent.Interact)) { - const sortedPlayers = Array.from(this.scene.MapPlayersByKey.values()).sort((p1, p2) => { - const sdistToP1 = Math.pow(p1.x - this.x, 2) + Math.pow(p1.y - this.y, 2); - const sdistToP2 = Math.pow(p2.x - this.x, 2) + Math.pow(p2.y - this.y, 2); - if (sdistToP1 > sdistToP2) { - return 1; - } else if (sdistToP1 < sdistToP2) { - return -1; - } else { - return 0; - } - }); - - const minFollowDist = 10000; - if (typeof sortedPlayers !== "undefined" && sortedPlayers.length > 0) { - const sdist = Math.pow(sortedPlayers[0].x - this.x, 2) + Math.pow(sortedPlayers[0].y - this.y, 2); - if (sdist < minFollowDist) { - this.follow = { - followPlayer: sortedPlayers[0], - direction: this.previousDirection, - }; - } + if (state === followStates.off && this.scene.groups.size > 0) { + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.leader); + } else if (state === followStates.active) { + followStateStore.set(followStates.ending); } } - if ( - activeEvents.get(UserInputEvent.MoveUp) || - activeEvents.get(UserInputEvent.MoveDown) || - activeEvents.get(UserInputEvent.MoveLeft) || - activeEvents.get(UserInputEvent.MoveRight) - ) { - this.follow = null; - } - - if (this.follow === null) { + if ((state !== followStates.active && state !== followStates.ending) || role !== followRoles.follower) { this.inputStep(activeEvents, delta); } else { this.followStep(activeEvents, delta); diff --git a/front/src/Stores/InteractStore.ts b/front/src/Stores/InteractStore.ts new file mode 100644 index 00000000..960a6954 --- /dev/null +++ b/front/src/Stores/InteractStore.ts @@ -0,0 +1,17 @@ +import { writable } from "svelte/store"; + +export const followStates = { + off: "off", + requesting: "requesting", + active: "active", + ending: "ending", +}; + +export const followRoles = { + leader: "leader", + follower: "follower", +}; + +export const followStateStore = writable(followStates.off); +export const followRoleStore = writable(followRoles.leader); +export const followUsersStore = writable([]); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index e9e75205..8e5f7c6b 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -80,13 +80,18 @@ message QueryJitsiJwtMessage { string tag = 2; // FIXME: rather than reading the tag from the query, we should read it from the current map! } -message FollowMeRequestMessage { +message FollowRequestMessage { string playerName = 1; } -message FollowMeResponseMessage { - string playerName = 1; - bool accepted = 2; +message FollowConfirmationMessage { + string leader = 1; + string follower = 2; +} + +message FollowAbortMessage { + string role = 1; + string playerName = 2; } message ClientToServerMessage { @@ -104,8 +109,9 @@ message ClientToServerMessage { QueryJitsiJwtMessage queryJitsiJwtMessage = 12; EmotePromptMessage emotePromptMessage = 13; VariableMessage variableMessage = 14; - FollowMeRequestMessage followMeRequestMessage = 15; - FollowMeResponseMessage followMeResponseMessage = 16; + FollowRequestMessage followRequestMessage = 15; + FollowConfirmationMessage followConfirmationMessage = 16; + FollowAbortMessage followAbortMessage = 17; } } @@ -296,8 +302,9 @@ message ServerToClientMessage { WorldConnexionMessage worldConnexionMessage = 18; //EmoteEventMessage emoteEventMessage = 19; TokenExpiredMessage tokenExpiredMessage = 20; - FollowMeRequestMessage followMeRequestMessage = 21; - FollowMeResponseMessage followMeResponseMessage = 22; + FollowRequestMessage followRequestMessage = 21; + FollowConfirmationMessage followConfirmationMessage = 22; + FollowAbortMessage followAbortMessage = 23; } } @@ -378,8 +385,9 @@ message PusherToBackMessage { BanUserMessage banUserMessage = 13; EmotePromptMessage emotePromptMessage = 14; VariableMessage variableMessage = 15; - FollowMeRequestMessage followMeRequestMessage = 16; - FollowMeResponseMessage followMeResponseMessage = 17; + FollowRequestMessage followRequestMessage = 16; + FollowConfirmationMessage followConfirmationMessage = 17; + FollowAbortMessage followAbortMessage = 18; } } diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 5d66c4df..930eb4cf 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -17,7 +17,9 @@ import { ServerToClientMessage, CompanionMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, VariableMessage, } from "../Messages/generated/messages_pb"; import { UserMovesMessage } from "../Messages/generated/messages_pb"; @@ -470,11 +472,18 @@ export class IoSocketController { client, message.getEmotepromptmessage() as EmotePromptMessage ); - } else if (message.hasFollowmerequestmessage()) { - socketManager.handleFollowMeRequest( + } else if (message.hasFollowrequestmessage()) { + socketManager.handleFollowRequest( client, - message.getFollowmerequestmessage() as FollowMeRequestMessage + message.getFollowrequestmessage() as FollowRequestMessage ); + } else if (message.hasFollowconfirmationmessage()) { + socketManager.handleFollowConfirmation( + client, + message.getFollowconfirmationmessage() as FollowConfirmationMessage + ); + } else if (message.hasFollowabortmessage()) { + socketManager.handleFollowAbort(client, message.getFollowabortmessage() as FollowAbortMessage); } /* Ok is false if backpressure was built up, wait for drain */ diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 2df167bd..2bbf83c1 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -8,7 +8,9 @@ import { CharacterLayerMessage, EmoteEventMessage, EmotePromptMessage, - FollowMeRequestMessage, + FollowRequestMessage, + FollowConfirmationMessage, + FollowAbortMessage, GroupDeleteMessage, ItemEventMessage, JoinRoomMessage, @@ -270,9 +272,21 @@ export class SocketManager implements ZoneEventListener { this.handleViewport(client, viewport.toObject()); } - handleFollowMeRequest(client: ExSocketInterface, requestMessage: FollowMeRequestMessage): void { + handleFollowRequest(client: ExSocketInterface, message: FollowRequestMessage): void { const pusherToBackMessage = new PusherToBackMessage(); - pusherToBackMessage.setFollowmerequestmessage(requestMessage); + pusherToBackMessage.setFollowrequestmessage(message); + client.backConnection.write(pusherToBackMessage); + } + + handleFollowConfirmation(client: ExSocketInterface, message: FollowConfirmationMessage): void { + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setFollowconfirmationmessage(message); + client.backConnection.write(pusherToBackMessage); + } + + handleFollowAbort(client: ExSocketInterface, message: FollowAbortMessage): void { + const pusherToBackMessage = new PusherToBackMessage(); + pusherToBackMessage.setFollowabortmessage(message); client.backConnection.write(pusherToBackMessage); } From c2f550123666ddc67367ab3fd9698adfec4b751b Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Mon, 13 Dec 2021 05:06:52 +0100 Subject: [PATCH 022/149] Do not leave group when following is active --- back/src/Model/GameRoom.ts | 3 ++- back/src/Model/User.ts | 16 ++++++++++++++++ back/src/Services/SocketManager.ts | 16 ++++++++++++++++ back/tests/PositionNotifierTest.ts | 8 ++++---- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 1b0db5eb..e8803762 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -139,6 +139,7 @@ export class GameRoom { joinRoomMessage.getIpaddress(), position, false, + [], this.positionNotifier, socket, joinRoomMessage.getTagList(), @@ -231,7 +232,7 @@ export class GameRoom { // If the user is part of a group: // should he leave the group? const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition()); - if (distance > this.groupRadius) { + if (user.following.length === 0 && distance > this.groupRadius) { this.leaveGroup(user); } } diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 186fb32a..09c0d3d9 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -25,6 +25,7 @@ export class User implements Movable { public readonly IPAddress: string, private position: PointInterface, public silent: boolean, + public following: string[], private positionNotifier: PositionNotifier, public readonly socket: UserSocket, public readonly tags: string[], @@ -48,6 +49,21 @@ export class User implements Movable { this.positionNotifier.updatePosition(this, position, oldPosition); } + public addFollower(name: string): void { + if (this.following.includes(name)) { + return; + } + this.following.push(name); + } + + public delFollower(name: string): void { + const idx = this.following.indexOf(name); + if (idx === -1) { + return; + } + this.following.splice(idx, 1); + } + private batchedMessages: BatchMessage = new BatchMessage(); private batchTimeout: NodeJS.Timeout | null = null; diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 3ab53719..94ad2ed3 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -847,19 +847,35 @@ export class SocketManager { const clientMessage = new ServerToClientMessage(); clientMessage.setFollowconfirmationmessage(message); room.sendToUserWithName(message.getLeader(), clientMessage); + + room.getUserByName(message.getLeader())?.addFollower(user.name); + user.addFollower(message.getLeader()); } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { if (message.getRole() === "leader") { + // Forward message const clientMessage = new ServerToClientMessage(); clientMessage.setFollowabortmessage(message); room.sendToOthersInGroupIncludingUser(user, clientMessage); + + // Update followers + room.getGroupIncludingUser(user) + ?.getUsers() + .forEach((user) => { + user.following = []; + }); } else { + // Forward message const recipient = message.getPlayername(); message.setPlayername(user.name); const clientMessage = new ServerToClientMessage(); clientMessage.setFollowabortmessage(message); room.sendToUserWithName(recipient, clientMessage); + + // Update followers + room.getUserByName(recipient)?.delFollower(user.name); + user.following = []; } } } diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 1aaf2e13..955ed40f 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -26,14 +26,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: -9999, y: -9999, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); positionNotifier.addZoneListener({} as ZoneSocket, 0, 0); positionNotifier.addZoneListener({} as ZoneSocket, 0, 1); @@ -101,14 +101,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: 0, y: 0, moving: false, direction: 'down' - }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); const listener = {} as ZoneSocket; positionNotifier.addZoneListener(listener, 0, 0); From 7bff782f7fae5f2fb308da7499bc6613a63416d1 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 14 Dec 2021 01:35:00 +0100 Subject: [PATCH 023/149] Improve follow abort message; avoid having to change it in backend --- back/src/Services/SocketManager.ts | 23 +++++++--------- .../InteractMenu/InteractMenu.svelte | 4 +-- front/src/Connexion/RoomConnection.ts | 27 ++++++++++--------- messages/protos/messages.proto | 6 ++--- 4 files changed, 28 insertions(+), 32 deletions(-) diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 94ad2ed3..727cf430 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -853,28 +853,23 @@ export class SocketManager { } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { - if (message.getRole() === "leader") { + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + if (user.name === message.getLeader()) { // Forward message - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowabortmessage(message); room.sendToOthersInGroupIncludingUser(user, clientMessage); // Update followers - room.getGroupIncludingUser(user) - ?.getUsers() - .forEach((user) => { - user.following = []; - }); + const group = room.getGroupIncludingUser(user); + group?.getUsers().forEach((user) => { + user.following = []; + }); } else { // Forward message - const recipient = message.getPlayername(); - message.setPlayername(user.name); - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowabortmessage(message); - room.sendToUserWithName(recipient, clientMessage); + room.sendToUserWithName(message.getLeader(), clientMessage); // Update followers - room.getUserByName(recipient)?.delFollower(user.name); + room.getUserByName(message.getLeader())?.delFollower(user.name); user.following = []; } } diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/InteractMenu/InteractMenu.svelte index cb78f5bc..6f5e62b2 100644 --- a/front/src/Components/InteractMenu/InteractMenu.svelte +++ b/front/src/Components/InteractMenu/InteractMenu.svelte @@ -67,9 +67,9 @@ vim: ft=typescript function reset() { if (followRole === followRoles.leader && followUsers.length > 0) { - gameScene.connection?.emitFollowAbort(followRole, gameManager.getPlayerName()); + gameScene.connection?.emitFollowAbort(gameManager.getPlayerName(), "*"); } else { - gameScene.connection?.emitFollowAbort(followRole, followUsers[0]); + gameScene.connection?.emitFollowAbort(followUsers[0], gameManager.getPlayerName()); } followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index e3f36cb7..6d0abaef 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -270,26 +270,27 @@ export class RoomConnection implements RoomConnection { //todo: implement a way to notify the user the room was refreshed. } else if (message.hasFollowrequestmessage()) { const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; - console.log("Got follow request from " + requestMessage.getPlayername()); + console.log("Got follow request from " + requestMessage.getLeader()); followStateStore.set(followStates.requesting); followRoleStore.set(followRoles.follower); - followUsersStore.set([requestMessage.getPlayername()]); + followUsersStore.set([requestMessage.getLeader()]); } else if (message.hasFollowconfirmationmessage()) { const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; console.log("Got follow response from " + responseMessage.getFollower()); followUsersStore.set([...get(followUsersStore), responseMessage.getFollower()]); } else if (message.hasFollowabortmessage()) { const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; - console.log("Got follow abort message from", abortMessage.getRole()); - if (abortMessage.getRole() === followRoles.leader) { + console.log("Got follow abort message"); + if (get(followRoleStore) === followRoles.follower) { followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); followUsersStore.set([]); } else { let followers = get(followUsersStore); - followers = followers.filter((name) => name !== abortMessage.getPlayername()); + const oldFollowerCount = followers.length; + followers = followers.filter((name) => name !== abortMessage.getFollower()); followUsersStore.set(followers); - if (followers.length === 0) { + if (followers.length === 0 && oldFollowerCount > 0) { followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); } @@ -755,14 +756,14 @@ export class RoomConnection implements RoomConnection { } console.log("Emitting follow request"); const message = new FollowRequestMessage(); - message.setPlayername(user); + message.setLeader(user); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowrequestmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowConfirmation(leader: string, follower: string | null): void { - if (!follower) { + public emitFollowConfirmation(leader: string | null, follower: string | null): void { + if (!leader || !follower) { return; } console.log("Emitting follow confirmation"); @@ -774,14 +775,14 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowAbort(role: string, user: string | null): void { - if (!user) { + public emitFollowAbort(leader: string | null, follower: string | null): void { + if (!leader || !follower) { return; } console.log("Emitting follow abort"); const message = new FollowAbortMessage(); - message.setRole(role); - message.setPlayername(user); + message.setLeader(leader); + message.setFollower(follower); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowabortmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 8e5f7c6b..7152f43f 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -81,7 +81,7 @@ message QueryJitsiJwtMessage { } message FollowRequestMessage { - string playerName = 1; + string leader = 1; } message FollowConfirmationMessage { @@ -90,8 +90,8 @@ message FollowConfirmationMessage { } message FollowAbortMessage { - string role = 1; - string playerName = 2; + string leader = 1; + string follower = 2; } message ClientToServerMessage { From 290e5131e95c086013c033f91056bab769096003 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 14 Dec 2021 18:47:51 +0100 Subject: [PATCH 024/149] Clean up follow implementation; stop following when leader leaves the scene --- front/src/Phaser/Entity/Character.ts | 32 +++++------ front/src/Phaser/Game/GameScene.ts | 4 ++ front/src/Phaser/Player/Player.ts | 83 ++++++++-------------------- 3 files changed, 40 insertions(+), 79 deletions(-) diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 2e0bd363..1666063f 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -32,7 +32,7 @@ export abstract class Character extends Container { private readonly playerName: Text; public PlayerValue: string; public sprites: Map; - private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; + protected lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; //private teleportation: Sprite; private invisible: boolean; public companion?: Companion; @@ -266,24 +266,20 @@ export abstract class Character extends Container { body.setVelocity(x, y); - // up or down animations are prioritized over left and right - if (body.velocity.y < 0) { - //moving up - this.lastDirection = PlayerAnimationDirections.Up; - this.playAnimation(PlayerAnimationDirections.Up, true); - } else if (body.velocity.y > 0) { - //moving down - this.lastDirection = PlayerAnimationDirections.Down; - this.playAnimation(PlayerAnimationDirections.Down, true); - } else if (body.velocity.x > 0) { - //moving right - this.lastDirection = PlayerAnimationDirections.Right; - this.playAnimation(PlayerAnimationDirections.Right, true); - } else if (body.velocity.x < 0) { - //moving left - this.lastDirection = PlayerAnimationDirections.Left; - this.playAnimation(PlayerAnimationDirections.Left, true); + if (Math.abs(body.velocity.x) > Math.abs(body.velocity.y)) { + if (body.velocity.x < 0) { + this.lastDirection = PlayerAnimationDirections.Left; + } else if (body.velocity.x > 0) { + this.lastDirection = PlayerAnimationDirections.Right; + } + } else { + if (body.velocity.y < 0) { + this.lastDirection = PlayerAnimationDirections.Up; + } else if (body.velocity.y > 0) { + this.lastDirection = PlayerAnimationDirections.Down; + } } + this.playAnimation(this.lastDirection, true); this.setDepth(this.y); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index ae89e2c3..6d735182 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1715,6 +1715,10 @@ ${escapedMessage} }); } + public findPlayer(testFunction: (player: RemotePlayer) => boolean): RemotePlayer | undefined { + return Array.from(this.MapPlayersByKey.values()).find(testFunction); + } + /** * Called by the connexion when a new player arrives on a map */ diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index d8de9ab6..61951514 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -18,11 +18,6 @@ export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; export class Player extends Character { - private previousDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; - private wasMoving: boolean = false; - private timeCounter: number = 0; - private follow: { followPlayer: RemotePlayer; direction: PlayerAnimationDirections } | null = null; - constructor( Scene: GameScene, x: number, @@ -41,9 +36,9 @@ export class Player extends Character { this.getBody().setImmovable(false); } - private inputStep(activeEvents: ActiveEventList, delta: number) { + private inputStep(activeEvents: ActiveEventList) { //if user client on shift, camera and player speed - let direction = null; + let direction = this.lastDirection; let moving = false; const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; @@ -77,22 +72,22 @@ export class Player extends Character { if (x !== 0 || y !== 0) { this.move(x, y); this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y, oldX: x, oldY: y }); - } else if (this.wasMoving && moving) { + } else if (get(userMovingStore) && moving) { // slow joystick movement this.move(0, 0); this.emit(hasMovedEventName, { moving, - direction: this.previousDirection, + direction: direction, x: this.x, y: this.y, oldX: x, oldY: y, }); - } else if (this.wasMoving && !moving) { + } else if (get(userMovingStore) && !moving) { this.stop(); this.emit(hasMovedEventName, { moving, - direction: this.previousDirection, + direction: direction, x: this.x, y: this.y, oldX: x, @@ -100,35 +95,27 @@ export class Player extends Character { }); } - if (direction !== null) { - this.previousDirection = direction; - } - - this.wasMoving = moving; userMovingStore.set(moving); } - private followStep(activeEvents: ActiveEventList, delta: number) { - let moving = false; - - if (this.follow === null) { + private followStep(delta: number) { + const player = this.scene.findPlayer((p) => p.PlayerValue === get(followUsersStore)[0]); + if (!player) { + this.scene.connection?.emitFollowAbort(get(followUsersStore)[0], this.PlayerValue); + followStateStore.set(followStates.off); return; } - this.timeCounter += delta; - if (this.timeCounter < 128) { - return; - } - this.timeCounter = 0; - - const xDist = this.follow.followPlayer.x - this.x; - const yDist = this.follow.followPlayer.y - this.y; - + const xDist = player.x - this.x; + const yDist = player.y - this.y; const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); + let moving = false; + let direction = this.lastDirection; if (distance < 2000) { this.stop(); } else { + moving = true; const moveAmount = 9 * 20; const xDir = xDist / Math.sqrt(distance); const yDir = yDist / Math.sqrt(distance); @@ -136,46 +123,24 @@ export class Player extends Character { this.move(xDir * moveAmount, yDir * moveAmount); if (Math.abs(xDist) > Math.abs(yDist)) { - if (xDist < 0) { - this.follow.direction = PlayerAnimationDirections.Left; - } else { - this.follow.direction = PlayerAnimationDirections.Right; - } + direction = xDist < 0 ? PlayerAnimationDirections.Left : PlayerAnimationDirections.Right; } else { - if (yDist < 0) { - this.follow.direction = PlayerAnimationDirections.Up; - } else { - this.follow.direction = PlayerAnimationDirections.Down; - } + direction = yDist < 0 ? PlayerAnimationDirections.Up : PlayerAnimationDirections.Down; } - - moving = true; } this.emit(hasMovedEventName, { moving: moving, - direction: this.follow.direction, + direction: direction, x: this.x, y: this.y, }); - this.previousDirection = this.follow.direction; - - this.wasMoving = moving; userMovingStore.set(moving); } public enableFollowing() { - Array.from(this.scene.MapPlayersByKey.values()).forEach((player) => { - if (player.PlayerValue !== get(followUsersStore)[0]) { - return; - } - this.follow = { - followPlayer: player, - direction: this.previousDirection, - }; - followStateStore.set(followStates.active); - }); + followStateStore.set(followStates.active); } public moveUser(delta: number): void { @@ -193,13 +158,9 @@ export class Player extends Character { } if ((state !== followStates.active && state !== followStates.ending) || role !== followRoles.follower) { - this.inputStep(activeEvents, delta); + this.inputStep(activeEvents); } else { - this.followStep(activeEvents, delta); + this.followStep(delta); } } - - public isMoving(): boolean { - return this.wasMoving; - } } From 1ab8165951d71e76cec0fced877a537fab48a9e3 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 00:10:28 +0100 Subject: [PATCH 025/149] Process input events in follow mode as well --- front/src/Phaser/Player/Player.ts | 129 +++++++++++------------------- 1 file changed, 48 insertions(+), 81 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 61951514..285163e8 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -36,107 +36,73 @@ export class Player extends Character { this.getBody().setImmovable(false); } - private inputStep(activeEvents: ActiveEventList) { - //if user client on shift, camera and player speed - let direction = this.lastDirection; - let moving = false; - - const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; - const moveAmount = speedMultiplier * 20; - - let x = 0; - let y = 0; - + private inputStep(activeEvents: ActiveEventList, x: number, y: number) { + // Process input events if (activeEvents.get(UserInputEvent.MoveUp)) { - y = -moveAmount; - direction = PlayerAnimationDirections.Up; - moving = true; + y = y - 1; } else if (activeEvents.get(UserInputEvent.MoveDown)) { - y = moveAmount; - direction = PlayerAnimationDirections.Down; - moving = true; + y = y + 1; } if (activeEvents.get(UserInputEvent.MoveLeft)) { - x = -moveAmount; - direction = PlayerAnimationDirections.Left; - moving = true; + x = x - 1; } else if (activeEvents.get(UserInputEvent.MoveRight)) { - x = moveAmount; - direction = PlayerAnimationDirections.Right; - moving = true; + x = x + 1; } - moving = moving || activeEvents.get(UserInputEvent.JoystickMove); + // Compute movement deltas + const speedMultiplier = activeEvents.get(UserInputEvent.SpeedUp) ? 25 : 9; + const moveAmount = speedMultiplier * 20; + x = x * moveAmount; + y = y * moveAmount; - if (x !== 0 || y !== 0) { + // Compute moving state + const joystickMovement = activeEvents.get(UserInputEvent.JoystickMove); + const moving = x !== 0 || y !== 0 || joystickMovement; + + // Compute direction + let direction = this.lastDirection; + if (moving && !joystickMovement) { + if (Math.abs(x) > Math.abs(y)) { + direction = x < 0 ? PlayerAnimationDirections.Left : PlayerAnimationDirections.Right; + } else { + direction = y < 0 ? PlayerAnimationDirections.Up : PlayerAnimationDirections.Down; + } + } + + // Send movement events + const emit = () => this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y }); + if (moving) { this.move(x, y); - this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y, oldX: x, oldY: y }); - } else if (get(userMovingStore) && moving) { - // slow joystick movement - this.move(0, 0); - this.emit(hasMovedEventName, { - moving, - direction: direction, - x: this.x, - y: this.y, - oldX: x, - oldY: y, - }); - } else if (get(userMovingStore) && !moving) { + emit(); + } else if (get(userMovingStore)) { this.stop(); - this.emit(hasMovedEventName, { - moving, - direction: direction, - x: this.x, - y: this.y, - oldX: x, - oldY: y, - }); + emit(); } + // Update state userMovingStore.set(moving); } - private followStep(delta: number) { + private computeFollowMovement(): number[] { + // Find followed WOKA and abort following if we lost it const player = this.scene.findPlayer((p) => p.PlayerValue === get(followUsersStore)[0]); if (!player) { this.scene.connection?.emitFollowAbort(get(followUsersStore)[0], this.PlayerValue); followStateStore.set(followStates.off); - return; + return [0, 0]; } - const xDist = player.x - this.x; - const yDist = player.y - this.y; - const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); - - let moving = false; - let direction = this.lastDirection; + // Compute movement direction + const xDistance = player.x - this.x; + const yDistance = player.y - this.y; + const distance = Math.pow(xDistance, 2) + Math.pow(yDistance, 2); if (distance < 2000) { - this.stop(); - } else { - moving = true; - const moveAmount = 9 * 20; - const xDir = xDist / Math.sqrt(distance); - const yDir = yDist / Math.sqrt(distance); - - this.move(xDir * moveAmount, yDir * moveAmount); - - if (Math.abs(xDist) > Math.abs(yDist)) { - direction = xDist < 0 ? PlayerAnimationDirections.Left : PlayerAnimationDirections.Right; - } else { - direction = yDist < 0 ? PlayerAnimationDirections.Up : PlayerAnimationDirections.Down; - } + return [0, 0]; } - - this.emit(hasMovedEventName, { - moving: moving, - direction: direction, - x: this.x, - y: this.y, - }); - - userMovingStore.set(moving); + const xMovement = xDistance / Math.sqrt(distance); + const yMovement = yDistance / Math.sqrt(distance); + return [xMovement, yMovement]; } public enableFollowing() { @@ -157,10 +123,11 @@ export class Player extends Character { } } - if ((state !== followStates.active && state !== followStates.ending) || role !== followRoles.follower) { - this.inputStep(activeEvents); - } else { - this.followStep(delta); + let x = 0; + let y = 0; + if ((state === followStates.active || state === followStates.ending) && role === followRoles.follower) { + [x, y] = this.computeFollowMovement(); } + this.inputStep(activeEvents, x, y); } } From e5286824037763701da23d9dc8fedcd97665101d Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 13:21:30 +0100 Subject: [PATCH 026/149] Use User.group instead of iterating over groups Thanks @moufmouf for pointing this out. --- back/src/Model/GameRoom.ts | 21 +++++---------------- back/src/Services/SocketManager.ts | 3 +-- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index e8803762..79ed9d3c 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -92,15 +92,6 @@ export class GameRoom { return gameRoom; } - public getGroups(): Group[] { - return Array.from(this.groups.values()); - } - - public getGroupIncludingUser(user: User): Group | undefined { - const foundGroups = this.getGroups().filter((grp) => grp.includes(user)); - return foundGroups[0]; - } - public getUsers(): Map { return this.users; } @@ -239,13 +230,11 @@ export class GameRoom { } public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void { - this.getGroupIncludingUser(user) - ?.getUsers() - .forEach((currentUser: User) => { - if (currentUser.name !== user.name) { - currentUser.socket.write(message); - } - }); + user.group?.getUsers().forEach((currentUser: User) => { + if (currentUser.name !== user.name) { + currentUser.socket.write(message); + } + }); } public sendToUserWithName(name: string, message: ServerToClientMessage): void { diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 727cf430..5818afa9 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -860,8 +860,7 @@ export class SocketManager { room.sendToOthersInGroupIncludingUser(user, clientMessage); // Update followers - const group = room.getGroupIncludingUser(user); - group?.getUsers().forEach((user) => { + user.group?.getUsers().forEach((user) => { user.following = []; }); } else { From 2bd71790b58c0ac26edb187dc402ac1342ab4b58 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 14:48:45 +0100 Subject: [PATCH 027/149] Use user IDs instead of names --- back/src/Model/GameRoom.ts | 9 ------ back/src/Model/User.ts | 12 ++++---- back/src/Services/SocketManager.ts | 12 ++++---- .../InteractMenu/InteractMenu.svelte | 28 +++++++++---------- front/src/Connexion/RoomConnection.ts | 24 ++++++++-------- front/src/Phaser/Game/GameScene.ts | 4 --- front/src/Phaser/Player/Player.ts | 4 +-- front/src/Stores/InteractStore.ts | 2 +- messages/protos/messages.proto | 10 +++---- 9 files changed, 48 insertions(+), 57 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 79ed9d3c..9afeae55 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -96,11 +96,6 @@ export class GameRoom { return this.users; } - public getUserByName(name: string): User | undefined { - let foundUsers = Array.from(this.users.values()); - foundUsers = foundUsers.filter((user: User) => user.name === name); - return foundUsers[0]; - } public getUserByUuid(uuid: string): User | undefined { return this.usersByUuid.get(uuid); } @@ -237,10 +232,6 @@ export class GameRoom { }); } - public sendToUserWithName(name: string, message: ServerToClientMessage): void { - this.getUserByName(name)?.socket.write(message); - } - setSilent(user: User, silent: boolean) { if (user.silent === silent) { return; diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 09c0d3d9..13a61c3f 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -25,7 +25,7 @@ export class User implements Movable { public readonly IPAddress: string, private position: PointInterface, public silent: boolean, - public following: string[], + public following: number[], private positionNotifier: PositionNotifier, public readonly socket: UserSocket, public readonly tags: string[], @@ -49,15 +49,15 @@ export class User implements Movable { this.positionNotifier.updatePosition(this, position, oldPosition); } - public addFollower(name: string): void { - if (this.following.includes(name)) { + public addFollower(userId: number): void { + if (this.following.includes(userId)) { return; } - this.following.push(name); + this.following.push(userId); } - public delFollower(name: string): void { - const idx = this.following.indexOf(name); + public delFollower(userId: number): void { + const idx = this.following.indexOf(userId); if (idx === -1) { return; } diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 5818afa9..8c7eecac 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -846,16 +846,17 @@ export class SocketManager { handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { const clientMessage = new ServerToClientMessage(); clientMessage.setFollowconfirmationmessage(message); - room.sendToUserWithName(message.getLeader(), clientMessage); + const leader = room.getUserById(message.getLeader()); + leader?.socket.write(clientMessage); - room.getUserByName(message.getLeader())?.addFollower(user.name); + leader?.addFollower(user.id); user.addFollower(message.getLeader()); } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { const clientMessage = new ServerToClientMessage(); clientMessage.setFollowabortmessage(message); - if (user.name === message.getLeader()) { + if (user.id === message.getLeader()) { // Forward message room.sendToOthersInGroupIncludingUser(user, clientMessage); @@ -865,10 +866,11 @@ export class SocketManager { }); } else { // Forward message - room.sendToUserWithName(message.getLeader(), clientMessage); + const leader = room.getUserById(message.getLeader()); + leader?.socket.write(clientMessage); // Update followers - room.getUserByName(message.getLeader())?.delFollower(user.name); + leader?.delFollower(user.id); user.following = []; } } diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/InteractMenu/InteractMenu.svelte index 6f5e62b2..dae7f099 100644 --- a/front/src/Components/InteractMenu/InteractMenu.svelte +++ b/front/src/Components/InteractMenu/InteractMenu.svelte @@ -19,7 +19,7 @@ vim: ft=typescript let followState: string; let followRole: string; - let followUsers: string[]; + let followUsers: number[]; let stateUnsubscriber: Unsubscriber; let roleUnsubscriber: Unsubscriber; let nameUnsubscriber: Unsubscriber; @@ -51,14 +51,18 @@ vim: ft=typescript } }); + function name(userId: number): string | undefined { + return gameScene.MapPlayersByKey.get(userId)?.PlayerValue; + } + function sendFollowRequest() { - gameScene.connection?.emitFollowRequest(gameManager.getPlayerName()); + gameScene.connection?.emitFollowRequest(); followStateStore.set(followStates.active); } function acceptFollowRequest() { gameScene.CurrentPlayer.enableFollowing(); - gameScene.connection?.emitFollowConfirmation(followUsers[0], gameManager.getPlayerName()); + gameScene.connection?.emitFollowConfirmation(); } function abortEnding() { @@ -66,11 +70,7 @@ vim: ft=typescript } function reset() { - if (followRole === followRoles.leader && followUsers.length > 0) { - gameScene.connection?.emitFollowAbort(gameManager.getPlayerName(), "*"); - } else { - gameScene.connection?.emitFollowAbort(followUsers[0], gameManager.getPlayerName()); - } + gameScene.connection?.emitFollowAbort(); followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); followUsersStore.set([]); @@ -92,7 +92,7 @@ vim: ft=typescript {#if followRole === followRoles.follower}
-

Do you want to follow {followUsers[0]}?

+

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

@@ -117,7 +117,7 @@ vim: ft=typescript
{#if followRole === followRoles.follower}
-

Do you want to stop following {followUsers[0]}?

+

Do you want to stop following {name(followUsers[0])}?

{:else if followRole === followRoles.leader}
@@ -135,15 +135,15 @@ vim: ft=typescript
{#if followRole === followRoles.follower} -

Following {followUsers[0]}

+

Following {name(followUsers[0])}

{:else if followUsers.length === 0}

Waiting for followers' confirmation

{:else if followUsers.length === 1} -

{followUsers[0]} is following you

+

{name(followUsers[0])} is following you

{:else if followUsers.length === 2} -

{followUsers[0]} and {followUsers[1]} are following you

+

{name(followUsers[0])} and {name(followUsers[1])} are following you

{:else} -

{followUsers[0]}, {followUsers[1]} and {followUsers[2]} are following you

+

{name(followUsers[0])}, {name(followUsers[1])} and {name(followUsers[2])} are following you

{/if}
diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 6d0abaef..49b2dc4c 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -750,39 +750,41 @@ export class RoomConnection implements RoomConnection { this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowRequest(user: string | null): void { - if (!user) { + public emitFollowRequest(): void { + if (!this.userId) { return; } console.log("Emitting follow request"); const message = new FollowRequestMessage(); - message.setLeader(user); + message.setLeader(this.userId); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowrequestmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowConfirmation(leader: string | null, follower: string | null): void { - if (!leader || !follower) { + public emitFollowConfirmation(): void { + if (!this.userId) { return; } console.log("Emitting follow confirmation"); const message = new FollowConfirmationMessage(); - message.setLeader(leader); - message.setFollower(follower); + message.setLeader(get(followUsersStore)[0]); + message.setFollower(this.userId); const clientToServerMessage = new ClientToServerMessage(); clientToServerMessage.setFollowconfirmationmessage(message); this.socket.send(clientToServerMessage.serializeBinary().buffer); } - public emitFollowAbort(leader: string | null, follower: string | null): void { - if (!leader || !follower) { + public emitFollowAbort(): void { + const isLeader = get(followRoleStore) === followRoles.leader; + const hasFollowers = get(followUsersStore).length > 0; + if (!this.userId || (isLeader && !hasFollowers)) { return; } console.log("Emitting follow abort"); const message = new FollowAbortMessage(); - message.setLeader(leader); - message.setFollower(follower); + 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); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 6d735182..ae89e2c3 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1715,10 +1715,6 @@ ${escapedMessage} }); } - public findPlayer(testFunction: (player: RemotePlayer) => boolean): RemotePlayer | undefined { - return Array.from(this.MapPlayersByKey.values()).find(testFunction); - } - /** * Called by the connexion when a new player arrives on a map */ diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 285163e8..159816e3 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -86,9 +86,9 @@ export class Player extends Character { private computeFollowMovement(): number[] { // Find followed WOKA and abort following if we lost it - const player = this.scene.findPlayer((p) => p.PlayerValue === get(followUsersStore)[0]); + const player = this.scene.MapPlayersByKey.get(get(followUsersStore)[0]); if (!player) { - this.scene.connection?.emitFollowAbort(get(followUsersStore)[0], this.PlayerValue); + this.scene.connection?.emitFollowAbort(); followStateStore.set(followStates.off); return [0, 0]; } diff --git a/front/src/Stores/InteractStore.ts b/front/src/Stores/InteractStore.ts index 960a6954..6c85ab17 100644 --- a/front/src/Stores/InteractStore.ts +++ b/front/src/Stores/InteractStore.ts @@ -14,4 +14,4 @@ export const followRoles = { export const followStateStore = writable(followStates.off); export const followRoleStore = writable(followRoles.leader); -export const followUsersStore = writable([]); +export const followUsersStore = writable([]); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 7152f43f..96fecb69 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -81,17 +81,17 @@ message QueryJitsiJwtMessage { } message FollowRequestMessage { - string leader = 1; + int32 leader = 1; } message FollowConfirmationMessage { - string leader = 1; - string follower = 2; + int32 leader = 1; + int32 follower = 2; } message FollowAbortMessage { - string leader = 1; - string follower = 2; + int32 leader = 1; + int32 follower = 2; } message ClientToServerMessage { From e3e7fba53924d38a3448dae9c5932525481711f8 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 14:57:44 +0100 Subject: [PATCH 028/149] Rename InteractMenu/-Store => FollowMenu/-Store --- front/src/Components/App.svelte | 6 +++--- .../InteractMenu.svelte => FollowMenu/FollowMenu.svelte} | 2 +- front/src/Connexion/RoomConnection.ts | 8 +------- front/src/Phaser/Player/Player.ts | 2 +- front/src/Stores/{InteractStore.ts => FollowStore.ts} | 0 5 files changed, 6 insertions(+), 12 deletions(-) rename front/src/Components/{InteractMenu/InteractMenu.svelte => FollowMenu/FollowMenu.svelte} (99%) rename front/src/Stores/{InteractStore.ts => FollowStore.ts} (100%) diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index b14801c9..5f09beef 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -42,8 +42,8 @@ import AudioManager from "./AudioManager/AudioManager.svelte"; import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore"; import ReportMenu from "./ReportMenu/ReportMenu.svelte"; - import { followStateStore, followStates } from "../Stores/InteractStore"; - import InteractMenu from "./InteractMenu/InteractMenu.svelte"; + import { followStateStore, followStates } from "../Stores/FollowStore"; + import FollowMenu from "./FollowMenu/FollowMenu.svelte"; export let game: Game; @@ -106,7 +106,7 @@ {/if} {#if $followStateStore !== followStates.off}
- +
{/if} {#if $menuIconVisiblilityStore} diff --git a/front/src/Components/InteractMenu/InteractMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte similarity index 99% rename from front/src/Components/InteractMenu/InteractMenu.svelte rename to front/src/Components/FollowMenu/FollowMenu.svelte index dae7f099..590fd61c 100644 --- a/front/src/Components/InteractMenu/InteractMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -13,7 +13,7 @@ vim: ft=typescript followUsersStore, followRoles, followStates, - } from "../../Stores/InteractStore"; + } from "../../Stores/FollowStore"; const gameScene = gameManager.getCurrentGameScene(); diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 49b2dc4c..f8d385d4 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -62,13 +62,7 @@ import { connectionManager } from "./ConnectionManager"; import { emoteEventStream } from "./EmoteEventStream"; import { get } from "svelte/store"; import { warningContainerStore } from "../Stores/MenuStore"; -import { - followStateStore, - followRoleStore, - followUsersStore, - followRoles, - followStates, -} from "../Stores/InteractStore"; +import { followStateStore, followRoleStore, followUsersStore, followRoles, followStates } from "../Stores/FollowStore"; const manualPingDelay = 20000; diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 159816e3..f34d1478 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -12,7 +12,7 @@ import { followUsersStore, followRoles, followStates, -} from "../../Stores/InteractStore"; +} from "../../Stores/FollowStore"; export const hasMovedEventName = "hasMoved"; export const requestEmoteEventName = "requestEmote"; diff --git a/front/src/Stores/InteractStore.ts b/front/src/Stores/FollowStore.ts similarity index 100% rename from front/src/Stores/InteractStore.ts rename to front/src/Stores/FollowStore.ts From d3297a448e061bbdb3843facc57692b3dedae5e1 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 19:47:14 +0100 Subject: [PATCH 029/149] Add setting that ignores follow invites --- front/src/Components/Menu/SettingsSubMenu.svelte | 14 ++++++++++++++ front/src/Connexion/LocalUserStore.ts | 8 ++++++++ front/src/Connexion/RoomConnection.ts | 12 ++++++------ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/front/src/Components/Menu/SettingsSubMenu.svelte b/front/src/Components/Menu/SettingsSubMenu.svelte index 93d3eaa9..1db14036 100644 --- a/front/src/Components/Menu/SettingsSubMenu.svelte +++ b/front/src/Components/Menu/SettingsSubMenu.svelte @@ -8,6 +8,7 @@ let fullscreen: boolean = localUserStore.getFullscreen(); let notification: boolean = localUserStore.getNotification() === "granted"; let forceCowebsiteTrigger: boolean = localUserStore.getForceCowebsiteTrigger(); + let ignoreFollowRequests: boolean = localUserStore.getIgnoreFollowRequests(); let valueGame: number = localUserStore.getGameQualityValue(); let valueVideo: number = localUserStore.getVideoQualityValue(); let previewValueGame = valueGame; @@ -59,6 +60,10 @@ localUserStore.setForceCowebsiteTrigger(forceCowebsiteTrigger); } + function changeIgnoreFollowRequests() { + localUserStore.setIgnoreFollowRequests(ignoreFollowRequests); + } + function closeMenu() { menuVisiblilityStore.set(false); } @@ -123,6 +128,15 @@ /> Always ask before opening websites and Jitsi Meet rooms +
diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 30755034..4dce6924 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -14,6 +14,7 @@ const audioPlayerMuteKey = "audioMute"; const helpCameraSettingsShown = "helpCameraSettingsShown"; const fullscreenKey = "fullscreen"; const forceCowebsiteTriggerKey = "forceCowebsiteTrigger"; +const ignoreFollowRequests = "ignoreFollowRequests"; const lastRoomUrl = "lastRoomUrl"; const authToken = "authToken"; const state = "state"; @@ -128,6 +129,13 @@ class LocalUserStore { return localStorage.getItem(forceCowebsiteTriggerKey) === "true"; } + setIgnoreFollowRequests(value: boolean): void { + localStorage.setItem(ignoreFollowRequests, value.toString()); + } + getIgnoreFollowRequests(): boolean { + return localStorage.getItem(ignoreFollowRequests) === "true"; + } + setLastRoomUrl(roomUrl: string): void { localStorage.setItem(lastRoomUrl, roomUrl.toString()); if ("caches" in window) { diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index f8d385d4..f4cbc251 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -63,6 +63,7 @@ import { emoteEventStream } from "./EmoteEventStream"; import { get } from "svelte/store"; import { warningContainerStore } from "../Stores/MenuStore"; import { followStateStore, followRoleStore, followUsersStore, followRoles, followStates } from "../Stores/FollowStore"; +import { localUserStore } from "./LocalUserStore"; const manualPingDelay = 20000; @@ -264,17 +265,16 @@ export class RoomConnection implements RoomConnection { //todo: implement a way to notify the user the room was refreshed. } else if (message.hasFollowrequestmessage()) { const requestMessage = message.getFollowrequestmessage() as FollowRequestMessage; - console.log("Got follow request from " + requestMessage.getLeader()); - followStateStore.set(followStates.requesting); - followRoleStore.set(followRoles.follower); - followUsersStore.set([requestMessage.getLeader()]); + if (!localUserStore.getIgnoreFollowRequests()) { + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.follower); + followUsersStore.set([requestMessage.getLeader()]); + } } else if (message.hasFollowconfirmationmessage()) { const responseMessage = message.getFollowconfirmationmessage() as FollowConfirmationMessage; - console.log("Got follow response from " + responseMessage.getFollower()); followUsersStore.set([...get(followUsersStore), responseMessage.getFollower()]); } else if (message.hasFollowabortmessage()) { const abortMessage = message.getFollowabortmessage() as FollowAbortMessage; - console.log("Got follow abort message"); if (get(followRoleStore) === followRoles.follower) { followStateStore.set(followStates.off); followRoleStore.set(followRoles.leader); From a48137663321e1657d03c0c5e422b865674520d3 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 15 Dec 2021 20:11:42 +0100 Subject: [PATCH 030/149] Clean up remaining debug log messages --- front/src/Connexion/RoomConnection.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index f4cbc251..0c8390b8 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -748,7 +748,6 @@ export class RoomConnection implements RoomConnection { if (!this.userId) { return; } - console.log("Emitting follow request"); const message = new FollowRequestMessage(); message.setLeader(this.userId); const clientToServerMessage = new ClientToServerMessage(); @@ -760,7 +759,6 @@ export class RoomConnection implements RoomConnection { if (!this.userId) { return; } - console.log("Emitting follow confirmation"); const message = new FollowConfirmationMessage(); message.setLeader(get(followUsersStore)[0]); message.setFollower(this.userId); @@ -775,7 +773,6 @@ export class RoomConnection implements RoomConnection { if (!this.userId || (isLeader && !hasFollowers)) { return; } - console.log("Emitting follow abort"); const message = new FollowAbortMessage(); message.setLeader(isLeader ? this.userId : get(followUsersStore)[0]); message.setFollower(isLeader ? 0 : this.userId); From 5aa2b469899b2867c0f9cd8ed7ed6b641d3d8d65 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Thu, 16 Dec 2021 09:51:30 +0100 Subject: [PATCH 031/149] camera docs update --- docs/maps/camera.md | 24 +++++++++++------- docs/maps/images/camera/4_add_zone_type.png | Bin 0 -> 5253 bytes ..._property.png => 5_click_add_property.png} | Bin ...able_prop.png => 6_add_focusable_prop.png} | Bin ...re_checked.png => 7_make_sure_checked.png} | Bin ..._zoom_margin.png => 8_add_zoom_margin.png} | Bin ...png => 9_optional_zoom_margin_defined.png} | Bin 7 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 docs/maps/images/camera/4_add_zone_type.png rename docs/maps/images/camera/{4_click_add_property.png => 5_click_add_property.png} (100%) rename docs/maps/images/camera/{5_add_focusable_prop.png => 6_add_focusable_prop.png} (100%) rename docs/maps/images/camera/{6_make_sure_checked.png => 7_make_sure_checked.png} (100%) rename docs/maps/images/camera/{7_add_zoom_margin.png => 8_add_zoom_margin.png} (100%) rename docs/maps/images/camera/{8_optional_zoom_margin_defined.png => 9_optional_zoom_margin_defined.png} (100%) diff --git a/docs/maps/camera.md b/docs/maps/camera.md index ac25c843..9e58fcad 100644 --- a/docs/maps/camera.md +++ b/docs/maps/camera.md @@ -29,22 +29,28 @@ It is possible to define special regions on the map that can make the camera zoo -4. Edit this new object and click on **Add Property**, like this: +4. Make sure your object is of type "zone"!
- +
-5. Add a **bool** property of name *focusable*: +5. Edit this new object and click on **Add Property**, like this:
- +
-6. Make sure it's checked! :) +6. Add a **bool** property of name *focusable*:
- + +
+ +7. Make sure it's checked! :) + +
+
All should be set up now and your new **Focusable Zone** should be working fine! @@ -56,19 +62,19 @@ If you want, you can add an additional property to control how much should the c 1. Like before, click on **Add Property**
- +
2. Add a **float** property of name *zoom_margin*:
- +
2. Define how much (in percentage value) should the zoom be decreased:
- +
For example, if you define your zone as a 300x200 rectangle, setting this property to 0.5 *(50%)* means the camera will try to fit within the viewport the entire zone + margin of 50% of its dimensions, so 450x300. diff --git a/docs/maps/images/camera/4_add_zone_type.png b/docs/maps/images/camera/4_add_zone_type.png new file mode 100644 index 0000000000000000000000000000000000000000..0416d1e475165e0eee273b036888825fe8bf33d7 GIT binary patch literal 5253 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 T%njhzA?Vf(1FeedwxRz8puncz literal 0 HcmV?d00001 diff --git a/docs/maps/images/camera/4_click_add_property.png b/docs/maps/images/camera/5_click_add_property.png similarity index 100% rename from docs/maps/images/camera/4_click_add_property.png rename to docs/maps/images/camera/5_click_add_property.png diff --git a/docs/maps/images/camera/5_add_focusable_prop.png b/docs/maps/images/camera/6_add_focusable_prop.png similarity index 100% rename from docs/maps/images/camera/5_add_focusable_prop.png rename to docs/maps/images/camera/6_add_focusable_prop.png diff --git a/docs/maps/images/camera/6_make_sure_checked.png b/docs/maps/images/camera/7_make_sure_checked.png similarity index 100% rename from docs/maps/images/camera/6_make_sure_checked.png rename to docs/maps/images/camera/7_make_sure_checked.png diff --git a/docs/maps/images/camera/7_add_zoom_margin.png b/docs/maps/images/camera/8_add_zoom_margin.png similarity index 100% rename from docs/maps/images/camera/7_add_zoom_margin.png rename to docs/maps/images/camera/8_add_zoom_margin.png diff --git a/docs/maps/images/camera/8_optional_zoom_margin_defined.png b/docs/maps/images/camera/9_optional_zoom_margin_defined.png similarity index 100% rename from docs/maps/images/camera/8_optional_zoom_margin_defined.png rename to docs/maps/images/camera/9_optional_zoom_margin_defined.png From fa2e583d485b0fee1a5cb7ab47d8fad1261c5811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 10:04:41 +0100 Subject: [PATCH 032/149] Reimporting correctly types moved to /messages --- front/src/Connexion/ConnectionManager.ts | 4 +++- front/src/Connexion/Room.ts | 7 +++---- pusher/src/Controller/IoSocketController.ts | 2 +- pusher/src/Services/AdminApi.ts | 6 +++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 39bb079c..6c40631c 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -197,7 +197,9 @@ class ConnectionManager { // try to connect with function loadOpenIDScreen if ( this._currentRoom.authenticationMandatory || - (axios.isAxiosError(err) && err.response?.data && err.response.data !== "User cannot to be connected on openid provider") + (axios.isAxiosError(err) && + err.response?.data && + err.response.data !== "User cannot to be connected on openid provider") ) { this.loadOpenIDScreen(); return Promise.reject(new Error("You will be redirect on login page")); diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts index 15ac9502..6bd53c8b 100644 --- a/front/src/Connexion/Room.ts +++ b/front/src/Connexion/Room.ts @@ -5,8 +5,8 @@ import type { CharacterTexture } from "./LocalUser"; import { localUserStore } from "./LocalUserStore"; import axios from "axios"; import { axiosWithRetry } from "./AxiosUtils"; -import {isMapDetailsData} from "../../../pusher/src/Messages/JsonMessages/MapDetailsData"; -import {isRoomRedirect} from "../Messages/JsonMessages/RoomRedirect"; +import { isMapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; +import { isRoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; export class MapDetail { constructor(public readonly mapUrl: string, public readonly textures: CharacterTexture[] | undefined) {} @@ -119,9 +119,8 @@ export class Room { this._contactPage = data.contactPage || CONTACT_URL; return new MapDetail(data.mapUrl, data.textures); } else { - throw new Error('Data received by the /map endpoint of the Pusher is not in a valid format.'); + throw new Error("Data received by the /map endpoint of the Pusher is not in a valid format."); } - } catch (e) { if (axios.isAxiosError(e) && e.response?.status == 401 && e.response?.data === "Token decrypted error") { console.warn("JWT token sent could not be decrypted. Maybe it expired?"); diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 4c09638a..4d62f7fa 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -29,7 +29,7 @@ import { emitInBatch } from "../Services/IoSocketHelpers"; import { ADMIN_API_URL, DISABLE_ANONYMOUS, SOCKET_IDLE_TIMER } from "../Enum/EnvironmentVariable"; import { Zone } from "_Model/Zone"; import { ExAdminSocketInterface } from "_Model/Websocket/ExAdminSocketInterface"; -import { CharacterTexture } from "../Services/AdminApi/CharacterTexture"; +import { CharacterTexture} from "../Messages/JsonMessages/CharacterTexture"; import { isAdminMessageInterface } from "../Model/Websocket/Admin/AdminMessages"; import Axios from "axios"; import { InvalidTokenError } from "../Controller/InvalidTokenError"; diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 416b9cb6..87c0d920 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -1,9 +1,9 @@ import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import Axios from "axios"; import { GameRoomPolicyTypes } from "_Model/PusherRoom"; -import { CharacterTexture } from "./AdminApi/CharacterTexture"; -import { MapDetailsData } from "./AdminApi/MapDetailsData"; -import { RoomRedirect } from "./AdminApi/RoomRedirect"; +import { CharacterTexture} from "../Messages/JsonMessages/CharacterTexture"; +import { MapDetailsData} from "../Messages/JsonMessages/MapDetailsData"; +import { RoomRedirect} from "../Messages/JsonMessages/RoomRedirect"; export interface AdminApiData { roomUrl: string; From 98d3a58861259da46ba3881ac8f1941d7f759bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 10:04:55 +0100 Subject: [PATCH 033/149] Improving typings of VirtualJoystick --- .../src/Phaser/UserInput/UserInputManager.ts | 2 +- front/src/rex-plugins.d.ts | 37 ++++++++++++++++++- front/src/types.ts | 23 ------------ messages/package.json | 2 +- 4 files changed, 37 insertions(+), 27 deletions(-) delete mode 100644 front/src/types.ts diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index bb7041d2..28b87e69 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,8 +1,8 @@ -import type { Direction } from "../../types"; import type { GameScene } from "../Game/GameScene"; import { touchScreenManager } from "../../Touch/TouchScreenManager"; import { MobileJoystick } from "../Components/MobileJoystick"; import { enableUserInputsStore } from "../../Stores/UserInputStore"; +import type { Direction } from "phaser3-rex-plugins/plugins/virtualjoystick.js"; interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; diff --git a/front/src/rex-plugins.d.ts b/front/src/rex-plugins.d.ts index 9884d425..e171f0ba 100644 --- a/front/src/rex-plugins.d.ts +++ b/front/src/rex-plugins.d.ts @@ -1,6 +1,39 @@ +//import Phaser from "phaser"; + declare module "phaser3-rex-plugins/plugins/virtualjoystick.js" { - const content: any; // eslint-disable-line - export default content; + /*const content: any; // eslint-disable-line + export default content;*/ + import GameObject = Phaser.GameObjects.GameObject; + import { Scene } from "phaser"; + + type CursorKey = { + isDown: boolean; + }; + + export type Direction = "left" | "right" | "up" | "down"; + + interface CursorKeys extends Record { + left: CursorKey; + right: CursorKey; + up: CursorKey; + down: CursorKey; + } + + class VirtualJoystick extends GameObject { + constructor(scene: Scene, config: unknown); + enable: boolean; + base: GameObjects.Image; + thumb: GameObjects.Image; + setRadius: (radius: number) => void; + y: number; + x: number; + forceX: number; + forceY: number; + visible: boolean; + createCursorKeys: () => CursorKeys; + } + + export default VirtualJoystick; } declare module "phaser3-rex-plugins/plugins/gestures-plugin.js" { const content: any; // eslint-disable-line diff --git a/front/src/types.ts b/front/src/types.ts deleted file mode 100644 index d957a2c2..00000000 --- a/front/src/types.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type Phaser from "phaser"; - -export type CursorKey = { - isDown: boolean; -}; - -export type Direction = "left" | "right" | "up" | "down"; - -export interface CursorKeys extends Record { - left: CursorKey; - right: CursorKey; - up: CursorKey; - down: CursorKey; -} - -export interface IVirtualJoystick extends Phaser.GameObjects.GameObject { - y: number; - x: number; - forceX: number; - forceY: number; - visible: boolean; - createCursorKeys: () => CursorKeys; -} diff --git a/messages/package.json b/messages/package.json index d4906977..596bb089 100644 --- a/messages/package.json +++ b/messages/package.json @@ -36,7 +36,7 @@ }, "repository": { "type": "git", - "url": "https://github.com/CatsMiaow/node-grpc-typescript.git" + "url": "https://github.com/CatsMiaow/node-grpc-typescript.git#grpc_tools_node_protoc_ts" }, "keywords": [ "Node.js", From a5ecac290c3b343818cb145cd541dff565d930f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 10:08:10 +0100 Subject: [PATCH 034/149] Changing slightly wording of misleading phrase --- docs/maps/scripting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/maps/scripting.md b/docs/maps/scripting.md index 8b11fe74..6da3ddbf 100644 --- a/docs/maps/scripting.md +++ b/docs/maps/scripting.md @@ -60,7 +60,7 @@ WA.chat.sendChatMessage('Hello world', 'Mr Robot'); The `WA` objects contains a number of useful methods enabling you to interact with the WorkAdventure game. For instance, `WA.chat.sendChatMessage` opens the chat and adds a message in it. -In your browser console, when you open the map, the chat message should be displayed right away. +The message should be displayed in the chat history as soon as you enter the room. ## Adding a script in an iFrame From 0c281db4113f6971a4265ef9f9732aacac39d80e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 15:57:37 +0100 Subject: [PATCH 035/149] More eslint fixes (+ ignoring no-unsafe-argument rule for now) --- front/.eslintrc.js | 3 ++- front/src/Connexion/ConnectionManager.ts | 10 ++++++- front/src/Stores/MediaStore.ts | 4 +-- front/src/Stores/ScreenSharingStore.ts | 2 +- front/src/Touch/TouchScreenManager.ts | 2 +- messages/JsonMessages/AdminApiData.ts | 23 ++++++++++++++++ messages/JsonMessages/RegisterData.ts | 24 +++++++++++++++++ .../src/Controller/AuthenticateController.ts | 3 ++- pusher/src/Services/AdminApi.ts | 26 +++++++++---------- 9 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 messages/JsonMessages/AdminApiData.ts create mode 100644 messages/JsonMessages/RegisterData.ts diff --git a/front/.eslintrc.js b/front/.eslintrc.js index 33466012..117cb7e6 100644 --- a/front/.eslintrc.js +++ b/front/.eslintrc.js @@ -41,7 +41,8 @@ module.exports = { "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/restrict-template-expressions": "off" + "@typescript-eslint/restrict-template-expressions": "off", + "@typescript-eslint/no-unsafe-argument": "off", }, "settings": { "svelte3/typescript": true, diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index 6c40631c..6f1e1f50 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -12,6 +12,8 @@ import { userIsConnected } from "../Stores/MenuStore"; import { analyticsClient } from "../Administration/AnalyticsClient"; import { axiosWithRetry } from "./AxiosUtils"; import axios from "axios"; +import { isRegisterData } from "../Messages/JsonMessages/RegisterData"; +import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData"; class ConnectionManager { private localUser!: LocalUser; @@ -126,6 +128,10 @@ class ConnectionManager { const data = await Axios.post(`${PUSHER_URL}/register`, { organizationMemberToken }).then( (res) => res.data ); + if (!isRegisterData(data)) { + console.error("Invalid data received from /register route. Data: ", data); + throw new Error("Invalid data received from /register route."); + } this.localUser = new LocalUser(data.userUuid, data.textures, data.email); this.authToken = data.authToken; localUserStore.saveUser(this.localUser); @@ -326,7 +332,9 @@ class ConnectionManager { } const { authToken, userUuid, textures, email } = await Axios.get(`${PUSHER_URL}/login-callback`, { params: { code, nonce, token, playUri: this.currentRoom?.key }, - }).then((res) => res.data); + }).then((res) => { + return res.data; + }); localUserStore.setAuthToken(authToken); this.localUser = new LocalUser(userUuid, textures, email); localUserStore.saveUser(this.localUser); diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts index 8a8e39e2..44c78ad2 100644 --- a/front/src/Stores/MediaStore.ts +++ b/front/src/Stores/MediaStore.ts @@ -426,7 +426,7 @@ export const localStreamStore = derived, LocalS // TODO: does it make sense to pop this error when retrying? set({ type: "error", - error: e, + error: e instanceof Error ? e : new Error("An unknown error happened"), }); // Let's try without video constraints if (constraints.video !== false) { @@ -444,7 +444,7 @@ export const localStreamStore = derived, LocalS console.info("Error. Unable to get microphone and/or camera access.", constraints, e); set({ type: "error", - error: e, + error: e instanceof Error ? e : new Error("An unknown error happened"), }); } } diff --git a/front/src/Stores/ScreenSharingStore.ts b/front/src/Stores/ScreenSharingStore.ts index e4ee6410..d68dbf8b 100644 --- a/front/src/Stores/ScreenSharingStore.ts +++ b/front/src/Stores/ScreenSharingStore.ts @@ -153,7 +153,7 @@ export const screenSharingLocalStreamStore = derived 0 || navigator.msMaxTouchPoints > 0; + return "ontouchstart" in window || navigator.maxTouchPoints > 0; } } diff --git a/messages/JsonMessages/AdminApiData.ts b/messages/JsonMessages/AdminApiData.ts new file mode 100644 index 00000000..f21cc17c --- /dev/null +++ b/messages/JsonMessages/AdminApiData.ts @@ -0,0 +1,23 @@ +import * as tg from "generic-type-guard"; +import { isCharacterTexture} from "./CharacterTexture"; + +/* + * WARNING! The original file is in /messages/JsonMessages. + * All other files are automatically copied from this file on container startup / build + */ + +export const isAdminApiData = new tg.IsInterface() + .withProperties({ + roomUrl: tg.isString, + email: tg.isNullable(tg.isString), + mapUrlStart: tg.isString, + tags: tg.isArray(tg.isString), + policy_type: tg.isNumber, + userUuid: tg.isString, + textures: tg.isArray(isCharacterTexture), + }) + .withOptionalProperties({ + messages: tg.isArray(tg.isUnknown), + }) + .get(); +export type AdminApiData = tg.GuardedType; diff --git a/messages/JsonMessages/RegisterData.ts b/messages/JsonMessages/RegisterData.ts new file mode 100644 index 00000000..c585731e --- /dev/null +++ b/messages/JsonMessages/RegisterData.ts @@ -0,0 +1,24 @@ +import * as tg from "generic-type-guard"; +import { isCharacterTexture} from "./CharacterTexture"; + +/* + * WARNING! The original file is in /messages/JsonMessages. + * All other files are automatically copied from this file on container startup / build + */ + +export const isRegisterData = new tg.IsInterface() + .withProperties({ + roomUrl: tg.isString, + email: tg.isNullable(tg.isString), + organizationMemberToken: tg.isNullable(tg.isString), + mapUrlStart: tg.isString, + userUuid: tg.isString, + textures: tg.isArray(isCharacterTexture), + authToken: tg.isString, + + }) + .withOptionalProperties({ + messages: tg.isArray(tg.isUnknown), + }) + .get(); +export type RegisterData = tg.GuardedType; diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index e2089c89..3e725da5 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -6,6 +6,7 @@ import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; import { DISABLE_ANONYMOUS, FRONT_URL } from "../Enum/EnvironmentVariable"; +import { RegisterData } from "../../../messages/JsonMessages/RegisterData"; export interface TokenInterface { userUuid: string; @@ -191,7 +192,7 @@ export class AuthenticateController extends BaseController { mapUrlStart, organizationMemberToken, textures, - }) + } as RegisterData) ); } catch (e) { console.error("register => ERROR", e); diff --git a/pusher/src/Services/AdminApi.ts b/pusher/src/Services/AdminApi.ts index 87c0d920..4410c29e 100644 --- a/pusher/src/Services/AdminApi.ts +++ b/pusher/src/Services/AdminApi.ts @@ -1,20 +1,10 @@ import { ADMIN_API_TOKEN, ADMIN_API_URL, ADMIN_URL, OPID_PROFILE_SCREEN_PROVIDER } from "../Enum/EnvironmentVariable"; import Axios from "axios"; import { GameRoomPolicyTypes } from "_Model/PusherRoom"; -import { CharacterTexture} from "../Messages/JsonMessages/CharacterTexture"; -import { MapDetailsData} from "../Messages/JsonMessages/MapDetailsData"; -import { RoomRedirect} from "../Messages/JsonMessages/RoomRedirect"; - -export interface AdminApiData { - roomUrl: string; - email: string | null; - mapUrlStart: string; - tags: string[]; - policy_type: number; - userUuid: string; - messages?: unknown[]; - textures: CharacterTexture[]; -} +import { CharacterTexture } from "../Messages/JsonMessages/CharacterTexture"; +import { MapDetailsData } from "../Messages/JsonMessages/MapDetailsData"; +import { RoomRedirect } from "../Messages/JsonMessages/RoomRedirect"; +import { AdminApiData, isAdminApiData } from "../Messages/JsonMessages/AdminApiData"; export interface AdminBannedData { is_banned: boolean; @@ -77,6 +67,10 @@ class AdminApi { const res = await Axios.get(ADMIN_API_URL + "/api/login-url/" + organizationMemberToken, { headers: { Authorization: `${ADMIN_API_TOKEN}` }, }); + if (!isAdminApiData(res.data)) { + console.error("Message received from /api/login-url is not in the expected format. Message: ", res.data); + throw new Error("Message received from /api/login-url is not in the expected format."); + } return res.data; } @@ -88,6 +82,10 @@ class AdminApi { const res = await Axios.get(ADMIN_API_URL + "/api/check-user/" + organizationMemberToken, { headers: { Authorization: `${ADMIN_API_TOKEN}` }, }); + if (!isAdminApiData(res.data)) { + console.error("Message received from /api/check-user is not in the expected format. Message: ", res.data); + throw new Error("Message received from /api/check-user is not in the expected format."); + } return res.data; } From 88a6e80a8ffae338318c50542596f29ac0f69d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 16:09:59 +0100 Subject: [PATCH 036/149] Adding JsonMessages to Docker image --- front/Dockerfile | 1 + pusher/Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/front/Dockerfile b/front/Dockerfile index 6fef9dc8..f781a37c 100644 --- a/front/Dockerfile +++ b/front/Dockerfile @@ -8,6 +8,7 @@ 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/JsonMessages /var/www/html/src/Messages/JsonMessages # Removing the iframe.html file from the final image as this adds a XSS attack. # iframe.html is only in dev mode to circumvent a limitation diff --git a/pusher/Dockerfile b/pusher/Dockerfile index 4aec9748..af4e107e 100644 --- a/pusher/Dockerfile +++ b/pusher/Dockerfile @@ -11,6 +11,7 @@ COPY pusher/yarn.lock pusher/package.json ./ RUN yarn install COPY pusher . COPY --from=builder /usr/src/generated src/Messages/generated +COPY --from=builder /usr/src/JsonMessages /var/www/html/src/Messages/JsonMessages ENV NODE_ENV=production RUN yarn run tsc From 805368dd3ff75131ae3d328fde03a305fd8802f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 16:15:12 +0100 Subject: [PATCH 037/149] Fixing CI with new messages --- .github/workflows/continuous_integration.yml | 4 ++-- .github/workflows/push-to-npm.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/continuous_integration.yml b/.github/workflows/continuous_integration.yml index fcde792a..1ad73027 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 + run: yarn run proto && yarn run copy-to-front && yarn run json-copy-to-front working-directory: "messages" - name: "Create index.html" @@ -97,7 +97,7 @@ jobs: working-directory: "messages" - name: "Build proto messages" - run: yarn run proto && yarn run copy-to-pusher + run: yarn run proto && yarn run copy-to-pusher && yarn run json-copy-to-pusher working-directory: "messages" - name: "Build" diff --git a/.github/workflows/push-to-npm.yml b/.github/workflows/push-to-npm.yml index 1208e0c0..71f2824f 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 + run: yarn run proto && yarn run copy-to-front && yarn run json-copy-to-front working-directory: "messages" - name: "Create index.html" From 8c938017468d54055849354f00a0855e1fbbcce7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 16:18:23 +0100 Subject: [PATCH 038/149] Fixing wrong import --- pusher/src/Model/Websocket/ExSocketInterface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 411d88fa..67078aa2 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -11,7 +11,7 @@ import { import { WebSocket } from "uWebSockets.js"; import { ClientDuplexStream } from "grpc"; import { Zone } from "_Model/Zone"; -import { CharacterTexture } from "../../Services/AdminApi/CharacterTexture"; +import { CharacterTexture} from "../../Messages/JsonMessages/CharacterTexture"; export type BackConnection = ClientDuplexStream; From fff45c2f92bab7a348791f55f96a09ea46b9b438 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 16:27:44 +0100 Subject: [PATCH 039/149] Adding prettier on messages --- messages/.prettierignore | 1 + messages/.prettierrc.json | 5 + messages/LICENSE.txt | 691 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 697 insertions(+) create mode 100644 messages/.prettierignore create mode 100644 messages/.prettierrc.json create mode 100644 messages/LICENSE.txt diff --git a/messages/.prettierignore b/messages/.prettierignore new file mode 100644 index 00000000..1f453464 --- /dev/null +++ b/messages/.prettierignore @@ -0,0 +1 @@ +src/Messages/generated diff --git a/messages/.prettierrc.json b/messages/.prettierrc.json new file mode 100644 index 00000000..057ed062 --- /dev/null +++ b/messages/.prettierrc.json @@ -0,0 +1,5 @@ +{ + "printWidth": 120, + "tabWidth": 4, + "plugins": ["prettier-plugin-svelte"] +} diff --git a/messages/LICENSE.txt b/messages/LICENSE.txt new file mode 100644 index 00000000..614e6268 --- /dev/null +++ b/messages/LICENSE.txt @@ -0,0 +1,691 @@ +NOTICE +This package contains software licensed under different +licenses, please refer to the NOTICE.txt file for further +information and LICENSES.txt for full license texts. + +WorkAdventure Enterprise edition can be licensed independently from +the source under separate commercial terms. + +The software ("Software") is developed and owned by TheCodingMachine +and is subject to the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +Version 3, with the Commons Clause as follows: + + + + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license +for software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are +designed to take away your freedom to share and change the works. By +contrast, our General Public Licenses are intended to guarantee your +freedom to share and change all versions of a program--to make sure it +remains free software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public +License. + + "Copyright" also means copyright-like laws that apply to other kinds +of works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further restriction, +you may remove that term. If a license document contains a further +restriction but permits relicensing or conveying under this License, you +may add to a covered work material governed by the terms of that license +document, provided that the further restriction does not survive such +relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have permission +to link or combine any covered work with a work licensed under version 3 +of the GNU General Public License into a single combined work, and to +convey the resulting work. The terms of this License will continue to +apply to the part which is the covered work, but the work with which it is +combined will remain governed by version 3 of the GNU General Public +License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may differ +in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero +General Public License "or any later version" applies to it, you have +the option of following the terms and conditions either of that +numbered version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number +of the GNU Affero General Public License, you may choose any version +ever published by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that +proxy's public statement of acceptance of a version permanently +authorizes you to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation, either version 3 of the + License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. + + +"Commons Clause" License Condition + +The Software is provided to you by the Licensor under the License, as +defined below, subject to the following condition. Without limiting +other conditions in the License, the grant of rights under the License +will not include, and the License does not grant to you, the right to +Sell the Software. For purposes of the foregoing, "Sell" means +practicing any or all of the rights granted to you under the License +to provide to third parties, for a fee or other consideration, +a product or service that consists, entirely or substantially, +of the Software or the functionality of the Software. Any license +notice or attribution required by the License must also include +this Commons Cause License Condition notice. From 1ed10b0cddde41834092df36f75ca64922659d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 16:40:50 +0100 Subject: [PATCH 040/149] Adding Prettier in messages --- .husky/pre-commit | 4 + front/.prettierignore | 1 + messages/.prettierignore | 2 +- messages/package.json | 18 +- messages/yarn.lock | 364 ++++++++++++++++++++++++++++++++++++++- pusher/.prettierignore | 1 + 6 files changed, 381 insertions(+), 9 deletions(-) diff --git a/.husky/pre-commit b/.husky/pre-commit index 5944be37..8fa7767b 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,10 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" +( +cd messages || exit +yarn run precommit +) ( cd front || exit yarn run precommit diff --git a/front/.prettierignore b/front/.prettierignore index 1f453464..26de759f 100644 --- a/front/.prettierignore +++ b/front/.prettierignore @@ -1 +1,2 @@ src/Messages/generated +src/Messages/JsonMessages diff --git a/messages/.prettierignore b/messages/.prettierignore index 1f453464..86d4c2dd 100644 --- a/messages/.prettierignore +++ b/messages/.prettierignore @@ -1 +1 @@ -src/Messages/generated +generated diff --git a/messages/package.json b/messages/package.json index 596bb089..6095a626 100644 --- a/messages/package.json +++ b/messages/package.json @@ -9,8 +9,11 @@ "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: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" + "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'" }, "dependencies": { "generic-type-guard": "^3.5.0", @@ -29,6 +32,8 @@ "eslint-plugin-sonarjs": "^0.5.0", "grpc-tools": "^1.9.1", "grpc_tools_node_protoc_ts": "^5.0.1", + "lint-staged": "^11.0.0", + "prettier": "^2.3.1", "protobufjs": "^6.10.1", "rimraf": "^3.0.2", "shelljs": "^0.8.4", @@ -43,7 +48,12 @@ "gRPC", "TypeScript" ], - "homepage": "https://github.com/CatsMiaow/node-grpc-typescript#readme", - "author": "CatsMiaow", - "license": "MIT" + "homepage": "https://github.com/thecodingmachine/workadventure", + "author": "David Négrier", + "license": "SEE LICENSE IN LICENSE.txt", + "lint-staged": { + "*.ts": [ + "yarn run pretty" + ] + } } diff --git a/messages/yarn.lock b/messages/yarn.lock index 2f4a00bf..5d040c6f 100644 --- a/messages/yarn.lock +++ b/messages/yarn.lock @@ -184,6 +184,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.30.tgz#1ed6e01e4ca576d5aec9cc802cc3bcf94c274192" integrity sha512-HmqFpNzp3TSELxU/bUuRK+xzarVOAsR00hzcvM0TXrMlt/+wcSLa5q6YhTb6/cA6wqDCZLDcfd8fSL95x5h7AA== +"@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/parsimmon@^1.10.1": version "1.10.4" resolved "https://registry.yarnpkg.com/@types/parsimmon/-/parsimmon-1.10.4.tgz#7639e16015440d9baf622f83c12dae47787226b7" @@ -319,6 +324,14 @@ acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -458,6 +471,13 @@ ansi-dim@^0.1.1: dependencies: ansi-wrap "0.1.0" +ansi-escapes@^4.3.0: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-gray@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" @@ -694,6 +714,11 @@ astral-regex@^1.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -930,6 +955,26 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-truncate@2.1.0, cli-truncate@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7" + integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== + dependencies: + slice-ansi "^3.0.0" + string-width "^4.2.0" + cliui@^3.0.3: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" @@ -985,6 +1030,16 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" + integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== + +colorette@^2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" + integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== + colour@~0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" @@ -1007,6 +1062,11 @@ commander@^2.12.1: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^8.2.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -1059,6 +1119,17 @@ core-util-is@1.0.2, core-util-is@~1.0.0: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cosmiconfig@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" + integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== + dependencies: + "@types/parse-json" "^4.0.0" + import-fresh "^3.2.1" + parse-json "^5.0.0" + path-type "^4.0.0" + yaml "^1.10.0" + create-frame@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/create-frame/-/create-frame-1.0.0.tgz#8b95f2691e3249b6080443e33d0bad9f8f6975aa" @@ -1069,7 +1140,7 @@ create-frame@^1.0.0: isobject "^3.0.0" lazy-cache "^2.0.2" -cross-spawn@^7.0.2: +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1113,6 +1184,13 @@ debug@^4.0.1, debug@^4.1.1: dependencies: ms "2.1.2" +debug@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -1270,7 +1348,7 @@ end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enquirer@^2.3.5: +enquirer@^2.3.5, enquirer@^2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -1282,7 +1360,7 @@ ent@^2.2.0: resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= -error-ex@^1.2.0: +error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== @@ -1530,6 +1608,21 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +execa@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + expand-brackets@^2.1.4: version "2.1.4" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" @@ -1815,6 +1908,16 @@ get-object@^0.2.0: is-number "^2.0.2" isobject "^0.2.0" +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664" + integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-value@^2.0.3, get-value@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" @@ -2123,6 +2226,11 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -2165,6 +2273,11 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2348,6 +2461,11 @@ is-number@^7.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +is-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= + is-odd@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-0.1.2.tgz#bc573b5ce371ef2aad6e6f49799b72bef13978a7" @@ -2369,6 +2487,11 @@ is-regex@^1.1.1: dependencies: has-symbols "^1.0.1" +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + is-self-closing@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-self-closing/-/is-self-closing-1.0.1.tgz#5f406b527c7b12610176320338af0fa3896416e4" @@ -2376,6 +2499,11 @@ is-self-closing@^1.0.1: dependencies: self-closing-tags "^1.0.1" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-string@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" @@ -2453,6 +2581,11 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" @@ -2555,6 +2688,45 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lint-staged@^11.0.0: + version "11.2.6" + resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-11.2.6.tgz#f477b1af0294db054e5937f171679df63baa4c43" + integrity sha512-Vti55pUnpvPE0J9936lKl0ngVeTdSZpEdTNhASbkaWX7J5R9OEifo1INBGQuGW4zmy6OG+TcWPJ3m5yuy5Q8Tg== + dependencies: + cli-truncate "2.1.0" + colorette "^1.4.0" + commander "^8.2.0" + cosmiconfig "^7.0.1" + debug "^4.3.2" + enquirer "^2.3.6" + execa "^5.1.1" + listr2 "^3.12.2" + micromatch "^4.0.4" + normalize-path "^3.0.0" + please-upgrade-node "^3.2.0" + string-argv "0.3.1" + stringify-object "3.3.0" + supports-color "8.1.1" + +listr2@^3.12.2: + version "3.13.5" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.13.5.tgz#105a813f2eb2329c4aae27373a281d610ee4985f" + integrity sha512-3n8heFQDSk+NcwBn3CgxEibZGaRzx+pC64n3YjpMD1qguV4nWus3Al+Oo3KooqFKTQEJ1v7MmnbnyyNspgx3NA== + dependencies: + cli-truncate "^2.1.0" + colorette "^2.0.16" + log-update "^4.0.0" + p-map "^4.0.0" + rfdc "^1.3.0" + rxjs "^7.4.0" + through "^2.3.8" + wrap-ansi "^7.0.0" + load-json-file@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" @@ -2623,6 +2795,16 @@ log-ok@^0.1.1: ansi-green "^0.1.1" success-symbol "^0.1.0" +log-update@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1" + integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg== + dependencies: + ansi-escapes "^4.3.0" + cli-cursor "^3.1.0" + slice-ansi "^4.0.0" + wrap-ansi "^6.2.0" + log-utils@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/log-utils/-/log-utils-0.2.1.tgz#a4c217a0dd9a50515d9b920206091ab3d4e031cf" @@ -2666,6 +2848,11 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + merge2@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -2698,6 +2885,14 @@ micromatch@^4.0.2: braces "^3.0.1" picomatch "^2.0.5" +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + mime-db@1.44.0: version "1.44.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" @@ -2710,6 +2905,11 @@ mime-types@^2.1.12, mime-types@~2.1.19: dependencies: mime-db "1.44.0" +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2858,6 +3058,11 @@ normalize-package-data@^2.3.2, "normalize-package-data@~1.0.1 || ^2.0.0": semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + npm-bundled@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" @@ -2908,6 +3113,13 @@ npm-registry-client@^8.6.0: optionalDependencies: npmlog "2 || ^3.1.0 || ^4.0.0" +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + "npmlog@2 || ^3.1.0 || ^4.0.0", npmlog@^4.0.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" @@ -3002,6 +3214,13 @@ once@^1.3.0, once@^1.3.3, once@^1.4.0: dependencies: wrappy "1" +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -3072,6 +3291,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" @@ -3096,6 +3322,16 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" +parse-json@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + parsimmon@^1.13.0: version "1.16.0" resolved "https://registry.yarnpkg.com/parsimmon/-/parsimmon-1.16.0.tgz#2834e3db645b6a855ab2ea14fbaad10d82867e0f" @@ -3121,7 +3357,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -3153,6 +3389,11 @@ picomatch@^2.0.5, picomatch@^2.2.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== +picomatch@^2.2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" + integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -3165,6 +3406,13 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" +please-upgrade-node@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942" + integrity sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg== + dependencies: + semver-compare "^1.0.0" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -3175,6 +3423,11 @@ 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: + version "2.5.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" + integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -3384,6 +3637,14 @@ resolve@^1.10.0: dependencies: path-parse "^1.0.6" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -3399,6 +3660,11 @@ reusify@^1.0.4: resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + rimraf@2, rimraf@^2.6.1: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -3425,6 +3691,13 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== +rxjs@^7.4.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.4.0.tgz#a12a44d7eebf016f5ff2441b87f28c9a51cebc68" + integrity sha512-7SQDi7xeTMCJpqViXh8gL/lebcwlp3d831F05+9B44A4B0WfsEwUQHR64gsH1kvJ+Ep/J9K2+n1hVl1CsGN23w== + dependencies: + tslib "~2.1.0" + safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -3457,6 +3730,11 @@ self-closing-tags@^1.0.1: resolved "https://registry.yarnpkg.com/self-closing-tags/-/self-closing-tags-1.0.1.tgz#6c5fa497994bb826b484216916371accee490a5d" integrity sha512-7t6hNbYMxM+VHXTgJmxwgZgLGktuXtVVD5AivWzNTdJBM4DBjnDKDzkf2SrNjihaArpeJYNjxkELBu1evI4lQA== +semver-compare@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" + integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= + "semver@2 >=2.2.1 || 3.x || 4 || 5", "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -3520,6 +3798,11 @@ signal-exit@^3.0.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== +signal-exit@^3.0.2, signal-exit@^3.0.3: + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -3534,6 +3817,24 @@ slice-ansi@^2.1.0: astral-regex "^1.0.0" is-fullwidth-code-point "^2.0.0" +slice-ansi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787" + integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slide@^1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -3663,6 +3964,11 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +string-argv@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da" + integrity sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg== + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -3728,6 +4034,15 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +stringify-object@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -3761,6 +4076,11 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -3781,6 +4101,13 @@ success-symbol@^0.1.0: resolved "https://registry.yarnpkg.com/success-symbol/-/success-symbol-0.1.0.tgz#24022e486f3bf1cdca094283b769c472d3b72897" integrity sha1-JAIuSG878c3KCUKDt2nEctO3KJc= +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -3856,6 +4183,11 @@ through2@^2.0.0: readable-stream "~2.3.6" xtend "~4.0.1" +through@^2.3.8: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= + time-stamp@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" @@ -3928,6 +4260,11 @@ tslib@^1.8.0, tslib@^1.8.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== + tslint@5.14.0: version "5.14.0" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.14.0.tgz#be62637135ac244fc9b37ed6ea5252c9eba1616e" @@ -3980,6 +4317,11 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" @@ -4142,6 +4484,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -4174,6 +4525,11 @@ yallist@^3.0.0, yallist@^3.0.3: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yaml@^1.10.0: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== + yargs-parser@^18.1.2: version "18.1.3" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" diff --git a/pusher/.prettierignore b/pusher/.prettierignore index 1f453464..26de759f 100644 --- a/pusher/.prettierignore +++ b/pusher/.prettierignore @@ -1 +1,2 @@ src/Messages/generated +src/Messages/JsonMessages From 36f5d42668212cc111233554aed00df370530c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Thu, 16 Dec 2021 16:46:22 +0100 Subject: [PATCH 041/149] Fixing prettier setup on messages --- messages/.prettierrc.json | 3 +-- messages/JsonMessages/AdminApiData.ts | 2 +- messages/JsonMessages/RegisterData.ts | 3 +-- pusher/src/Controller/AuthenticateController.ts | 2 +- pusher/src/Model/Websocket/ExSocketInterface.ts | 2 +- 5 files changed, 5 insertions(+), 7 deletions(-) diff --git a/messages/.prettierrc.json b/messages/.prettierrc.json index 057ed062..e8980d15 100644 --- a/messages/.prettierrc.json +++ b/messages/.prettierrc.json @@ -1,5 +1,4 @@ { "printWidth": 120, - "tabWidth": 4, - "plugins": ["prettier-plugin-svelte"] + "tabWidth": 4 } diff --git a/messages/JsonMessages/AdminApiData.ts b/messages/JsonMessages/AdminApiData.ts index f21cc17c..72bfe682 100644 --- a/messages/JsonMessages/AdminApiData.ts +++ b/messages/JsonMessages/AdminApiData.ts @@ -1,5 +1,5 @@ import * as tg from "generic-type-guard"; -import { isCharacterTexture} from "./CharacterTexture"; +import { isCharacterTexture } from "./CharacterTexture"; /* * WARNING! The original file is in /messages/JsonMessages. diff --git a/messages/JsonMessages/RegisterData.ts b/messages/JsonMessages/RegisterData.ts index c585731e..473ee592 100644 --- a/messages/JsonMessages/RegisterData.ts +++ b/messages/JsonMessages/RegisterData.ts @@ -1,5 +1,5 @@ import * as tg from "generic-type-guard"; -import { isCharacterTexture} from "./CharacterTexture"; +import { isCharacterTexture } from "./CharacterTexture"; /* * WARNING! The original file is in /messages/JsonMessages. @@ -15,7 +15,6 @@ export const isRegisterData = new tg.IsInterface() userUuid: tg.isString, textures: tg.isArray(isCharacterTexture), authToken: tg.isString, - }) .withOptionalProperties({ messages: tg.isArray(tg.isUnknown), diff --git a/pusher/src/Controller/AuthenticateController.ts b/pusher/src/Controller/AuthenticateController.ts index f063de10..149253b3 100644 --- a/pusher/src/Controller/AuthenticateController.ts +++ b/pusher/src/Controller/AuthenticateController.ts @@ -6,7 +6,7 @@ import { AuthTokenData, jwtTokenManager } from "../Services/JWTTokenManager"; import { parse } from "query-string"; import { openIDClient } from "../Services/OpenIDClient"; import { DISABLE_ANONYMOUS, FRONT_URL } from "../Enum/EnvironmentVariable"; -import { RegisterData } from "../../../messages/JsonMessages/RegisterData"; +import { RegisterData } from "../Messages/JsonMessages/RegisterData"; export interface TokenInterface { userUuid: string; diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 67078aa2..47eba2dd 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -11,7 +11,7 @@ import { import { WebSocket } from "uWebSockets.js"; import { ClientDuplexStream } from "grpc"; import { Zone } from "_Model/Zone"; -import { CharacterTexture} from "../../Messages/JsonMessages/CharacterTexture"; +import { CharacterTexture } from "../../Messages/JsonMessages/CharacterTexture"; export type BackConnection = ClientDuplexStream; From ab994183e5eb0be3c503d41798e55f9307cb7b68 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Thu, 16 Dec 2021 21:26:30 +0100 Subject: [PATCH 042/149] Fix not following users getting "stuck" in groups --- back/src/Model/GameRoom.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 9afeae55..43605778 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -217,9 +217,22 @@ export class GameRoom { } else { // If the user is part of a group: // should he leave the group? - const distance = GameRoom.computeDistanceBetweenPositions(user.getPosition(), user.group.getPosition()); - if (user.following.length === 0 && distance > this.groupRadius) { - this.leaveGroup(user); + const leaveIfOutOfRadius = (user: User) => { + if (user.group === undefined) { + return; + } + const usrPos = user.getPosition(); + const grpPos = user.group.getPosition(); + const distance = GameRoom.computeDistanceBetweenPositions(usrPos, grpPos); + if (distance > this.groupRadius) { + this.leaveGroup(user); + } + }; + if (user.following.length > 0) { + const users = user.group.getUsers().filter((u) => u.following.length === 0); + users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); + } else { + leaveIfOutOfRadius(user); } } } From 404404d2bf88f89f09c69cbe8b43f4745f037748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 10:18:31 +0100 Subject: [PATCH 043/149] Fixing pusher build --- pusher/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pusher/Dockerfile b/pusher/Dockerfile index af4e107e..3ead0913 100644 --- a/pusher/Dockerfile +++ b/pusher/Dockerfile @@ -11,7 +11,7 @@ COPY pusher/yarn.lock pusher/package.json ./ RUN yarn install COPY pusher . COPY --from=builder /usr/src/generated src/Messages/generated -COPY --from=builder /usr/src/JsonMessages /var/www/html/src/Messages/JsonMessages +COPY --from=builder /usr/src/JsonMessages src/Messages/JsonMessages ENV NODE_ENV=production RUN yarn run tsc From 44ff9e30d5df375509c8b2afff64f75f3f5dd619 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 11:54:37 +0100 Subject: [PATCH 044/149] Using id instead of name to identify other Wokas --- back/src/Model/GameRoom.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 43605778..ba555c92 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -239,7 +239,7 @@ export class GameRoom { public sendToOthersInGroupIncludingUser(user: User, message: ServerToClientMessage): void { user.group?.getUsers().forEach((currentUser: User) => { - if (currentUser.name !== user.name) { + if (currentUser.id !== user.id) { currentUser.socket.write(message); } }); From fd9cb09de618a82250fea7f2ede5f4fe54821621 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 11:55:27 +0100 Subject: [PATCH 045/149] Checking if a user should leave a group when someone moves in the group every time. This fixes a long standing issue. --- back/src/Model/GameRoom.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index ba555c92..0e8c5a10 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -228,12 +228,8 @@ export class GameRoom { this.leaveGroup(user); } }; - if (user.following.length > 0) { - const users = user.group.getUsers().filter((u) => u.following.length === 0); - users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); - } else { - leaveIfOutOfRadius(user); - } + const users = user.group.getUsers().filter((u) => u.following.length === 0); + users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); } } From cd805fab310c32ea28e73093b8b8f7903c318a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 18:26:17 +0100 Subject: [PATCH 046/149] Refactoring following code to make a distinction beween followed and following users. Also, moving sending messages to the User class. --- back/src/Model/GameRoom.ts | 3 +- back/src/Model/Group.ts | 12 ++++++++ back/src/Model/User.ts | 49 ++++++++++++++++++++++-------- back/src/Services/SocketManager.ts | 32 ++++++++----------- 4 files changed, 63 insertions(+), 33 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 0e8c5a10..07b611e8 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -125,7 +125,6 @@ export class GameRoom { joinRoomMessage.getIpaddress(), position, false, - [], this.positionNotifier, socket, joinRoomMessage.getTagList(), @@ -228,7 +227,7 @@ export class GameRoom { this.leaveGroup(user); } }; - const users = user.group.getUsers().filter((u) => u.following.length === 0); + const users = user.group.getUsers().filter((u) => !u.hasFollowers() && !u.following); users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); } } diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index 570eaedf..db9f6305 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -152,4 +152,16 @@ export class Group implements Movable { get getSize() { return this.users.size; } + + /** + * A group can have at most one person leading the way in it. + */ + get leader(): User|undefined { + for (const user of this.users) { + if (user.hasFollowers()) { + return user; + } + } + return undefined; + } } diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 13a61c3f..4619ce2c 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -6,7 +6,7 @@ import { PositionNotifier } from "_Model/PositionNotifier"; import { ServerDuplexStream } from "grpc"; import { BatchMessage, - CompanionMessage, + CompanionMessage, FollowAbortMessage, FollowConfirmationMessage, PusherToBackMessage, ServerToClientMessage, SubMessage, @@ -18,6 +18,8 @@ export type UserSocket = ServerDuplexStream; public group?: Group; + private _following: User|undefined; + private followedBy: Set = new Set(); public constructor( public id: number, @@ -25,7 +27,6 @@ export class User implements Movable { public readonly IPAddress: string, private position: PointInterface, public silent: boolean, - public following: number[], private positionNotifier: PositionNotifier, public readonly socket: UserSocket, public readonly tags: string[], @@ -49,19 +50,43 @@ export class User implements Movable { this.positionNotifier.updatePosition(this, position, oldPosition); } - public addFollower(userId: number): void { - if (this.following.includes(userId)) { - return; - } - this.following.push(userId); + public addFollower(follower: User): void { + this.followedBy.add(follower); + follower._following = this; + + const message = new FollowConfirmationMessage(); + message.setFollower(follower.id); + message.setLeader(this.id); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowconfirmationmessage(message); + this.socket.write(clientMessage); } - public delFollower(userId: number): void { - const idx = this.following.indexOf(userId); - if (idx === -1) { - return; + public delFollower(follower: User): void { + this.followedBy.delete(follower); + follower._following = undefined; + + const message = new FollowAbortMessage(); + message.setFollower(follower.id); + message.setLeader(this.id); + const clientMessage = new ServerToClientMessage(); + clientMessage.setFollowabortmessage(message); + this.socket.write(clientMessage); + follower.socket.write(clientMessage); + } + + public hasFollowers(): boolean { + return this.followedBy.size !== 0; + } + + get following(): User | undefined { + return this._following; + } + + public stopLeading(): void { + for (const follower of this.followedBy) { + this.delFollower(follower); } - this.following.splice(idx, 1); } private batchedMessages: BatchMessage = new BatchMessage(); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 8c7eecac..d7178d3d 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -844,34 +844,28 @@ export class SocketManager { } handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowconfirmationmessage(message); const leader = room.getUserById(message.getLeader()); - leader?.socket.write(clientMessage); + if (!leader) { + console.info('Could not find user "', message.getLeader(), '" while handling a follow confirmation in room "', room.roomUrl,'". Maybe the user just left.'); + return; + } - leader?.addFollower(user.id); - user.addFollower(message.getLeader()); + // By security, we look at the group leader. If the group leader is NOT the leader in the message, everybody should + // stop following the group leader (to avoid having 2 group leaders) + if (user?.group?.leader && user?.group?.leader !== leader) { + user?.group?.leader?.stopLeading(); + } + + leader.addFollower(user); } handleFollowAbortMessage(room: GameRoom, user: User, message: FollowAbortMessage) { - const clientMessage = new ServerToClientMessage(); - clientMessage.setFollowabortmessage(message); if (user.id === message.getLeader()) { - // Forward message - room.sendToOthersInGroupIncludingUser(user, clientMessage); - - // Update followers - user.group?.getUsers().forEach((user) => { - user.following = []; - }); + user?.group?.leader?.stopLeading(); } else { // Forward message const leader = room.getUserById(message.getLeader()); - leader?.socket.write(clientMessage); - - // Update followers - leader?.delFollower(user.id); - user.following = []; + leader?.delFollower(user); } } } From c96b65549f92de55652d427aa81eda16a684f7ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 17 Dec 2021 18:29:52 +0100 Subject: [PATCH 047/149] Performing proper cleanup when a user leaves --- back/src/Model/GameRoom.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 07b611e8..9b0fef45 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -154,6 +154,14 @@ export class GameRoom { if (userObj !== undefined && typeof userObj.group !== "undefined") { this.leaveGroup(userObj); } + + if (user.hasFollowers()) { + user.stopLeading(); + } + if (user.following) { + user.following.delFollower(user); + } + this.users.delete(user.id); this.usersByUuid.delete(user.uuid); From 5c385c520a4f8848bb69947390877b166371ba89 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sat, 18 Dec 2021 11:30:58 +0100 Subject: [PATCH 048/149] Cleanup; pretty --- back/src/Model/GameRoom.ts | 15 +++++++-------- back/src/Model/Group.ts | 2 +- back/src/Model/User.ts | 6 ++++-- back/src/Services/SocketManager.ts | 7 ++++--- back/tests/PositionNotifierTest.ts | 8 ++++---- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 9b0fef45..e7f23549 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -224,19 +224,18 @@ export class GameRoom { } else { // If the user is part of a group: // should he leave the group? - const leaveIfOutOfRadius = (user: User) => { - if (user.group === undefined) { + const users = user.group.getUsers().filter((u) => !u.hasFollowers() && !u.following); + users.forEach((foreignUser: User) => { + if (foreignUser.group === undefined) { return; } - const usrPos = user.getPosition(); - const grpPos = user.group.getPosition(); + const usrPos = foreignUser.getPosition(); + const grpPos = foreignUser.group.getPosition(); const distance = GameRoom.computeDistanceBetweenPositions(usrPos, grpPos); if (distance > this.groupRadius) { - this.leaveGroup(user); + this.leaveGroup(foreignUser); } - }; - const users = user.group.getUsers().filter((u) => !u.hasFollowers() && !u.following); - users.forEach((foreignUser) => leaveIfOutOfRadius(foreignUser)); + }); } } diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index db9f6305..08d1d21e 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -156,7 +156,7 @@ export class Group implements Movable { /** * A group can have at most one person leading the way in it. */ - get leader(): User|undefined { + get leader(): User | undefined { for (const user of this.users) { if (user.hasFollowers()) { return user; diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 4619ce2c..50f140fc 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -6,7 +6,9 @@ import { PositionNotifier } from "_Model/PositionNotifier"; import { ServerDuplexStream } from "grpc"; import { BatchMessage, - CompanionMessage, FollowAbortMessage, FollowConfirmationMessage, + CompanionMessage, + FollowAbortMessage, + FollowConfirmationMessage, PusherToBackMessage, ServerToClientMessage, SubMessage, @@ -18,7 +20,7 @@ export type UserSocket = ServerDuplexStream; public group?: Group; - private _following: User|undefined; + private _following: User | undefined; private followedBy: Set = new Set(); public constructor( diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index d7178d3d..bafce4d3 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -846,12 +846,13 @@ export class SocketManager { handleFollowConfirmationMessage(room: GameRoom, user: User, message: FollowConfirmationMessage) { const leader = room.getUserById(message.getLeader()); if (!leader) { - console.info('Could not find user "', message.getLeader(), '" while handling a follow confirmation in room "', room.roomUrl,'". Maybe the user just left.'); + const message = `Could not follow user "{message.getLeader()}" in room "{room.roomUrl}".`; + console.info(message, "Maybe the user just left."); return; } - // By security, we look at the group leader. If the group leader is NOT the leader in the message, everybody should - // stop following the group leader (to avoid having 2 group leaders) + // By security, we look at the group leader. If the group leader is NOT the leader in the message, + // everybody should stop following the group leader (to avoid having 2 group leaders) if (user?.group?.leader && user?.group?.leader !== leader) { user?.group?.leader?.stopLeading(); } diff --git a/back/tests/PositionNotifierTest.ts b/back/tests/PositionNotifierTest.ts index 955ed40f..1aaf2e13 100644 --- a/back/tests/PositionNotifierTest.ts +++ b/back/tests/PositionNotifierTest.ts @@ -26,14 +26,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: -9999, y: -9999, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); positionNotifier.addZoneListener({} as ZoneSocket, 0, 0); positionNotifier.addZoneListener({} as ZoneSocket, 0, 1); @@ -101,14 +101,14 @@ describe("PositionNotifier", () => { y: 500, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const user2 = new User(2, 'test', '10.0.0.2', { x: 0, y: 0, moving: false, direction: 'down' - }, false, [], positionNotifier, {} as UserSocket, [], null, 'foo', []); + }, false, positionNotifier, {} as UserSocket, [], null, 'foo', []); const listener = {} as ZoneSocket; positionNotifier.addZoneListener(listener, 0, 0); From 2cd088c0499b9de561ea80df0c9053b8c216d12d Mon Sep 17 00:00:00 2001 From: Lurkars Date: Sat, 18 Dec 2021 10:43:23 +0100 Subject: [PATCH 049/149] Change follow request to "F" button, use nes-css buttons --- .../Components/FollowMenu/FollowMenu.svelte | 28 ++++++------------- front/src/Phaser/Player/Player.ts | 2 +- .../src/Phaser/UserInput/UserInputManager.ts | 5 ++++ 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 590fd61c..983fc507 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -95,16 +95,19 @@ vim: ft=typescript

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

- - + +
{:else if followRole === followRoles.leader}

Ask others to follow you?

- - + +
{/if} @@ -125,8 +128,8 @@ vim: ft=typescript {/if}
- - + +
{/if} @@ -206,19 +209,6 @@ vim: ft=typescript font-weight: bold; height: 2.5em; } - - .accept { - background-color: #00ff0088; - } - .accept:hover { - background-color: #00ff00cc; - } - .deny { - background-color: #ff000088; - } - .deny:hover { - background-color: #ff0000cc; - } } } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index f34d1478..f37927e3 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -114,7 +114,7 @@ export class Player extends Character { const state = get(followStateStore); const role = get(followRoleStore); - if (activeEvents.get(UserInputEvent.Interact)) { + if (activeEvents.get(UserInputEvent.Follow)) { if (state === followStates.off && this.scene.groups.size > 0) { followStateStore.set(followStates.requesting); followRoleStore.set(followRoles.leader); diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index bb7041d2..71dbb6c3 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -16,6 +16,7 @@ export enum UserInputEvent { MoveDown, SpeedUp, Interact, + Follow, Shout, JoystickMove, } @@ -147,6 +148,10 @@ export class UserInputManager { event: UserInputEvent.Interact, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false), }, + { + event: UserInputEvent.Follow, + keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false), + }, { event: UserInputEvent.Shout, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false), From 3916d9c58e803e30ef6d4a24cc0a0c7c75c75b96 Mon Sep 17 00:00:00 2001 From: Lurkars Date: Sun, 19 Dec 2021 12:51:19 +0100 Subject: [PATCH 050/149] Add follow button to ui, improved flow --- front/src/Components/App.svelte | 4 +- .../Components/FollowMenu/FollowMenu.svelte | 40 +++++++++++++++++++ front/src/Components/images/follow.svg | 1 + front/src/Phaser/Player/Player.ts | 7 ++++ front/src/Stores/FollowStore.ts | 1 + 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 front/src/Components/images/follow.svg diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte index 5f09beef..36f815bd 100644 --- a/front/src/Components/App.svelte +++ b/front/src/Components/App.svelte @@ -42,7 +42,7 @@ import AudioManager from "./AudioManager/AudioManager.svelte"; import { showReportScreenStore, userReportEmpty } from "../Stores/ShowReportScreenStore"; import ReportMenu from "./ReportMenu/ReportMenu.svelte"; - import { followStateStore, followStates } from "../Stores/FollowStore"; + import { followStateStore, followRoleStore, followStates, followRoles } from "../Stores/FollowStore"; import FollowMenu from "./FollowMenu/FollowMenu.svelte"; export let game: Game; @@ -104,7 +104,7 @@ {/if} - {#if $followStateStore !== followStates.off} + {#if $followStateStore !== followStates.off || $followRoleStore === followRoles.open}
diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 983fc507..e26032bb 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -6,6 +6,7 @@ vim: ft=typescript import type { Unsubscriber } from "svelte/store"; import { get } from "svelte/store"; import { gameManager } from "../../Phaser/Game/GameManager"; + import followImg from "../images/follow.svg"; import { followStateStore, @@ -76,6 +77,11 @@ vim: ft=typescript followUsersStore.set([]); } + function request() { + followStateStore.set(followStates.requesting); + followRoleStore.set(followRoles.leader); + } + function onKeyDown(e: KeyboardEvent) { if (e.key === "Escape") { reset(); @@ -152,6 +158,33 @@ vim: ft=typescript {/if} +{#if followRole === followRoles.open} + +{/if} + +{#if followState === followStates.active || followState === followStates.ending} + {#if followRole === followRoles.follower} + + {:else if followUsers.length > 0} + + {/if} +{/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 @@ - {/if} - {#if $banMessageVisibleStore} + {#if $banMessageStore.length > 0}
- +
- {/if} - {#if $textMessageVisibleStore} + {:else if $textMessageStore.length > 0}
- +
{/if} {#if $soundPlayingStore} diff --git a/front/src/Components/TypeMessage/BanMessage.svelte b/front/src/Components/TypeMessage/BanMessage.svelte index faeae923..ce230067 100644 --- a/front/src/Components/TypeMessage/BanMessage.svelte +++ b/front/src/Components/TypeMessage/BanMessage.svelte @@ -1,9 +1,11 @@ -
+

*** Important message ***

-

{text}

+

{message.text}

@@ -160,7 +151,7 @@ vim: ft=typescript {/if} From d6e8bd522ff8df10f20fee88eb2cb6bb2bb2a467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 24 Dec 2021 14:07:29 +0100 Subject: [PATCH 086/149] Fixing issue if group was deleted when out of bounds. --- back/src/Model/GameRoom.ts | 1 - back/src/Model/Group.ts | 4 ++++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index ff8b7cf0..aefade43 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -312,7 +312,6 @@ export class GameRoom { } group.leave(user); if (group.isEmpty()) { - this.positionNotifier.leave(group); group.destroy(); if (!this.groups.has(group)) { throw new Error(`Could not find group ${group.getId()} referenced by user ${user.id} in World.`); diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index c35ac7a8..0782bd1b 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -141,6 +141,10 @@ export class Group implements Movable { * Usually used when there is only one user left. */ destroy(): void { + if (!this.outOfBounds) { + this.positionNotifier.leave(this); + } + for (const user of this.users) { this.leave(user); } From 87bc7f8099ac5fcf8aadfbc7e4528d2d647ae9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 24 Dec 2021 14:36:38 +0100 Subject: [PATCH 087/149] Slightly improving follow request popup design --- .../Components/FollowMenu/FollowMenu.svelte | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 035bc06f..59194db2 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -87,12 +87,9 @@ vim: ft=typescript {#if followState === followStates.requesting}
-
-

Interaction

-
{#if followRole === followRoles.follower} -
-

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

+
+

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

@@ -197,7 +197,6 @@ vim: ft=typescript color: whitesmoke; position: relative; - height: 19vh; width: 60vw; top: 60vh; margin: auto; @@ -220,17 +219,11 @@ vim: ft=typescript section.interact-menu-action { display: grid; - grid-template-columns: 50% 50%; + grid-gap: 10%; + grid-template-columns: 45% 45%; margin-bottom: 20px; - - button { - display: inline-block; - margin: 4px; - padding: 0px; - border: medium solid black; - font-weight: bold; - height: 2.5em; - } + margin-left: 5%; + margin-right: 5%; } } From 9a3bca065976c3981d7552cccf84567818005773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 24 Dec 2021 14:59:40 +0100 Subject: [PATCH 088/149] Updating Node version to latest minor in Pusher and Back for prod images --- back/Dockerfile | 6 +++--- pusher/Dockerfile | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/back/Dockerfile b/back/Dockerfile index e95145cd..b63db51c 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -1,11 +1,11 @@ # protobuf build -FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d WORKDIR /usr/src COPY messages . RUN yarn install && yarn proto # typescript build -FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder2 +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d WORKDIR /usr/src COPY back/yarn.lock back/package.json ./ RUN yarn install @@ -15,7 +15,7 @@ ENV NODE_ENV=production RUN yarn run tsc # final production image -FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d WORKDIR /usr/src COPY back/yarn.lock back/package.json ./ COPY --from=builder2 /usr/src/dist /usr/src/dist diff --git a/pusher/Dockerfile b/pusher/Dockerfile index 3ead0913..5a448ee8 100644 --- a/pusher/Dockerfile +++ b/pusher/Dockerfile @@ -1,11 +1,11 @@ # protobuf build -FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d WORKDIR /usr/src COPY messages . RUN yarn install && yarn proto # typescript build -FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 as builder2 +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d WORKDIR /usr/src COPY pusher/yarn.lock pusher/package.json ./ RUN yarn install @@ -16,7 +16,7 @@ ENV NODE_ENV=production RUN yarn run tsc # final production image -FROM node:14.15.4-buster-slim@sha256:cbae886186467bbfd274b82a234a1cdfbbd31201c2a6ee63a6893eefcf3c6e76 +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d WORKDIR /usr/src COPY pusher/yarn.lock pusher/package.json ./ COPY --from=builder2 /usr/src/dist /usr/src/dist From a4a89735f6874aa02189b6ab1330efda68c9f8b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 24 Dec 2021 15:04:01 +0100 Subject: [PATCH 089/149] Fixing missing builders --- back/Dockerfile | 4 ++-- pusher/Dockerfile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/back/Dockerfile b/back/Dockerfile index b63db51c..a8ce3c1c 100644 --- a/back/Dockerfile +++ b/back/Dockerfile @@ -1,11 +1,11 @@ # protobuf build -FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder WORKDIR /usr/src COPY messages . RUN yarn install && yarn proto # typescript build -FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2 WORKDIR /usr/src COPY back/yarn.lock back/package.json ./ RUN yarn install diff --git a/pusher/Dockerfile b/pusher/Dockerfile index 5a448ee8..cd5bfc59 100644 --- a/pusher/Dockerfile +++ b/pusher/Dockerfile @@ -1,11 +1,11 @@ # protobuf build -FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder WORKDIR /usr/src COPY messages . RUN yarn install && yarn proto # typescript build -FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d +FROM node:14.18.2-buster-slim@sha256:20bedf0c09de887379e59a41c04284974f5fb529cf0e13aab613473ce298da3d as builder2 WORKDIR /usr/src COPY pusher/yarn.lock pusher/package.json ./ RUN yarn install From 840f8626ada4c611ca76285d8128a9bb8aa8d3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 24 Dec 2021 15:39:28 +0100 Subject: [PATCH 090/149] Refactoring code to use Svelte native "$" store unpacking. --- .../Components/FollowMenu/FollowMenu.svelte | 74 +++++-------------- front/src/Stores/FollowStore.ts | 1 - 2 files changed, 20 insertions(+), 55 deletions(-) diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 59194db2..6506b1d6 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -18,40 +18,6 @@ vim: ft=typescript const gameScene = gameManager.getCurrentGameScene(); - let followState: string; - let followRole: string; - let followUsers: number[]; - let stateUnsubscriber: Unsubscriber; - let roleUnsubscriber: Unsubscriber; - let nameUnsubscriber: Unsubscriber; - - onMount(() => { - followState = get(followStateStore); - followRole = get(followRoleStore); - followUsers = get(followUsersStore); - stateUnsubscriber = followStateStore.subscribe((state) => { - followState = state; - }); - roleUnsubscriber = followRoleStore.subscribe((role) => { - followRole = role; - }); - nameUnsubscriber = followUsersStore.subscribe((users) => { - followUsers = users; - }); - }); - - onDestroy(() => { - if (stateUnsubscriber) { - stateUnsubscriber(); - } - if (roleUnsubscriber) { - roleUnsubscriber(); - } - if (nameUnsubscriber) { - nameUnsubscriber(); - } - }); - function name(userId: number): string | undefined { return gameScene.MapPlayersByKey.get(userId)?.PlayerValue; } @@ -85,11 +51,11 @@ vim: ft=typescript -{#if followState === followStates.requesting} +{#if $followStateStore === followStates.requesting}
- {#if followRole === followRoles.follower} + {#if $followRoleStore === followRoles.follower}
-

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

+

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

- {:else if followRole === followRoles.leader} + {:else if $followRoleStore === followRoles.leader}

Should never be displayed

@@ -105,16 +71,16 @@ vim: ft=typescript
{/if} -{#if followState === followStates.ending} +{#if $followStateStore === followStates.ending}

Interaction

- {#if followRole === followRoles.follower} + {#if $followRoleStore === followRoles.follower}
-

Do you want to stop following {name(followUsers[0])}?

+

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

- {:else if followRole === followRoles.leader} + {:else if $followRoleStore === followRoles.leader}

Do you want to stop leading the way?

@@ -126,20 +92,20 @@ vim: ft=typescript
{/if} -{#if followState === followStates.active || followState === followStates.ending} +{#if $followStateStore === followStates.active || $followStateStore === followStates.ending}
- {#if followRole === followRoles.follower} -

Following {name(followUsers[0])}

- {:else if followUsers.length === 0} + {#if $followRoleStore === followRoles.follower} +

Following {name($followUsersStore[0])}

+ {:else if $followUsersStore.length === 0}

Waiting for followers' confirmation

- {:else if followUsers.length === 1} -

{name(followUsers[0])} is following you

- {:else if followUsers.length === 2} -

{name(followUsers[0])} and {name(followUsers[1])} are following you

+ {:else if $followUsersStore.length === 1} +

{name($followUsersStore[0])} is following you

+ {:else if $followUsersStore.length === 2} +

{name($followUsersStore[0])} and {name($followUsersStore[1])} are following you

{:else}

- {followUsers.slice(0, -1).map(name).join(", ")} and {name(followUsers[followUsers.length - 1])} are following + {$followUsersStore.slice(0, -1).map(name).join(", ")} and {name($followUsersStore[$followUsersStore.length - 1])} are following you

{/if} @@ -147,7 +113,7 @@ vim: ft=typescript
{/if} -{#if followState === followStates.off} +{#if $followStateStore === followStates.off}
{/if} - {#if $followStateStore !== followStates.off || $peerStore.size > 0} + {#if $followStateStore !== "off" || $peerStore.size > 0}
diff --git a/front/src/Components/FollowMenu/FollowMenu.svelte b/front/src/Components/FollowMenu/FollowMenu.svelte index 6506b1d6..264b27ed 100644 --- a/front/src/Components/FollowMenu/FollowMenu.svelte +++ b/front/src/Components/FollowMenu/FollowMenu.svelte @@ -2,19 +2,10 @@ vim: ft=typescript --> @@ -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 113/149] 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 114/149] 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 115/149] 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 116/149] 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 117/149] 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 118/149] 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 119/149] 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 120/149] 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 121/149] 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 122/149] 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 123/149] 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 124/149] 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 125/149] 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 126/149] 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 127/149] 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 128/149] 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 129/149] 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 130/149] 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 131/149] 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 132/149] 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 133/149] 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 134/149] 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 135/149] 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 136/149] 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 @@ -
+