From eb5bdb07a830fded873443b5a89c2c1eefefb533 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Wed, 10 Apr 2024 01:32:49 -0400 Subject: [PATCH] Add outage handling --- src/account.ts | 15 +++-- src/phases.ts | 84 ++++++++++++++++---------- src/system/game-data.ts | 4 +- src/ui/ui.ts | 10 ++- src/ui/unavailable-modal-ui-handler.ts | 68 +++++++++++++++++++++ 5 files changed, 139 insertions(+), 42 deletions(-) create mode 100644 src/ui/unavailable-modal-ui-handler.ts diff --git a/src/account.ts b/src/account.ts index b1f008e5a..104ad1570 100644 --- a/src/account.ts +++ b/src/account.ts @@ -8,8 +8,8 @@ export interface UserInfo { export let loggedInUser: UserInfo = null; -export function updateUserInfo(): Promise { - return new Promise(resolve => { +export function updateUserInfo(): Promise<[boolean, integer]> { + return new Promise<[boolean, integer]>(resolve => { if (bypassLogin) { let lastSessionSlot = -1; for (let s = 0; s < 2; s++) { @@ -19,18 +19,21 @@ export function updateUserInfo(): Promise { } } loggedInUser = { username: 'Guest', lastSessionSlot: lastSessionSlot }; - return resolve(true); + return resolve([ true, 200 ]); } Utils.apiFetch('account/info').then(response => { + console.log(response.status); if (!response.ok) { - loggedInUser = null; - resolve(false); + resolve([ false, response.status ]); return; } return response.json(); }).then(jsonResponse => { loggedInUser = jsonResponse; - resolve(true); + resolve([ true, 200 ]); + }).catch(err => { + console.error(err); + resolve([ false, 500 ]); }); }); } \ No newline at end of file diff --git a/src/phases.ts b/src/phases.ts index 489a8c768..b8414e6b7 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -69,39 +69,48 @@ export class LoginPhase extends Phase { start(): void { super.start(); - this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] }); - Utils.executeIf(bypassLogin || !!Utils.getCookie(Utils.sessionIdKey), updateUserInfo).then(success => { - if (!success) { - if (this.showText) - this.scene.ui.showText('Log in or create an account to start. No email required!'); - - this.scene.playSound('menu_open'); + const hasSession = !!Utils.getCookie(Utils.sessionIdKey); - const loadData = () => { - updateUserInfo().then(() => this.scene.gameData.loadSystem().then(() => this.end())); - }; + this.scene.ui.setMode(Mode.LOADING, { buttonActions: [] }); + Utils.executeIf(bypassLogin || hasSession, updateUserInfo).then(response => { + const success = response ? response[0] : false; + const statusCode = response ? response[1] : null; + if (!success) { + if (!statusCode || statusCode === 400) { + if (this.showText) + this.scene.ui.showText('Log in or create an account to start. No email required!'); - this.scene.ui.setMode(Mode.LOGIN_FORM, { - buttonActions: [ - () => { - this.scene.ui.playSelect(); - loadData(); - }, () => { - this.scene.playSound('menu_open'); - this.scene.ui.setMode(Mode.REGISTRATION_FORM, { - buttonActions: [ - () => { - this.scene.ui.playSelect(); - updateUserInfo().then(() => this.end()); - }, () => { - this.scene.unshiftPhase(new LoginPhase(this.scene, false)); - this.end(); - } - ] - }); - } - ] - }); + this.scene.playSound('menu_open'); + + const loadData = () => { + updateUserInfo().then(() => this.scene.gameData.loadSystem().then(() => this.end())); + }; + + this.scene.ui.setMode(Mode.LOGIN_FORM, { + buttonActions: [ + () => { + this.scene.ui.playSelect(); + loadData(); + }, () => { + this.scene.playSound('menu_open'); + this.scene.ui.setMode(Mode.REGISTRATION_FORM, { + buttonActions: [ + () => { + this.scene.ui.playSelect(); + updateUserInfo().then(() => this.end()); + }, () => { + this.scene.unshiftPhase(new LoginPhase(this.scene, false)); + this.end(); + } + ] + }); + } + ] + }); + } else { + this.scene.unshiftPhase(new UnavailablePhase(this.scene)); + super.end(); + } return null; } else { this.scene.gameData.loadSystem().then(success => { @@ -336,6 +345,19 @@ export class TitlePhase extends Phase { } } +export class UnavailablePhase extends Phase { + constructor(scene: BattleScene) { + super(scene); + } + + start(): void { + this.scene.ui.setMode(Mode.UNAVAILABLE, () => { + this.scene.unshiftPhase(new LoginPhase(this.scene, true)); + this.end(); + }); + } +} + export class SelectGenderPhase extends Phase { constructor(scene: BattleScene) { super(scene); diff --git a/src/system/game-data.ts b/src/system/game-data.ts index fbf94776e..248a37051 100644 --- a/src/system/game-data.ts +++ b/src/system/game-data.ts @@ -224,8 +224,8 @@ export class GameData { public saveSystem(): Promise { return new Promise(resolve => { this.scene.ui.savingIcon.show(); - updateUserInfo().then((success: boolean) => { - if (!success) { + updateUserInfo().then(response => { + if (!response[0]) { this.scene.ui.savingIcon.hide(); return resolve(false); } diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 3a0442558..422a0ab3e 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -33,6 +33,7 @@ import AwaitableUiHandler from './awaitable-ui-handler'; import SaveSlotSelectUiHandler from './save-slot-select-ui-handler'; import TitleUiHandler from './title-ui-handler'; import SavingIconHandler from './saving-icon-handler'; +import UnavailableModalUiHandler from './unavailable-modal-ui-handler'; export enum Mode { MESSAGE, @@ -61,7 +62,8 @@ export enum Mode { EGG_GACHA, LOGIN_FORM, REGISTRATION_FORM, - LOADING + LOADING, + UNAVAILABLE }; const transitionModes = [ @@ -87,7 +89,8 @@ const noTransitionModes = [ Mode.VOUCHERS, Mode.LOGIN_FORM, Mode.REGISTRATION_FORM, - Mode.LOADING + Mode.LOADING, + Mode.UNAVAILABLE ]; export default class UI extends Phaser.GameObjects.Container { @@ -137,7 +140,8 @@ export default class UI extends Phaser.GameObjects.Container { new EggGachaUiHandler(scene), new LoginFormUiHandler(scene), new RegistrationFormUiHandler(scene), - new LoadingModalUiHandler(scene) + new LoadingModalUiHandler(scene), + new UnavailableModalUiHandler(scene) ]; } diff --git a/src/ui/unavailable-modal-ui-handler.ts b/src/ui/unavailable-modal-ui-handler.ts new file mode 100644 index 000000000..4481746c7 --- /dev/null +++ b/src/ui/unavailable-modal-ui-handler.ts @@ -0,0 +1,68 @@ +import BattleScene from "../battle-scene"; +import { ModalConfig, ModalUiHandler } from "./modal-ui-handler"; +import { addTextObject, TextStyle } from "./text"; +import { Mode } from "./ui"; +import { updateUserInfo } from "#app/account"; + +export default class UnavailableModalUiHandler extends ModalUiHandler { + private reconnectTimer: number; + private reconnectCallback: () => void; + + constructor(scene: BattleScene, mode?: Mode) { + super(scene, mode); + } + + getModalTitle(): string { + return ''; + } + + getWidth(): number { + return 160; + } + + getHeight(): number { + return 64; + } + + getMargin(): [number, number, number, number] { + return [ 0, 0, 48, 0 ]; + } + + getButtonLabels(): string[] { + return [ ]; + } + + setup(): void { + super.setup(); + + const label = addTextObject(this.scene, this.getWidth() / 2, this.getHeight() / 2, 'Oops! There was an issue contacting the server.\n\nYou may leave this window open,\nthe game will automatically reconnect.', TextStyle.WINDOW, { fontSize: '48px', align: 'center' }); + label.setOrigin(0.5, 0.5); + + this.modalContainer.add(label); + } + + show(args: any[]): boolean { + if (args.length >= 1 && args[0] instanceof Function) { + const config: ModalConfig = { + buttonActions: [] + }; + + this.reconnectCallback = args[0]; + + this.reconnectTimer = setInterval(() => { + updateUserInfo().then(response => { + if (response[0] || [200, 400].includes(response[1])) { + clearInterval(this.reconnectTimer); + this.reconnectTimer = null; + this.scene.playSound('pb_bounce_1'); + this.reconnectCallback(); + } + }) + }, 5000); + + return super.show([ config ]); + } + + return false; + } +} \ No newline at end of file