Merge branch 'develop' of github.com:thecodingmachine/workadventure

This commit is contained in:
_Bastler 2022-01-07 09:59:00 +01:00
commit 5cc573ac49
41 changed files with 538 additions and 278 deletions

View File

@ -1,15 +1,15 @@
// lib/server.ts // lib/server.ts
import App from "./src/App"; import App from "./src/App";
import grpc from "grpc"; import grpc from "grpc";
import {roomManager} from "./src/RoomManager"; import { roomManager } from "./src/RoomManager";
import {IRoomManagerServer, RoomManagerService} from "./src/Messages/generated/messages_grpc_pb"; import { IRoomManagerServer, RoomManagerService } from "./src/Messages/generated/messages_grpc_pb";
import {HTTP_PORT, GRPC_PORT} from "./src/Enum/EnvironmentVariable"; import { HTTP_PORT, GRPC_PORT } from "./src/Enum/EnvironmentVariable";
App.listen(HTTP_PORT, () => console.log(`WorkAdventure HTTP API starting on port %d!`, HTTP_PORT)) App.listen(HTTP_PORT, () => console.log(`WorkAdventure HTTP API starting on port %d!`, HTTP_PORT));
const server = new grpc.Server(); const server = new grpc.Server();
server.addService<IRoomManagerServer>(RoomManagerService, roomManager); server.addService<IRoomManagerServer>(RoomManagerService, roomManager);
server.bind(`0.0.0.0:${GRPC_PORT}`, grpc.ServerCredentials.createInsecure()); server.bind(`0.0.0.0:${GRPC_PORT}`, grpc.ServerCredentials.createInsecure());
server.start(); server.start();
console.log('WorkAdventure HTTP/2 API starting on port %d!', GRPC_PORT); console.log("WorkAdventure HTTP/2 API starting on port %d!", GRPC_PORT);

View File

@ -73,7 +73,7 @@ services:
DEBUG: "socket:*" DEBUG: "socket:*"
STARTUP_COMMAND_1: yarn install STARTUP_COMMAND_1: yarn install
# wait for files generated by "messages" container to exists # wait for files generated by "messages" container to exists
STARTUP_COMMAND_2: while [ ! -f /usr/src/app/src/Messages/generated/messages_pb.js ]; do sleep 1; done STARTUP_COMMAND_2: sleep 5; while [ ! -f /usr/src/app/src/Messages/generated/messages_pb.js ]; do sleep 1; done
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY" SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
SECRET_KEY: yourSecretKey SECRET_KEY: yourSecretKey
ADMIN_API_TOKEN: "$ADMIN_API_TOKEN" ADMIN_API_TOKEN: "$ADMIN_API_TOKEN"
@ -132,7 +132,7 @@ services:
DEBUG: "*" DEBUG: "*"
STARTUP_COMMAND_1: yarn install STARTUP_COMMAND_1: yarn install
# wait for files generated by "messages" container to exists # wait for files generated by "messages" container to exists
STARTUP_COMMAND_2: while [ ! -f /usr/src/app/src/Messages/generated/messages_pb.js ]; do sleep 1; done STARTUP_COMMAND_2: sleep 5; while [ ! -f /usr/src/app/src/Messages/generated/messages_pb.js ]; do sleep 1; done
SECRET_KEY: yourSecretKey SECRET_KEY: yourSecretKey
SECRET_JITSI_KEY: "$SECRET_JITSI_KEY" SECRET_JITSI_KEY: "$SECRET_JITSI_KEY"
ALLOW_ARTILLERY: "true" ALLOW_ARTILLERY: "true"

View File

@ -35,7 +35,6 @@ module.exports = {
"no-unused-vars": "off", "no-unused-vars": "off",
"@typescript-eslint/no-explicit-any": "error", "@typescript-eslint/no-explicit-any": "error",
// TODO: remove those ignored rules and write a stronger code! // TODO: remove those ignored rules and write a stronger code!
"@typescript-eslint/no-floating-promises": "off",
"@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-call": "off",
"@typescript-eslint/restrict-plus-operands": "off", "@typescript-eslint/restrict-plus-operands": "off",
"@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-assignment": "off",

View File

@ -18,64 +18,84 @@ class AnalyticsClient {
} }
identifyUser(uuid: string, email: string | null) { identifyUser(uuid: string, email: string | null) {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.identify(uuid, { uuid, email, wa: true }); ?.then((posthog) => {
}); posthog.identify(uuid, { uuid, email, wa: true });
})
.catch((e) => console.error(e));
} }
loggedWithSso() { loggedWithSso() {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-logged-sso"); ?.then((posthog) => {
}); posthog.capture("wa-logged-sso");
})
.catch((e) => console.error(e));
} }
loggedWithToken() { loggedWithToken() {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-logged-token"); ?.then((posthog) => {
}); posthog.capture("wa-logged-token");
})
.catch((e) => console.error(e));
} }
enteredRoom(roomId: string, roomGroup: string | null) { enteredRoom(roomId: string, roomGroup: string | null) {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("$pageView", { roomId, roomGroup }); ?.then((posthog) => {
posthog.capture("enteredRoom"); posthog.capture("$pageView", { roomId, roomGroup });
}); posthog.capture("enteredRoom");
})
.catch((e) => console.error(e));
} }
openedMenu() { openedMenu() {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-opened-menu"); ?.then((posthog) => {
}); posthog.capture("wa-opened-menu");
})
.catch((e) => console.error(e));
} }
launchEmote(emote: string) { launchEmote(emote: string) {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-emote-launch", { emote }); ?.then((posthog) => {
}); posthog.capture("wa-emote-launch", { emote });
})
.catch((e) => console.error(e));
} }
enteredJitsi(roomName: string, roomId: string) { enteredJitsi(roomName: string, roomId: string) {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-entered-jitsi", { roomName, roomId }); ?.then((posthog) => {
}); posthog.capture("wa-entered-jitsi", { roomName, roomId });
})
.catch((e) => console.error(e));
} }
validationName() { validationName() {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-name-validation"); ?.then((posthog) => {
}); posthog.capture("wa-name-validation");
})
.catch((e) => console.error(e));
} }
validationWoka(scene: string) { validationWoka(scene: string) {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-woka-validation", { scene }); ?.then((posthog) => {
}); posthog.capture("wa-woka-validation", { scene });
})
.catch((e) => console.error(e));
} }
validationVideo() { validationVideo() {
this.posthogPromise?.then((posthog) => { this.posthogPromise
posthog.capture("wa-video-validation"); ?.then((posthog) => {
}); posthog.capture("wa-video-validation");
})
.catch((e) => console.error(e));
} }
} }
export const analyticsClient = new AnalyticsClient(); export const analyticsClient = new AnalyticsClient();

View File

@ -26,7 +26,7 @@ export class ActionMessage {
this.message = actionMessageOptions.message; this.message = actionMessageOptions.message;
this.type = actionMessageOptions.type ?? "message"; this.type = actionMessageOptions.type ?? "message";
this.callback = actionMessageOptions.callback; this.callback = actionMessageOptions.callback;
this.create(); this.create().catch((e) => console.error(e));
} }
private async create() { private async create() {

View File

@ -95,7 +95,7 @@ export function createState(target: "global" | "player"): WorkadventureStateComm
set(target: WorkadventureStateCommands, p: PropertyKey, value: unknown, receiver: unknown): boolean { 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. // 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. // 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; return true;
}, },
has(target: WorkadventureStateCommands, p: PropertyKey): boolean { has(target: WorkadventureStateCommands, p: PropertyKey): boolean {

View File

@ -23,6 +23,9 @@
import { chatVisibilityStore } from "../Stores/ChatStore"; import { chatVisibilityStore } from "../Stores/ChatStore";
import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore"; import { helpCameraSettingsVisibleStore } from "../Stores/HelpCameraSettingsStore";
import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte"; import HelpCameraSettingsPopup from "./HelpCameraSettings/HelpCameraSettingsPopup.svelte";
import { showLimitRoomModalStore, showShareLinkMapModalStore } from "../Stores/ModalStore";
import LimitRoomModal from "./Modal/LimitRoomModal.svelte";
import ShareLinkMapModal from "./Modal/ShareLinkMapModal.svelte";
import AudioPlaying from "./UI/AudioPlaying.svelte"; import AudioPlaying from "./UI/AudioPlaying.svelte";
import { soundPlayingStore } from "../Stores/SoundPlayingStore"; import { soundPlayingStore } from "../Stores/SoundPlayingStore";
import ErrorDialog from "./UI/ErrorDialog.svelte"; import ErrorDialog from "./UI/ErrorDialog.svelte";
@ -136,6 +139,16 @@
<HelpCameraSettingsPopup /> <HelpCameraSettingsPopup />
</div> </div>
{/if} {/if}
{#if $showLimitRoomModalStore}
<div>
<LimitRoomModal />
</div>
{/if}
{#if $showShareLinkMapModalStore}
<div>
<ShareLinkMapModal />
</div>
{/if}
{#if $requestVisitCardsStore} {#if $requestVisitCardsStore}
<VisitCard visitCardUrl={$requestVisitCardsStore} /> <VisitCard visitCardUrl={$requestVisitCardsStore} />
{/if} {/if}

View File

@ -25,7 +25,7 @@
HTMLAudioPlayer.loop = get(audioManagerVolumeStore).loop; HTMLAudioPlayer.loop = get(audioManagerVolumeStore).loop;
HTMLAudioPlayer.volume = get(audioManagerVolumeStore).volume; HTMLAudioPlayer.volume = get(audioManagerVolumeStore).volume;
HTMLAudioPlayer.muted = get(audioManagerVolumeStore).muted; HTMLAudioPlayer.muted = get(audioManagerVolumeStore).muted;
HTMLAudioPlayer.play(); void HTMLAudioPlayer.play();
}); });
unsubscriberVolumeStore = audioManagerVolumeStore.subscribe((audioManager: audioManagerVolume) => { unsubscriberVolumeStore = audioManagerVolumeStore.subscribe((audioManager: audioManagerVolume) => {
const reduceVolume = audioManager.talking && audioManager.decreaseWhileTalking; const reduceVolume = audioManager.talking && audioManager.decreaseWhileTalking;

View File

@ -19,12 +19,12 @@
uploadAudioActive = true; uploadAudioActive = true;
} }
function send() { async function send(): Promise<void> {
if (inputSendTextActive) { if (inputSendTextActive) {
handleSendText.sendTextMessage(broadcastToWorld); return handleSendText.sendTextMessage(broadcastToWorld);
} }
if (uploadAudioActive) { if (uploadAudioActive) {
handleSendAudio.sendAudioMessage(broadcastToWorld); return handleSendAudio.sendAudioMessage(broadcastToWorld);
} }
} }
</script> </script>

View File

@ -21,12 +21,12 @@
<div class="guest-main"> <div class="guest-main">
<section class="container-overflow"> <section class="container-overflow">
<section class="share-url not-mobile"> <section class="share-url not-mobile">
<h3>Share the link of the room !</h3> <h3>Share the link of the room!</h3>
<input type="text" readonly id="input-share-link" value={location.toString()} /> <input type="text" readonly id="input-share-link" value={location.toString()} />
<button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button> <button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button>
</section> </section>
<section class="is-mobile"> <section class="is-mobile">
<h3>Share the link of the room !</h3> <h3>Share the link of the room!</h3>
<input type="hidden" readonly id="input-share-link" value={location.toString()} /> <input type="hidden" readonly id="input-share-link" value={location.toString()} />
<button type="button" class="nes-btn is-primary" on:click={shareLink}>Share</button> <button type="button" class="nes-btn is-primary" on:click={shareLink}>Share</button>
</section> </section>

View File

@ -1,9 +1,14 @@
<script lang="typescript"> <script lang="typescript">
import logoWA from "../images/menu.svg" import logoWA from "../images/menu.svg"
import logoTalk from "../images/chat.svg" import logoTalk from "../images/chat.svg"
import logoInvite from "../images/logo-invite-pixel.png";
import logoRegister from "../images/logo-register-pixel.png";
import { menuVisiblilityStore } from "../../Stores/MenuStore"; import { menuVisiblilityStore } from "../../Stores/MenuStore";
import { chatVisibilityStore } from "../../Stores/ChatStore"; import { chatVisibilityStore } from "../../Stores/ChatStore";
import { limitMapStore } from "../../Stores/GameStore";
import { get } from "svelte/store"; import { get } from "svelte/store";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
import { showShareLinkMapModalStore } from "../../Stores/ModalStore";
function showMenu() { function showMenu() {
menuVisiblilityStore.set(!get(menuVisiblilityStore)); menuVisiblilityStore.set(!get(menuVisiblilityStore));
@ -11,17 +16,33 @@
function showChat() { function showChat() {
chatVisibilityStore.set(true); chatVisibilityStore.set(true);
} }
function register() {
window.open(`${ADMIN_URL}/second-step-register`, "_self");
}
function showInvite() {
showShareLinkMapModalStore.set(true);
}
</script> </script>
<svelte:window /> <svelte:window />
<main class="menuIcon"> <main class="menuIcon">
<span class="nes-btn is-dark"> {#if $limitMapStore}
<img src={logoWA} alt="open menu" on:click|preventDefault={showMenu} /> <span class="nes-btn is-dark">
</span> <img src={logoInvite} alt="open menu" on:click|preventDefault={showInvite} />
<span class="nes-btn is-dark"> </span>
<img src={logoTalk} alt="open menu" on:click|preventDefault={showChat} /> <span class="nes-btn is-dark">
</span> <img src={logoRegister} alt="open menu" on:click|preventDefault={register} />
</span>
{:else}
<span class="nes-btn is-dark">
<img src={logoWA} alt="open menu" on:click|preventDefault={showMenu} />
</span>
<span class="nes-btn is-dark">
<img src={logoTalk} alt="open menu" on:click|preventDefault={showChat} />
</span>
{/if}
</main> </main>
<style lang="scss"> <style lang="scss">

View File

@ -41,10 +41,10 @@
gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene());
} }
function logOut() { async function logOut() {
disableMenuStores(); disableMenuStores();
loginSceneVisibleStore.set(true); loginSceneVisibleStore.set(true);
connectionManager.logout(); return connectionManager.logout();
} }
function getProfileUrl() { function getProfileUrl() {

View File

@ -33,9 +33,9 @@
const body = HtmlUtils.querySelectorOrFail("body"); const body = HtmlUtils.querySelectorOrFail("body");
if (body) { if (body) {
if (document.fullscreenElement !== null && !fullscreen) { if (document.fullscreenElement !== null && !fullscreen) {
document.exitFullscreen(); document.exitFullscreen().catch((e) => console.error(e));
} else { } else {
body.requestFullscreen(); body.requestFullscreen().catch((e) => console.error(e));
} }
localUserStore.setFullscreen(fullscreen); localUserStore.setFullscreen(fullscreen);
} }
@ -45,14 +45,16 @@
if (Notification.permission === "granted") { if (Notification.permission === "granted") {
localUserStore.setNotification(notification ? "granted" : "denied"); localUserStore.setNotification(notification ? "granted" : "denied");
} else { } else {
Notification.requestPermission().then((response) => { Notification.requestPermission()
if (response === "granted") { .then((response) => {
localUserStore.setNotification(notification ? "granted" : "denied"); if (response === "granted") {
} else { localUserStore.setNotification(notification ? "granted" : "denied");
localUserStore.setNotification("denied"); } else {
notification = false; localUserStore.setNotification("denied");
} notification = false;
}); }
})
.catch((e) => console.error(e));
} }
} }

View File

@ -0,0 +1,47 @@
<script lang="typescript">
import { fly } from "svelte/transition";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
function register() {
window.open(`${ADMIN_URL}/second-step-register`, "_self");
}
</script>
<div class="limit-map nes-container" transition:fly={{ y: -900, duration: 500 }}>
<section>
<h2>Limit of your room</h2>
<p>Register your account!</p>
<p>
This map is limited in the time and to continue to use WorkAdventure, you must register your account in our
back office.
</p>
</section>
<section>
<button class="nes-btn is-primary" on:click|preventDefault={register}>Register</button>
</section>
</div>
<style lang="scss">
.limit-map {
pointer-events: auto;
background: #eceeee;
margin-left: auto;
margin-right: auto;
margin-top: 10vh;
max-height: 80vh;
max-width: 80vw;
overflow: auto;
text-align: center;
h2 {
font-family: "Press Start 2P";
}
section {
p {
margin: 15px;
font-family: "Press Start 2P";
}
}
}
</style>

View File

@ -0,0 +1,90 @@
<script lang="typescript">
import { fly } from "svelte/transition";
import { showShareLinkMapModalStore } from "../../Stores/ModalStore";
interface ExtNavigator extends Navigator {
canShare?(data?: ShareData): Promise<boolean>;
}
const myNavigator: ExtNavigator = window.navigator;
const haveNavigatorSharingFeature: boolean =
myNavigator && myNavigator.canShare != null && myNavigator.share != null;
let copied: boolean = false;
function copyLink() {
try {
const input: HTMLInputElement = document.getElementById("input-share-link") as HTMLInputElement;
input.focus();
input.select();
document.execCommand("copy");
copied = true;
} catch (e) {
console.error(e);
copied = false;
}
}
async function shareLink() {
const shareData = { url: location.toString() };
try {
await myNavigator.share(shareData);
} catch (err) {
console.error("Error: " + err);
copyLink();
}
}
function close() {
showShareLinkMapModalStore.set(false);
copied = false;
}
</script>
<div class="share-link-map nes-container" transition:fly={{ y: -900, duration: 500 }}>
<section>
<h2>Invite your friends or colleagues</h2>
<p>Share the link of the room!</p>
</section>
<section>
{#if haveNavigatorSharingFeature}
<input type="hidden" readonly id="input-share-link" value={location.toString()} />
<button type="button" class="nes-btn is-primary" on:click={shareLink}>Share</button>
{:else}
<input type="text" readonly id="input-share-link" value={location.toString()} />
<button type="button" class="nes-btn is-primary" on:click={copyLink}>Copy</button>
{/if}
{#if copied}
<p>Copied!</p>
{/if}
</section>
<section>
<button class="nes-btn" on:click|preventDefault={close}>Close</button>
</section>
</div>
<style lang="scss">
div.share-link-map {
pointer-events: auto;
background: #eceeee;
margin-left: auto;
margin-right: auto;
margin-top: 10vh;
max-height: 80vh;
max-width: 80vw;
overflow: auto;
text-align: center;
h2 {
font-family: "Press Start 2P";
}
section {
p {
margin: 15px;
font-family: "Press Start 2P";
}
}
}
</style>

View File

@ -6,12 +6,11 @@
import type { Unsubscriber } from "svelte/store"; import type { Unsubscriber } from "svelte/store";
import { playersStore } from "../../Stores/PlayersStore"; import { playersStore } from "../../Stores/PlayersStore";
import { connectionManager } from "../../Connexion/ConnectionManager"; import { connectionManager } from "../../Connexion/ConnectionManager";
import { GameConnexionTypes } from "../../Url/UrlManager";
import { get } from "svelte/store"; import { get } from "svelte/store";
let blockActive = true; let blockActive = true;
let reportActive = !blockActive; let reportActive = !blockActive;
let anonymous: boolean = false; let disableReport: boolean = false;
let userUUID: string | undefined = playersStore.getPlayerById(get(showReportScreenStore).userId)?.userUuid; let userUUID: string | undefined = playersStore.getPlayerById(get(showReportScreenStore).userId)?.userUuid;
let userName = "No name"; let userName = "No name";
let unsubscriber: Unsubscriber; let unsubscriber: Unsubscriber;
@ -26,7 +25,7 @@
} }
} }
}); });
anonymous = connectionManager.getConnexionType === GameConnexionTypes.anonymous; disableReport = !connectionManager.currentRoom?.canReport ?? true;
}); });
onDestroy(() => { onDestroy(() => {
@ -65,7 +64,7 @@
<button type="button" class="nes-btn" on:click|preventDefault={close}>X</button> <button type="button" class="nes-btn" on:click|preventDefault={close}>X</button>
</section> </section>
</section> </section>
<section class="report-menu-action {anonymous ? 'hidden' : ''}"> <section class="report-menu-action {disableReport ? 'hidden' : ''}">
<section class="justify-center"> <section class="justify-center">
<button <button
type="button" type="button"

View File

@ -12,7 +12,7 @@
} }
afterUpdate(() => { afterUpdate(() => {
audio.play(); audio.play().catch((e) => console.error(e));
}); });
</script> </script>

View File

@ -1,20 +1,26 @@
<script lang="typescript"> <script lang="typescript">
import { fly } from "svelte/transition"; import { fly } from "svelte/transition";
import { userIsAdminStore } from "../../Stores/GameStore"; import { userIsAdminStore, limitMapStore } from "../../Stores/GameStore";
import { ADMIN_URL } from "../../Enum/EnvironmentVariable"; import { ADMIN_URL } from "../../Enum/EnvironmentVariable";
const upgradeLink = ADMIN_URL + "/pricing"; const upgradeLink = ADMIN_URL + "/pricing";
const registerLink = ADMIN_URL + "/second-step-register";
</script> </script>
<main class="warningMain" transition:fly={{ y: -200, duration: 500 }}> <main class="warningMain" transition:fly={{ y: -200, duration: 500 }}>
<h2>Warning!</h2>
{#if $userIsAdminStore} {#if $userIsAdminStore}
<h2>Warning!</h2>
<p> <p>
This world is close to its limit!. You can upgrade its capacity <a href={upgradeLink} target="_blank" This world is close to its limit!. You can upgrade its capacity <a href={upgradeLink} target="_blank"
>here</a >here</a
> >
</p> </p>
{:else if $limitMapStore}
<p>
This map is available for 2 days. You can register your domain <a href={registerLink}>here</a>!
</p>
{:else} {:else}
<h2>Warning!</h2>
<p>This world is close to its limit!</p> <p>This world is close to its limit!</p>
{/if} {/if}
</main> </main>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 977 B

View File

@ -8,13 +8,15 @@ import { CharacterTexture, LocalUser } from "./LocalUser";
import { Room } from "./Room"; import { Room } from "./Room";
import { _ServiceWorker } from "../Network/ServiceWorker"; import { _ServiceWorker } from "../Network/ServiceWorker";
import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore"; import { loginSceneVisibleIframeStore } from "../Stores/LoginSceneStore";
import { userIsConnected } from "../Stores/MenuStore"; import { userIsConnected, warningContainerStore } from "../Stores/MenuStore";
import { analyticsClient } from "../Administration/AnalyticsClient"; import { analyticsClient } from "../Administration/AnalyticsClient";
import { gameManager } from "../Phaser/Game/GameManager"; import { gameManager } from "../Phaser/Game/GameManager";
import { axiosWithRetry } from "./AxiosUtils"; import { axiosWithRetry } from "./AxiosUtils";
import axios from "axios"; import axios from "axios";
import { isRegisterData } from "../Messages/JsonMessages/RegisterData"; import { isRegisterData } from "../Messages/JsonMessages/RegisterData";
import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData"; import { isAdminApiData } from "../Messages/JsonMessages/AdminApiData";
import { limitMapStore } from "../Stores/GameStore";
import { showLimitRoomModalStore } from "../Stores/ModalStore";
class ConnectionManager { class ConnectionManager {
private localUser!: LocalUser; private localUser!: LocalUser;
@ -153,11 +155,7 @@ class ConnectionManager {
) )
); );
urlManager.pushRoomIdToUrl(this._currentRoom); urlManager.pushRoomIdToUrl(this._currentRoom);
} else if ( } else if (connexionType === GameConnexionTypes.room || connexionType === GameConnexionTypes.empty) {
connexionType === GameConnexionTypes.organization ||
connexionType === GameConnexionTypes.anonymous ||
connexionType === GameConnexionTypes.empty
) {
this.authToken = localUserStore.getAuthToken(); this.authToken = localUserStore.getAuthToken();
let roomPath: string; let roomPath: string;
@ -189,7 +187,7 @@ class ConnectionManager {
//Set last room visited! (connected or nor, must to be saved in localstorage and cache API) //Set last room visited! (connected or nor, must to be saved in localstorage and cache API)
//use href to keep # value //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. //todo: add here some kind of warning if authToken has expired.
if (!this.authToken && !this._currentRoom.authenticationMandatory) { if (!this.authToken && !this._currentRoom.authenticationMandatory) {
@ -238,6 +236,17 @@ class ConnectionManager {
analyticsClient.identifyUser(this.localUser.uuid, this.localUser.email); analyticsClient.identifyUser(this.localUser.uuid, this.localUser.email);
} }
//if limit room active test headband
if (this._currentRoom.expireOn !== undefined) {
warningContainerStore.activateWarningContainer();
limitMapStore.set(true);
//check time of map
if (new Date() > this._currentRoom.expireOn) {
showLimitRoomModalStore.set(true);
}
}
this.serviceWorker = new _ServiceWorker(); this.serviceWorker = new _ServiceWorker();
return Promise.resolve(this._currentRoom); return Promise.resolve(this._currentRoom);
} }
@ -302,7 +311,7 @@ class ConnectionManager {
this.reconnectingTimeout = setTimeout(() => { this.reconnectingTimeout = setTimeout(() => {
//todo: allow a way to break recursion? //todo: allow a way to break recursion?
//todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely. //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) (connection) => resolve(connection)
); );
}, 4000 + Math.floor(Math.random() * 2000)); }, 4000 + Math.floor(Math.random() * 2000));

View File

@ -136,13 +136,12 @@ class LocalUserStore {
return localStorage.getItem(ignoreFollowRequests) === "true"; return localStorage.getItem(ignoreFollowRequests) === "true";
} }
setLastRoomUrl(roomUrl: string): void { async setLastRoomUrl(roomUrl: string): Promise<void> {
localStorage.setItem(lastRoomUrl, roomUrl.toString()); localStorage.setItem(lastRoomUrl, roomUrl.toString());
if ("caches" in window) { if ("caches" in window) {
caches.open(cacheAPIIndex).then((cache) => { const cache = await caches.open(cacheAPIIndex);
const stringResponse = new Response(JSON.stringify({ roomUrl })); const stringResponse = new Response(JSON.stringify({ roomUrl }));
cache.put(`/${lastRoomUrl}`, stringResponse); await cache.put(`/${lastRoomUrl}`, stringResponse);
});
} }
} }
getLastRoomUrl(): string { getLastRoomUrl(): string {

View File

@ -18,7 +18,10 @@ export interface RoomRedirect {
export class Room { export class Room {
public readonly id: string; public readonly id: string;
public readonly isPublic: boolean; /**
* @deprecated
*/
private readonly isPublic: boolean;
private _authenticationMandatory: boolean = DISABLE_ANONYMOUS; private _authenticationMandatory: boolean = DISABLE_ANONYMOUS;
private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER; private _iframeAuthentication?: string = OPID_LOGIN_SCREEN_PROVIDER;
private _mapUrl: string | undefined; private _mapUrl: string | undefined;
@ -27,6 +30,8 @@ export class Room {
private readonly _search: URLSearchParams; private readonly _search: URLSearchParams;
private _contactPage: string | undefined; private _contactPage: string | undefined;
private _group: string | null = null; private _group: string | null = null;
private _expireOn: Date | undefined;
private _canReport: boolean = false;
private constructor(private roomUrl: URL) { private constructor(private roomUrl: URL) {
this.id = roomUrl.pathname; this.id = roomUrl.pathname;
@ -34,7 +39,7 @@ export class Room {
if (this.id.startsWith("/")) { if (this.id.startsWith("/")) {
this.id = this.id.substr(1); this.id = this.id.substr(1);
} }
if (this.id.startsWith("_/")) { if (this.id.startsWith("_/") || this.id.startsWith("*/")) {
this.isPublic = true; this.isPublic = true;
} else if (this.id.startsWith("@/")) { } else if (this.id.startsWith("@/")) {
this.isPublic = false; this.isPublic = false;
@ -121,6 +126,10 @@ export class Room {
data.authenticationMandatory != null ? data.authenticationMandatory : DISABLE_ANONYMOUS; data.authenticationMandatory != null ? data.authenticationMandatory : DISABLE_ANONYMOUS;
this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER; this._iframeAuthentication = data.iframeAuthentication || OPID_LOGIN_SCREEN_PROVIDER;
this._contactPage = data.contactPage || CONTACT_URL; this._contactPage = data.contactPage || CONTACT_URL;
if (data.expireOn) {
this._expireOn = new Date(data.expireOn);
}
this._canReport = data.canReport ?? false;
return new MapDetail(data.mapUrl, data.textures); return new MapDetail(data.mapUrl, data.textures);
} else { } 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.");
@ -143,6 +152,8 @@ export class Room {
* Instance name is: * Instance name is:
* - In a public URL: the second part of the URL ( _/[instance]/map.json) * - In a public URL: the second part of the URL ( _/[instance]/map.json)
* - In a private URL: [organizationId/worldId] * - In a private URL: [organizationId/worldId]
*
* @deprecated
*/ */
public getInstance(): string { public getInstance(): string {
if (this.instance !== undefined) { if (this.instance !== undefined) {
@ -150,7 +161,7 @@ export class Room {
} }
if (this.isPublic) { 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 + '"'); if (!match) throw new Error('Could not extract instance from "' + this.id + '"');
this.instance = match[1]; this.instance = match[1];
return this.instance; return this.instance;
@ -223,4 +234,12 @@ export class Room {
get group(): string | null { get group(): string | null {
return this._group; return this._group;
} }
get expireOn(): Date | undefined {
return this._expireOn;
}
get canReport(): boolean {
return this._canReport;
}
} }

View File

@ -352,7 +352,7 @@ export class RoomConnection implements RoomConnection {
break; break;
} }
case "tokenExpiredMessage": { 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 this.closed = true; //technically, this isn't needed since loadOpenIDScreen() will do window.location.assign() but I prefer to leave it for consistency
break; break;
} }

View File

@ -41,13 +41,15 @@ export class Companion extends Container {
this.companionName = name; this.companionName = name;
this._pictureStore = writable(undefined); this._pictureStore = writable(undefined);
texturePromise.then((resource) => { texturePromise
this.addResource(resource); .then((resource) => {
this.invisible = false; this.addResource(resource);
return this.getSnapshot().then((htmlImageElementSrc) => { this.invisible = false;
this._pictureStore.set(htmlImageElementSrc); return this.getSnapshot().then((htmlImageElementSrc) => {
}); this._pictureStore.set(htmlImageElementSrc);
}); });
})
.catch((e) => console.error(e));
this.scene.physics.world.enableBody(this); this.scene.physics.world.enableBody(this);

View File

@ -3,7 +3,7 @@ import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./Co
export const getAllCompanionResources = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => { export const getAllCompanionResources = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => {
COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => { COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => {
lazyLoadCompanionResource(loader, resource.name); lazyLoadCompanionResource(loader, resource.name).catch((e) => console.error(e));
}); });
return COMPANION_RESOURCES; return COMPANION_RESOURCES;

View File

@ -72,9 +72,11 @@ export class Loader {
if (this.loadingText) { if (this.loadingText) {
this.loadingText.destroy(); this.loadingText.destroy();
} }
promiseLoadLogoTexture.then((resLoadingImage: Phaser.GameObjects.Image) => { promiseLoadLogoTexture
resLoadingImage.destroy(); .then((resLoadingImage: Phaser.GameObjects.Image) => {
}); resLoadingImage.destroy();
})
.catch((e) => console.error(e));
this.progress.destroy(); this.progress.destroy();
this.progressContainer.destroy(); this.progressContainer.destroy();
if (this.scene instanceof DirtyScene) { if (this.scene instanceof DirtyScene) {

View File

@ -127,7 +127,7 @@ export class GameMapPropertiesListener {
.then((coWebsite) => { .then((coWebsite) => {
const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer); const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer);
if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) { if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) {
coWebsiteManager.closeCoWebsite(coWebsite); coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => console.error(e));
this.coWebsitesOpenByLayer.delete(layer); this.coWebsitesOpenByLayer.delete(layer);
this.coWebsitesActionTriggerByLayer.delete(layer); this.coWebsitesActionTriggerByLayer.delete(layer);
} else { } else {
@ -136,7 +136,8 @@ export class GameMapPropertiesListener {
state: OpenCoWebsiteState.OPENED, state: OpenCoWebsiteState.OPENED,
}); });
} }
}); })
.catch((e) => console.error(e));
layoutManagerActionStore.removeAction(actionUuid); layoutManagerActionStore.removeAction(actionUuid);
}; };
@ -236,7 +237,7 @@ export class GameMapPropertiesListener {
} }
if (coWebsiteOpen.coWebsite !== undefined) { if (coWebsiteOpen.coWebsite !== undefined) {
coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite); coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite).catch((e) => console.error(e));
} }
this.coWebsitesOpenByLayer.delete(layer); this.coWebsitesOpenByLayer.delete(layer);

View File

@ -243,7 +243,7 @@ export class GameScene extends DirtyScene {
const textures = localUser?.textures; const textures = localUser?.textures;
if (textures) { if (textures) {
for (const texture of textures) { for (const texture of textures) {
loadCustomTexture(this.load, texture); loadCustomTexture(this.load, texture).catch((e) => console.error(e));
} }
} }
@ -270,7 +270,7 @@ export class GameScene extends DirtyScene {
this.load.on( this.load.on(
"filecomplete-tilemapJSON-" + this.MapUrlFile, "filecomplete-tilemapJSON-" + this.MapUrlFile,
(key: string, type: string, data: unknown) => { (key: string, type: string, data: unknown) => {
this.onMapLoad(data); this.onMapLoad(data).catch((e) => console.error(e));
} }
); );
return; return;
@ -294,14 +294,14 @@ export class GameScene extends DirtyScene {
this.load.on( this.load.on(
"filecomplete-tilemapJSON-" + this.MapUrlFile, "filecomplete-tilemapJSON-" + this.MapUrlFile,
(key: string, type: string, data: unknown) => { (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. // 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. // 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)) { if (this.cache.tilemap.exists(this.MapUrlFile)) {
const data = this.cache.tilemap.get(this.MapUrlFile); const data = this.cache.tilemap.get(this.MapUrlFile);
this.onMapLoad(data); this.onMapLoad(data).catch((e) => console.error(e));
} }
return; return;
} }
@ -322,7 +322,7 @@ export class GameScene extends DirtyScene {
}); });
this.load.scenePlugin("AnimatedTiles", AnimatedTiles, "animatedTiles", "animatedTiles"); this.load.scenePlugin("AnimatedTiles", AnimatedTiles, "animatedTiles", "animatedTiles");
this.load.on("filecomplete-tilemapJSON-" + this.MapUrlFile, (key: string, type: string, data: unknown) => { 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 //TODO strategy to add access token
this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile);
@ -330,7 +330,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. // 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)) { if (this.cache.tilemap.exists(this.MapUrlFile)) {
const data = this.cache.tilemap.get(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 //eslint-disable-next-line @typescript-eslint/no-explicit-any
@ -408,21 +408,23 @@ export class GameScene extends DirtyScene {
this.load.on("complete", () => { this.load.on("complete", () => {
// FIXME: the factory might fail because the resources might not be loaded yet... // 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 // We would need to add a loader ended event in addition to the createPromise
this.createPromise.then(async () => { this.createPromise
itemFactory.create(this); .then(async () => {
itemFactory.create(this);
const roomJoinedAnswer = await this.connectionAnswerPromise; const roomJoinedAnswer = await this.connectionAnswerPromise;
for (const object of objectsOfType) { for (const object of objectsOfType) {
// TODO: we should pass here a factory to create sprites (maybe?) // TODO: we should pass here a factory to create sprites (maybe?)
// Do we have a state for this object? // Do we have a state for this object?
const state = roomJoinedAnswer.items[ object.id ]; const state = roomJoinedAnswer.items[object.id];
const actionableItem = itemFactory.factory(this, object, state); const actionableItem = itemFactory.factory(this, object, state);
this.actionableItems.set(actionableItem.getId(), actionableItem); this.actionableItems.set(actionableItem.getId(), actionableItem);
} }
}); })
.catch((e) => console.error(e));
}); });
} }
} }
@ -503,11 +505,11 @@ export class GameScene extends DirtyScene {
if (exitSceneUrl !== undefined) { if (exitSceneUrl !== undefined) {
this.loadNextGame( this.loadNextGame(
Room.getRoomPathFromExitSceneUrl(exitSceneUrl, window.location.toString(), this.MapUrlFile) Room.getRoomPathFromExitSceneUrl(exitSceneUrl, window.location.toString(), this.MapUrlFile)
); ).catch((e) => console.error(e));
} }
const exitUrl = this.getExitUrl(layer); const exitUrl = this.getExitUrl(layer);
if (exitUrl !== undefined) { if (exitUrl !== undefined) {
this.loadNextGameFromExitUrl(exitUrl); this.loadNextGameFromExitUrl(exitUrl).catch((e) => console.error(e));
} }
} }
if (layer.type === "objectgroup") { if (layer.type === "objectgroup") {
@ -547,7 +549,7 @@ export class GameScene extends DirtyScene {
} }
this.gameMap.exitUrls.forEach((exitUrl) => { this.gameMap.exitUrls.forEach((exitUrl) => {
this.loadNextGameFromExitUrl(exitUrl); this.loadNextGameFromExitUrl(exitUrl).catch((e) => console.error(e));
}); });
this.startPositionCalculator = new StartPositionCalculator( this.startPositionCalculator = new StartPositionCalculator(
@ -568,7 +570,10 @@ export class GameScene extends DirtyScene {
mediaManager.setUserInputManager(this.userInputManager); mediaManager.setUserInputManager(this.userInputManager);
if (localUserStore.getFullscreen()) { 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 //notify game manager can to create currentUser in map
@ -674,9 +679,16 @@ export class GameScene extends DirtyScene {
} }
}); });
Promise.all([this.connectionAnswerPromise as Promise<unknown>, ...scriptPromises]).then(() => { Promise.all([this.connectionAnswerPromise as Promise<unknown>, ...scriptPromises])
this.scene.wake(); .then(() => {
}); this.scene.wake();
})
.catch((e) =>
console.error(
"Some scripts failed to load ot the connection failed to establish to WorkAdventure server",
e
)
);
} }
/** /**
@ -876,7 +888,8 @@ export class GameScene extends DirtyScene {
// iframeListener.sendLeaveLayerEvent(layer.name); // iframeListener.sendLeaveLayerEvent(layer.name);
// }); // });
// }); // });
}); })
.catch((e) => console.error(e));
} }
//todo: into dedicated classes //todo: into dedicated classes
@ -929,7 +942,7 @@ export class GameScene extends DirtyScene {
if (newValue) { if (newValue) {
this.onMapExit( this.onMapExit(
Room.getRoomPathFromExitSceneUrl(newValue as string, window.location.toString(), this.MapUrlFile) Room.getRoomPathFromExitSceneUrl(newValue as string, window.location.toString(), this.MapUrlFile)
); ).catch((e) => console.error(e));
} else { } else {
setTimeout(() => { setTimeout(() => {
layoutManagerActionStore.removeAction("roomAccessDenied"); layoutManagerActionStore.removeAction("roomAccessDenied");
@ -938,7 +951,9 @@ export class GameScene extends DirtyScene {
}); });
this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue, oldValue) => { this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue, oldValue) => {
if (newValue) { 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 { } else {
setTimeout(() => { setTimeout(() => {
layoutManagerActionStore.removeAction("roomAccessDenied"); layoutManagerActionStore.removeAction("roomAccessDenied");
@ -1128,7 +1143,9 @@ export class GameScene extends DirtyScene {
this.iframeSubscriptionList.push( this.iframeSubscriptionList.push(
iframeListener.playSoundStream.subscribe((playSoundEvent) => { iframeListener.playSoundStream.subscribe((playSoundEvent) => {
const url = new URL(playSoundEvent.url, this.MapUrlFile); 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));
}) })
); );
@ -1166,7 +1183,7 @@ export class GameScene extends DirtyScene {
this.iframeSubscriptionList.push( this.iframeSubscriptionList.push(
iframeListener.loadSoundStream.subscribe((loadSoundEvent) => { iframeListener.loadSoundStream.subscribe((loadSoundEvent) => {
const url = new URL(loadSoundEvent.url, this.MapUrlFile); 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));
}) })
); );
@ -1177,11 +1194,15 @@ export class GameScene extends DirtyScene {
); );
this.iframeSubscriptionList.push( this.iframeSubscriptionList.push(
iframeListener.loadPageStream.subscribe((url: string) => { iframeListener.loadPageStream.subscribe((url: string) => {
this.loadNextGameFromExitUrl(url).then(() => { this.loadNextGameFromExitUrl(url)
this.events.once(EVENT_TYPE.POST_UPDATE, () => { .then(() => {
this.onMapExit(Room.getRoomPathFromExitUrl(url, window.location.toString())); 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; let scriptedBubbleSprite: Sprite;
@ -1450,7 +1471,7 @@ export class GameScene extends DirtyScene {
propertyValue: string | number | boolean | undefined propertyValue: string | number | boolean | undefined
): void { ): void {
if (propertyName === GameMapProperties.EXIT_URL && typeof propertyValue === "string") { 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); this.gameMap.setLayerProperty(layerName, propertyName, propertyValue);
} }
@ -1535,7 +1556,7 @@ export class GameScene extends DirtyScene {
public cleanupClosingScene(): void { public cleanupClosingScene(): void {
// stop playing audio, close any open website, stop any open Jitsi // 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 // Stop the script, if any
const scripts = this.getScriptUrls(this.mapFile); const scripts = this.getScriptUrls(this.mapFile);
for (const script of scripts) { for (const script of scripts) {
@ -2077,10 +2098,11 @@ export class GameScene extends DirtyScene {
const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined;
const jitsiWidth = allProps.get(GameMapProperties.JITSI_WIDTH) as number | undefined; const jitsiWidth = allProps.get(GameMapProperties.JITSI_WIDTH) as number | undefined;
jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth);
const jitsiKeepCircle = allProps.get("jitsiKeepCircle") as boolean | false; const jitsiKeepCircle = allProps.get("jitsiKeepCircle") as boolean | false;
jitsiFactory
.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl, jitsiWidth)
.catch((e) => console.error(e));
this.connection?.setSilent(true); this.connection?.setSilent(true);
mediaManager.hideGameOverlay(); mediaManager.hideGameOverlay();

View File

@ -40,19 +40,21 @@ export class CustomizeScene extends AbstractCharacterScene {
} }
preload() { preload() {
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => { this.loadCustomSceneSelectCharacters()
bodyResourceDescriptions.forEach((bodyResourceDescription) => { .then((bodyResourceDescriptions) => {
if ( bodyResourceDescriptions.forEach((bodyResourceDescription) => {
bodyResourceDescription.level == undefined || if (
bodyResourceDescription.level < 0 || bodyResourceDescription.level == undefined ||
bodyResourceDescription.level > 5 bodyResourceDescription.level < 0 ||
) { bodyResourceDescription.level > 5
throw "Texture level is null"; ) {
} throw "Texture level is null";
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription); }
}); this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
this.lazyloadingAttempt = true; });
}); this.lazyloadingAttempt = true;
})
.catch((e) => console.error(e));
this.layers = loadAllLayers(this.load); this.layers = loadAllLayers(this.load);
this.lazyloadingAttempt = false; this.lazyloadingAttempt = false;

View File

@ -41,12 +41,14 @@ export class SelectCharacterScene extends AbstractCharacterScene {
} }
preload() { preload() {
this.loadSelectSceneCharacters().then((bodyResourceDescriptions) => { this.loadSelectSceneCharacters()
bodyResourceDescriptions.forEach((bodyResourceDescription) => { .then((bodyResourceDescriptions) => {
this.playerModels.push(bodyResourceDescription); bodyResourceDescriptions.forEach((bodyResourceDescription) => {
}); this.playerModels.push(bodyResourceDescription);
this.lazyloadingAttempt = true; });
}); this.lazyloadingAttempt = true;
})
.catch((e) => console.error(e));
this.playerModels = loadAllDefaultModels(this.load); this.playerModels = loadAllDefaultModels(this.load);
this.lazyloadingAttempt = false; this.lazyloadingAttempt = false;

View File

@ -5,3 +5,5 @@ export const userMovingStore = writable(false);
export const requestVisitCardsStore = writable<string | null>(null); export const requestVisitCardsStore = writable<string | null>(null);
export const userIsAdminStore = writable(false); export const userIsAdminStore = writable(false);
export const limitMapStore = writable(false);

View File

@ -360,32 +360,27 @@ const implementCorrectTrackBehavior = getNavigatorType() === NavigatorType.firef
/** /**
* Stops the camera from filming * Stops the camera from filming
*/ */
function applyCameraConstraints(currentStream: MediaStream | null, constraints: MediaTrackConstraints | boolean): void { async function applyCameraConstraints(
currentStream: MediaStream | null,
constraints: MediaTrackConstraints | boolean
): Promise<void[]> {
if (!currentStream) { if (!currentStream) {
return; return [];
}
for (const track of currentStream.getVideoTracks()) {
toggleConstraints(track, constraints).catch((e) =>
console.error("Error while setting new camera constraints:", e)
);
} }
return Promise.all(currentStream.getVideoTracks().map((track) => toggleConstraints(track, constraints)));
} }
/** /**
* Stops the microphone from listening * Stops the microphone from listening
*/ */
function applyMicrophoneConstraints( async function applyMicrophoneConstraints(
currentStream: MediaStream | null, currentStream: MediaStream | null,
constraints: MediaTrackConstraints | boolean constraints: MediaTrackConstraints | boolean
): void { ): Promise<void[]> {
if (!currentStream) { if (!currentStream) {
return; return [];
}
for (const track of currentStream.getAudioTracks()) {
toggleConstraints(track, constraints).catch((e) =>
console.error("Error while setting new audio constraints:", e)
);
} }
return Promise.all(currentStream.getAudioTracks().map((track) => toggleConstraints(track, constraints)));
} }
async function toggleConstraints(track: MediaStreamTrack, constraints: MediaTrackConstraints | boolean): Promise<void> { async function toggleConstraints(track: MediaStreamTrack, constraints: MediaTrackConstraints | boolean): Promise<void> {
@ -477,8 +472,8 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
} }
} }
applyMicrophoneConstraints(currentStream, constraints.audio || false); applyMicrophoneConstraints(currentStream, constraints.audio || false).catch((e) => console.error(e));
applyCameraConstraints(currentStream, constraints.video || false); applyCameraConstraints(currentStream, constraints.video || false).catch((e) => console.error(e));
if (implementCorrectTrackBehavior) { if (implementCorrectTrackBehavior) {
//on good navigators like firefox, we can instantiate the stream once and simply disable or enable the tracks as needed //on good navigators like firefox, we can instantiate the stream once and simply disable or enable the tracks as needed

View File

@ -0,0 +1,4 @@
import { writable } from "svelte/store";
export const showLimitRoomModalStore = writable(false);
export const showShareLinkMapModalStore = writable(false);

View File

@ -156,7 +156,7 @@ export const screenSharingLocalStreamStore = derived<Readable<MediaStreamConstra
error: e instanceof Error ? e : new Error("An unknown error happened"), error: e instanceof Error ? e : new Error("An unknown error happened"),
}); });
} }
})(); })().catch((e) => console.error(e));
} }
); );

View File

@ -2,8 +2,7 @@ import type { Room } from "../Connexion/Room";
import { localUserStore } from "../Connexion/LocalUserStore"; import { localUserStore } from "../Connexion/LocalUserStore";
export enum GameConnexionTypes { export enum GameConnexionTypes {
anonymous = 1, room = 1,
organization,
register, register,
empty, empty,
unknown, unknown,
@ -19,10 +18,8 @@ class UrlManager {
return GameConnexionTypes.login; return GameConnexionTypes.login;
} else if (url === "/jwt") { } else if (url === "/jwt") {
return GameConnexionTypes.jwt; return GameConnexionTypes.jwt;
} else if (url.includes("_/")) { } else if (url.includes("_/") || url.includes("*/") || url.includes("@/")) {
return GameConnexionTypes.anonymous; return GameConnexionTypes.room;
} else if (url.includes("@/")) {
return GameConnexionTypes.organization;
} else if (url.includes("register/")) { } else if (url.includes("register/")) {
return GameConnexionTypes.register; return GameConnexionTypes.register;
} else if (url === "/") { } else if (url === "/") {
@ -41,7 +38,7 @@ class UrlManager {
if (window.location.pathname === room.id) return; if (window.location.pathname === room.id) return;
//Set last room visited! (connected or nor, must to be saved in localstorage and cache API) //Set last room visited! (connected or nor, must to be saved in localstorage and cache API)
//use href to keep # value //use href to keep # value
localUserStore.setLastRoomUrl(room.href); localUserStore.setLastRoomUrl(room.href).catch((e) => console.error(e));
const hash = window.location.hash; const hash = window.location.hash;
const search = room.search.toString(); const search = room.search.toString();
history.pushState({}, "WorkAdventure", room.id + (search ? "?" + search : "") + hash); history.pushState({}, "WorkAdventure", room.id + (search ? "?" + search : "") + hash);

View File

@ -155,7 +155,7 @@ class CoWebsiteManager {
} }
buttonCloseCoWebsites.blur(); buttonCloseCoWebsites.blur();
this.closeCoWebsites(); this.closeCoWebsites().catch((e) => console.error(e));
}); });
const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId); const buttonFullScreenFrame = HtmlUtils.getElementByIdOrFail(cowebsiteFullScreenButtonId);
@ -523,71 +523,62 @@ class CoWebsiteManager {
throw new Error("Too many we"); throw new Error("Too many we");
} }
Promise.resolve(callback(this.cowebsiteBufferDom)).then((iframe) => { Promise.resolve(callback(this.cowebsiteBufferDom))
iframe?.classList.add("pixel"); .then((iframe) => {
iframe?.classList.add("pixel");
if (!iframe.id) { if (!iframe.id) {
do { do {
iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7); iframe.id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7);
} while (this.getCoWebsiteById(iframe.id)); } while (this.getCoWebsiteById(iframe.id));
} }
const onloadPromise = new Promise<void>((resolve) => { const onloadPromise = new Promise<void>((resolve) => {
iframe.onload = () => resolve(); iframe.onload = () => resolve();
}); });
// const icon = this.generateCoWebsiteIcon(iframe); const coWebsite = {
iframe,
state : CoWebsiteState.OPENED,
position: position ?? this.coWebsites.length,
};
const coWebsite = { this.coWebsites.push(coWebsite);
iframe,
// icon,
state : CoWebsiteState.OPENED,
position: position ?? this.coWebsites.length,
};
// Iframe management on mobile const onTimeoutPromise = new Promise<void>((resolve) => {
// icon.addEventListener("click", () => { setTimeout(() => resolve(), 2000);
// if (this.isSmallScreen()) { });
// this.moveRightPreviousCoWebsite(coWebsite, 0);
// }
// });
this.coWebsites.push(coWebsite); this.currentOperationPromise = this.currentOperationPromise
// this.cowebsiteSubIconsDom.appendChild(icon); .then(() => Promise.race([onloadPromise, onTimeoutPromise]))
.then(() => {
if (coWebsite.position === 0) {
this.openMain();
if (widthPercent) {
this.widthPercent = widthPercent;
}
const onTimeoutPromise = new Promise<void>((resolve) => { setTimeout(() => {
setTimeout(() => resolve(), 2000); this.fire();
}); position !== undefined
? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position)
this.currentOperationPromise = this.currentOperationPromise : this.moveCoWebsite(coWebsite, coWebsite.position);
.then(() => Promise.race([onloadPromise, onTimeoutPromise])) }, animationTime);
.then(() => { } else {
if (coWebsite.position === 0) {
this.openMain();
if (widthPercent) {
this.widthPercent = widthPercent;
}
setTimeout(() => {
this.fire();
position !== undefined position !== undefined
? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position) ? this.moveRightPreviousCoWebsite(coWebsite, coWebsite.position)
: this.moveCoWebsite(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); return resolve(coWebsite);
}) })
.catch((err) => { .catch((err) => {
console.error("Error loadCoWebsite => ", err); console.error("Error loadCoWebsite => ", err);
this.removeCoWebsiteFromStack(coWebsite); this.removeCoWebsiteFromStack(coWebsite);
return reject(); return reject();
}); });
}); })
.catch((e) => console.error("Error loadCoWebsite >=> ", e));
}); });
} }
@ -612,17 +603,21 @@ class CoWebsiteManager {
return this.currentOperationPromise; return this.currentOperationPromise;
} }
public closeJitsi() { public async closeJitsi() {
const jitsi = this.searchJitsi(); const jitsi = this.searchJitsi();
if (jitsi) { if (jitsi) {
this.closeCoWebsite(jitsi); return this.closeCoWebsite(jitsi);
} }
} }
public closeCoWebsites(): Promise<void> { public closeCoWebsites(): Promise<void> {
this.currentOperationPromise = this.currentOperationPromise.then(() => { this.currentOperationPromise = this.currentOperationPromise.then(() => {
const promises: Promise<void>[] = [];
this.coWebsites.forEach((coWebsite: CoWebsite) => { this.coWebsites.forEach((coWebsite: CoWebsite) => {
this.closeCoWebsite(coWebsite); promises.push(this.closeCoWebsite(coWebsite));
});
return Promise.all(promises).then(() => {
return;
}); });
}); });
return this.currentOperationPromise; return this.currentOperationPromise;

View File

@ -1,5 +1,5 @@
import { JITSI_URL } from "../Enum/EnvironmentVariable"; import { JITSI_URL } from "../Enum/EnvironmentVariable";
import { coWebsiteManager } from "./CoWebsiteManager"; import { CoWebsite, coWebsiteManager } from "./CoWebsiteManager";
import { requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore"; import { requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore";
import { get } from "svelte/store"; import { get } from "svelte/store";
@ -143,8 +143,8 @@ class JitsiFactory {
interfaceConfig?: object, interfaceConfig?: object,
jitsiUrl?: string, jitsiUrl?: string,
jitsiWidth?: number jitsiWidth?: number
): void { ): Promise<CoWebsite> {
coWebsiteManager.addCoWebsite( return coWebsiteManager.addCoWebsite(
async (cowebsiteDiv) => { async (cowebsiteDiv) => {
// Jitsi meet external API maintains some data in local storage // Jitsi meet external API maintains some data in local storage
// which is sent via the appData URL parameter when joining a // which is sent via the appData URL parameter when joining a
@ -203,7 +203,7 @@ class JitsiFactory {
const jitsiCoWebsite = coWebsiteManager.searchJitsi(); const jitsiCoWebsite = coWebsiteManager.searchJitsi();
if (jitsiCoWebsite) { if (jitsiCoWebsite) {
coWebsiteManager.closeJitsi(); coWebsiteManager.closeJitsi().catch((e) => console.error(e));
} }
this.jitsiApi.removeListener("audioMuteStatusChanged", this.audioCallback); this.jitsiApi.removeListener("audioMuteStatusChanged", this.audioCallback);

View File

@ -9,7 +9,7 @@ import {
} from "./Api/Events/IframeEvent"; } from "./Api/Events/IframeEvent";
import chat from "./Api/iframe/chat"; import chat from "./Api/iframe/chat";
import type { IframeCallback } from "./Api/iframe/IframeApiContribution"; 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 controls from "./Api/iframe/controls";
import ui from "./Api/iframe/ui"; import ui from "./Api/iframe/ui";
import sound from "./Api/iframe/sound"; import sound from "./Api/iframe/sound";
@ -136,17 +136,17 @@ const wa = {
/** /**
* @deprecated Use WA.nav.openCoWebSite instead * @deprecated Use WA.nav.openCoWebSite instead
*/ */
openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): void { openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = ""): Promise<CoWebsite> {
console.warn("Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead"); 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 * @deprecated Use WA.nav.closeCoWebSite instead
*/ */
closeCoWebSite(): void { closeCoWebSite(): Promise<void> {
console.warn("Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead"); console.warn("Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead");
nav.closeCoWebSite(); return nav.closeCoWebSite();
}, },
/** /**

View File

@ -1,22 +1,24 @@
import "jasmine"; import "jasmine";
import {PlayerMovement} from "../../../src/Phaser/Game/PlayerMovement"; import { PlayerMovement } from "../../../src/Phaser/Game/PlayerMovement";
describe("Interpolation / Extrapolation", () => { describe("Interpolation / Extrapolation", () => {
it("should interpolate", () => { it("should interpolate", () => {
const playerMovement = new PlayerMovement({ const playerMovement = new PlayerMovement(
x: 100, y: 200 {
}, 42000, x: 100,
y: 200,
},
42000,
{ {
x: 200, x: 200,
y: 100, y: 100,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
moving: true, moving: true,
direction: "up" direction: "up",
}, },
42200 42200
); );
expect(playerMovement.isOutdated(42100)).toBe(false); expect(playerMovement.isOutdated(42100)).toBe(false);
expect(playerMovement.isOutdated(43000)).toBe(true); expect(playerMovement.isOutdated(43000)).toBe(true);
@ -26,8 +28,8 @@ describe("Interpolation / Extrapolation", () => {
y: 150, y: 150,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
direction: 'up', direction: "up",
moving: true moving: true,
}); });
expect(playerMovement.getPosition(42200)).toEqual({ expect(playerMovement.getPosition(42200)).toEqual({
@ -35,8 +37,8 @@ describe("Interpolation / Extrapolation", () => {
y: 100, y: 100,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
direction: 'up', direction: "up",
moving: true moving: true,
}); });
expect(playerMovement.getPosition(42300)).toEqual({ expect(playerMovement.getPosition(42300)).toEqual({
@ -44,22 +46,25 @@ describe("Interpolation / Extrapolation", () => {
y: 50, y: 50,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
direction: 'up', direction: "up",
moving: true moving: true,
}); });
}); });
it("should not extrapolate if we stop", () => { it("should not extrapolate if we stop", () => {
const playerMovement = new PlayerMovement({ const playerMovement = new PlayerMovement(
x: 100, y: 200 {
}, 42000, x: 100,
y: 200,
},
42000,
{ {
x: 200, x: 200,
y: 100, y: 100,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
moving: false, moving: false,
direction: "up" direction: "up",
}, },
42200 42200
); );
@ -69,22 +74,25 @@ describe("Interpolation / Extrapolation", () => {
y: 100, y: 100,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
direction: 'up', direction: "up",
moving: false moving: false,
}); });
}); });
it("should keep moving until it stops", () => { it("should keep moving until it stops", () => {
const playerMovement = new PlayerMovement({ const playerMovement = new PlayerMovement(
x: 100, y: 200 {
}, 42000, x: 100,
y: 200,
},
42000,
{ {
x: 200, x: 200,
y: 100, y: 100,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
moving: false, moving: false,
direction: "up" direction: "up",
}, },
42200 42200
); );
@ -94,8 +102,8 @@ describe("Interpolation / Extrapolation", () => {
y: 150, y: 150,
oldX: 100, oldX: 100,
oldY: 200, oldY: 200,
direction: 'up', direction: "up",
moving: false moving: false,
}); });
}); });
}) });

View File

@ -20,6 +20,10 @@ export const isMapDetailsData = new tg.IsInterface()
}) })
.withOptionalProperties({ .withOptionalProperties({
iframeAuthentication: tg.isNullable(tg.isString), iframeAuthentication: tg.isNullable(tg.isString),
// The date (in ISO 8601 format) at which the room will expire
expireOn: tg.isString,
// Whether the "report" feature is enabled or not on this room
canReport: tg.isBoolean,
}) })
.get(); .get();