Skip to content

External inputs enter a world once per tick and are recorded ​

Status: accepted

Anything outside a world's tick reaches that world as an input: inbound packets, chunk completions, and the results of offloaded work such as pathfinding, lighting or encoding. A world applies its inputs in one step at the start of its tick. Inside the tick nothing reads the wall clock, and every limit on work is a count. The wall clock may decide only when an input is admitted to a tick, as the 2 ms drain of the chunk completion queue does. Each admitted input is recorded in the replay trace with its tick number, and a replay admits exactly the recorded set.

This is what makes the replay contract in ADR 0006 hold. Today an inbound handler can run between the awaits of World.update, and capture stamps a frame that arrives mid-tick with the current tick while replay injects it at the start of that tick, so live play and its replay can differ. Block updates and scheduled ticks also stop on performance.now(), with budgets of 20 ms and 25 ms. Those limits become counts. An overloaded host then ticks slower, as Java does, and never simulates differently.

Not every inbound packet is an input. A handler declares one of three lanes, and the lane decides when it runs.

  • The connection lane is for a handler that touches only its own network session, such as network settings, cache status, latency and resource pack transfer. It runs when the packet arrives and never enters a world.
  • The read lane is for a handler that reads authoritative state, writes none, and replies only to the connection that asked, such as a sub-chunk request. It runs when the packet arrives, between ticks, against the state the last tick closed with. It may wait for a chunk, and the chunk still enters the world as an input. Its replies are not recorded, because they cannot change a digest.
  • The input lane is for every handler that writes authoritative state or produces something other players see. Only these packets are inputs, applied in the input step and recorded.

A connection's packets keep their order across lanes. A later packet of any lane does not overtake an earlier packet of the same connection that is still held for the input step, because a sub-chunk request that follows a move has to see the move. A step shares its budget across connections, one input from each in turn, so one connection's backlog cannot hold back another's inputs by whole ticks. Every input a connection sends before a step is applied in that step while its share of the budget lasts, in arrival order. A connection may hold as many inputs as one step applies, and a live input past that is dropped with a warning, as Bedrock Dedicated Server's packet rate limiter rejects a packet over its limit instead of closing the connection. A connection-lane or read-lane packet that follows a held input runs once the step that applies that input is over, and holds back nothing the connection sends after it: later inputs are still held for that step, and later connection-lane and read-lane packets keep their order behind it. That step can also apply inputs the connection sent after the read, so the read sees state at least as new as it asked about. Such a read waits for the step that takes that input. For a connection alone on the server that is the next step, since its bound is what one step applies. When several connections hold inputs, each step gives each of them an equal share of the budget, so a read behind a long backlog waits several ticks: behind a full backlog, about one tick for each connection that holds inputs. Packets from other connections still run when they arrive. Because an input can be applied after something the server did between its arrival and its step, a position the client reported before a server teleport is dropped until an input carries the HandledTeleport flag at the teleport's target or the hold times out, as Mojang's packet documentation asks. PlayerSession.holdsMovementForTeleport keeps the rule. PlayerAuthInputPacket is the only packet a client moves with. A client's MovePlayerPacket moved a player only in the movement mode protocol 818 removed, so it is read in the connection lane and dropped; the research note has the evidence.

Without the read lane, chunk streaming would wait for the next tick and slow down with the tick rate. The read lane depends on the handler and the tick sharing a thread. A world that moves to its own thread serves these reads from replication's replica (ADR 0016) and the chunk payload cache instead.

The rule says nothing about threads. It describes how a world talks to anything outside it, which is what lets pathfinding, replication or a whole world run elsewhere later without changing the model.