diff --git a/client/src/scenes/GameScene.ts b/client/src/scenes/GameScene.ts index 4b9a0c6..3845cde 100644 --- a/client/src/scenes/GameScene.ts +++ b/client/src/scenes/GameScene.ts @@ -130,13 +130,12 @@ export class GameScene extends Phaser.Scene { }).setScrollFactor(0).setDepth(1000); this.updateModeText(); - // Spawn initial entities - this.spawnEntities(this.worldState.entities); - - // Listen for state updates - this.client.onStateUpdate = (update: StateUpdate) => { - this.handleStateUpdate(update); - }; + // Spawn initial entities, then listen for state updates + this.spawnEntities(this.worldState.entities).then(() => { + this.client.onStateUpdate = (update: StateUpdate) => { + this.handleStateUpdate(update); + }; + }); this.client.onPlayerLeft = (_data) => { // Entity removal will be handled by next state update diff --git a/client/src/sprites/CharacterCompositor.ts b/client/src/sprites/CharacterCompositor.ts index 463cf55..1732391 100644 --- a/client/src/sprites/CharacterCompositor.ts +++ b/client/src/sprites/CharacterCompositor.ts @@ -15,7 +15,7 @@ function appearanceKey(appearance: Appearance): string { export class CharacterCompositor { private scene: Phaser.Scene; - private loadedImages: Set = new Set(); + private loadingImages: Map> = new Map(); constructor(scene: Phaser.Scene) { this.scene = scene; @@ -35,33 +35,32 @@ export class CharacterCompositor { // Already composited if (this.scene.textures.exists(key)) return key; - // Collect all image paths needed - const imagesToLoad: { key: string; path: string }[] = []; + // Collect all image keys needed and ensure each is loaded const skinKey = `skin_${appearance.skinId}`; - if (!this.scene.textures.exists(skinKey) && !this.loadedImages.has(skinKey)) { - imagesToLoad.push({ key: skinKey, path: this.skinPath(appearance.skinId) }); - this.loadedImages.add(skinKey); + const pendingLoads: Promise[] = []; + + if (!this.scene.textures.exists(skinKey)) { + if (!this.loadingImages.has(skinKey)) { + this.loadingImages.set(skinKey, this.loadImage(skinKey, this.skinPath(appearance.skinId))); + } + pendingLoads.push(this.loadingImages.get(skinKey)!); } for (const slot of ACCESSORY_SLOTS) { const fileId = appearance.accessories[slot]; if (!fileId) continue; const accKey = `acc_${slot}_${fileId}`; - if (!this.scene.textures.exists(accKey) && !this.loadedImages.has(accKey)) { - imagesToLoad.push({ key: accKey, path: this.accessoryPath(slot, fileId) }); - this.loadedImages.add(accKey); + if (!this.scene.textures.exists(accKey)) { + if (!this.loadingImages.has(accKey)) { + this.loadingImages.set(accKey, this.loadImage(accKey, this.accessoryPath(slot, fileId))); + } + pendingLoads.push(this.loadingImages.get(accKey)!); } } - // Load any missing images - if (imagesToLoad.length > 0) { - await new Promise((resolve) => { - for (const img of imagesToLoad) { - this.scene.load.image(img.key, img.path); - } - this.scene.load.once('complete', resolve); - this.scene.load.start(); - }); + // Wait for all pending loads to complete + if (pendingLoads.length > 0) { + await Promise.all(pendingLoads); } // Composite onto offscreen canvas @@ -96,6 +95,14 @@ export class CharacterCompositor { return key; } + private loadImage(key: string, path: string): Promise { + return new Promise((resolve) => { + this.scene.load.image(key, path); + this.scene.load.once(`filecomplete-image-${key}`, () => resolve()); + this.scene.load.start(); + }); + } + getKey(appearance: Appearance): string { return appearanceKey(appearance); }