#4135·mineflayer

Design: shared interaction, client tick, and viewer lifecycle foundations

Author: rom1504Created Sep 18, 2026Updated Sep 18, 2026

Astra agent design proposal — AI-generated, not manually written by the maintainer.

Prepared at the maintainer's request after reviewing the PR families in #4119. This proposes shared foundations that could make U9G's changes smaller, easier to validate and easier to maintain. It is a proposal for discussion, not a declaration that all affected PRs must wait for a redesign.

The affected PRs below were checked as open on 2026-09-18. These are related families, not counts of PRs automatically unblocked by an extraction.

Scope and ownership

PrismarineJS's independent packages should retain clear responsibilities (package organization discussion):

  • minecraft-data owns versioned facts and schemas; prismarine-registry combines those facts with negotiated server state.
  • prismarine-world / prismarine-chunk own world access and column representation; prismarine-entity owns reusable entity state and attribute behavior.
  • node-minecraft-protocol owns protocol transport and connection phases.
  • Mineflayer owns bot actions, local action scheduling and translation between packets and models.
  • Viewer owns rendering and render-resource lifetimes, consuming world/entity state.

Start with internal modules and existing APIs. An extraction should identify its inputs, state owner and actual callers before adding another plugin or package. Each preparatory PR should preserve existing behavior where possible; behavior fixes and public API changes can then be reviewed separately.

1. Share the interaction implementation in Mineflayer

Related PRs: #4021, #4085, #4089, #4094, #4114, #4115, #4118. Pathfinder's bridge work is a downstream consumer.

Repeated problem

Activation, placement, entity interaction and digging assemble related behavior in separate paths: hand selection, prediction sequences, hit coordinates, rotation and swing order. For example, #4085 shares prediction sequencing across several actions but boat placement in #4114 remains a separate sender. #4115 computes a hit before awaiting a look operation, after which the original hit can be stale; its aim-point generation also needs actual partial-block shapes.

Proposed boundary

Introduce a small internal interaction module used by the existing plugins:

  • Shared item-use and block-use senders own packet construction and consume the appropriate prediction sequence. Entity interaction retains its distinct packet semantics; dig start/finish share the sequence owner where required.
  • Sequence policy distinguishes actions that consume a value from abort/release paths that intentionally use zero. A shared counter must cover all actual consumers, including boats.
  • A block hit travels as one coherent result: reference block, face and intersection, with the eye/look state used to obtain it. Use the existing world raycast and actual block shapes. Revalidate after an awaited look or movement before using that hit to send an action.
  • Preserve explicit cursor/force-look overrides and existing public behavior. Any stricter reach or visibility rejection is a separate API decision.

Keep reusable facts in their existing owners: effective attributes in prismarine-entity (existing review); replaceability in data/registry (existing review). The bot's packet sequencing remains in Mineflayer.

First implementation slice and checks

Extract the common item-use sender and migrate activateItem and boat placement first. Then migrate block-use paths and their shared sequence consumption, followed by geometry-related improvements.

Verify interleaved use/place/dig/boat sequences, full outgoing packets on representative version boundaries, slab/stair hits, and movement during awaited aiming. Use existing NMP client/server tests for packet contracts and world-shape tests for geometry; server-observed placement remains the check for a placement-success claim.

2. Separate the client tick from physics simulation

Related PRs: #4063, #4064, #4093, #4107, #4108, #4116.

Repeated problem

physics.js currently combines the timer, simulation, reply scheduling, input state and movement emission. Several PRs change the boundaries independently. Examples include sprint-stop synchronization being gated by simulation, dismount waiting for a physics event when simulation is disabled, and queued teleport work surviving a lifecycle transition.

Proposed boundary

Extract an internal client-tick coordinator. Give reply processing, input synchronization, optional physics simulation and outgoing movement explicit stages and preconditions. Determine exact packet ordering per supported version from the actual protocol/client behavior.

  • Protocol replies must have their own readiness rules; lack of a loaded chunk must not accidentally suppress required replies.
  • Simulation can be disabled without making control releases or other required protocol work depend on physicsTick.
  • Requested controls, simulated player state and last-sent state remain distinct. Resetting a sent-state cache must not silently release held user input.
  • Each queued operation has explicit invalidation rules for newer work, respawn, reconfiguration and disconnect. Do not assume every reply has the same lifetime or indiscriminately clear every queue on every event.
  • World-load readiness used by player_loaded remains a world-scoped condition. Entering play alone does not mean the entity and its column are ready.

NMP continues to own connection phases; Mineflayer observes them to govern bot work. Preserve the existing public physicsTick / waitForTicks contracts unless a separate API change is agreed.

First implementation slice and checks

Extract ordered reply scheduling and cancellation first, starting with #4107. Then move input synchronization out of the simulation-only path and make the reset rules explicit.

Check interleaved ping/teleport arrivals, a newer teleport superseding delayed work, reconfiguration/respawn before drain, unloaded chunks, disabled simulation, and held controls across lifecycle changes. Assert actual emitted packet order with the existing NMP fixture.

3. Give viewer state and asynchronous work explicit ownership

Related PRs: viewer #484, #488, #503, #506, #513, #514.

This cross-repository section lives here because #4119 tracks the whole set; implementation belongs in viewer and the appropriate model owners.

Repeated problem

A bounds response, column fetch or texture load can finish after its target has changed. Separately, mesh existence is sometimes used as a substitute for entity state, so a position-only update can recreate an invisible entity. Readiness currently observes only some of the work required for a complete frame.

Proposed boundary

  • Give work a world generation and, where necessary, a per-column/per-entity revision. A world generation alone cannot distinguish unload/reload at the same coordinate within one world.
  • Check ownership before applying a result, including the bounds-loading callback itself. Define cleanup for resources allocated by a job that becomes obsolete.
  • Carry actual column/dimension bounds through the worker boundary; a Minecraft version is insufficient to determine custom-world bounds.
  • Preserve logical entity state independently of whether a mesh exists. Merge partial updates without treating omitted fields as resets, and use the upstream model where possible rather than inventing another authority.
  • Register required render work when it is scheduled. A readiness barrier must cover that work, report required-resource failures and define what happens if its world is replaced. Entity assets needed for the promised first frame must be scheduled before waiting for that frame.

First implementation slice and checks

Start with bounds and column-load ownership across #484, #488 and #513. Then extend the mechanism to asset readiness and entity resources.

Hold loader/worker responses, replace the world or unload/reload a coordinate, and complete the old work last. Also cover custom negative bounds, invisible entity → movement-only update, removal during an item-texture load, and required-asset rejection. Use production renderer/THREE checks for ownership; retain a rendered-frame check for first-frame readiness.

Suggested landing order

  1. Land #4126's existing internal-test split. Eighteen other PRs in the reviewed pending set edit test/internalTest.js; move their tests to the new layout as they are rebased. Preserve the common server/bot fixture.
  2. Agree on the interaction boundary and implement the small shared item-use sender with #4085 / #4114 as its first consumers.
  3. Adapt the rest of the interaction family incrementally, keeping shared facts in data/registry/entity owners.
  4. Develop the tick and viewer foundations independently, each with its first consumers and regression checks.

These are incremental designs. Existing bounded fixes can still land when their own contracts, dependencies and tests are satisfied. The useful outcome is fewer independently maintained implementations of the same behavior and smaller later reviews.

Design questions to settle here: the smallest useful interaction API; which tick work remains active with simulation disabled; and the precise ownership/readiness contract across viewer world replacement.