record: resync the view when the bot changes servers mid-recording - #70
Open
u9g wants to merge 2 commits into
Open
record: resync the view when the bot changes servers mid-recording#70u9g wants to merge 2 commits into
u9g wants to merge 2 commits into
Conversation
video.done's rejection was swallowed by a bare .catch, so if ffmpeg died mid-recording -- disk full, a crash, an outside kill -- nothing learned of it. The tick loop kept rendering at the duty cycle forever, writing into a closed pipe, and status still reported the recording as running. An exit before stop() now tears the recording down -- stops the loop, detaches the bot listeners, disposes the GL context -- and calls a new onEnd callback so the daemon drops the recording it holds. stop() sets a flag first so a deliberate stop is not mistaken for a crash, and becomes a no-op if ffmpeg has already gone. A write in flight when ffmpeg exits rejects async with EPIPE; a stdin error handler swallows it, since video.done is the signal that matters. stats() gains `ended`, carried in status, so a recording that stopped on its own is visible rather than a loop spinning against a dead encoder.
On a Bungee transfer or dimension change the server unloads the bot's world and streams a new one. mineflayer unloads every column of bot.world in place; the viewer's WorldView, which has no chunkColumnUnload listener, keeps the old geometry and never meshes the new world. A survival recording that transferred seven seconds after it began kept rendering the lobby it had left. The recorder now watches for a login followed by a spawn -- the signature of a transfer, as against a same-world death respawn, which spawns with no login -- and on the spawn drops the chunks the viewer still holds and reloads from the current world. This is the harness-side fix for the vendored viewer as it stands; prismarine-viewer handling the unload itself is the durable one.
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.
Stacked on #69 (base
record-ffmpeg-exit).A survival recording on 2026-09-08 transferred servers 7 s after it began and kept rendering the lobby it had left, for 42 minutes, until the daemon was OOM-killed.
Mechanism
On a Bungee transfer or dimension change mineflayer runs
switchWorld()(blocks.js). When the bot already has a world — the normal case — it takes the in-place branch: it callsbot.world.unloadColumn(x, z)for every column (emittingchunkColumnUnload) and swaps the storage provider, without replacingbot.world. So the recorder'sWorldView, constructed with that same world object, still reads the right world — but it has nochunkColumnUnloadlistener, so it never drops the geometry the server unloaded, and it never meshes the incoming columns.Change
startRecordingnow watches the bot for aloginimmediately followed by aspawn— the signature of a transfer, distinct from a same-world death respawn, which firesspawnwith no precedinglogin. On that spawn it unloads the chunks the viewer is still tracking and callsworldView.init(bot.entity.position)to reload from the current world. Both listeners are removed instop()and in the ffmpeg-exit teardown from #69.Scope
This is the harness-side fix for the vendored viewer as it stands. The durable fix is prismarine-viewer handling
chunkColumnUnloaditself (separate PR against PrismarineJS/prismarine-viewer); once that reaches the vendored build this becomes redundant, and the two are idempotent if both are present (unloading an already-unloaded chunk is a no-op).Test
Driving the
login/spawnhandlers with the victim's real event sequence: transfer then death respawn → 1 reset; two transfers → 2; three deaths, no login → 0.