Skip to content

fix: prevent ~2.5-min world-exit freeze after teleporting far along the train - #579

Open
bh679 wants to merge 3 commits into
mainfrom
dev/world-exit-teardown-hang
Open

fix: prevent ~2.5-min world-exit freeze after teleporting far along the train#579
bh679 wants to merge 3 commits into
mainfrom
dev/world-exit-teardown-hang

Conversation

@bh679

@bh679 bh679 commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Leaving a Dungeon Train world (go-to-title / Board anew / Quit Game) after the player
has teleported/travelled far along the +X corridor froze the client for ~158 s before it
recovered, ending with DeathScreenLayout: pre-drain wait timed out
TimeoutException at preDrainTrainSubLevels.

Root cause — a pre-existing Sable teardown hang (same class as the known Save-and-Quit hang
= Sable #679
). The world-exit pre-drain busy-spun container.tick() for ~1 s/level but never
reflectively evicted the PlotChunkHolders that leak past that drain, so the integrated-server
stopServer wait loop spun on ChunkMap.hasWork() == true. The repo already had the proven fix,
but only on the ServerStopping path (ShipShutdownEvents). This makes the world-exit path use
the same hardened drain.

Changes

  • New event/TrainSubLevelTeardown.java — the proven Sable feat: Books-written tally on the death screen #679 drain extracted from
    ShipShutdownEvents: delete trains → bounded pump → reflective PlotChunkHolder sweep →
    vanilla-holder drain (after save(flush=true)). Shared by both teardown paths.
  • event/ShipShutdownEvents.java — refactored to a thin ServerStopping subscriber delegating
    to the helper (no behaviour change; ~249 → ~80 lines).
  • client/DeathScreenLayoutHandler.javapreDrainTrainSubLevels now runs the hardened drain
    with a 3 s global server-thread pump budget (under the existing 10 s client give-up). The
    sweep/drain run even when pump is squeezed to 0 — that's what lets hasWork() go false. Covers
    all three callers (go-to-title, board-anew, quit-to-desktop).
  • train/Trains.javabyTrainId log demoted DEBUG → TRACE (was ~471 lines/s during the
    teleport burst).

Mod-impact

No new deps, no version/mixin/registry/worldgen/networking changes. Touches Sable teardown timing
only. Reflective updatingChunkMap access is the same already-shipped technique as
ShipShutdownEvents/ShutdownDiagnostics (try/catch → warn, never throws).

Test plan

  • ./gradlew build — green; all 88 unit-test classes pass.
  • ./gradlew runClient — loads clean to title; a real world-exit ran the new path and
    drained 2360 vanilla ChunkHolders with no pre-drain wait timed out and an immediate
    ServerStopped.
  • Train-loaded repro (/tp @s ~3000 ~ ~, ride to spawn carriages, then go-to-title) — expect
    swept N PlotChunkHolders and a ~1–3 s return to title (no freeze). Sable: exercised on the
    moving train.

🤖 Generated with Claude Code

bh679 and others added 3 commits June 25, 2026 12:55
…he train

Leaving a world (go-to-title / board-anew / quit) after teleporting far along
the +X corridor froze the client ~158s. The world-exit pre-drain
(DeathScreenLayoutHandler.preDrainTrainSubLevels) busy-spun container.tick() but
never reflectively evicted leaked Sable PlotChunkHolders, so the integrated-server
stopServer wait loop spun on ChunkMap.hasWork()=true (same class as Sable #679).

- Extract the proven Sable #679 drain from ShipShutdownEvents into a shared
  TrainSubLevelTeardown helper: delete trains -> bounded pump -> reflective
  PlotChunkHolder sweep -> vanilla-holder drain after pre-save.
- preDrainTrainSubLevels now runs that hardened drain with a 3s global
  server-thread pump budget (under the existing 10s client give-up); the
  sweep/drain run even when pump is squeezed to 0, so hasWork() can go false.
- ShipShutdownEvents refactored to delegate to the shared helper (no behaviour
  change at ServerStopping).
- Demote Trains.byTrainId DEBUG log to TRACE (~471 lines/s during teleport burst).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
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.

1 participant