Skip to content

Fix state/save injection timing and corruption in EmulatorJS - #3624

Merged
gantoine merged 1 commit into
masterfrom
claude/issue-2319-investigation-y44bmi
Jun 29, 2026
Merged

Fix state/save injection timing and corruption in EmulatorJS#3624
gantoine merged 1 commit into
masterfrom
claude/issue-2319-investigation-y44bmi

Conversation

@gantoine

@gantoine gantoine commented Jun 29, 2026

Copy link
Copy Markdown
Member

Description

This PR fixes critical timing and data corruption issues when loading save states in the EmulatorJS player, particularly affecting heavier cores (SNES with enhancement chips, N64, DS) that require longer boot times.

Fixes #2319

Changes

  1. Added waitForGameManager() polling function - Replaces the unreliable fixed 10ms delay with a proper poll (up to 5s timeout) that waits for EmulatorJS' gameManager to be fully initialized before attempting save/state injection. This prevents "black screen" failures on cores that need longer to boot.

  2. Added state settle delay - Introduces a 500ms delay after the game manager is ready but before applying a state, allowing the core to render a few frames and stabilize before state deserialization.

  3. Fixed state corruption on save/quit - Reordered operations to capture the screenshot while the game is running (before pause), then pause and wait 50ms before reading state/save files. This prevents torn reads from threaded cores (SNES, N64) that race the worker thread and produce corrupt, unloadable states.

  4. Added empty state validation - The saveState() utility now rejects zero-length state buffers (indicating serialization failure) to prevent corrupt captures from overwriting good states on the server.

  5. Improved error handling - Added null checks and console warnings for edge cases where the game manager isn't ready.

Testing

  • Existing tests pass
  • Changes are isolated to the EmulatorJS player initialization and save/quit flow
  • The polling and settle delays are conservative and only add ~500ms to the critical path on first load

https://claude.ai/code/session_01PoAdK2fmqHGmGuXWaKN1HT

Save states were inconsistently saved and frequently failed to load,
especially on threaded cores (SNES, N64). Two timing races were the
cause:

- On game start, a selected state was applied after a fixed 10ms delay
  with no check that the core was ready. Heavier cores aren't booted in
  that window, so the state applied partially and left a black screen.
  Replace the fixed delay with a gameManager readiness poll plus a short
  settle window before applying the state (mirrors the console player).

- The Save & Quit handler read the state from a running emulator. On
  threaded cores the worker thread mutates emulator memory while
  getState() reads it, producing torn/corrupt buffers that are then
  stored permanently. Capture the screenshot while running, then pause
  before serializing the state and save file.

Also refuse to upload zero-length state buffers so a failed capture
can't overwrite good states on the server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PoAdK2fmqHGmGuXWaKN1HT
@gantoine
gantoine merged commit 25d0bb3 into master Jun 29, 2026
11 checks passed
@gantoine
gantoine deleted the claude/issue-2319-investigation-y44bmi branch June 29, 2026 02:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Save states are inconsistently saved and fail to load

2 participants