<!-- https://minecraftdocs.dev/systems/world/scheduled-ticks — this page as markdown. The book's index: https://minecraftdocs.dev/llms.txt; the whole book in one file: https://minecraftdocs.dev/llms-full.txt -->

# Scheduled ticks

> Verified against **Minecraft 26.3** · Part IV · A repeater's input goes high, and the two ticks before its output follows are one entry in a queue.

A repeater set to its shortest delay is not counting anything. When the wire
behind it changes, `DiodeBlock.neighborChanged`, which `RepeaterBlock`
inherits, runs on the Server thread,
notices that `DiodeBlock.POWERED` no longer matches the input, and books an
appointment — a `ScheduledTick` naming this `Block` at this `BlockPos`, due
two game ticks from now, at `TickPriority.HIGH` — and then forgets. Two ticks
later a scheduler the repeater has never heard of hands the position back to
`ServerLevel.tickBlock`, which checks that a repeater is still there and
calls the block again. Nearly everything the world does *later* is one of
these: a fluid flowing, a pressure plate releasing, a dispenser firing, a
redstone torch burning out. They all share two queues per chunk
and one rule that surprises everybody: **the queue dedups on type and
position alone, so a second tick for the same block — even a much sooner
one — is silently dropped.**

"Rescheduling moves the tick" is folklore. `ScheduledTick.UNIQUE_TICK_HASH`
hashes the position and compares the type by object identity — the trigger
tick, the priority and the sub-order are no part of it — and
`LevelChunkTicks.schedule` queues a tick only if its dedup set,
`LevelChunkTicks.ticksPerPosition`, did not already hold that pair. The
booking that loses is not queued, not merged and not logged, which is why so
many blocks ask before they book: `DiodeBlock`, `ComparatorBlock` and
`RedstoneTorchBlock` consult `LevelTickAccess.willTickThisTick`, and seven
blocks — `ObserverBlock`, `TargetBlock`, `LightningRodBlock`, `TripWireBlock`,
`SculkSensorBlock`, `DriedGhastBlock` and `SpeleothemBlock` — consult
`TickAccess.hasScheduledTick`.

## The cast

| class | what it decides | thread |
|---|---|---|
| `ScheduledTick` | the appointment itself — type, position, trigger tick, `TickPriority`, sub-order — and the comparisons the system sorts and dedups by | a record, no thread |
| `LevelChunkTicks` | one chunk's queue and its dedup set: whether a booking is new, and which of its ticks is next | Server |
| `LevelTicks` | the per-level scheduler: which chunks are due, which ticks run this level tick, and where the budget falls | Server |
| `ScheduledTickAccess` | the write side every block sees, so no block knows which container it is booking into | any — the worldgen executor books through it |
| `ServerLevel` | when the drain runs, the per-chunk gate it runs under, and the type re-check that makes a tick cancellable | Server |
| `SavedTick` | the disk form: a *relative* delay in place of an absolute time | Server, written by the IO worker |
| `ProtoChunkTicks` | a generating chunk's bookings, all at delay zero | the worldgen executor |
| `BlackholeTickAccess` | accept every booking and run nothing — the client's answer to the whole system, and also an `ImposterProtoChunk`'s | Render, and the worldgen executor |

Those containers implement one small interface stack — `TickAccess`
(schedule, ask, count), `TickContainerAccess` per chunk, `LevelTickAccess` per
level, which also answers `LevelTickAccess.willTickThisTick`, and
`SerializableTickContainer` for one that can `SerializableTickContainer.pack`
itself for disk — which is why `LiquidBlock` and `RepeaterBlock` run unchanged
during generation, on a client that will never tick them, and on a server that
will.

## The pipeline, end to end

The pipeline has a seam in it, booking and the drain, so it is two figures. This
is booking into a live level; the worldgen executor books into a generating chunk's
own list instead ([below](#appointments-that-survive-a-restart)).

```mermaid
flowchart TD
    B["a block books: ScheduledTickAccess.scheduleTick"]
    C["LevelAccessor.createTick — game time plus delay, a TickPriority, a sub-order"]
    S{"LevelTicks.schedule looks for the chunk's container"}
    DROP["Util.logAndPauseIfInIde — logged and dropped, never deferred"]
    D{"LevelChunkTicks.ticksPerPosition already holds this type and position"}
    SKIP["dropped in silence, even when this one is sooner"]
    Q[("one priority queue per chunk, ordered by ScheduledTick.DRAIN_ORDER")]
    B --> C
    C --> S
    S -- "no container" --> DROP
    S -- "found" --> D
    D -- "yes" --> SKIP
    D -- "no" --> Q
```

*Everything that can happen to an appointment booked into a live level before
it is a queued tick — and two of the three outcomes are that it is not one.
The right-hand refusal is the
page's subject: the dedup slot is checked against the type and position alone,
so the second booking is thrown away whether or not it is due sooner than the
one already there.*

The drain is the other half, it runs once per level tick per type, and it never
leaves the Server thread.

```mermaid
flowchart TD
    IDX[("LevelTicks.nextTickForContainer: one entry per container, the head's time")]
    SC{"LevelTicks.sortContainersToTick asks LevelTicks.tickCheck"}
    DR["LevelTicks.drainContainers, while the budget lasts"]
    RUN["LevelTicks.runCollectedTicks, to ServerLevel.tickBlock or ServerLevel.tickFluid"]
    CL["LevelTicks.cleanupAfterTick empties all four working collections"]
    AGAIN["a block books again, from inside its own run"]
    IDX --> SC
    SC -- "the chunk is not ticking" --> IDX
    SC -- "due, and ticking" --> DR
    DR -- "overtaken, budget left" --> DR
    DR -- "not due, or the budget spent" --> IDX
    DR --> RUN
    RUN --> CL
    RUN --> AGAIN
```

*The three phases of one drain, and the ways back to the index, none of them a
failure: a container whose next tick is not yet due waits for it, and one
whose chunk is not ticking or whose budget ran out is late, never lost. The
bottom-right box is the loop this page's repeater lives in — the run books the
next appointment, which re-enters the figure above.*

Everything below is one stage of those two figures.

## Booking: a type, a position, a time and a tie-breaker

A block calls one of the `ScheduledTickAccess.scheduleTick` defaults with a
delay in ticks and, optionally, a `TickPriority`. The default asks the level
for `LevelAccessor.createTick`, which stamps the appointment with
`LevelAccessor.getGameTime` plus the delay, the priority (`TickPriority.NORMAL` if
none was given) and a fresh sub-order from `LevelAccessor.nextSubTickCount`,
then hands it to `ScheduledTickAccess.getBlockTicks` or
`ScheduledTickAccess.getFluidTicks`. Two type *arguments*, two parallel
worlds: every class here — `LevelTicks`, `LevelChunkTicks`, `ScheduledTick` —
declares one parameter, and it is filled with `Block` in one world and `Fluid`
in the other, so a block tick and a fluid tick never meet and `ServerLevel`
owns one `LevelTicks` of each.

The sub-order is the FIFO tie-breaker for two ticks at the same time and
priority, and it carries the one threading fact of this page.
`Level.subTickCount` is a plain counter incremented by
`Level.nextSubTickCount`, because a level's scheduler is touched only from the
Server thread; `WorldGenRegion.subTickCount` is an atomic one, because
generation books ticks off the Server thread, on the worldgen executor. **The drain is server-thread
only. Booking is not.**

`TickPriority` runs `TickPriority.EXTREMELY_HIGH` (−3) through
`TickPriority.NORMAL` (0) to `TickPriority.EXTREMELY_LOW` (3), lower first,
and it is not a queue-jump across time: a `TickPriority.LOW` tick due now
still beats a `TickPriority.EXTREMELY_HIGH` tick due next tick. Priority only
settles ties between ticks already due together.

Fluids are the scheduler's largest customer by a distance, and they exploit
the dedup key: `Fluids.WATER` and `Fluids.FLOWING_WATER` are different
registry objects, so one tick of each can be pending at one position. What
those ticks then *do* is [fluids](fluids).

## Where an appointment waits

Every `LevelChunk` owns exactly two containers, `LevelChunk.blockTicks` and
`LevelChunk.fluidTicks`, and they are the only place a live chunk's tick waits
until a drain collects it ([the four shapes a chunk
takes](chunk-anatomy#the-four-shapes-a-chunk-takes)): a priority queue,
`LevelChunkTicks.tickQueue`, in `ScheduledTick.DRAIN_ORDER`, beside the dedup
set that decides what gets into it.

`LevelTicks` never scans those queues looking for work. It keeps
`LevelTicks.allContainers`, chunk key to container, and
`LevelTicks.nextTickForContainer`, chunk key to the earliest trigger time that
chunk holds, defaulting for an unknown chunk to the largest possible long. The
index is maintained by `LevelTicks.chunkScheduleUpdater`, the callback every
container is handed through `LevelChunkTicks.setOnTickAdded` when it
registers, and it acts only when the tick just added *is* the container's new
head. **A chunk with nothing due costs one map entry and one comparison per
level tick, and a chunk with an empty queue costs nothing at all** — which is
what makes tens of thousands of loaded chunks affordable.

Registration follows the chunk's life exactly: `ChunkStatusTasks.full` calls
`LevelChunk.registerTickContainerInLevel` (`LevelTicks.addContainer`, both
types), and `ServerLevel.unload` calls
`LevelChunk.unregisterTickContainerFromLevel` → `LevelTicks.removeContainer`,
which also drops the callback so an orphaned container can no longer touch the
index. In between, a tick aimed at a chunk with no registered container is
neither deferred nor queued: `LevelTicks.schedule` finds nothing, calls
`Util.logAndPauseIfInIde`, and the appointment ceases to exist. `ClientLevel`
short-circuits all of it — `ClientLevel.getBlockTicks` and
`ClientLevel.getFluidTicks` return `BlackholeTickAccess.emptyLevelList`, which
accepts every booking, answers false to every question and runs nothing, so
everything a client sees of a flowing fluid or a firing repeater arrives as
block-update packets. (`ContainerSingleItem` also lives in `world/ticks`, a
one-slot inventory interface with nothing to do with ticks — a packaging
accident, noted only so it does not confuse you.)

## What one drain actually does

`ServerLevel.tick` calls `LevelTicks.tick` twice in its *tickPending*
section, blocks first and then fluids, each with the current game time and a
budget of `ServerLevel.MAX_SCHEDULED_TICKS_PER_TICK`, 65536 — a budget per
call, so 65536 block ticks *and* 65536 fluid ticks ([the level
tick](../server/server-level-tick#scheduled-ticks-twice-and-a-promise-to-the-same-block)).
The whole section is skipped in a
debug world and whenever `TickRateManager.runsNormally` is false. Each call
is three phases.

**Collect.** `LevelTicks.sortContainersToTick` walks the index, not the
containers: it leaves a future entry alone, deletes one whose container has
vanished or emptied, corrects one whose head is later than the index claimed,
and tests a genuinely due one against `LevelTicks.tickCheck`. That predicate
is `ServerLevel.isPositionTickingWithEntitiesLoaded`, asked about the
**chunk**, not the position, and true only when the chunk is in
`DistanceManager.inBlockTickingRange`, its
`ChunkHolder.getTickingChunkFuture` has already succeeded, and
`PersistentEntitySectionManager.areEntitiesLoaded` holds ([two graphs, one
store](tickets-and-loading#two-graphs-one-store)). A chunk that fails keeps its index entry
untouched and is asked again next tick — its ticks are late, never lost — and
one that passes moves into `LevelTicks.containersToTick`, a priority queue of
*containers* ordered by `LevelTicks.CONTAINER_DRAIN_ORDER`, on their heads.

`LevelTicks.drainContainers` polls the best container, takes one tick and
hands to `LevelTicks.drainFromCurrentContainer`, which keeps pulling from that
same container while its next tick is still due and still beats the next-best
container's head — containers are re-heaped only when the winner stops
winning. Where a container goes next turns on the budget as much as on its
own head. **Overtaken but still due, with budget left**: back into the
container queue, to be polled again this tick. **Head no longer due**: back to
the index at its head's time. **Still due but the budget is spent**: back to
the index, and asked again next tick.
**Drained empty**: neither. Then
`LevelTicks.rescheduleLeftoverContainers` runs over whatever is still sitting
in the container queue when the drain stopped and returns each to the index at
its head's trigger time, already in the past — which gets it *collected* next
tick but buys it no place in the order, because
`LevelTicks.CONTAINER_DRAIN_ORDER` compares priority and sub-order and has no
time term at all.

**Run.** `LevelTicks.runCollectedTicks` drains `LevelTicks.toRunThisTick` in
order, moving each entry to `LevelTicks.alreadyRunThisTick` and handing its
position and type to `ServerLevel.tickBlock` or `ServerLevel.tickFluid`. Both
re-read the world there and run `BlockBehaviour.BlockStateBase.tick` or
`FluidState.tick` only if the block or fluid is still the one the appointment
named. **A tick is a promise to a type**, and that check is the whole of
cancellation for anything a block does: break the block and its pending ticks
evaporate with no cancellation code anywhere. The only code that removes a
pending tick outright is bulk — `LevelChunkTicks.removeIf`, through
`LevelTicks.clearArea` below. **Clean up** is
`LevelTicks.cleanupAfterTick`, emptying all four working collections including
`LevelTicks.toRunThisTickSet`, which is built lazily and only if somebody asks
`LevelTicks.willTickThisTick`. That list is the whole difference between the
two questions a block can ask. `TickAccess.hasScheduledTick`
reads the container and answers *is one booked*; `LevelTickAccess.willTickThisTick`
reads this already-collected list and answers *is one about to run in this very
level tick* — a thing `TickAccess.hasScheduledTick` can no longer see, because
`LevelChunkTicks.poll` freed the slot during collect.

### The comparisons, and which is used where

| comparison | compares | used by |
|---|---|---|
| `ScheduledTick.DRAIN_ORDER` | trigger tick, then priority, then sub-order | the priority queue inside every `LevelChunkTicks` |
| `ScheduledTick.INTRA_TICK_DRAIN_ORDER` | priority, then sub-order — **no time term** | `LevelTicks.drainFromCurrentContainer`, comparing two already-due ticks |
| `LevelTicks.CONTAINER_DRAIN_ORDER` | the same, applied to two containers' heads | `LevelTicks.containersToTick` |
| `LevelChunkTicks.SUB_TICK_ORDERING` | sub-order alone | `LevelChunkTicks.pack`, on save only |
| `ScheduledTick.UNIQUE_TICK_HASH` | not an ordering — identity on type and position | `LevelChunkTicks.ticksPerPosition` and `LevelTicks.toRunThisTickSet` |

Time drops out of the second comparison because a container reaches the
collect queue only once its head is already due, and once everything in play
is due, time no longer discriminates.

Separating the phases has two consequences. A tick booked *during* the run
phase lands in its container after collect has finished, so it waits for a
later drain even at delay zero — the one exception being a block tick that
books a **fluid** tick at delay zero, which the fluid drain, running
afterwards in the same `ServerLevel.tick`, still catches. And the dedup slot
is released by `LevelChunkTicks.poll` during *collect*, not at run, so a tick
may book its own successor from inside its own run: exactly how a fluid keeps
flowing and how a repeater arms its turn-off.

That the run list outlives the run is what makes bulk edits correct across the
phase boundary. `LevelTicks.copyAreaFrom`, called by `/clone` in
`CloneCommands`, harvests matching ticks from `LevelTicks.alreadyRunThisTick`,
from `LevelTicks.toRunThisTick` *and* from the containers in the area, then
re-bases every sub-order above the highest it found so copies keep their
relative order without colliding with the originals. `LevelTicks.clearArea`
does the mirror image for the gametest framework
(`StructureUtils.clearSpaceForStructure`, `GameTestInfo`). Both touch block
ticks only — nothing in the game copies or clears fluid ticks by area.

## A repeater, appointment by appointment

A repeater with `RepeaterBlock.DELAY` 1 — `RepeaterBlock.getDelay` doubles
it, so two game ticks — with a redstone wire behind it that goes to 15 and
then back to 0 one tick later.

```mermaid
sequenceDiagram
    participant SL as ServerLevel
    participant RB as RepeaterBlock
    participant LTs as LevelTicks
    participant LCTs as LevelChunkTicks

    SL->>RB: neighborChanged — the wire behind went to 15
    RB->>RB: DiodeBlock.<br/>checkTickOnNeighbor<br/>— unlocked, unpowered,<br/>so it should turn on
    RB->>LTs: willTickThisTick at this position? no
    RB->>SL: scheduleTick — delay 2, TickPriority.HIGH
    SL->>SL: createTick — gameTime plus 2, HIGH, nextSubTickCount
    SL->>LTs: schedule
    LTs->>LCTs: schedule — the dedup slot is free, the queue takes it
    LCTs-->>LTs: updateContainerScheduling — the index learns gameTime plus 2
    Note over SL,RB: next tick, the wire drops to 0 — the diode books nothing and cancels nothing
    Note over SL,LCTs: two ticks after the booking, in the level tick's pending-block phase
    LTs->>LCTs: poll — the tick leaves the queue and the dedup set
    LTs->>SL: tickBlock at this position, for Blocks.REPEATER
    SL->>RB: still a repeater, so<br/>BlockBehaviour.<br/>BlockStateBase.tick
    RB->>SL: setBlock POWERED true, with Block.UPDATE_CLIENTS
    SL->>RB: the section write lands, then DiodeBlock.onPlace
    RB->>SL: neighborChanged on the block it powers, then that block's other sides
    RB->>SL: scheduleTick again — the turn-off, at TickPriority.VERY_HIGH
```

*One repeater, from the wire behind it changing to the appointment for turning
itself off again. The first note bar is the tick the page is built on — the
wire went back to zero and the booking already made was neither cancelled nor
noticed — and the second opens the tick in which the appointment runs.*

**The booking is a priority the block chooses, not one this queue assigns.**
The repeater above asks for `TickPriority.HIGH` to turn on and
`TickPriority.VERY_HIGH` to turn off, and which of the seven each redstone
block asks for in which situation — and why a comparator cannot ask for the
two most urgent at all — is [diodes and the
observer](../blocks/diodes-and-observers#booking-a-turn-and-why-a-repeater-turns-off-first).
What this page supplies is what the priority then buys: the sub-tick order
above.

**A booking cannot be called off.** Nothing in the game cancels a single
scheduled tick: the only removal is the gametest framework's bulk
`LevelTicks.clearArea` above. So a booking made while the input was
present survives the input going away, and the block finds out only when its
turn comes. That is the queue's half of pulse extension; what a repeater does
when it gets that turn is the diode page's.

**Nothing here uses `Block.UPDATE_NEIGHBORS`** ([block update
flags](../../reference/block-update-flags) has the bits), so the write in
the diagram above wakes no neighbour through `Level.setBlock`'s neighbour
channel; only its shape pass runs. How a
diode's signal leaves instead is [diodes and the
observer](../blocks/diodes-and-observers#a-diode-never-writes-into-its-target).

## The other kind of turn: random ticks

The appointment book is one of the ways a block gets a turn, and the contrast
with random ticks is what defines it: a random tick is booked by nobody, aimed
at no block, and
carries no promise.

It also reaches a different set of chunks, and the difference is one ring.
Random ticks walk `ChunkMap.forEachBlockTickingChunk`, which despite its name
is the *entity*-ticking set; the scheduled-tick gate,
`ServerLevel.isPositionTickingWithEntitiesLoaded`, reads the wider
block-ticking radius instead. So at the edge of a player's simulation distance
there is a ring of chunks where appointments still come due and nothing is
ever chosen at random. How the level tick then spends the rule's number, and
why a section of solid stone costs nothing, belongs to [the level
tick](../server/server-level-tick#random-ticks-are-counted-per-section-and-empty-sections-are-free).

Two things about that walk belong here, because they are about what a *promise*
is worth. The first is that the eligible blocks are decided at bootstrap, not
per tick: `BlockBehaviour.BlockStateBase.isRandomlyTicking` is baked into the
state at `BlockBehaviour.BlockStateBase.initCache` from
`BlockBehaviour.isRandomlyTicking`, which defaults to
`BlockBehaviour.Properties.randomTicks`, so unlike an appointment, which names
a type and is checked against the world when it comes due, a random tick's
eligibility is settled before the world exists.

The second is that **lava gets its random tick twice.** `LiquidBlock.isRandomlyTicking` and
`LiquidBlock.randomTick` both delegate straight to the fluid, so a chosen lava
position runs `LavaFluid.randomTick` once through the block branch of that
loop and again through the fluid branch. It is the only fluid it happens to:
`Fluid.isRandomlyTicking` is false by default and `LavaFluid.isRandomlyTicking`
is the sole override. Water is never randomly ticked at all — every inch of
its motion is a scheduled tick.

## Appointments that survive a restart

A `SavedTick` stores a **relative** delay rather than an absolute time, so a
chunk that unloads while the world runs on fires its ticks the same number of
ticks after it starts ticking again:
`ScheduledTick.toSavedTick` subtracts the current game time on the way out and
`SavedTick.unpack` adds the new one on the way back. `LevelChunkTicks.pack`
writes the pending list first and then the live queue sorted by
`LevelChunkTicks.SUB_TICK_ORDERING`, and `SerializableChunkData` stores the two
lists under *block_ticks* and *fluid_ticks* through `SavedTick.codec` ([copy on
the server, encode on a worker, write on the IO
lane](chunk-storage#copy-on-the-server-encode-on-a-worker-write-on-the-io-lane)). On the way in,
`SavedTick.filterTickListForChunk` discards any saved tick whose position is
not in the chunk being loaded.

Coming back is two-stage. `SerializableChunkData` builds a `LevelChunkTicks`
for a chunk at `ChunkStatus.FULL` and a `ProtoChunkTicks` for one below it, and
the `LevelChunkTicks` constructor holds the saved list as
`LevelChunkTicks.pendingTicks` — *not* in the queue — while pre-seeding the
dedup set from it, so a fresh booking cannot double up a saved one that is not
unpacked yet. The saved ticks enter the queue only when `ChunkMap.prepareTickingChunk` reaches
`ServerLevel.startTickingChunk` → `LevelChunk.unpackTicks` →
`LevelChunkTicks.unpack`, which counts sub-orders up from minus the list's
length. **Every unpacked tick gets a negative sub-order**, and
`Level.subTickCount` starts each session at zero and only rises, so a loaded
tick always sorts before anything this session booked at the same time and
priority.

Generation takes the same road. A `ProtoChunk` holds `ProtoChunkTicks`, which
records everything at delay **zero** and dedups under
`SavedTick.UNIQUE_TICK_HASH`, and `WorldGenRegion` exposes both of its
containers as a `WorldGenTickAccess` — a router that finds the right chunk per
position and answers `LevelTickAccess.willTickThisTick` with a flat false. At
promotion `ProtoChunk.unpackBlockTicks` turns that list into a
`LevelChunkTicks` in the pending state, and it becomes real at the same
`ServerLevel.startTickingChunk` a loaded chunk goes through — due immediately,
at the game time the chunk started ticking.

## Questions players ask

**Why did breaking one block stop a machine that was two ticks from firing?**
A tick names a type. The appointment stays in the queue and still runs, but
`ServerLevel.tickBlock` re-reads the position, finds a different block, and
runs it to nothing.

**Does `/tick freeze` stop scheduled ticks?** Yes, and among the tick
commands it is the only one that does — a debug world skips the section too.
`TickRateManager.runsNormally` returns `TickRateManager.runGameElements`,
recomputed every tick as *not frozen, or stepping* — so `/tick step` runs the
*tickPending* section normally for the ticks it steps, and `/tick sprint`
clears the freeze flag outright for the length of the sprint
(`ServerTickRateManager.requestGameToSprint`) and puts back whatever it found
when the sprint ends.

**Where do my ticks go when a chunk stops ticking?** Nowhere. They sit in the
chunk's own queue, the index entry is left untouched, and the moment the chunk
is block-ticking again the due ones are collected, as far as the drain's
budget goes. If the chunk
unloads first they are written to disk with it. Outside generation and the
test framework's area clear, the only appointment lost is one booked into a
chunk with no registered container.

> **For a 1.21-era reader.** `BlockBehaviour.updateShape` no longer takes a
> `LevelAccessor`. It takes a `LevelReader` and a separate
> `ScheduledTickAccess` — a small interface for booking:
> `ScheduledTickAccess.createTick`, `ScheduledTickAccess.getBlockTicks`,
> `ScheduledTickAccess.getFluidTicks` and four `ScheduledTickAccess.scheduleTick`
> overloads that compose them. Every waterloggable block books water's tick
> from its own override of it.

## Where to look

The appointment and the two things that decide its fate: `ScheduledTick` ·
`ScheduledTick.UNIQUE_TICK_HASH` · `ScheduledTick.DRAIN_ORDER`. Booking, from
the block's side in: `ScheduledTickAccess.scheduleTick` ·
`LevelAccessor.createTick` · `LevelChunkTicks.schedule`. One drain, in its
three phases: `LevelTicks.tick` · `LevelTicks.sortContainersToTick` ·
`LevelTicks.drainContainers` · `LevelTicks.runCollectedTicks` ·
`ServerLevel.tickBlock` · `LevelTicks.cleanupAfterTick`. The gate the collect
phase asks:
`ServerLevel.isPositionTickingWithEntitiesLoaded`. And the disk form and the
two stand-ins for a live chunk's containers: `SavedTick` · `ProtoChunkTicks` ·
`BlackholeTickAccess`.

---

*Rules: names, never code · how the system works, not how the code reads ·
newest version only · every backticked name passes `tools/verify_names.py`.*
