gate: don't strand the terrain generator; bound the splash's waits - #104
Open
dsrw wants to merge 1 commit into
Open
gate: don't strand the terrain generator; bound the splash's waits#104dsrw wants to merge 1 commit into
dsrw wants to merge 1 commit into
Conversation
Loading a level from inside another one (a sign link running load_level) left the splash up for the full spawn-gate timeout, with the level fully loaded behind it, one unit reporting work that never arrived. The node drops the terrain's generator and stream on RESETTING and restores them on its removal, but a reset adds and removes the flag inside one worker tick, and the node is created in the middle of that window. It can see the `added` as its watch replaying an already-set flag while the `removed` that preceded registration never arrives as an edge, leaving the terrain with no generator at all. That turns streaming off wholesale in VoxelTerrain::_process: nothing is requested, has_stream_started() stays false, and the unit publishes the not-started floor forever. Derive it from the flag instead. Then bound both halves of the gate, so no single unit can hold the splash again: - the not-started floor expires after 3s (pairing takes a frame or two) and logs the unit it dropped, with whether it had a generator - the warm-up lifts after 1s even if frames_drawn never advances -- a minimized or occluded window never draws, and the splash used to stay up forever with nothing logged - the settle timeout drops to 10s and the error names the units that were still pending, not just the whole gate set
dsrw
force-pushed
the
fix/spawn-gate-stranded-generator
branch
from
July 27, 2026 21:02
cb821ea to
ccdbbd7
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Loading a level from inside another one (clicking a sign link that evals
load_level("welcome")) held the splash for the full 30s spawn-gate timeout, then revealed withspawn gate: deadline exceeded. The level had finished loading well before that — the process was completely idle for the whole wait (snapshots_delta=0, no meshing), withbuild_waterthe only unit still reporting pending work.Root cause
build_nodedrops the terrain's generator and stream onRESETTING.addedand restores them onRESETTING.removed. ButBuild.reset()adds and removesRESETTINGinside one worker tick, and a build's node is created in the middle of that window — its script runs right after the thing joinsstate.things. The node can see theadded, as its watch replaying a flag that is already set, while theremovedthat preceded its registration never arrives as an edge.The terrain is then left with no generator and no stream at all, which turns streaming off wholesale in
VoxelTerrain::_process(stream_enabledfalse): no blocks are ever requested,has_stream_started()never becomes true, and the unit publishes the not-started floor of 1 forever with no work behind it. The spawn gate waits on that until it times out.keep_generator_in_syncnow derives the generator from the flag each frame instead of trusting its edges.Evidence: booting
tutorial-3stranded three units deterministically —build_3npxfmddql9kx,build_mlsxf11t2oaz6,build_wevpat48mxjheeach logged "stream never started" on both pre-fix runs. After the fix, none, across every run since.Bounding the gate
So no single unit can hold the splash again:
get_frames_drawn()never advances. It waits for two real draws behind the splash, which needs the engine to actually be drawing; a minimized or occluded window (every MCP-managed and test instance) freezes that counter, and the splash never lifted at all, with nothing logged. This was a second, independent way to hang the splash forever.blocking=["build_water=1"]) instead of just the whole gate set.Testing
nim test_allpasses. Switch timings for tutorial-3 → welcome: 628ms with a drawing window, 1.58s minimized (previously: 30s timeout, or never).🤖 Generated with Claude Code