From 29aa1a68e785409faf47dfb8836a123a066ec7c2 Mon Sep 17 00:00:00 2001 From: flx-sta <50131232+flx-sta@users.noreply.github.com> Date: Mon, 29 Jul 2024 21:28:43 -0700 Subject: [PATCH] [Optimization] Destroy LoadingScene when done (after beta rebase) (#3215) * destroy loading-scene when done - unused event listeners are shut off - children are removed - `loading_bg` will be removed after transition is finished - Added some simple types for `rex` plugins * fix tests * fix pokemonSprite.test.ts on local runs it would include hidden dirs like `.DS_store`. Any files starting with `.` is now excluded * add `mockGameObjectCreator` and use in `gameWrapper` * add battle-scene.test.ts add test to verify that LoadingScene is being removed on `BatleScene.create()` call * update types usage for phaser3-rex-plugins * remove phaser-extensions.d.ts fk you typedoc... --- src/battle-scene.ts | 10 +++++-- src/loading-scene.ts | 22 ++++++++++++-- src/test/battle-scene.test.ts | 27 +++++++++++++++++ src/test/sprites/pokemonSprite.test.ts | 29 ++++++++++--------- src/test/utils/gameWrapper.ts | 9 ++---- src/test/utils/mocks/mockGameObjectCreator.ts | 23 +++++++++++++++ 6 files changed, 95 insertions(+), 25 deletions(-) create mode 100644 src/test/battle-scene.test.ts create mode 100644 src/test/utils/mocks/mockGameObjectCreator.ts diff --git a/src/battle-scene.ts b/src/battle-scene.ts index 9f8612633fc..0ac3f9c6245 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -69,6 +69,7 @@ import { TimedEventManager } from "#app/timed-event-manager.js"; import i18next from "i18next"; import {TrainerType} from "#enums/trainer-type"; import { battleSpecDialogue } from "./data/dialogue"; +import { LoadingScene } from "./loading-scene"; export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1"; @@ -320,6 +321,7 @@ export default class BattleScene extends SceneBase { } create() { + this.scene.remove(LoadingScene.KEY); initGameSpeed.apply(this); this.inputController = new InputsController(this); this.uiInputs = new UiInputs(this, this.inputController); @@ -370,7 +372,7 @@ export default class BattleScene extends SceneBase { this.fieldUI = fieldUI; - const transition = (this.make as any).rexTransitionImagePack({ + const transition = this.make.rexTransitionImagePack({ x: 0, y: 0, scale: 6, @@ -378,11 +380,15 @@ export default class BattleScene extends SceneBase { origin: { x: 0, y: 0 } }, true); + //@ts-ignore (the defined types in the package are incromplete...) transition.transit({ mode: "blinds", ease: "Cubic.easeInOut", duration: 1250, - oncomplete: () => transition.destroy() + }); + transition.once("complete", () => { + this.textures.remove("loading_bg"); + transition.destroy(); }); this.add.existing(transition); diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 61178144ded..5275411055e 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -24,10 +24,12 @@ import { Biome } from "#enums/biome"; import { TrainerType } from "#enums/trainer-type"; export class LoadingScene extends SceneBase { + public static readonly KEY = "loading"; + readonly LOAD_EVENTS = Phaser.Loader.Events; constructor() { - super("loading"); + super(LoadingScene.KEY); Phaser.Plugins.PluginCache.register("Loader", CacheBustedLoaderPlugin, "load"); initI18n(); @@ -434,7 +436,7 @@ export class LoadingScene extends SceneBase { } const intro = this.add.video(0, 0); - intro.on(Phaser.GameObjects.Events.VIDEO_COMPLETE, (video: Phaser.GameObjects.Video) => { + intro.once(Phaser.GameObjects.Events.VIDEO_COMPLETE, (video: Phaser.GameObjects.Video) => { this.tweens.add({ targets: intro, duration: 500, @@ -482,7 +484,10 @@ export class LoadingScene extends SceneBase { } }); - this.load.on(this.LOAD_EVENTS.COMPLETE, () => loadingGraphics.forEach(go => go.destroy())); + this.load.on(this.LOAD_EVENTS.COMPLETE, () => { + loadingGraphics.forEach(go => go.destroy()); + intro.destroy(); + }); } get gameHeight() { @@ -494,6 +499,17 @@ export class LoadingScene extends SceneBase { } async create() { + this.events.once(Phaser.Scenes.Events.DESTROY, () => this.handleDestroy()); this.scene.start("battle"); } + + handleDestroy() { + console.debug(`Destroying ${LoadingScene.KEY} scene`); + this.load.off(this.LOAD_EVENTS.PROGRESS); + this.load.off(this.LOAD_EVENTS.FILE_COMPLETE); + this.load.off(this.LOAD_EVENTS.COMPLETE); + // this.textures.remove("loading_bg"); is removed in BattleScene.launchBattle() + this.children.removeAll(true); + console.debug(`Destroyed ${LoadingScene.KEY} scene`); + } } diff --git a/src/test/battle-scene.test.ts b/src/test/battle-scene.test.ts new file mode 100644 index 00000000000..21d3f689d1c --- /dev/null +++ b/src/test/battle-scene.test.ts @@ -0,0 +1,27 @@ +import { LoadingScene } from "#app/loading-scene.js"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import GameManager from "./utils/gameManager"; + +describe("BattleScene", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + it("should remove LoadingScene on create", () => { + // `BattleScene.create()` is called during the `new GameManager()` call + expect(game.scene.scene.remove).toHaveBeenCalledWith(LoadingScene.KEY); + }); +}); diff --git a/src/test/sprites/pokemonSprite.test.ts b/src/test/sprites/pokemonSprite.test.ts index 52b20017715..0fd725b2f58 100644 --- a/src/test/sprites/pokemonSprite.test.ts +++ b/src/test/sprites/pokemonSprite.test.ts @@ -1,20 +1,21 @@ -import {beforeAll, describe, expect, it} from "vitest"; +import { beforeAll, describe, expect, it } from "vitest"; import _masterlist from "../../../public/images/pokemon/variant/_masterlist.json"; import fs from "fs"; import path from "path"; -import {getAppRootDir} from "#app/test/sprites/spritesUtils"; +import { getAppRootDir } from "#app/test/sprites/spritesUtils"; -const deepCopy = (data) => { +type PokemonVariantMasterlist = typeof _masterlist; + +const deepCopy = (data: any) => { return JSON.parse(JSON.stringify(data)); }; - describe("check if every variant's sprite are correctly set", () => { - let masterlist; - let expVariant; - let femaleVariant; - let backVariant; - let rootDir; + let masterlist: PokemonVariantMasterlist; + let expVariant: PokemonVariantMasterlist["exp"]; + let femaleVariant: PokemonVariantMasterlist["female"]; + let backVariant: PokemonVariantMasterlist["back"]; + let rootDir: string; beforeAll(() => { rootDir = `${getAppRootDir()}${path.sep}public${path.sep}images${path.sep}pokemon${path.sep}variant${path.sep}`; @@ -34,11 +35,11 @@ describe("check if every variant's sprite are correctly set", () => { expect(backVariant).not.toBeUndefined(); }); - function getMissingMasterlist(mlist, dirpath, excludes = []) { - const errors = []; + function getMissingMasterlist(mlist: any, dirpath: string, excludes: string[] = []): string[] { + const errors: string[] = []; const trimmedDirpath = `variant${path.sep}${dirpath.split(rootDir)[1]}`; if (fs.existsSync(dirpath)) { - const files = fs.readdirSync(dirpath).filter(filename => !filename.startsWith(".")); + const files = fs.readdirSync(dirpath).filter((filename) => !/^\..*/.test(filename)); for (const filename of files) { const filePath = `${dirpath}${filename}`; const trimmedFilePath = `${trimmedDirpath}${filename}`; @@ -101,12 +102,12 @@ describe("check if every variant's sprite are correctly set", () => { return errors; } - function getMissingFiles(keys, dirPath) { + function getMissingFiles(keys: Record, dirPath: string): string[] { const errors = []; for (const key of Object.keys(keys)) { const row = keys[key]; for (const [index, elm] of row.entries()) { - let url; + let url: string; if (elm === 0) { continue; } else if (elm === 1) { diff --git a/src/test/utils/gameWrapper.ts b/src/test/utils/gameWrapper.ts index b1b00c8e15d..de32215fabb 100644 --- a/src/test/utils/gameWrapper.ts +++ b/src/test/utils/gameWrapper.ts @@ -25,6 +25,7 @@ import {MoveAnim} from "#app/data/battle-anims"; import Pokemon from "#app/field/pokemon"; import * as battleScene from "#app/battle-scene"; import MockImage from "#app/test/utils/mocks/mocksContainer/mockImage.js"; +import { MockGameObjectCreator } from "./mocks/mockGameObjectCreator"; Object.defineProperty(window, "localStorage", { value: mockLocalStorage(), @@ -223,13 +224,9 @@ export default class GameWrapper { return resolve(response); }); }; - this.scene.make = { - graphics: (config) => new MockGraphics(mockTextureManager, config), - rexTransitionImagePack: () => ({ - transit: () => null, - }), - }; + this.scene.make = new MockGameObjectCreator(mockTextureManager); this.scene.time = new MockClock(this.scene); + this.scene.remove = vi.fn(); } } diff --git a/src/test/utils/mocks/mockGameObjectCreator.ts b/src/test/utils/mocks/mockGameObjectCreator.ts new file mode 100644 index 00000000000..19406a46923 --- /dev/null +++ b/src/test/utils/mocks/mockGameObjectCreator.ts @@ -0,0 +1,23 @@ +import { vi } from "vitest"; +import MockGraphics from "./mocksContainer/mockGraphics"; +import MockTextureManager from "./mockTextureManager"; + +export class MockGameObjectCreator { + private readonly textureManager: MockTextureManager; + + constructor(textureManager: MockTextureManager) { + console.log("Mocking Phaser.GameObjects.GameObjectCreator;"); + this.textureManager = textureManager; + } + + graphics(config: any) { + return new MockGraphics(this.textureManager, config); + } + + rexTransitionImagePack() { + return { + transit: vi.fn(), + once: vi.fn(), + }; + } +}