Refactoring Error Screen
This commit is contained in:
parent
a689756d85
commit
748baa9bbc
@ -2,6 +2,7 @@
|
|||||||
import type { Game } from "../Phaser/Game/Game";
|
import type { Game } from "../Phaser/Game/Game";
|
||||||
import { chatVisibilityStore } from "../Stores/ChatStore";
|
import { chatVisibilityStore } from "../Stores/ChatStore";
|
||||||
import { errorStore } from "../Stores/ErrorStore";
|
import { errorStore } from "../Stores/ErrorStore";
|
||||||
|
import { errorScreenStore } from "../Stores/ErrorScreenStore";
|
||||||
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
|
import { loginSceneVisibleStore } from "../Stores/LoginSceneStore";
|
||||||
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
|
import { enableCameraSceneVisibilityStore } from "../Stores/MediaStore";
|
||||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||||
@ -13,11 +14,17 @@
|
|||||||
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
|
import SelectCharacterScene from "./selectCharacter/SelectCharacterScene.svelte";
|
||||||
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
|
import SelectCompanionScene from "./SelectCompanion/SelectCompanionScene.svelte";
|
||||||
import ErrorDialog from "./UI/ErrorDialog.svelte";
|
import ErrorDialog from "./UI/ErrorDialog.svelte";
|
||||||
|
import ErrorScreen from "./UI/ErrorScreen.svelte";
|
||||||
|
|
||||||
|
|
||||||
export let game: Game;
|
export let game: Game;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $errorStore.length > 0}
|
{#if $errorScreenStore !== undefined}
|
||||||
|
<div>
|
||||||
|
<ErrorScreen />
|
||||||
|
</div>
|
||||||
|
{:else if $errorStore.length > 0}
|
||||||
<div>
|
<div>
|
||||||
<ErrorDialog />
|
<ErrorDialog />
|
||||||
</div>
|
</div>
|
||||||
|
158
front/src/Components/UI/ErrorScreen.svelte
Normal file
158
front/src/Components/UI/ErrorScreen.svelte
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { fly } from "svelte/transition";
|
||||||
|
import { errorScreenStore } from "../../Stores/ErrorScreenStore";
|
||||||
|
|
||||||
|
import logo from "../images/logo-min-white.png";
|
||||||
|
import error from "../images/error.png";
|
||||||
|
import cup from "../images/cup.png";
|
||||||
|
import reload from "../images/reload.png";
|
||||||
|
import external from "../images/external-link.png";
|
||||||
|
import {get} from "svelte/store";
|
||||||
|
|
||||||
|
let errorScreen = get(errorScreenStore);
|
||||||
|
|
||||||
|
function click(){
|
||||||
|
if(errorScreen.urlToRedirect) window.location.replace(errorScreen.urlToRedirect);
|
||||||
|
else if(errorScreen.type === 'redirect' && window.history.length > 2) history.back();
|
||||||
|
else window.location.reload(true);
|
||||||
|
}
|
||||||
|
let details = errorScreen.details;
|
||||||
|
let timeVar = errorScreen.timeToRetry ?? 0;
|
||||||
|
if(errorScreen.type === 'retry') {
|
||||||
|
setInterval(() => {
|
||||||
|
if (timeVar <= 1000) click();
|
||||||
|
timeVar -= 1000;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
$: detailsStylized = details.replace("{time}", `${timeVar/1000}`);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<main class="errorScreen" transition:fly={{ y: -200, duration: 500 }}>
|
||||||
|
<div style="width: 90%;">
|
||||||
|
<img src={logo} alt="WorkAdventure" class="logo"/>
|
||||||
|
<div><img src={$errorScreenStore.type === 'retry'?cup:error} alt="" class="icon"/></div>
|
||||||
|
{#if $errorScreenStore.type !== 'retry'}<h2>{$errorScreenStore.title}</h2>{/if}
|
||||||
|
<p>{$errorScreenStore.subtitle}</p>
|
||||||
|
{#if $errorScreenStore.type !== 'retry'}<p class="code">Code : {$errorScreenStore.code}</p>{/if}
|
||||||
|
<p class="details">{detailsStylized}{#if $errorScreenStore.type === 'retry'}<div class="loading"></div>{/if}</p>
|
||||||
|
{#if ($errorScreenStore.type === 'retry' && $errorScreenStore.canRetryManual) || ($errorScreenStore.type === 'redirect' && (window.history.length > 2 || $errorScreenStore.urlToRedirect))}
|
||||||
|
<div class="button" on:click={click}>
|
||||||
|
<img src={$errorScreenStore.type === 'retry'?reload:external} alt="" class="reload"/>
|
||||||
|
{$errorScreenStore.buttonTitle}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
main.errorScreen {
|
||||||
|
pointer-events: auto;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #000000;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-align: center;
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
min-width: 300px;
|
||||||
|
z-index: 700;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding: 20px 0;
|
||||||
|
.logo{
|
||||||
|
width: 50%;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
.icon{
|
||||||
|
height: 125px;
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
}
|
||||||
|
p.code{
|
||||||
|
font-size: 12px;
|
||||||
|
opacity: .6;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
p.details{
|
||||||
|
font-size: 12px;
|
||||||
|
max-width: 80%;
|
||||||
|
margin: 0 auto 35px auto;
|
||||||
|
}
|
||||||
|
.loading{
|
||||||
|
display: inline-block;
|
||||||
|
min-width: 20px;
|
||||||
|
position: relative;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
.loading:after {
|
||||||
|
overflow: hidden;
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: bottom;
|
||||||
|
-webkit-animation: ellipsis steps(4,end) 900ms infinite;
|
||||||
|
animation: ellipsis steps(4,end) 900ms infinite;
|
||||||
|
content: "\2026";
|
||||||
|
width: 0;
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
font-size: 16px;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: -19px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ellipsis {
|
||||||
|
to {
|
||||||
|
width: 1.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes ellipsis {
|
||||||
|
to {
|
||||||
|
width: 1.25em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button{
|
||||||
|
cursor: pointer;
|
||||||
|
background-image: url('../images/button-large.png');
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
padding: 16px 20px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-family: "Press Start 2P";
|
||||||
|
font-size: 14px;
|
||||||
|
.reload{
|
||||||
|
margin-top: -4px;
|
||||||
|
width: 22px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.button:hover{
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-top: -5px;
|
||||||
|
padding: 18px 22px;
|
||||||
|
font-size: 16px;
|
||||||
|
.reload{
|
||||||
|
margin-top: -5px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</style>
|
BIN
front/src/Components/images/button-large.png
Normal file
BIN
front/src/Components/images/button-large.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 KiB |
BIN
front/src/Components/images/cup.png
Normal file
BIN
front/src/Components/images/cup.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
front/src/Components/images/error.png
Normal file
BIN
front/src/Components/images/error.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
front/src/Components/images/external-link.png
Normal file
BIN
front/src/Components/images/external-link.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
front/src/Components/images/logo-min-white.png
Normal file
BIN
front/src/Components/images/logo-min-white.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.4 KiB |
BIN
front/src/Components/images/reload.png
Normal file
BIN
front/src/Components/images/reload.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.6 KiB |
@ -25,6 +25,7 @@ import {
|
|||||||
TokenExpiredMessage,
|
TokenExpiredMessage,
|
||||||
WorldConnexionMessage,
|
WorldConnexionMessage,
|
||||||
ErrorMessage as ErrorMessageTsProto,
|
ErrorMessage as ErrorMessageTsProto,
|
||||||
|
ErrorV2Message as ErrorV2MessageTsProto,
|
||||||
UserMovedMessage as UserMovedMessageTsProto,
|
UserMovedMessage as UserMovedMessageTsProto,
|
||||||
GroupUpdateMessage as GroupUpdateMessageTsProto,
|
GroupUpdateMessage as GroupUpdateMessageTsProto,
|
||||||
GroupDeleteMessage as GroupDeleteMessageTsProto,
|
GroupDeleteMessage as GroupDeleteMessageTsProto,
|
||||||
@ -46,6 +47,8 @@ import { Subject } from "rxjs";
|
|||||||
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
import { selectCharacterSceneVisibleStore } from "../Stores/SelectCharacterStore";
|
||||||
import { gameManager } from "../Phaser/Game/GameManager";
|
import { gameManager } from "../Phaser/Game/GameManager";
|
||||||
import { SelectCharacterScene, SelectCharacterSceneName } from "../Phaser/Login/SelectCharacterScene";
|
import { SelectCharacterScene, SelectCharacterSceneName } from "../Phaser/Login/SelectCharacterScene";
|
||||||
|
import {errorScreenStore} from "../Stores/ErrorScreenStore";
|
||||||
|
import {WAError} from "../Phaser/Reconnecting/WAError";
|
||||||
|
|
||||||
const manualPingDelay = 20000;
|
const manualPingDelay = 20000;
|
||||||
|
|
||||||
@ -61,6 +64,9 @@ export class RoomConnection implements RoomConnection {
|
|||||||
private readonly _errorMessageStream = new Subject<ErrorMessageTsProto>();
|
private readonly _errorMessageStream = new Subject<ErrorMessageTsProto>();
|
||||||
public readonly errorMessageStream = this._errorMessageStream.asObservable();
|
public readonly errorMessageStream = this._errorMessageStream.asObservable();
|
||||||
|
|
||||||
|
private readonly _errorV2MessageStream = new Subject<ErrorV2MessageTsProto>();
|
||||||
|
public readonly errorV2MessageStream = this._errorV2MessageStream.asObservable();
|
||||||
|
|
||||||
private readonly _roomJoinedMessageStream = new Subject<{
|
private readonly _roomJoinedMessageStream = new Subject<{
|
||||||
connection: RoomConnection;
|
connection: RoomConnection;
|
||||||
room: RoomJoinedMessageInterface;
|
room: RoomJoinedMessageInterface;
|
||||||
@ -475,6 +481,13 @@ export class RoomConnection implements RoomConnection {
|
|||||||
console.error("An error occurred server side: " + message.errorMessage.message);
|
console.error("An error occurred server side: " + message.errorMessage.message);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case "errorV2Message": {
|
||||||
|
this._errorV2MessageStream.next(message.errorV2Message);
|
||||||
|
if(message.errorV2Message.code !== 'retry') this.closed = true;
|
||||||
|
console.error("An error occurred server side: " + message.errorV2Message.code);
|
||||||
|
errorScreenStore.setError(message.errorV2Message as unknown as WAError);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default: {
|
default: {
|
||||||
// Security check: if we forget a "case", the line below will catch the error at compile-time.
|
// Security check: if we forget a "case", the line below will catch the error at compile-time.
|
||||||
const tmp: never = message;
|
const tmp: never = message;
|
||||||
|
@ -4,9 +4,8 @@ import { ErrorScene } from "../Reconnecting/ErrorScene";
|
|||||||
import { WAError } from "../Reconnecting/WAError";
|
import { WAError } from "../Reconnecting/WAError";
|
||||||
import { waScaleManager } from "../Services/WaScaleManager";
|
import { waScaleManager } from "../Services/WaScaleManager";
|
||||||
import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
|
import { ReconnectingTextures } from "../Reconnecting/ReconnectingScene";
|
||||||
import LL from "../../i18n/i18n-svelte";
|
|
||||||
import { get } from "svelte/store";
|
|
||||||
import { localeDetector } from "../../i18n/locales";
|
import { localeDetector } from "../../i18n/locales";
|
||||||
|
import {errorScreenStore} from "../../Stores/ErrorScreenStore";
|
||||||
|
|
||||||
export const EntrySceneName = "EntryScene";
|
export const EntrySceneName = "EntryScene";
|
||||||
|
|
||||||
@ -47,27 +46,18 @@ export class EntryScene extends Scene {
|
|||||||
this.scene.start(nextSceneName);
|
this.scene.start(nextSceneName);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
const $LL = get(LL);
|
if (err.response.data?.code) {
|
||||||
if (err.response && err.response.status == 404) {
|
errorScreenStore.setError(new WAError(
|
||||||
ErrorScene.showError(
|
err.response.data.type,
|
||||||
new WAError(
|
err.response.data.code,
|
||||||
$LL.error.accessLink.title(),
|
err.response.data.title,
|
||||||
$LL.error.accessLink.subTitle(),
|
err.response.data.subtitle,
|
||||||
$LL.error.accessLink.details()
|
err.response.data.details,
|
||||||
),
|
err.response.data.timeToRetry,
|
||||||
this.scene
|
err.response.data.canRetryManual,
|
||||||
);
|
err.response.data.urlToRedirect,
|
||||||
} else if (err.response && err.response.status == 403) {
|
err.response.data.buttonTitle
|
||||||
ErrorScene.showError(
|
));
|
||||||
new WAError(
|
|
||||||
$LL.error.connectionRejected.title(),
|
|
||||||
$LL.error.connectionRejected.subTitle({
|
|
||||||
error: err.response.data ? ". \n\r \n\r" + `${err.response.data}` : "",
|
|
||||||
}),
|
|
||||||
$LL.error.connectionRejected.details()
|
|
||||||
),
|
|
||||||
this.scene
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
ErrorScene.showError(err, this.scene);
|
ErrorScene.showError(err, this.scene);
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@ import Image = Phaser.GameObjects.Image;
|
|||||||
import Sprite = Phaser.GameObjects.Sprite;
|
import Sprite = Phaser.GameObjects.Sprite;
|
||||||
import Text = Phaser.GameObjects.Text;
|
import Text = Phaser.GameObjects.Text;
|
||||||
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
import ScenePlugin = Phaser.Scenes.ScenePlugin;
|
||||||
import { WAError } from "./WAError";
|
|
||||||
import Axios from "axios";
|
import Axios from "axios";
|
||||||
|
|
||||||
export const ErrorSceneName = "ErrorScene";
|
export const ErrorSceneName = "ErrorScene";
|
||||||
@ -88,12 +87,6 @@ export class ErrorScene extends Phaser.Scene {
|
|||||||
title: "An error occurred",
|
title: "An error occurred",
|
||||||
subTitle: error,
|
subTitle: error,
|
||||||
});
|
});
|
||||||
} else if (error instanceof WAError) {
|
|
||||||
scene.start(ErrorSceneName, {
|
|
||||||
title: error.title,
|
|
||||||
subTitle: error.subTitle,
|
|
||||||
message: error.details,
|
|
||||||
});
|
|
||||||
} else if (Axios.isAxiosError(error) && error.response) {
|
} else if (Axios.isAxiosError(error) && error.response) {
|
||||||
// Axios HTTP error
|
// Axios HTTP error
|
||||||
// client received an error response (5xx, 4xx)
|
// client received an error response (5xx, 4xx)
|
||||||
|
@ -1,26 +1,55 @@
|
|||||||
export class WAError extends Error {
|
export class WAError extends Error {
|
||||||
|
private _type: string;
|
||||||
|
private _code: string;
|
||||||
private _title: string;
|
private _title: string;
|
||||||
private _subTitle: string;
|
private _subtitle: string;
|
||||||
private _details: string;
|
private _details: string;
|
||||||
|
private _timeToRetry:number;
|
||||||
|
private _canRetryManual: boolean;
|
||||||
|
private _urlToRedirect: string;
|
||||||
|
private _buttonTitle: string;
|
||||||
|
|
||||||
constructor(title: string, subTitle: string, details: string) {
|
constructor(type: string, code: string, title: string, subtitle: string, details: string, timeToRetry: number, canRetryManual: boolean, urlToRedirect: string, buttonTitle: string) {
|
||||||
super(title + " - " + subTitle + " - " + details);
|
super(title + " - " + subtitle + " - " + details);
|
||||||
|
|
||||||
|
this._type = type;
|
||||||
|
this._code = code;
|
||||||
this._title = title;
|
this._title = title;
|
||||||
this._subTitle = subTitle;
|
this._subtitle = subtitle;
|
||||||
this._details = details;
|
this._details = details;
|
||||||
|
this._timeToRetry = timeToRetry;
|
||||||
|
this._canRetryManual = canRetryManual;
|
||||||
|
this._urlToRedirect = urlToRedirect;
|
||||||
|
this._buttonTitle = buttonTitle;
|
||||||
// Set the prototype explicitly.
|
// Set the prototype explicitly.
|
||||||
Object.setPrototypeOf(this, WAError.prototype);
|
Object.setPrototypeOf(this, WAError.prototype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get type(): string {
|
||||||
|
return this._type;
|
||||||
|
}
|
||||||
|
get code(): string {
|
||||||
|
return this._code;
|
||||||
|
}
|
||||||
get title(): string {
|
get title(): string {
|
||||||
return this._title;
|
return this._title;
|
||||||
}
|
}
|
||||||
|
get subtitle(): string {
|
||||||
get subTitle(): string {
|
return this._subtitle;
|
||||||
return this._subTitle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get details(): string {
|
get details(): string {
|
||||||
return this._details;
|
return this._details;
|
||||||
}
|
}
|
||||||
|
get timeToRetry(): number {
|
||||||
|
return this._timeToRetry;
|
||||||
|
}
|
||||||
|
get buttonTitle(): string {
|
||||||
|
return this._buttonTitle;
|
||||||
|
}
|
||||||
|
get urlToRedirect(): string {
|
||||||
|
return this._urlToRedirect;
|
||||||
|
}
|
||||||
|
get canRetryManual(): boolean {
|
||||||
|
return this._canRetryManual;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
18
front/src/Stores/ErrorScreenStore.ts
Normal file
18
front/src/Stores/ErrorScreenStore.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import {writable} from "svelte/store";
|
||||||
|
import {WAError} from "../Phaser/Reconnecting/WAError";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A store that contains one error of type WAError to be displayed.
|
||||||
|
*/
|
||||||
|
function createErrorScreenStore() {
|
||||||
|
const { subscribe, set } = writable<WAError>(undefined);
|
||||||
|
|
||||||
|
return {
|
||||||
|
subscribe,
|
||||||
|
setError: (
|
||||||
|
e: WAError
|
||||||
|
): void => set(e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const errorScreenStore = createErrorScreenStore();
|
@ -218,6 +218,18 @@ message ErrorMessage {
|
|||||||
string message = 1;
|
string message = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message ErrorV2Message {
|
||||||
|
string type = 1;
|
||||||
|
string code = 2;
|
||||||
|
string title = 3;
|
||||||
|
string subtitle = 4;
|
||||||
|
string details = 5;
|
||||||
|
int32 timeToRetry = 6;
|
||||||
|
bool canRetryManual = 7;
|
||||||
|
string urlToRedirect = 8;
|
||||||
|
string buttonTitle = 9;
|
||||||
|
}
|
||||||
|
|
||||||
message ItemStateMessage {
|
message ItemStateMessage {
|
||||||
int32 itemId = 1;
|
int32 itemId = 1;
|
||||||
string stateJson = 2;
|
string stateJson = 2;
|
||||||
@ -329,6 +341,7 @@ message ServerToClientMessage {
|
|||||||
FollowAbortMessage followAbortMessage = 23;
|
FollowAbortMessage followAbortMessage = 23;
|
||||||
InvalidTextureMessage invalidTextureMessage = 24;
|
InvalidTextureMessage invalidTextureMessage = 24;
|
||||||
GroupUsersUpdateMessage groupUsersUpdateMessage = 25;
|
GroupUsersUpdateMessage groupUsersUpdateMessage = 25;
|
||||||
|
ErrorV2Message errorV2Message = 26;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -318,6 +318,8 @@ export class AuthenticateController extends BaseHttpController {
|
|||||||
(async () => {
|
(async () => {
|
||||||
const param = await req.json();
|
const param = await req.json();
|
||||||
|
|
||||||
|
adminApi.setLocale(req.header('accept-language'));
|
||||||
|
|
||||||
//todo: what to do if the organizationMemberToken is already used?
|
//todo: what to do if the organizationMemberToken is already used?
|
||||||
const organizationMemberToken: string | null = param.organizationMemberToken;
|
const organizationMemberToken: string | null = param.organizationMemberToken;
|
||||||
const playUri: string | null = param.playUri;
|
const playUri: string | null = param.playUri;
|
||||||
|
@ -31,12 +31,14 @@ export class BaseHttpController {
|
|||||||
|
|
||||||
if (axios.isAxiosError(e) && e.response) {
|
if (axios.isAxiosError(e) && e.response) {
|
||||||
res.status(e.response.status);
|
res.status(e.response.status);
|
||||||
res.send(
|
if(!e.response.data?.code) {
|
||||||
"An error occurred: " +
|
res.send(
|
||||||
e.response.status +
|
"An error occurred: " +
|
||||||
" " +
|
e.response.status +
|
||||||
(e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText)
|
" " +
|
||||||
);
|
(e.response.data && e.response.data.message ? e.response.data.message : e.response.statusText)
|
||||||
|
);
|
||||||
|
} else res.json(e.response.data);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
res.status(500);
|
res.status(500);
|
||||||
|
@ -68,7 +68,7 @@ interface UpgradeData {
|
|||||||
|
|
||||||
interface UpgradeFailedData {
|
interface UpgradeFailedData {
|
||||||
rejected: true;
|
rejected: true;
|
||||||
reason: "tokenInvalid" | "textureInvalid" | null;
|
reason: "tokenInvalid" | "textureInvalid" | "error" | null;
|
||||||
message: string;
|
message: string;
|
||||||
roomId: string;
|
roomId: string;
|
||||||
}
|
}
|
||||||
@ -231,6 +231,8 @@ export class IoSocketController {
|
|||||||
const websocketExtensions = req.getHeader("sec-websocket-extensions");
|
const websocketExtensions = req.getHeader("sec-websocket-extensions");
|
||||||
const IPAddress = req.getHeader("x-forwarded-for");
|
const IPAddress = req.getHeader("x-forwarded-for");
|
||||||
|
|
||||||
|
adminApi.setLocale(req.getHeader('accept-language'));
|
||||||
|
|
||||||
const roomId = query.roomId;
|
const roomId = query.roomId;
|
||||||
try {
|
try {
|
||||||
if (typeof roomId !== "string") {
|
if (typeof roomId !== "string") {
|
||||||
@ -306,7 +308,7 @@ export class IoSocketController {
|
|||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (Axios.isAxiosError(err)) {
|
if (Axios.isAxiosError(err)) {
|
||||||
if (err?.response?.status == 404) {
|
if (err?.response?.status == 404 || !err?.response?.data.code) {
|
||||||
// If we get an HTTP 404, the token is invalid. Let's perform an anonymous login!
|
// If we get an HTTP 404, the token is invalid. Let's perform an anonymous login!
|
||||||
|
|
||||||
console.warn(
|
console.warn(
|
||||||
@ -314,16 +316,18 @@ export class IoSocketController {
|
|||||||
(userIdentifier || "anonymous") +
|
(userIdentifier || "anonymous") +
|
||||||
'". Performing an anonymous login instead.'
|
'". Performing an anonymous login instead.'
|
||||||
);
|
);
|
||||||
} else if (err?.response?.status == 403) {
|
} else if (err?.response?.data.code) {
|
||||||
// If we get an HTTP 403, the world is full. We need to broadcast a special error to the client.
|
//OLD // If we get an HTTP 403, the world is full. We need to broadcast a special error to the client.
|
||||||
// we finish immediately the upgrade then we will close the socket as soon as it starts opening.
|
//OLD // we finish immediately the upgrade then we will close the socket as soon as it starts opening.
|
||||||
return res.upgrade(
|
return res.upgrade(
|
||||||
{
|
{
|
||||||
rejected: true,
|
rejected: true,
|
||||||
message: err?.response?.data.message,
|
reason: "error",
|
||||||
|
message: err?.response?.data.code,
|
||||||
status: err?.response?.status,
|
status: err?.response?.status,
|
||||||
|
error: err?.response?.data,
|
||||||
roomId,
|
roomId,
|
||||||
},
|
} as UpgradeFailedData,
|
||||||
websocketKey,
|
websocketKey,
|
||||||
websocketProtocol,
|
websocketProtocol,
|
||||||
websocketExtensions,
|
websocketExtensions,
|
||||||
@ -476,8 +480,8 @@ export class IoSocketController {
|
|||||||
socketManager.emitTokenExpiredMessage(ws);
|
socketManager.emitTokenExpiredMessage(ws);
|
||||||
} else if (ws.reason === "textureInvalid") {
|
} else if (ws.reason === "textureInvalid") {
|
||||||
socketManager.emitInvalidTextureMessage(ws);
|
socketManager.emitInvalidTextureMessage(ws);
|
||||||
} else if (ws.message === "World is full") {
|
} else if (ws.reason === "error") {
|
||||||
socketManager.emitWorldFullMessage(ws);
|
socketManager.emitErrorV2Message(ws, ws.error.type, ws.error.code, ws.error.title, ws.error.subtitle, ws.error.details, ws.error.timeToRetry, ws.error.canRetryManual, ws.error.urlToRedirect, ws.error.buttonTitle);
|
||||||
} else {
|
} else {
|
||||||
socketManager.emitConnexionErrorMessage(ws, ws.message);
|
socketManager.emitConnexionErrorMessage(ws, ws.message);
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,11 @@ const isFetchMemberDataByUuidResponse = new tg.IsInterface()
|
|||||||
export type FetchMemberDataByUuidResponse = tg.GuardedType<typeof isFetchMemberDataByUuidResponse>;
|
export type FetchMemberDataByUuidResponse = tg.GuardedType<typeof isFetchMemberDataByUuidResponse>;
|
||||||
|
|
||||||
class AdminApi {
|
class AdminApi {
|
||||||
|
private locale: string = 'en';
|
||||||
|
setLocale(locale: string){
|
||||||
|
//console.info('PUSHER LOCALE SET TO :', locale);
|
||||||
|
this.locale = locale;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @var playUri: is url of the room
|
* @var playUri: is url of the room
|
||||||
* @var userId: can to be undefined or email or uuid
|
* @var userId: can to be undefined or email or uuid
|
||||||
@ -47,7 +52,7 @@ class AdminApi {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const res = await Axios.get<unknown, AxiosResponse<unknown>>(ADMIN_API_URL + "/api/map", {
|
const res = await Axios.get<unknown, AxiosResponse<unknown>>(ADMIN_API_URL + "/api/map", {
|
||||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
headers: { Authorization: `${ADMIN_API_TOKEN}`, 'Accept-Language': this.locale },
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
if (!isMapDetailsData(res.data) && !isRoomRedirect(res.data)) {
|
if (!isMapDetailsData(res.data) && !isRoomRedirect(res.data)) {
|
||||||
@ -75,7 +80,7 @@ class AdminApi {
|
|||||||
ipAddress,
|
ipAddress,
|
||||||
characterLayers,
|
characterLayers,
|
||||||
},
|
},
|
||||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
headers: { Authorization: `${ADMIN_API_TOKEN}`, 'Accept-Language': this.locale },
|
||||||
paramsSerializer: (p) => {
|
paramsSerializer: (p) => {
|
||||||
return qs.stringify(p, { arrayFormat: "brackets" });
|
return qs.stringify(p, { arrayFormat: "brackets" });
|
||||||
},
|
},
|
||||||
@ -96,7 +101,7 @@ class AdminApi {
|
|||||||
//todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case.
|
//todo: this call can fail if the corresponding world is not activated or if the token is invalid. Handle that case.
|
||||||
const res = await Axios.get(ADMIN_API_URL + "/api/login-url/" + organizationMemberToken, {
|
const res = await Axios.get(ADMIN_API_URL + "/api/login-url/" + organizationMemberToken, {
|
||||||
params: { playUri },
|
params: { playUri },
|
||||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
headers: { Authorization: `${ADMIN_API_TOKEN}`, 'Accept-Language': this.locale },
|
||||||
});
|
});
|
||||||
if (!isAdminApiData(res.data)) {
|
if (!isAdminApiData(res.data)) {
|
||||||
console.error("Message received from /api/login-url is not in the expected format. Message: ", res.data);
|
console.error("Message received from /api/login-url is not in the expected format. Message: ", res.data);
|
||||||
@ -123,7 +128,7 @@ class AdminApi {
|
|||||||
reportWorldSlug,
|
reportWorldSlug,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
headers: { Authorization: `${ADMIN_API_TOKEN}`, 'Accept-Language': this.locale },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -142,7 +147,7 @@ class AdminApi {
|
|||||||
encodeURIComponent(userUuid) +
|
encodeURIComponent(userUuid) +
|
||||||
"&roomUrl=" +
|
"&roomUrl=" +
|
||||||
encodeURIComponent(roomUrl),
|
encodeURIComponent(roomUrl),
|
||||||
{ headers: { Authorization: `${ADMIN_API_TOKEN}` } }
|
{ headers: { Authorization: `${ADMIN_API_TOKEN}`, 'Accept-Language': this.locale } }
|
||||||
).then((data) => {
|
).then((data) => {
|
||||||
return data.data;
|
return data.data;
|
||||||
});
|
});
|
||||||
@ -154,7 +159,7 @@ class AdminApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return Axios.get(ADMIN_API_URL + "/api/room/sameWorld" + "?roomUrl=" + encodeURIComponent(roomUrl), {
|
return Axios.get(ADMIN_API_URL + "/api/room/sameWorld" + "?roomUrl=" + encodeURIComponent(roomUrl), {
|
||||||
headers: { Authorization: `${ADMIN_API_TOKEN}` },
|
headers: { Authorization: `${ADMIN_API_TOKEN}`, 'Accept-Language': this.locale },
|
||||||
}).then((data) => {
|
}).then((data) => {
|
||||||
return data.data;
|
return data.data;
|
||||||
});
|
});
|
||||||
|
@ -39,7 +39,7 @@ import {
|
|||||||
WorldFullMessage,
|
WorldFullMessage,
|
||||||
PlayerDetailsUpdatedMessage,
|
PlayerDetailsUpdatedMessage,
|
||||||
LockGroupPromptMessage,
|
LockGroupPromptMessage,
|
||||||
InvalidTextureMessage,
|
InvalidTextureMessage, ErrorV2Message,
|
||||||
} from "../Messages/generated/messages_pb";
|
} from "../Messages/generated/messages_pb";
|
||||||
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
import { ProtobufUtils } from "../Model/Websocket/ProtobufUtils";
|
||||||
import { ADMIN_API_URL, JITSI_ISS, JITSI_URL, SECRET_JITSI_KEY } from "../Enum/EnvironmentVariable";
|
import { ADMIN_API_URL, JITSI_ISS, JITSI_URL, SECRET_JITSI_KEY } from "../Enum/EnvironmentVariable";
|
||||||
@ -644,6 +644,26 @@ export class SocketManager implements ZoneEventListener {
|
|||||||
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public emitErrorV2Message(client: compressors.WebSocket, type: string, code: string, title: string, subtitle: string, details: string, timeToRetry: number, canRetryManual: boolean, urlToRedirect: string, buttonTitle: string) {
|
||||||
|
const errorMessage = new ErrorV2Message();
|
||||||
|
errorMessage.setType(type);
|
||||||
|
errorMessage.setCode(code);
|
||||||
|
errorMessage.setTitle(title);
|
||||||
|
errorMessage.setSubtitle(subtitle);
|
||||||
|
errorMessage.setDetails(details);
|
||||||
|
errorMessage.setTimetoretry(timeToRetry);
|
||||||
|
errorMessage.setCanretrymanual(canRetryManual);
|
||||||
|
errorMessage.setUrltoredirect(urlToRedirect);
|
||||||
|
errorMessage.setButtontitle(buttonTitle);
|
||||||
|
|
||||||
|
const serverToClientMessage = new ServerToClientMessage();
|
||||||
|
serverToClientMessage.setErrorv2message(errorMessage);
|
||||||
|
|
||||||
|
//if (!client.disconnecting) {
|
||||||
|
client.send(serverToClientMessage.serializeBinary().buffer, true);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
private refreshRoomData(roomId: string, versionNumber: number): void {
|
private refreshRoomData(roomId: string, versionNumber: number): void {
|
||||||
const room = this.rooms.get(roomId);
|
const room = this.rooms.get(roomId);
|
||||||
//this function is run for every users connected to the room, so we need to make sure the room wasn't already refreshed.
|
//this function is run for every users connected to the room, so we need to make sure the room wasn't already refreshed.
|
||||||
|
Loading…
Reference in New Issue
Block a user