diff --git a/src/battle-scene.ts b/src/battle-scene.ts index b0199980981..c537728442c 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -720,12 +720,10 @@ export class BattleScene extends SceneBase { } cachedFetch(url: string, init?: RequestInit): Promise { - const manifest = this.game["manifest"]; - if (manifest) { - const timestamp = manifest[`/${url.replace("./", "")}`]; - if (timestamp) { - url += `?t=${timestamp}`; - } + const { manifest } = this.game; + const timestamp = manifest?.[`/${url.replace("./", "")}`]; + if (timestamp) { + url += `?t=${timestamp}`; } return fetch(url, init); } diff --git a/src/loading-scene.ts b/src/loading-scene.ts index a4cbaf9ae64..d87faa322a4 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -25,7 +25,6 @@ export class LoadingScene extends SceneBase { preload() { localPing(); - this.load["manifest"] = this.game["manifest"]; this.loadImage("loading_bg", "arenas"); this.loadImage("logo", ""); diff --git a/src/main.ts b/src/main.ts index 9903a257af6..7fdb2a7a024 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,7 @@ -import "#app/polyfills"; -// All polyfills MUST be loaded first for side effects +import "#app/polyfills"; // All polyfills MUST be loaded first for side effects +import { BattleScene } from "#app/battle-scene"; +import { LoadingScene } from "#app/loading-scene"; import { InvertPostFX } from "#app/pipelines/invert"; import { initI18n } from "#app/plugins/i18n"; import { isBeta, isDev } from "#constants/app-constants"; @@ -31,6 +32,11 @@ window.addEventListener("unhandledrejection", event => { //alert(errorString); }); +interface GuideObject + extends Pick, + Pick, + Pick {} + /** * Set this object's position relative to another object with a given offset. * @param guideObject - The object to base this object's position off of; must have defined @@ -41,9 +47,7 @@ window.addEventListener("unhandledrejection", event => { */ function setPositionRelative( this: T, - guideObject: Pick & - Pick & - Pick, + guideObject: GuideObject, x: number, y: number, ): T { @@ -59,17 +63,9 @@ Phaser.GameObjects.NineSlice.prototype.setPositionRelative = setPositionRelative Phaser.GameObjects.Text.prototype.setPositionRelative = setPositionRelative; Phaser.GameObjects.Rectangle.prototype.setPositionRelative = setPositionRelative; -document.fonts.load("16px emerald").then(() => document.fonts.load("10px pkmnems")); -// biome-ignore lint: TODO -let game; -// biome-ignore lint: TODO -let manifest; - -const startGame = async () => { +async function startGame(gameManifest?: Record): Promise { await initI18n(); - const LoadingScene = (await import("./loading-scene")).LoadingScene; - const BattleScene = (await import("./battle-scene")).BattleScene; - game = new Phaser.Game({ + const game = new Phaser.Game({ type: Phaser.WEBGL, parent: "app", scale: { @@ -121,22 +117,18 @@ const startGame = async () => { version, }); game.sound.pauseOnBlur = false; - if (manifest) { - game["manifest"] = manifest; - } -}; + game.manifest = gameManifest; +} -fetch("/manifest.json") - .then(res => res.json()) - .then(jsonResponse => { - manifest = jsonResponse.manifest; - }) - .catch(err => { - // Manifest not found (likely local build or path error on live) - console.log(`Manifest not found. ${err}`); - }) - .finally(() => { - startGame(); - }); - -export default game; +let manifest: Record | undefined; +try { + const loadFonts = Promise.all([document.fonts.load("16px emerald"), document.fonts.load("10px pkmnems")]); + const [jsonResponse] = await Promise.all([fetch("/manifest.json").then(r => r.json()), loadFonts]); + manifest = jsonResponse.manifest; +} catch (err) { + // Manifest not found (likely local build or path error on live) + // TODO: Do we want actual error handling here? + console.log("Manifest not found:", err); +} finally { + await startGame(manifest); +} diff --git a/src/plugins/cache-busted-loader-plugin.ts b/src/plugins/cache-busted-loader-plugin.ts index da0850c613c..3509f4fb55c 100644 --- a/src/plugins/cache-busted-loader-plugin.ts +++ b/src/plugins/cache-busted-loader-plugin.ts @@ -1,28 +1,27 @@ +import { globalScene } from "#app/global-scene"; import { coerceArray } from "#utils/array"; -let manifest: object; - export class CacheBustedLoaderPlugin extends Phaser.Loader.LoaderPlugin { - get manifest() { - return manifest; - } + addFile(files: Phaser.Loader.File | Phaser.Loader.File[]): void { + files = coerceArray(files); + const { manifest } = globalScene.game; - set manifest(manifestObj: object) { - manifest = manifestObj; - } + if (!manifest) { + super.addFile(files); + return; + } - addFile(file): void { - file = coerceArray(file); - - file.forEach(item => { - if (manifest) { - const timestamp = manifest[`/${item.url.replace(/\/\//g, "/")}`]; - if (timestamp) { - item.url += `?t=${timestamp}`; - } + for (const item of files) { + if (typeof item.url !== "string") { + continue; } - }); - super.addFile(file); + const timestamp = manifest[`/${item.url.replace(/\/\//g, "/")}`]; + if (timestamp) { + item.url += `?t=${timestamp}`; + } + } + + super.addFile(files); } } diff --git a/src/scene-base.ts b/src/scene-base.ts index 26a680f2162..72acca868ad 100644 --- a/src/scene-base.ts +++ b/src/scene-base.ts @@ -18,12 +18,16 @@ export class SceneBase extends Phaser.Scene { }; getCachedUrl(url: string): string { - const manifest = this.game["manifest"]; - if (manifest) { - const timestamp = manifest[`/${url}`]; - if (timestamp) { - url += `?t=${timestamp}`; - } + const manifest = this.game.manifest; + if (!manifest) { + return url; + } + + // TODO: This is inconsistent with how the battle scene cached fetch + // uses the manifest + const timestamp = manifest[`/${url}`]; + if (timestamp) { + url += `?t=${timestamp}`; } return url; } @@ -40,10 +44,7 @@ export class SceneBase extends Phaser.Scene { } } - loadSpritesheet(key: string, folder: string, size: number, filename?: string) { - if (!filename) { - filename = `${key}.png`; - } + loadSpritesheet(key: string, folder: string, size: number, filename = `${key}.png`) { this.load.spritesheet(key, this.getCachedUrl(`images/${folder}/${filename}`), { frameWidth: size, frameHeight: size, @@ -58,10 +59,7 @@ export class SceneBase extends Phaser.Scene { } } - loadAtlas(key: string, folder: string, filenameRoot?: string) { - if (!filenameRoot) { - filenameRoot = key; - } + loadAtlas(key: string, folder: string, filenameRoot = key) { if (folder) { folder += "/"; } diff --git a/src/typings/phaser/index.d.ts b/src/typings/phaser/index.d.ts index 275aafba353..aa4d257f9e0 100644 --- a/src/typings/phaser/index.d.ts +++ b/src/typings/phaser/index.d.ts @@ -51,4 +51,9 @@ declare module "phaser" { } } } + + interface Game { + /** A manifest used to cache various files requested from the server. */ + manifest?: Record; + } } diff --git a/src/utils/cookies.ts b/src/utils/cookies.ts index c95c777243a..b3ab554e6c7 100644 --- a/src/utils/cookies.ts +++ b/src/utils/cookies.ts @@ -1,8 +1,11 @@ import { isBeta } from "#constants/app-constants"; +// 90 days +const COOKIE_EXPIRATION_BUFFER = 3600000 * 24 * 30 * 3; + export function setCookie(cName: string, cValue: string): void { const expiration = new Date(); - expiration.setTime(Date.now() + 3600000 * 24 * 30 * 3 /*7*/); + expiration.setTime(Date.now() + COOKIE_EXPIRATION_BUFFER); document.cookie = `${cName}=${cValue};Secure;SameSite=Strict;Domain=${window.location.hostname};Path=/;Expires=${expiration.toUTCString()}`; }