# MinecraftDocs — how Java Minecraft works One file, every page, in reading order. Source: https://minecraftdocs.dev --- # Introduction > Verified against **Minecraft 26.2** Java Minecraft is one codebase that runs as two programs. The **server** is the world: a loop that ticks twenty times a second, owns every chunk, entity and block, and is the only copy allowed to decide what they are. The **client** is a window, a loop that draws a frame as often as it can, and a copy of the world it is *told about* — which it will happily change on its own and be overruled about. They talk over a real Netty connection even when both run in the same process — in singleplayer the connection never touches a socket, but the packets are real, and an `IntegratedServer` is a `MinecraftServer` with the client half attached rather than absent. Almost everything a player experiences is a consequence of that split: the client predicts and the server overrules; a chunk exists on the server long before the client is sent it; a sword swing is a packet, a hit is a reply. ```mermaid flowchart LR subgraph Client["the client (Render thread)"] MC["Minecraft: a frame, and 0 to 10 ticks inside it"] CL["ClientLevel: the copy of the world"] MC --> CL end subgraph Wire["the wire"] Conn["Connection: Netty, a socket or an in-process channel"] end subgraph Server["the server (Server thread)"] MS["MinecraftServer: a tick every 50 ms"] SL["ServerLevel: the world, one per dimension"] MS --> SL end CL -- "serverbound: what the player did" --> Conn Conn -- "clientbound: what the world became" --> CL Conn --> SL SL --> Conn Worker["Worker-Main-n: chunk generation, lighting, meshing"] -.-> SL Worker -.-> CL ``` The whole thing is 7,055 classes and about 720,000 lines of Java 25. Just under a third of it is client-only; the rest ships in both jars, and the picture below is the split — orange is the client's, blue is everything the dedicated server also runs, and the hatched boxes are the corners this book leaves out.
*(figure: packages-treemap.svg — a generated SVG, not reproduced here)*
The two jars. Every box is a package, its area is lines of decompiled source; the atlas walks through it. Click to enlarge.
Four threads carry nearly all of it — the Render thread, which is also the client's game thread; the Server thread; the Netty event loop; and a shared worker pool — and the first lecture of the book, [Anatomy](systems/anatomy/anatomy.md), is those four threads and the two loops. ## How the book is read The site is a book in three tiers, and the sidebar is its table of contents. **Parts** are watched in order. Thirteen of them, I to XIII, each a system — the server tick, the world, blocks, entities, the player, networking, the client, rendering, world generation, commands — and each opening on a landing page that says what shape the part is, what it assumes from earlier parts, and which of its pages to watch in which order. A page is one lecture's notes: it follows one scenario through the system (a player walks east across a chunk boundary; a server is clicked in the list) and its figure is the artefact — a sequence diagram whose lanes are class names, a state machine, a flowchart of a decision. Every diagram enlarges on click. Each part only assumes the ones before it, and the [lecture map](lectures.md) says where that is not quite true. The picture below is the whole of that: an arrow is *watch before*, the two dependencies every part shares — Part I for the threads, Part II for codecs and registries — are left off because their arrows would reach almost every box, and the two dashed arrows are the only places a part reaches forward, each cut on purpose rather than solved by reordering. ```mermaid flowchart TB P1["I · Anatomy"] P2["II · Foundations"] P3["III · The server"] P4["IV · The world"] P5["V · Blocks"] P6["VI · Entities"] P7["VII · Items and inventories"] P8["VIII · The player"] P9["IX · Networking"] P10["X · The client"] P11["XI · Rendering"] P12["XII · World generation"] P13["XIII · Commands and data packs"] P1 --> P2 --> P3 P3 --> P4 P3 --> P5 P3 --> P6 P3 --> P7 P3 --> P8 P3 --> P9 P3 --> P13 P4 --> P5 P4 --> P6 P4 --> P11 P4 --> P12 P5 --> P6 P5 --> P7 P5 --> P10 P6 --> P8 P6 --> P9 P6 --> P10 P7 --> P8 P7 --> P13 P9 --> P10 P9 --> P13 P10 --> P11 P4 -. "tickets and loading, environment attributes" .-> P3 P10 -. "prediction and acknowledgement, cut at Part V" .-> P5 ``` **Maps** are looked at once. The [atlas](maps/README.md) is four views generated from the decompile on every deploy — [where the code is](maps/packages.md), [where the mass is](maps/biggest.md), [what everything imports](maps/fanin.md) and [what extends what](maps/hierarchy.md) — each with a page of prose and the table it was drawn from. It is the "where is everything" answer a newcomer wants before any system page makes sense. **Reference** is looked up. Every packet, registry, data component, game rule and thread, the coordinate spaces and the random sources, the [glossary](reference/glossary.md), the [naming drift](reference/naming-drift.md) table, a class index that answers "which page talks about `ChunkMap`", and the [diagram lanes](reference/lanes.md). The rule for what belongs here is *would a viewer pause the video to read this*. For agents, the whole site is one file: [llms-full.txt](https://minecraftdocs.dev/llms-full.txt), regenerated on every deploy. ## The rules the book keeps **Names, never code.** A page names classes, methods, fields and packages so that anyone with the decompiled source can find them in a minute, and explains what they own, when they run and how they interact. It never reproduces the source — not a method body, not a snippet. Anyone who needs the code decompiles the game themselves, which is also the line Mojang's mappings licence draws. **Mojang's names.** Every identifier is Mojang's official mapping, which the decompile uses. Fabric's Yarn names differ; where a modder would not recognise a class under its official name, the Yarn name is noted once. Names have moved since 1.21 — `Identifier` was *ResourceLocation*, `Lightmap` was *LightTexture*, `DeltaTracker` was *Timer* — and the [naming drift](reference/naming-drift.md) table is the list. **Newest version only.** Every page says what it was verified against, and it is one version: 26.2. There are no version-difference sections and no "in 1.x this was". When a release lands, every page is re-read against it. **Verified means tested.** Every backticked name on every page is checked against the decompile before the site publishes, every diagram is parsed by the same mermaid the site ships, every lane in every diagram is checked against the one [key](reference/lanes.md) the whole book uses, and every link and anchor between pages is checked to land. A page that fails any of those does not go up, and neither does a change that puts the landing pages, the lecture map and the dependency figure out of step with each other. That is a narrow guarantee, and it is worth stating narrowly: it proves the names are real and current in 26.2, not that the sentence around them is true. The sentences are what the passes are for. Every claim has been fact-checked against the decompile twice — once as drafted, and once again after the pages were restructured into the book you are reading. Both times, every page had something wrong on it. What is left is a correction waiting to be filed, and the [repository](https://github.com/AlexanderjFraser/MinecraftDocs) is where to file it. **How the system works, not how the code reads.** Object-level: this class owns that state, this call happens on that thread, this packet is sent then. Never line-level. Code makes boring video and dates fast. ## What this book skips Save migration (the `util/datafix` and `util/filefix` trees, which are version-difference code by definition), Realms, telemetry, the profiler, the management server, RCON, the data generators, statistics and the recipe book, the OpenAL audio backend and two packages nobody will recognise are all in the jar and not in the parts. [What this book skips](systems/anatomy/what-this-book-skips.md), the closing page of Part I, draws that boundary honestly — what each thing is, how big, whether the dedicated server ships it, and the two or three class names to start at if you need it anyway — so a viewer knows the edge of the map before investing in thirteen parts. ## Unofficial, and free to reuse This is an independent description of how the game works. It is not endorsed by, sponsored by or associated with Mojang Studios or Microsoft, and *Minecraft* is a trademark of Mojang Synergies AB. The book is [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/): take it, adapt it, teach from it — credit [minecraftdocs.dev](https://minecraftdocs.dev) and keep what you build under the same licence. That covers the writing and the figures, which are the only things here that are anyone's to license. It does not cover the game, its source, its assets or Mojang's mappings; none of those are in this book, which is why it names identifiers and never reproduces code. Corrections, and the repository the book is written in, are on [GitHub](https://github.com/AlexanderjFraser/MinecraftDocs). --- # The atlas > Verified against **Minecraft 26.2** · Maps · Four views of the whole decompile, drawn by `tools/map_source.py` from the source tree on every deploy. Before any system page makes sense you want the answer to a newcomer's question: *where is everything?* The atlas is that answer, looked at once. Each map is a figure drawn from the decompile, a page of prose saying what the figure shows, and then the table the figure was drawn from. Nothing here is hand-counted: the tool reads the 7,055 files, and the pages are regenerated with the figures each time the site is built, so a number on a map cannot drift from the source it describes. ## The four maps | map | the question it answers | the figure | |---|---|---| | [Where the code is](packages.md) | how big is each package, which jar ships it, and which parts of the book cover it | the jar as a treemap of packages, area by lines | | [Where the mass is](biggest.md) | which classes are the largest, and what kind of thing gets that big | the thirty largest classes as bars | | [What everything imports](fanin.md) | which Mojang classes the rest of the code cannot be written without — the vocabulary Part II teaches | the thirty most-imported Mojang classes as bars | | [What extends what](hierarchy.md) | which inheritance trees are widest, and what shape they are | four trees with the descendant count on every node | The treemap is also the book's picture of the *two jars*: the introduction uses it to show how much of the code the dedicated server ships, and Part I's *what this book skips* uses its hatching to draw the boundary of the book. ## How the numbers are counted The decompile is the **client jar**, which is a strict superset of the server jar; beside it sits `server-classes.txt`, the list of classes the dedicated server also ships. From those two things every number on these pages follows. | number | how it is counted | |---|---| | a class | one `.java` file in the decompile — nested types are not counted as classes, and the 542 four-line `package-info.java` markers are counted like any other file | | a line | one line of the decompiled file, so counts are comparable with each other and not with Mojang's own source | | client-only | the file is not listed in `server-classes.txt`; *shared* means it is | | fan-in | how many files have a *net.minecraft* or *com.mojang* import statement naming the class — the JDK is not counted, and same-package use needs no import | | descendants | every type reachable from a root through *extends* and *implements*, nested types included; a parent written as *outer dot inner* resolves inside the outer class's own file, so two types with the same simple name stay two types | The line counts include what the decompiler adds — braces on their own lines, expanded switches — which is why a table-of-constants class can be longer than a class that does something. Read length as *where the reading is*, not where the difficulty is. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Where the code is > Verified against **Minecraft 26.2** · Maps · The jar as a treemap of packages: area is lines of decompiled source, colour is which jar ships the package, hatching is what this book skips. Java Minecraft is 7,055 classes and 719,302 lines of decompiled Java 25, and the first surprise in it is which half is bigger. Everything a player sees — every screen, the HUD, the whole renderer, the entity models, both GPU back-ends, the sound engine, Realms — is the orange near-third of this picture. The blue seven tenths ship in both jars, and the biggest box of all, `world/level`, is a fifth of the game by itself: blocks, block states, chunks, lighting and world generation, all of it code the dedicated server runs with no window attached.
*(figure: packages-treemap.svg — a generated SVG, not reproduced here)*
The 26.2 decompile as a treemap. Each outer box is a package directly under net/minecraft or com/mojang, labelled with its share of the lines; the boxes inside are its sub-packages. Hover a box for its counts; click the figure to enlarge it.
## Two jars, one tree The client jar is a strict superset of the server jar, and the split is clean: the 2,206 client-only classes live in exactly four packages — `net/minecraft/client` (1,864 classes), `com/mojang/blaze3d` (211), `com/mojang/realmsclient` (127) and `net/minecraft/realms` (4). Every other package in the table below ships in both jars, class for class; no sub-package at this depth is mixed. The client-only side is 212,242 lines, 29.5% of the total, which is the "just under a third" the introduction quotes. The consequence for reading this book: when a page says a class is *client-only* it is saying which package the class lives in, and when a page in Part IV or V names a class, the dedicated server has it. The class index in Reference says which side each named class is on; the oracle behind it is `server-classes.txt`. ## What the big boxes are **world — 42%.** `world/level` (146,417 lines, 1,312 classes) is Parts IV, V and XII: chunks, block states, lighting and, under `world/level/levelgen`, most of world generation — `world/level/biome` is its sibling rather than part of it. `world/entity` (109,061 lines, 716 classes) is Parts VI and VIII: the entity hierarchy, its AI and the player. `world/item` and `world/inventory` (36,363 lines between them) are Part VII. **client — 24%.** `client/gui` (59,057 lines) is the screens and the HUD, Part X; `client/renderer` and `client/model` (61,108 lines together) are the frame, section meshing and the entity models, Part XI; `client/multiplayer` (11,169) holds `ClientPacketListener` and `ClientLevel`, the client's copy of the world. The 41 classes directly in `net/minecraft/client` — `Minecraft`, `Options`, `KeyMapping`, `MouseHandler` — are 10,709 lines on their own. **util — 7%, two thirds of it skipped.** `util/datafix`, `util/filefix` and `util/profiling` are 34,176 of the package's 53,275 lines and are all outside this book; the hatched box is the save-migration history. What is left is the toolbox every part uses — `Mth`, `RandomSource`, `Util` — and the parsing and debug packages. **server — 7%.** `server/commands` (12,781) is Part XIII's command implementations; `server/level` is 42 classes and 11,977 lines — 285 lines a class, nearly three times the map's average, in a package small enough to list (`ServerLevel`, `ChunkMap`, `ServerPlayer`), Parts III and IV; `server/network` is the serverbound packet handlers, Part IX; `server/packs` is the pack system, Part II; `server/jsonrpc`, the management server, is skipped. **blaze3d — 4%.** The GPU abstraction has two back-ends behind `GpuDevice`, and the Vulkan one (`blaze3d/vulkan`, 7,477 lines) is larger than the OpenGL one (`blaze3d/opengl`, 5,627). **network — 3%, in 411 classes.** `network/protocol` is 293 classes in 12,934 lines: the packet catalogue is many tiny classes, one per packet. `network/chat` (4,818) is `Component` and chat signing. Below 3% the boxes are Part II's foundations — `net/minecraft/core`, `net/minecraft/nbt`, `net/minecraft/tags`, `net/minecraft/resources`, `net/minecraft/commands` (the command source, the argument types and the execution engine, Part XIII) and `net/minecraft/advancements`, plus `com/mojang/realmsclient` and the skipped `net/minecraft/data`. That last one is the program that writes the vanilla data pack — and it is not build-time only: the dedicated server ships all 163 classes, and `Blocks` and `MinecraftServer` both read `data/worldgen` constants at run time ([what this book skips](../systems/anatomy/what-this-book-skips.md) has the three exceptions). ## Where each part lives The parts follow the tree, but not one box each. This table is the map from the book's order to the jar's, and it is the one table on this page that is a decision rather than a measurement: the packages are assigned by hand, in the tool that draws the atlas, and the counts follow. A package under `net/minecraft` is written without that prefix; *itself only* means the files directly in the package and not its sub-packages; a package two parts share is counted in both, which is why the total row is more than the jar. A part's landing page quotes its own row of this table as its size, and everything the book skips is left out of every row. | part | packages | classes | client-only | lines | |---|---|---:|---:|---:| | I · Anatomy | `client/main`, `Minecraft`, `Main`, `MinecraftServer` | 7 | 5 | 6,770 | | II · Foundations | `core`, `resources`, `tags`, `nbt`, `server/packs`, `util`, `world/flag` | 453 | 0 | 47,222 | | III · The server | `server` (itself only), `server/level`, `server/players`, `server/dedicated` | 95 | 0 | 21,779 | | IV · The world | `world/level/chunk`, `world/level/lighting`, `world/ticks`, `world/level/gameevent`, `world/level/entity`, `world/level/material`, `world/attribute`, `world/timeline`, `world/clock`, `world/level/border`, `server/level` | 218 | 0 | 30,950 | | V · Blocks | `world/level/block`, `world/level/redstone` | 478 | 0 | 60,403 | | VI · Entities | `world/entity`, minus `world/entity/player`, `network/syncher`, `world/level/pathfinder`, `world/damagesource`, `world/effect` | 755 | 0 | 110,407 | | VII · Items and inventories | `world/item`, `world/inventory`, `world/level/storage/loot` | 507 | 0 | 45,289 | | VIII · The player | `world/entity/player`, `world/food`, `ServerPlayer`, `client/player` | 29 | 9 | 8,135 | | IX · Networking | `network`, minus `network/syncher`, `server/network`, `client/multiplayer` | 496 | 63 | 39,434 | | X · The client | `client` (itself only), `client/gui`, `client/multiplayer`, `client/sounds`, `client/resources`, `client/player`, `client/input`, `client/server` | 689 | 689 | 93,640 | | XI · Rendering | `client/renderer`, `client/model`, `client/particle`, `com/mojang/blaze3d` | 1,254 | 1,254 | 93,012 | | XII · World generation | `world/level/levelgen`, `world/level/biome` | 451 | 0 | 45,749 | | XIII · Commands and data packs | `commands`, `server/commands`, `server/dialog`, `server/permissions`, `server/bossevents`, `advancements`, `gametest`, `world/scores`, `client/gui/screens/dialog` | 470 | 17 | 43,126 | | **the thirteen parts, with the shared packages counted twice** | | 5,902 | 2,037 | 645,916 | ## The table Depth three is the outer boxes of the treemap; depth four is the inner ones. *client-only* counts the classes not in `server-classes.txt`. | package (depth 3) | classes | client-only | lines | |---|---:|---:|---:| | `net/minecraft/world` | 2585 | 0 | 304,897 | | `net/minecraft/client` | 1864 | 1864 | 172,711 | | `net/minecraft/util` | 717 | 0 | 53,275 | | `net/minecraft/server` | 420 | 0 | 52,720 | | `com/mojang/blaze3d` | 211 | 211 | 26,111 | | `net/minecraft/network` | 411 | 0 | 23,378 | | `net/minecraft/data` | 163 | 0 | 15,587 | | `com/mojang/realmsclient` | 127 | 127 | 13,217 | | `net/minecraft/commands` | 122 | 0 | 13,001 | | `net/minecraft/core` | 110 | 0 | 11,239 | | `net/minecraft/nbt` | 43 | 0 | 8,001 | | `net/minecraft/advancements` | 116 | 0 | 7,735 | | `net/minecraft/gametest` | 47 | 0 | 5,514 | | `net/minecraft/sounds` | 6 | 0 | 2,101 | | `net/minecraft/tags` | 29 | 0 | 1,788 | | `net/minecraft/resources` | 15 | 0 | 1,764 | | `net/minecraft` | 19 | 0 | 1,665 | | `net/minecraft/references` | 5 | 0 | 1,434 | | `com/mojang/math` | 10 | 0 | 922 | | `net/minecraft/stats` | 10 | 0 | 873 | | `net/minecraft/gizmos` | 15 | 0 | 569 | | `net/minecraft/recipebook` | 3 | 0 | 350 | | `net/minecraft/locale` | 3 | 0 | 247 | | `net/minecraft/realms` | 4 | 4 | 203 | | **total** | 7055 | 2206 | 719,302 | | package (depth 4) | classes | client-only | lines | |---|---:|---:|---:| | `net/minecraft/world/level` | 1312 | 0 | 146,417 | | `net/minecraft/world/entity` | 716 | 0 | 109,061 | | `net/minecraft/client/gui` | 444 | 444 | 59,057 | | `net/minecraft/client/renderer` | 701 | 701 | 48,412 | | `net/minecraft/world/item` | 314 | 0 | 29,281 | | `net/minecraft/util/datafix` | 396 | 0 | 26,372 | | `net/minecraft/network/protocol` | 293 | 0 | 12,934 | | `net/minecraft/server/commands` | 102 | 0 | 12,781 | | `net/minecraft/client/model` | 267 | 267 | 12,696 | | `net/minecraft/util` | 91 | 0 | 12,067 | | `net/minecraft/server/level` | 42 | 0 | 11,977 | | `net/minecraft/client/multiplayer` | 63 | 63 | 11,169 | | `net/minecraft/client` | 41 | 41 | 10,709 | | `net/minecraft/commands/arguments` | 71 | 0 | 8,847 | | `net/minecraft/client/resources` | 101 | 101 | 7,612 | | `net/minecraft/nbt` | 36 | 0 | 7,489 | | `com/mojang/blaze3d/vulkan` | 40 | 40 | 7,477 | | `net/minecraft/world/inventory` | 64 | 0 | 7,082 | | `net/minecraft/client/particle` | 87 | 87 | 6,806 | | `net/minecraft/client/data` | 28 | 28 | 6,176 | | `com/mojang/realmsclient/gui` | 40 | 40 | 5,830 | | `com/mojang/blaze3d/opengl` | 28 | 28 | 5,627 | | `net/minecraft/gametest/framework` | 45 | 0 | 5,495 | | `net/minecraft/core` | 38 | 0 | 5,493 | | `net/minecraft/server/network` | 28 | 0 | 5,409 | | `net/minecraft/data/worldgen` | 56 | 0 | 5,369 | | `net/minecraft/server` | 27 | 0 | 5,227 | | `net/minecraft/server/packs` | 55 | 0 | 4,975 | | `net/minecraft/network/chat` | 63 | 0 | 4,818 | | `net/minecraft/util/profiling` | 70 | 0 | 4,260 | | `net/minecraft/server/jsonrpc` | 65 | 0 | 4,094 | | `com/mojang/blaze3d/platform` | 29 | 29 | 3,896 | | `net/minecraft/network` | 42 | 0 | 3,732 | | `net/minecraft/util/filefix` | 57 | 0 | 3,544 | | `net/minecraft/advancements/predicates` | 54 | 0 | 3,459 | | `net/minecraft/world/phys` | 28 | 0 | 3,020 | | `net/minecraft/data/loot` | 21 | 0 | 2,883 | | `net/minecraft/advancements/triggers` | 49 | 0 | 2,809 | | `net/minecraft/server/players` | 19 | 0 | 2,766 | | `net/minecraft/core/component` | 30 | 0 | 2,668 | | `com/mojang/realmsclient/client` | 19 | 19 | 2,534 | | `com/mojang/blaze3d/vertex` | 16 | 16 | 2,420 | | `com/mojang/blaze3d/systems` | 26 | 26 | 2,308 | | `net/minecraft/data/recipes` | 17 | 0 | 2,227 | | `net/minecraft/client/sounds` | 17 | 17 | 2,152 | | `net/minecraft/sounds` | 6 | 0 | 2,101 | | `net/minecraft/util/parsing` | 29 | 0 | 1,879 | | `net/minecraft/commands` | 14 | 0 | 1,873 | | `com/mojang/realmsclient/dto` | 39 | 39 | 1,866 | | `net/minecraft/client/player` | 9 | 9 | 1,821 | | `net/minecraft/server/dedicated` | 7 | 0 | 1,809 | | `net/minecraft/tags` | 29 | 0 | 1,788 | | `net/minecraft/resources` | 15 | 0 | 1,764 | | `net/minecraft/world/attribute` | 28 | 0 | 1,705 | | `net/minecraft` | 19 | 0 | 1,665 | | `net/minecraft/world` | 25 | 0 | 1,664 | | `net/minecraft/data/tags` | 29 | 0 | 1,660 | | `com/mojang/realmsclient` | 4 | 4 | 1,524 | | `net/minecraft/advancements` | 13 | 0 | 1,467 | | `net/minecraft/world/scores` | 16 | 0 | 1,442 | | `net/minecraft/references` | 5 | 0 | 1,434 | | `com/mojang/realmsclient/util` | 20 | 20 | 1,399 | | `net/minecraft/network/codec` | 7 | 0 | 1,372 | | `net/minecraft/util/debug` | 19 | 0 | 1,370 | | `net/minecraft/world/effect` | 20 | 0 | 1,306 | | `net/minecraft/data` | 10 | 0 | 1,264 | | `net/minecraft/client/telemetry` | 18 | 18 | 1,221 | | `net/minecraft/core/dispenser` | 14 | 0 | 1,094 | | `com/mojang/blaze3d/audio` | 12 | 12 | 1,013 | | `com/mojang/blaze3d/pipeline` | 11 | 11 | 954 | | `net/minecraft/util/worldupdate` | 6 | 0 | 937 | | `net/minecraft/world/damagesource` | 12 | 0 | 936 | | `com/mojang/math` | 10 | 0 | 922 | | `net/minecraft/client/color` | 18 | 18 | 915 | | `net/minecraft/commands/synchronization` | 12 | 0 | 895 | | `net/minecraft/server/dialog` | 35 | 0 | 886 | | `net/minecraft/data/advancements` | 10 | 0 | 874 | | `net/minecraft/stats` | 10 | 0 | 873 | | `net/minecraft/world/ticks` | 14 | 0 | 869 | | `net/minecraft/core/particles` | 21 | 0 | 860 | | `net/minecraft/util/thread` | 9 | 0 | 842 | | `net/minecraft/server/rcon` | 9 | 0 | 839 | | `net/minecraft/client/server` | 6 | 6 | 838 | | `net/minecraft/core/registries` | 4 | 0 | 817 | | `net/minecraft/commands/execution` | 18 | 0 | 756 | | `net/minecraft/world/waypoints` | 9 | 0 | 677 | | `net/minecraft/client/tutorial` | 10 | 10 | 671 | | `net/minecraft/util/valueproviders` | 18 | 0 | 670 | | `net/minecraft/commands/functions` | 7 | 0 | 630 | | `net/minecraft/gizmos` | 15 | 0 | 569 | | `net/minecraft/data/info` | 8 | 0 | 530 | | `net/minecraft/client/main` | 4 | 4 | 523 | | `net/minecraft/network/syncher` | 6 | 0 | 522 | | `net/minecraft/nbt/visitors` | 7 | 0 | 512 | | `net/minecraft/client/animation` | 23 | 23 | 509 | | `net/minecraft/client/searchtree` | 8 | 8 | 505 | | `net/minecraft/data/structures` | 5 | 0 | 469 | | `com/mojang/blaze3d` | 12 | 12 | 462 | | `net/minecraft/util/eventlog` | 4 | 0 | 459 | | `net/minecraft/world/timeline` | 5 | 0 | 447 | | `com/mojang/blaze3d/framegraph` | 3 | 3 | 437 | | `com/mojang/blaze3d/font` | 7 | 7 | 432 | | `net/minecraft/server/permissions` | 12 | 0 | 402 | | `net/minecraft/util/random` | 4 | 0 | 369 | | `net/minecraft/world/flag` | 7 | 0 | 356 | | `net/minecraft/server/chase` | 3 | 0 | 355 | | `net/minecraft/world/clock` | 10 | 0 | 351 | | `net/minecraft/recipebook` | 3 | 0 | 350 | | `net/minecraft/server/bossevents` | 3 | 0 | 328 | | `com/mojang/blaze3d/buffers` | 6 | 6 | 327 | | `net/minecraft/server/gui` | 4 | 0 | 310 | | `net/minecraft/core/cauldron` | 3 | 0 | 307 | | `net/minecraft/server/notifications` | 5 | 0 | 295 | | `net/minecraft/client/quickplay` | 3 | 3 | 284 | | `net/minecraft/world/food` | 5 | 0 | 283 | | `net/minecraft/client/input` | 8 | 8 | 282 | | `net/minecraft/data/registries` | 5 | 0 | 248 | | `net/minecraft/locale` | 3 | 0 | 247 | | `net/minecraft/client/entity` | 4 | 4 | 228 | | `net/minecraft/realms` | 4 | 4 | 203 | | `net/minecraft/util/debugchart` | 8 | 0 | 199 | | `com/mojang/blaze3d/util` | 2 | 2 | 189 | | `net/minecraft/util/context` | 4 | 0 | 187 | | `com/mojang/blaze3d/resource` | 6 | 6 | 185 | | `net/minecraft/server/waypoints` | 2 | 0 | 180 | | `com/mojang/blaze3d/preprocessor` | 2 | 2 | 164 | | `com/mojang/blaze3d/textures` | 6 | 6 | 150 | | `net/minecraft/util/monitoring` | 2 | 0 | 120 | | `net/minecraft/server/advancements` | 2 | 0 | 87 | | `net/minecraft/client/profiling` | 2 | 2 | 81 | | `com/mojang/blaze3d/shaders` | 5 | 5 | 70 | | `com/mojang/realmsclient/exception` | 5 | 5 | 64 | | `net/minecraft/data/metadata` | 2 | 0 | 63 | | `net/minecraft/client/waypoints` | 2 | 2 | 44 | | `net/minecraft/gametest` | 2 | 0 | 19 | | **total** | 7055 | 2206 | 719,302 | --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Where the mass is > Verified against **Minecraft 26.2** · Maps · The thirty largest classes by lines of decompiled source, coloured by which jar ships them. The two largest classes in the game are the top of one hierarchy: `Entity` and `LivingEntity`, 8,785 lines between them, the thing every mob and player is before it is anything else. Third is `Minecraft`, the client itself. And then the list turns odd: two of the top ten never run while anyone is playing. `BlockModelGenerators` runs only inside the data generator — its single caller is `ModelProvider` — and writes the block model JSON that ships in the jar; `BlockStateData` is a table that the save-migration fixes consult when a world from an older version is opened, and nothing outside `util/datafix` reads it. Size is where the reading is, not where the game is.
*(figure: biggest.svg — a generated SVG on the site; its data follows)* | class | lines | side | |---|---:|---| | `net/minecraft/world/entity/Entity` | 4,464 | shared | | `net/minecraft/world/entity/LivingEntity` | 4,321 | shared | | `net/minecraft/client/Minecraft` | 3,274 | client | | `net/minecraft/client/data/models/BlockModelGenerators` | 3,090 | client | | `net/minecraft/client/multiplayer/ClientPacketListener` | 3,051 | client | | `net/minecraft/server/MinecraftServer` | 2,632 | shared | | `net/minecraft/server/network/ServerGamePacketListenerImpl` | 2,499 | shared | | `net/minecraft/server/level/ServerPlayer` | 2,445 | shared | | `net/minecraft/util/datafix/fixes/BlockStateData` | 2,270 | shared | | `net/minecraft/server/level/ServerLevel` | 2,126 | shared | | `net/minecraft/world/entity/player/Player` | 2,053 | shared | | `net/minecraft/sounds/SoundEvents` | 2,000 | shared | | `net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces` | 1,983 | shared | | `net/minecraft/client/Options` | 1,972 | client | | `net/minecraft/world/level/block/Blocks` | 1,969 | shared | | `net/minecraft/world/item/CreativeModeTabs` | 1,706 | shared | | `net/minecraft/world/item/Items` | 1,694 | shared | | `net/minecraft/server/level/ChunkMap` | 1,668 | shared | | `net/minecraft/world/entity/animal/fox/Fox` | 1,625 | shared | | `net/minecraft/world/entity/Mob` | 1,612 | shared | | `net/minecraft/util/datafix/DataFixers` | 1,582 | shared | | `net/minecraft/network/FriendlyByteBuf` | 1,546 | shared | | `net/minecraft/client/gui/Hud` | 1,478 | client | | `net/minecraft/world/entity/animal/bee/Bee` | 1,451 | shared | | `net/minecraft/world/level/levelgen/DensityFunctions` | 1,445 | shared | | `net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces` | 1,436 | shared | | `net/minecraft/util/Util` | 1,415 | shared | | `net/minecraft/client/player/LocalPlayer` | 1,392 | client | | `com/mojang/realmsclient/RealmsMainScreen` | 1,376 | client | | `net/minecraft/data/loot/packs/VanillaBlockLoot` | 1,360 | shared | | `net/minecraft/world/level/block/state/BlockBehaviour` | 1,357 | shared | | `net/minecraft/gametest/framework/GameTestHelper` | 1,353 | shared | | `net/minecraft/client/multiplayer/ClientLevel` | 1,320 | client | | `net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces` | 1,317 | shared | | `net/minecraft/world/item/ItemStack` | 1,253 | shared | | `net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces` | 1,185 | shared | | `net/minecraft/world/entity/animal/panda/Panda` | 1,121 | shared | | `net/minecraft/world/entity/animal/equine/AbstractHorse` | 1,114 | shared | | `net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen` | 1,101 | client | | `net/minecraft/world/entity/monster/cubemob/SulfurCube` | 1,082 | shared |
The thirty largest classes of 26.2. Blue ships in both jars, orange is client-only; the small grey text is the package under net/minecraft. Click to enlarge.
**The number:** 62,935 — the lines in these thirty classes, 8.7% of the game in 0.4% of its files. ## Three kinds of big Read down the bars and the thirty sort themselves into three kinds, and the kind tells you how to read the class. **The god objects.** `Entity`, `LivingEntity`, `Player`, `ServerPlayer`, `LocalPlayer` and `Mob` are one chain of inheritance, and each level adds a thousand lines or more because each is the base of everything below it. `Minecraft` and `MinecraftServer` are the two programs' roots; `ServerLevel` is the world; `ChunkMap` is the world's loader. Only two concrete mobs make the list — `Fox` and `Bee`, the two with the most bespoke behaviour — and they are the pages a reader of Part VI should expect to be long. **The switchboards.** `ClientPacketListener` is a handler method for every clientbound play packet, and `ServerGamePacketListenerImpl` is the same for every serverbound one — the packets both phases share are handled one class up, in `ClientCommonPacketListenerImpl` and `ServerCommonPacketListenerImpl`; between them they are the whole of what the wire can say, which is why Part IX's pages keep coming back to them. `FriendlyByteBuf` is the buffer both read from. **The catalogues written as code.** `SoundEvents`, `Blocks`, `Items` and `CreativeModeTabs` are registries populated one constant per line; `DensityFunctions` is the node types the vanilla noise graph is built from — the graph itself is `NoiseRouterData` and the worldgen JSON; `DataFixers` is the migration history; `OceanMonumentPieces` and `StrongholdPieces` are structures built by hand, room by room, in Java rather than in a template; `Options` is every setting the client has, and `Hud` is the in-world overlay — the crosshair, the hotbar, the bars — inside the wider `Gui` that also draws screens, toasts and the loading overlay. These are long because they are lists. None is hard. ## The table Forty rows, of which the figure draws thirty. *side* is which jar ships the class. | class | lines | side | |---|---:|---| | `net/minecraft/world/entity/Entity` | 4,464 | shared | | `net/minecraft/world/entity/LivingEntity` | 4,321 | shared | | `net/minecraft/client/Minecraft` | 3,274 | client | | `net/minecraft/client/data/models/BlockModelGenerators` | 3,090 | client | | `net/minecraft/client/multiplayer/ClientPacketListener` | 3,051 | client | | `net/minecraft/server/MinecraftServer` | 2,632 | shared | | `net/minecraft/server/network/ServerGamePacketListenerImpl` | 2,499 | shared | | `net/minecraft/server/level/ServerPlayer` | 2,445 | shared | | `net/minecraft/util/datafix/fixes/BlockStateData` | 2,270 | shared | | `net/minecraft/server/level/ServerLevel` | 2,126 | shared | | `net/minecraft/world/entity/player/Player` | 2,053 | shared | | `net/minecraft/sounds/SoundEvents` | 2,000 | shared | | `net/minecraft/world/level/levelgen/structure/structures/OceanMonumentPieces` | 1,983 | shared | | `net/minecraft/client/Options` | 1,972 | client | | `net/minecraft/world/level/block/Blocks` | 1,969 | shared | | `net/minecraft/world/item/CreativeModeTabs` | 1,706 | shared | | `net/minecraft/world/item/Items` | 1,694 | shared | | `net/minecraft/server/level/ChunkMap` | 1,668 | shared | | `net/minecraft/world/entity/animal/fox/Fox` | 1,625 | shared | | `net/minecraft/world/entity/Mob` | 1,612 | shared | | `net/minecraft/util/datafix/DataFixers` | 1,582 | shared | | `net/minecraft/network/FriendlyByteBuf` | 1,546 | shared | | `net/minecraft/client/gui/Hud` | 1,478 | client | | `net/minecraft/world/entity/animal/bee/Bee` | 1,451 | shared | | `net/minecraft/world/level/levelgen/DensityFunctions` | 1,445 | shared | | `net/minecraft/world/level/levelgen/structure/structures/StrongholdPieces` | 1,436 | shared | | `net/minecraft/util/Util` | 1,415 | shared | | `net/minecraft/client/player/LocalPlayer` | 1,392 | client | | `com/mojang/realmsclient/RealmsMainScreen` | 1,376 | client | | `net/minecraft/data/loot/packs/VanillaBlockLoot` | 1,360 | shared | | `net/minecraft/world/level/block/state/BlockBehaviour` | 1,357 | shared | | `net/minecraft/gametest/framework/GameTestHelper` | 1,353 | shared | | `net/minecraft/client/multiplayer/ClientLevel` | 1,320 | client | | `net/minecraft/world/level/levelgen/structure/structures/NetherFortressPieces` | 1,317 | shared | | `net/minecraft/world/item/ItemStack` | 1,253 | shared | | `net/minecraft/world/level/levelgen/structure/structures/WoodlandMansionPieces` | 1,185 | shared | | `net/minecraft/world/entity/animal/panda/Panda` | 1,121 | shared | | `net/minecraft/world/entity/animal/equine/AbstractHorse` | 1,114 | shared | | `net/minecraft/client/gui/screens/inventory/CreativeModeInventoryScreen` | 1,101 | client | | `net/minecraft/world/entity/monster/cubemob/SulfurCube` | 1,082 | shared | --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # What everything imports > Verified against **Minecraft 26.2** · Maps · The thirty most-imported Mojang classes: how many files name each one in an import statement. One file in six imports `BlockPos`. That is the least surprising fact on this page; the next one is not. The chart counts Mojang imports only — *net.minecraft* and *com.mojang* — because the JDK's `List` and `Optional` and the nullability annotation outrank everything on it and say nothing about the game. Under that rule the second most-imported type is not Minecraft's: it is `Codec`, from Mojang's DataFixerUpper library, imported by 987 files, with `MapCodec` third and `RecordCodecBuilder` sixth. Three of the six classes the game most depends on are the serialisation vocabulary that turns objects into NBT and JSON and back, which is why Part II teaches codecs before anything a player can see.
*(figure: fanin.svg — a generated SVG on the site; its data follows)* | class | package | imported by | side | |---|---|---:|---| | `BlockPos` | `net.minecraft.core` | 1221 | shared | | `Codec` | `com.mojang.serialization` | 987 | library | | `MapCodec` | `com.mojang.serialization` | 917 | library | | `Identifier` | `net.minecraft.resources` | 882 | shared | | `BlockState` | `net.minecraft.world.level.block.state` | 862 | shared | | `RecordCodecBuilder` | `com.mojang.serialization.codecs` | 804 | library | | `RandomSource` | `net.minecraft.util` | 773 | shared | | `Component` | `net.minecraft.network.chat` | 766 | shared | | `Level` | `net.minecraft.world.level` | 750 | shared | | `ServerLevel` | `net.minecraft.server.level` | 726 | shared | | `ItemStack` | `net.minecraft.world.item` | 682 | shared | | `Mth` | `net.minecraft.util` | 677 | shared | | `Entity` | `net.minecraft.world.entity` | 667 | shared | | `Holder` | `net.minecraft.core` | 597 | shared | | `Vec3` | `net.minecraft.world.phys` | 575 | shared | | `Direction` | `net.minecraft.core` | 570 | shared | | `LivingEntity` | `net.minecraft.world.entity` | 533 | shared | | `StreamCodec` | `net.minecraft.network.codec` | 508 | shared | | `LogUtils` | `com.mojang.logging` | 470 | library | | `Util` | `net.minecraft.util` | 454 | shared | | `Player` | `net.minecraft.world.entity.player` | 449 | shared | | `Registries` | `net.minecraft.core.registries` | 448 | shared | | `ResourceKey` | `net.minecraft.resources` | 402 | shared | | `Schema` | `com.mojang.datafixers.schemas` | 389 | library | | `SoundEvents` | `net.minecraft.sounds` | 316 | shared | | `BlockBehaviour` | `net.minecraft.world.level.block.state` | 311 | shared | | `Blocks` | `net.minecraft.world.level.block` | 310 | shared | | `Packet` | `net.minecraft.network.protocol` | 287 | shared | | `Minecraft` | `net.minecraft.client` | 280 | client | | `DSL` | `com.mojang.datafixers` | 278 | library | | `ByteBufCodecs` | `net.minecraft.network.codec` | 275 | shared | | `EntityType` | `net.minecraft.world.entity` | 259 | shared | | `Block` | `net.minecraft.world.level.block` | 257 | shared | | `BuiltInRegistries` | `net.minecraft.core.registries` | 257 | shared | | `DataComponents` | `net.minecraft.core.component` | 253 | shared | | `PacketType` | `net.minecraft.network.protocol` | 241 | shared | | `SoundEvent` | `net.minecraft.sounds` | 232 | shared | | `RegistryFriendlyByteBuf` | `net.minecraft.network` | 229 | shared | | `Dynamic` | `com.mojang.serialization` | 229 | library | | `ServerPlayer` | `net.minecraft.server.level` | 223 | shared | | `LevelReader` | `net.minecraft.world.level` | 220 | shared | | `BlockGetter` | `net.minecraft.world.level` | 217 | shared | | `Items` | `net.minecraft.world.item` | 216 | shared | | `SoundSource` | `net.minecraft.sounds` | 210 | shared | | `Pair` | `com.mojang.datafixers.util` | 208 | library | | `VoxelShape` | `net.minecraft.world.phys.shapes` | 206 | shared | | `Item` | `net.minecraft.world.item` | 204 | shared | | `EntityTypes` | `net.minecraft.world.entity` | 204 | shared | | `FriendlyByteBuf` | `net.minecraft.network` | 202 | shared | | `GuiGraphicsExtractor` | `net.minecraft.client.gui` | 195 | client | | `Registry` | `net.minecraft.core` | 192 | shared | | `ModelPart` | `net.minecraft.client.model.geom` | 191 | client | | `PoseStack` | `com.mojang.blaze3d.vertex` | 191 | client | | `ExtraCodecs` | `net.minecraft.util` | 189 | shared | | `InteractionResult` | `net.minecraft.world` | 188 | shared | | `ValueOutput` | `net.minecraft.world.level.storage` | 184 | shared | | `BlockTags` | `net.minecraft.tags` | 183 | shared | | `ValueInput` | `net.minecraft.world.level.storage` | 183 | shared | | `DamageSource` | `net.minecraft.world.damagesource` | 182 | shared | | `MemoryModuleType` | `net.minecraft.world.entity.ai.memory` | 182 | shared |
The thirty most-imported Mojang classes of 26.2; JDK and annotation imports are not counted. Blue ships in both jars, orange is client-only, grey is a library outside the decompile. Click to enlarge.
## The vocabulary Part II teaches The thirty hubs are not thirty ideas; they are seven, and Part II is the first six of them. The world's nouns are the seventh, and the rest of the book is about them. | idea | the hubs | where the book teaches it | |---|---|---| | a position | `BlockPos`, `Vec3`, `Direction`, `Mth` | [Math and primitives](../reference/math-and-primitives.md) | | a name and a registry | `Identifier`, `ResourceKey`, `Registries`, `BuiltInRegistries`, `Holder` | [Identifiers and registries](../systems/foundations/identifiers-and-registries.md) | | a shape on disk | `Codec`, `MapCodec`, `RecordCodecBuilder` | [Codecs, NBT and JSON](../systems/foundations/codecs-nbt-json.md) | | a shape on the wire | `StreamCodec`, `ByteBufCodecs`, `RegistryFriendlyByteBuf`, `Packet`, `PacketType` | [Packets and stream codecs](../systems/networking/packets-and-stream-codecs.md) | | text | `Component` | [Text components](../systems/foundations/text-components.md) | | chance | `RandomSource` | [Math and primitives](../reference/math-and-primitives.md) | | the world's nouns | `Level`, `ServerLevel`, `BlockState`, `Block`, `Blocks`, `Entity`, `LivingEntity`, `Player`, `EntityType`, `ItemStack`, `SoundEvents`, `SoundEvent`, `DataComponents` | Parts IV to VIII | Three rows of the table are worth a second look. `ServerLevel` (726) is imported by nearly as many files as `Level` (750): most code that touches the world knows it is on the server, and says so in its types. `Minecraft` (280) is the only client-only class in the thirty, and it is twenty-ninth — the client's hub is a hub for a quarter of the code, and the shared three quarters never name it. And `Schema` (389) and `DSL` (278), the other library classes on the chart, are the migration tree talking to itself: all but ten of the files that import `Schema` are in `util/datafix`. *LogUtils*, at 470, is Mojang's logging library — the one line at the top of nearly every class that does anything. ## What the count misses An import is counted once per file, so the chart says *how many files name the class*, not how often. It also undercounts every class used inside its own package, because that needs no import: the 257 files that import `Block` are the files outside `world/level/block` that use it, and the same is true of `BlockBehaviour` and `Minecraft`. A class's true reach is this number plus its package. ## The table Sixty rows, of which the figure draws thirty. *side* is which jar ships the class, or *library* for a class outside the decompile. | class | package | imported by | side | |---|---|---:|---| | `BlockPos` | `net.minecraft.core` | 1221 | shared | | `Codec` | `com.mojang.serialization` | 987 | library | | `MapCodec` | `com.mojang.serialization` | 917 | library | | `Identifier` | `net.minecraft.resources` | 882 | shared | | `BlockState` | `net.minecraft.world.level.block.state` | 862 | shared | | `RecordCodecBuilder` | `com.mojang.serialization.codecs` | 804 | library | | `RandomSource` | `net.minecraft.util` | 773 | shared | | `Component` | `net.minecraft.network.chat` | 766 | shared | | `Level` | `net.minecraft.world.level` | 750 | shared | | `ServerLevel` | `net.minecraft.server.level` | 726 | shared | | `ItemStack` | `net.minecraft.world.item` | 682 | shared | | `Mth` | `net.minecraft.util` | 677 | shared | | `Entity` | `net.minecraft.world.entity` | 667 | shared | | `Holder` | `net.minecraft.core` | 597 | shared | | `Vec3` | `net.minecraft.world.phys` | 575 | shared | | `Direction` | `net.minecraft.core` | 570 | shared | | `LivingEntity` | `net.minecraft.world.entity` | 533 | shared | | `StreamCodec` | `net.minecraft.network.codec` | 508 | shared | | `LogUtils` | `com.mojang.logging` | 470 | library | | `Util` | `net.minecraft.util` | 454 | shared | | `Player` | `net.minecraft.world.entity.player` | 449 | shared | | `Registries` | `net.minecraft.core.registries` | 448 | shared | | `ResourceKey` | `net.minecraft.resources` | 402 | shared | | `Schema` | `com.mojang.datafixers.schemas` | 389 | library | | `SoundEvents` | `net.minecraft.sounds` | 316 | shared | | `BlockBehaviour` | `net.minecraft.world.level.block.state` | 311 | shared | | `Blocks` | `net.minecraft.world.level.block` | 310 | shared | | `Packet` | `net.minecraft.network.protocol` | 287 | shared | | `Minecraft` | `net.minecraft.client` | 280 | client | | `DSL` | `com.mojang.datafixers` | 278 | library | | `ByteBufCodecs` | `net.minecraft.network.codec` | 275 | shared | | `EntityType` | `net.minecraft.world.entity` | 259 | shared | | `Block` | `net.minecraft.world.level.block` | 257 | shared | | `BuiltInRegistries` | `net.minecraft.core.registries` | 257 | shared | | `DataComponents` | `net.minecraft.core.component` | 253 | shared | | `PacketType` | `net.minecraft.network.protocol` | 241 | shared | | `SoundEvent` | `net.minecraft.sounds` | 232 | shared | | `RegistryFriendlyByteBuf` | `net.minecraft.network` | 229 | shared | | `Dynamic` | `com.mojang.serialization` | 229 | library | | `ServerPlayer` | `net.minecraft.server.level` | 223 | shared | | `LevelReader` | `net.minecraft.world.level` | 220 | shared | | `BlockGetter` | `net.minecraft.world.level` | 217 | shared | | `Items` | `net.minecraft.world.item` | 216 | shared | | `SoundSource` | `net.minecraft.sounds` | 210 | shared | | `Pair` | `com.mojang.datafixers.util` | 208 | library | | `VoxelShape` | `net.minecraft.world.phys.shapes` | 206 | shared | | `Item` | `net.minecraft.world.item` | 204 | shared | | `EntityTypes` | `net.minecraft.world.entity` | 204 | shared | | `FriendlyByteBuf` | `net.minecraft.network` | 202 | shared | | `GuiGraphicsExtractor` | `net.minecraft.client.gui` | 195 | client | | `Registry` | `net.minecraft.core` | 192 | shared | | `ModelPart` | `net.minecraft.client.model.geom` | 191 | client | | `PoseStack` | `com.mojang.blaze3d.vertex` | 191 | client | | `ExtraCodecs` | `net.minecraft.util` | 189 | shared | | `InteractionResult` | `net.minecraft.world` | 188 | shared | | `ValueOutput` | `net.minecraft.world.level.storage` | 184 | shared | | `BlockTags` | `net.minecraft.tags` | 183 | shared | | `ValueInput` | `net.minecraft.world.level.storage` | 183 | shared | | `DamageSource` | `net.minecraft.world.damagesource` | 182 | shared | | `MemoryModuleType` | `net.minecraft.world.entity.ai.memory` | 182 | shared | --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # What extends what > Verified against **Minecraft 26.2** · Maps · The widest inheritance trees, four of them drawn with the number of descendants on every node. `Block` has 293 subclasses and `Entity` 191, and those are the two trees a reader of this book will climb most often. But neither is the widest hierarchy in the game. That is `FeatureElement`, with 386 descendants from seven implementers — and it is not a hierarchy at all. It is a marker interface for *anything that can be put behind a feature flag*, and its seven implementers are `BlockBehaviour`, `Item`, `EntityType`, `MenuType`, `MobEffect`, `Potion` and `GameRule`, so it inherits the whole block tree and the whole item tree at once. `ItemLike`, second at 366, is `Block` plus `Item`. The interface table is a list of which mix-ins reach furthest; the class table is where the real trees are, and four of them are drawn below. ## Entity
*(figure: tree-Entity.svg — a generated SVG, not reproduced here)*
The Entity tree to three levels. The number is how many types descend from the node; subclasses with no subclasses of their own are folded into one italic line per parent. Click to enlarge.
The shape is a spine with a few branches. `LivingEntity` holds 124 of the 191, `Mob` 114 of those, `PathfinderMob` 108 of those: a mob is four classes deep before it is a species, and Part VI's [entity anatomy](../systems/entities/entity-anatomy.md) is that spine. The non-living entities are two families and a scattering — `Projectile` (26) and `VehicleEntity` (15), then hanging things, displays, and thirteen direct subclasses of `Entity` with no children of their own, from `ItemEntity` to `LightningBolt`. ## Block
*(figure: tree-Block.svg — a generated SVG, not reproduced here)*
The Block tree to three levels; 61 of its 92 direct subclasses have no subclasses of their own and are folded into the last line. Click to enlarge.
`Block` is wide and shallow: 92 direct subclasses, most of them terminal. The one deep branch is `BaseEntityBlock` (64), the blocks that own a block entity, which is Part V's [block entities](../systems/blocks/block-entities.md) page in tree form. The table's first row, `BlockBehaviour` at 294, is the same tree seen from one class higher — `Block` is its only subclass, and it exists so that a block's behaviour and its registry identity can be separate classes. ## Item
*(figure: tree-Item.svg — a generated SVG, not reproduced here)*
The Item tree to three levels. Click to enlarge.
Seventy-one subclasses for over a thousand registered items. The tree is small because an item's behaviour mostly is not in its class: what a stack does is in its data components, and `Items` registers most of the game as a plain `Item` with a `Item.Properties` describing it. Part VII's [items and stacks](../systems/items/items-and-stacks.md) is why the tree is this shape. ## Screen
*(figure: tree-Screen.svg — a generated SVG, not reproduced here)*
The Screen tree to three levels; 60 of its 72 direct subclasses have no subclasses of their own. Click to enlarge.
`Screen` (158) is `Block`'s shape again — 72 direct subclasses, 60 of them terminal — with one deep branch, `AbstractContainerScreen` (27), the screens that show a menu, and one branch that is not in this book, `RealmsScreen` (23). The row above it in the table, `AbstractContainerEventHandler` at 159, is `Screen`'s parent with `Screen` as its only subclass, the same one-class-higher effect as `BlockBehaviour`. ## Two trees the table shows and the figures do not `Goal` has 200 descendants from 99 direct subclasses, and 130 of the 200 are nested classes inside the mob they serve — a fox's goals are declared in `Fox`, not in `world/entity/ai/goal`. `Packet` is an interface with 236 descendants from 227 direct implementers, and almost nothing below them: the packet catalogue is flat, and the [packets](../reference/packets.md) reference is its list. ## The tables Class roots first, then interface roots; a root needs fifteen descendants to appear. *direct* is the number of immediate subclasses or implementers; *where* is the package of the root. | root | descendants | direct | kind | where | |---|---:|---:|---|---| | `BlockBehaviour` | 294 | 1 | class | `net/minecraft/world/level/block/state` | | `Block` | 293 | 92 | class | `net/minecraft/world/level/block` | | `Goal` | 200 | 99 | class | `net/minecraft/world/entity/ai/goal` | | `Entity` | 191 | 18 | class | `net/minecraft/world/entity` | | `Model` | 170 | 14 | class | `net/minecraft/client/model` | | `AbstractContainerEventHandler` | 159 | 1 | class | `net/minecraft/client/gui/components/events` | | `Screen` | 158 | 72 | class | `net/minecraft/client/gui/screens` | | `EntityModel` | 153 | 70 | class | `net/minecraft/client/model` | | `EntityRenderer` | 132 | 27 | class | `net/minecraft/client/renderer/entity` | | `LivingEntity` | 124 | 3 | class | `net/minecraft/world/entity` | | `Mob` | 114 | 5 | class | `net/minecraft/world/entity` | | `PathfinderMob` | 108 | 5 | class | `net/minecraft/world/entity` | | `NamespacedSchema` | 103 | 103 | class | `net/minecraft/util/datafix/schemas` | | `EntityRenderState` | 98 | 22 | class | `net/minecraft/client/renderer/entity/state` | | `LivingEntityRenderer` | 96 | 3 | class | `net/minecraft/client/renderer/entity` | | `AbstractWidget` | 95 | 12 | class | `net/minecraft/client/gui/components` | | `MobRenderer` | 93 | 39 | class | `net/minecraft/client/renderer/entity` | | `Particle` | 83 | 4 | class | `net/minecraft/client/particle` | | `SingleQuadParticle` | 73 | 46 | class | `net/minecraft/client/particle` | | `Item` | 71 | 51 | class | `net/minecraft/world/item` | | `LivingEntityRenderState` | 70 | 44 | class | `net/minecraft/client/renderer/entity/state` | | `Feature` | 66 | 60 | class | `net/minecraft/world/level/levelgen/feature` | | `BaseEntityBlock` | 64 | 40 | class | `net/minecraft/world/level/block` | | `StructurePiece` | 63 | 8 | class | `net/minecraft/world/level/levelgen/structure` | | `Entry` | 59 | 2 | class | `net/minecraft/client/gui/components` | | `Behavior` | 58 | 53 | class | `net/minecraft/world/entity/ai/behavior` | | `BlockEntity` | 52 | 35 | class | `net/minecraft/world/level/block/entity` | | `AgeableMob` | 52 | 4 | class | `net/minecraft/world/entity` | | `RenderLayer` | 48 | 39 | class | `net/minecraft/client/renderer/entity/layers` | | `NamedEntityFix` | 46 | 46 | class | `net/minecraft/util/datafix/fixes` | | root | descendants | direct | kind | where | |---|---:|---:|---|---| | `FeatureElement` | 386 | 7 | interface | `net/minecraft/world/flag` | | `ItemLike` | 366 | 2 | interface | `net/minecraft/world/level` | | `TabOrderedElement` | 323 | 2 | interface | `net/minecraft/client/gui/components` | | `GuiEventListener` | 321 | 5 | interface | `net/minecraft/client/gui/components/events` | | `Renderable` | 262 | 7 | interface | `net/minecraft/client/gui/components` | | `TypedInstance` | 251 | 5 | interface | `net/minecraft/core` | | `DebugValueSource` | 247 | 3 | interface | `net/minecraft/util/debug` | | `Packet` | 236 | 227 | interface | `net/minecraft/network/protocol` | | `SlotProvider` | 230 | 2 | interface | `net/minecraft/world/entity` | | `ContainerEventHandler` | 219 | 3 | interface | `net/minecraft/client/gui/components/events` | | `Nameable` | 211 | 6 | interface | `net/minecraft/world` | | `DataComponentGetter` | 199 | 4 | interface | `net/minecraft/core/component` | | `ItemOwner` | 194 | 3 | interface | `net/minecraft/world/entity` | | `UniquelyIdentifyable` | 193 | 1 | interface | `net/minecraft/world/level/entity` | | `EntityAccess` | 192 | 1 | interface | `net/minecraft/world/level/entity` | | `ScoreHolder` | 192 | 1 | interface | `net/minecraft/world/scores` | | `SyncedDataHolder` | 192 | 1 | interface | `net/minecraft/network/syncher` | | `LayoutElement` | 165 | 4 | interface | `net/minecraft/client/gui/layouts` | | `StringRepresentable` | 163 | 162 | interface | `net/minecraft/util` | | `NarrationSupplier` | 138 | 3 | interface | `net/minecraft/client/gui/narration` | | `Waypoint` | 131 | 2 | interface | `net/minecraft/world/waypoints` | | `Attackable` | 126 | 2 | interface | `net/minecraft/world/entity` | | `WaypointTransmitter` | 125 | 1 | interface | `net/minecraft/world/waypoints` | | `Leashable` | 121 | 2 | interface | `net/minecraft/world/entity` | | `ParticleProvider` | 118 | 113 | interface | `net/minecraft/client/particle` | | `Targeting` | 116 | 2 | interface | `net/minecraft/world/entity` | | `EquipmentUser` | 115 | 1 | interface | `net/minecraft/world/entity` | | `Validatable` | 114 | 9 | interface | `net/minecraft/world/level/storage/loot` | | `NarratableEntry` | 99 | 3 | interface | `net/minecraft/client/gui/narration` | | `RenderLayerParent` | 97 | 1 | interface | `net/minecraft/client/renderer/entity` | --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # I · Anatomy > Verified against **Minecraft 26.2** · Part I · The whole program at once: which threads exist, which loop each runs, and how the two halves talk. Part I is the program the other twelve parts run inside. Java Minecraft is one codebase running as two programs on four threads worth memorising — a server whose whole life is a tick loop, and a client whose life is a frame loop with ticks inside it — and almost every surprise later in this book is one of those two waiting on the other. A player recognises the split by its symptom: the game that keeps drawing while the world stands still, because the two are not the same loop and never were. Nearly every [lane](../../reference/lanes.md) in the sequence diagrams after this part is a class, and the handful that are not stand for a thread — which is the same reason for reading this part first either way. ## The shape of the part Two pages: what the program is, and where it ends. The first is a trace and the second is a boundary, and the boundary is drawn second because a reader decides whether to go on once they can see the whole program. ```mermaid flowchart LR A["Anatomy: four threads, two loops, one wire — from main to a running singleplayer world"] B["What this book skips: the fourteen packages the parts do not reach, and why"] A -- "now that you can see the whole program, here is the part of it this book does not teach" --> B ``` ## Before you start Nothing. This is the first part, and it assumes only that you have played the game. ## Watch in this order 1. [Anatomy](anatomy.md) — from *main* to a running singleplayer world: the Render thread that is also the game thread, the Server thread that is the world, the Netty threads that run more than bytes, and the one CPU pool that chunk generation, lighting and section meshing all share. 2. [What this book skips](what-this-book-skips.md) — the boundary, drawn honestly on the treemap of the jar: save migration, Realms, telemetry, the profiler, the management server, the data generators, and where to start if you need one anyway. It is the second lecture and not the last because a boundary is worth drawing before the investment, not after. ## Reference this part uses [Threads](../../reference/threads.md) — every thread, who makes it and what may run on it; Part I names the four to memorise and this is the rest. [Diagram lanes](../../reference/lanes.md) — the abbreviations every sequence diagram uses, needed from the first figure of the first lecture onward. [Naming drift](../../reference/naming-drift.md) — the 1.21-era names that have moved. The [atlas](../../maps/packages.md) — where everything is, and the treemap the second lecture reads. ## Where the part stops Part I says which threads exist and hands each of them on. What the Server thread *does* with a tick is [Part III](../server/README.md); what the frame loop does between ticks is [Part X](../client/README.md); what crosses the wire between them is [Part IX](../networking/README.md). This part owes you only enough of each to read a lane. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Anatomy > Verified against **Minecraft 26.2** · Part I · Clicking Singleplayer, picking a world, and standing in it a few seconds later. A player clicks Singleplayer, picks a world from the list and waits. One thread has been running since *main*; by the time the world appears there are two that matter, and the second was created by the first, mid-frame, while the first went on drawing. They are two programs sharing a JVM. The server is the world — every chunk, entity and block, and the only thing allowed to change them; the client is a window, a frame loop and a copy of the world it is told about. Between them runs a Netty channel that never touches a socket, and over it the client walks the same handshake, login, configuration and play state machine it would walk against a server on the other side of the planet. The packets are real. What leaks between the two halves is not world state but a setting: pause is *decided* on the client, by `Minecraft.isPaused`, and *enforced* on the server, by `IntegratedServer.tickServer` running `IntegratedServer.tickPaused` instead of the world — which is why a world published to LAN never pauses, however deep in the options menu you are. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | the client: the `Window`, the resource system, the renderers, input and `Options` — and, in three fields, whether we are in a world at all | Render | | `MinecraftServer` | the world and the loop that advances it. Abstract, with three concrete subclasses: `IntegratedServer`, `DedicatedServer`, and `GameTestServer`, the headless harness the gametest entry point launches | Server | | `IntegratedServer` | everything singleplayer does differently: the pause, LAN publishing, the player cap, the relaxed limits | Server | | `BlockableEventLoop` | the queue-and-thread pairing both loops are — `Minecraft` and `MinecraftServer` each extend `ReentrantBlockableEventLoop` | one per queue, and a thread may own more than one | | `Connection` | one channel, and which `PacketListener` is currently on it | Netty | | `ServerConnectionListener` | which channels the server listens on, including the in-memory one singleplayer uses | Server, binding into Netty | | `PacketProcessor` | which decoded packets are waiting to be handled on the thread that owns their state | filled from Netty, drained by the owner | | `Util` | the pools everything else is serialised onto: `Util.backgroundExecutor`, `Util.ioPool`, `Util.nonCriticalIoPool` | — | Three of `Minecraft`'s nullable fields between them mean "we are in a world": `Minecraft.level` (a `ClientLevel`), `Minecraft.player` (a `LocalPlayer`) and `Minecraft.gameMode` (a `MultiPlayerGameMode`). A fourth, `Minecraft.singleplayerServer`, holds the `IntegratedServer` when one is running, and is the client's answer to "am I the host". > **For a 1.21-era reader.** The client's clock is `DeltaTracker`, which was > *Timer*, and the partial tick is a `DeltaTracker.Timer` you ask rather than > a float you are handed — it appears in the frame loop below and on every > renderer in Part XI. The rest of the drift a 1.21 reader will trip on is > [naming drift](../../reference/naming-drift.md). ## From *main* to a world ```mermaid sequenceDiagram participant Main as Main participant RS as RenderSystem participant MC as Minecraft participant MS as MinecraftServer participant IS as IntegratedServer participant SCL as ServerConnectionListener participant Conn as Connection Main->>Main: tryDetectVersion, loadLibraries, DataFixers.optimize in the background, bootStrap, ClientBootstrap, validate Main->>RS: initRenderThread — this thread is the Render thread from here on Main->>MC: the constructor — initBackendSystem, a backend, a Window, every reload listener registered MC->>MC: the first ReloadInstance — prepare on the workers, apply here, LoadingOverlay on screen Main->>MC: run — pollEvents, then runTick, until running goes false Note over Main,MC: one thread so far. The next line makes the second. MC->>MS: doWorldLoad calls spin — the IntegratedServer is built here, the Server thread is started MS->>IS: runServer calls initServer, which loads the level and prepares its chunks MC->>MC: managedBlock — draw a frame, drain the queue, repeat, until MinecraftServer.isReady MC->>SCL: startMemoryChannel — a Netty local address, no socket anywhere MC->>Conn: connectToLocalServer — the client's end of that same channel Conn->>SCL: handshake, then login — the handlers run on Netty, the login tick on the Server thread Conn->>SCL: configuration, then play — from here the client is a client like any other Note over MC,IS: two loops, one wire ``` That is the book's first sequence diagram, and its lanes are abbreviated the way every later one is: two or more letters of a class name, one meaning throughout. The key is [diagram lanes](../../reference/lanes.md). **Bootstrap before anything exists.** `SharedConstants.tryDetectVersion` reads *version.json* as the first statement of both *main* methods, before the option parser exists; `NativeLibrariesBootstrap.loadLibraries` unpacks the natives, `CrashReport.preload` warms the reporter, and `Bootstrap.bootStrap` builds and freezes the static registries — blocks, items, entity types, the things that cannot be data-driven because the data loader itself needs them ([identifiers and registries](../foundations/identifiers-and-registries.md#before-the-game-exists) is what the freeze proves). `ClientBootstrap` does the client-only equivalents between that and `Bootstrap.validate`, which checks the result. `DataFixers.optimize` is kicked off concurrently before the registries are built and joined much later. That ordering is why nothing in `world/` can be touched from a static initialiser. **The GPU backend is chosen in the constructor.** `RenderSystem.initBackendSystem` runs first and returns GLFW's clock, which `Minecraft` installs through `Util.setTimeSource` — on the client, the game's entire notion of time comes from the windowing library. Then a `GpuBackend` is chosen by trying candidates in an order `Options` sets until one of them makes a `Window` — there are two, `GlBackend` and `VulkanBackend` ([the window](../rendering/the-window.md#trying-backends-until-one-of-them-makes-a-window)) — and from then on the renderer only ever sees the `GpuDevice` abstraction in `com/mojang/blaze3d`. **Construction registers, it does not load.** The constructor creates each manager and registers it on the `ReloadableResourceManager`; the loading is one `ReloadInstance` whose *prepare* phases run on `Util.backgroundExecutor` and whose *apply* phases run on the Render thread, with the `LoadingOverlay` on screen. Pressing F3+T re-runs exactly that path — see [the resource system](../foundations/resource-system.md#f3t-end-to-end). **Opening a world spins a server.** `Minecraft.doWorldLoad` calls `MinecraftServer.spin`, which constructs the `IntegratedServer` *on the caller's thread* before starting the new one ([starting a server](../server/starting-a-server.md#minecraftserverspin-and-the-last-thing-main-does) has that order in full); the new thread's body is `MinecraftServer.runServer`, which calls `IntegratedServer.initServer` and enters the loop. Meanwhile the Render thread keeps drawing frames and draining its own queue through `BlockableEventLoop.managedBlock` until `MinecraftServer.isReady` — the textbook case of *waiting drains*. **The client connects like any other client.** `ServerConnectionListener.startMemoryChannel` binds a Netty local address and `Connection.connectToLocalServer` connects to it; the client then walks handshake, login, configuration and play through `ClientHandshakePacketListenerImpl` exactly as it would against a remote server. Almost nothing in the play path knows it is singleplayer: the exceptions are the handful of places that ask `Connection.isMemoryConnection` directly, among them `ClientPacketListener.handleUpdateTags`, which skips applying the tags it was sent because the server's registries are already the client's. [Protocol phases](../networking/protocol-phases.md) is that walk in full. ## Two loops, and a wire between them The client's loop is a frame loop with ticks inside it; the server's is a tick loop with no frames at all. They are not the same shape, and no page later in this book is readable until that difference is fixed in mind. ```mermaid flowchart LR subgraph Client["the Render thread"] direction TB CR["Minecraft.run: RenderSystem.pollEvents"] --> CD["runTick: the DeltaTracker says how many whole ticks are owed"] CD --> CP["PacketProcessor.processQueuedPackets"] CP --> CQ["BlockableEventLoop.runAllTasks: this thread's own queue"] CQ --> CT["Minecraft.tick, run 0 to 10 times"] CT --> CF["renderFrame, interpolating by the leftover partial tick"] CF --> CR end subgraph Wire["the Netty event loop"] direction TB N["Connection.channelRead0 decodes and calls the PacketListener. PacketUtils.ensureRunningOnSameThread queues it on the owner and aborts the handler"] end subgraph Server["the Server thread"] direction TB SR["MinecraftServer.runServer: the next deadline is set first"] --> SP["processPacketsAndTick: PacketProcessor.processQueuedPackets"] SP --> SS["MinecraftServer.tickServer: every ServerLevel, then the connections"] SS --> SW["waitUntilNextTick: run queued tasks, then park until the deadline"] SW --> SR end N -- "a clientbound packet" --> CP N -- "a serverbound packet" --> SP CT -- "Connection.send" --> N SS -- "Connection.send" --> N ``` **The frame loop.** `Minecraft.run` polls GLFW events and calls `Minecraft.runTick` once per **frame**, as fast as vsync or the frame-rate limit allow. Inside each frame a `DeltaTracker.Timer` running at twenty ticks a second says how many whole game ticks have elapsed since the last frame — usually zero or one, at most ten are run — and `Minecraft.tick` is called that many times. The fractional remainder is the partial tick the renderers interpolate with. So the client *has* a 20 Hz tick, but it is a sub-step of the frame loop rather than a loop of its own; [the client loop](../client/the-client-loop.md#the-ten-and-the-arithmetic-behind-it) is the arithmetic in detail. **The tick loop.** `MinecraftServer.runServer` re-reads this tick's length every iteration from `TickRateManager.nanosecondsPerTick` — 50 ms by default, whatever `/tick rate` says otherwise, and zero while sprinting — and calls `MinecraftServer.processPacketsAndTick`, which drains the `PacketProcessor` and then runs `MinecraftServer.tickServer`. Afterwards `MinecraftServer.waitUntilNextTick` spends the slack running queued tasks and then parks until the next tick is due. There is no frame and no partial tick here at all. The "Can't keep up!" warning is `MinecraftServer.runServer`'s own, decided before either call from how far behind the deadline already is; [the server tick](../server/server-tick.md#what-minecraftservertickchildren-runs-and-in-what-order) owns what is inside them — the tick budget, the deferrable work and the flush bracket around outbound packets. **Both are event loops first and game loops second.** `Minecraft` and `MinecraftServer` both extend `ReentrantBlockableEventLoop` — the same base class, not an analogy — so each is an `Executor` whose queue drains on its own thread, and any other thread that wants to touch that half's state submits a task and waits. `BlockableEventLoop.managedBlock` is the blocking form, and the reason the owning thread can wait for a future without deadlocking: it keeps draining its own queue while it waits. **A packet is decoded on one thread and handled on another.** A packet arrives on a Netty IO thread and `Connection.channelRead0` hands it to the current `PacketListener`; a handler that touches game state calls `PacketUtils.ensureRunningOnSameThread`, which, when it is off-thread, queues the packet on the owning side's `PacketProcessor` instead of running it — [the connection](../networking/the-connection.md#the-threads-underneath-it) is that crossing in both directions. What matters here is *when* the queue is drained, because the two loops do not agree: first thing in `MinecraftServer.processPacketsAndTick`, but on the client early in `Minecraft.runTick`, after the delta tracker advances and before the ticks — so a client at 200 frames a second takes the server's packets ten times more often than it ticks. ## Four threads worth memorising | thread | made by | runs | notes | |---|---|---|---| | **Render thread** | the JVM main thread, renamed in `client/main/Main` | `Minecraft.run` | Also the client's game thread: `Minecraft.gameThread` is this thread. Priority 10 on machines with more than four cores. | | **Server thread** | `MinecraftServer.spin` | `MinecraftServer.runServer` | One per server, so singleplayer has exactly one. Priority 8, on the same more-than-four-cores condition. | | **Netty IO** | `EventLoopGroupHolder` | the `Connection` pipeline | Named *Netty NIO IO n* — Epoll or Kqueue when native transport is on, *Netty Local IO n* for the in-process singleplayer channel. Decode, decrypt, decompress — and, unlike the play phase, the handshake and login *handlers*, which never call `PacketUtils.ensureRunningOnSameThread`; the first handler that hops is in configuration. The login state machine is still advanced from the Server thread, because `ServerLoginPacketListenerImpl` is a `TickablePacketListener` and `MinecraftServer.tickConnection` ticks it. | | **Worker-Main-n** | `Util.backgroundExecutor` | a `ForkJoinPool` sized to the JDK's available-processor count minus one | `Util.maxAllowedExecutorThreads` clamps it, and `Util.getMaxThreads` reads a *max.bg.threads* system property that overrides the ceiling. The shared CPU pool: chunk generation and lighting (`ChunkMap` through `ChunkTaskDispatcher`), section meshing (`SectionRenderDispatcher`), resource-reload *prepare* phases, chunk serialisation. | That is the set worth memorising, not the set that exists. The IO workers, the sound engine's event loop, the dedicated server's watchdog, console, RCON, query and management threads, the timer hack thread and the situational ones — authentication, chat filtering, server pinging, telemetry, world upgrades, the shutdown hooks — are all in [Threads](../../reference/threads.md), with who makes each and what it is allowed to touch. There is no fifth thread hiding on the client. The Render thread *is* the game thread: `Minecraft.gameThread` and the thread `RenderSystem` guards with `RenderSystem.assertOnRenderThread` are the same one, and there is no *initGameThread* and no *isOnGameThread* anywhere in the tree — only `RenderSystem.isOnRenderThread`. A slow client tick costs frames directly. What that thread does with a world is animate and predict one — `Minecraft.tick` calls `ClientLevel.tickEntities`, and block entities tick too — but nothing it concludes is authoritative, and the server's packets overwrite whatever the prediction got wrong — which is a claim about the *world*, and the client's own player is the exception the book spends Part VIII and [prediction and acknowledgement](../client/prediction-and-acks.md#two-state-machines-running-against-each-other) on. Everything else that matters is *serialised onto* a pool rather than given a thread. The two `ConsecutiveExecutor` classes in `util/thread` are the mechanism: a queue that promises to run its tasks one at a time on a pool that otherwise runs many, which is how "worldgen" and "light" stay ordered on the worker pool, and how the `IOWorker` stays ordered on `Util.ioPool` — which is its own pool of *IO-Worker-n* threads, not one of the four. `PriorityConsecutiveExecutor` adds a priority to the same idea. And `ServerChunkCache.MainThreadExecutor` is a further event loop layered on the server thread, which is why a tick that waits on a chunk does not deadlock the chunk that needs the tick. ## What singleplayer shares by direct call Nothing on the client writes server world state and nothing on the server writes client world state. Every block, entity and inventory change crosses as a packet, even in one process. But the two halves share a JVM, and a handful of things do cross by direct call — every one of them a setting rather than world state. The server reads `Minecraft.isPaused` and the client's render and simulation distances every tick; `IntegratedServer.updateCommandsAllowedForOtherPlayers` reaches into `LocalPlayer.setPermissions`; the options screens call `IntegratedServer.publishServer` and its siblings straight from the Render thread; and `IntegratedServer.latestTicksGizmos` is a volatile list the server thread writes and the client reads. Treat "everything crosses as a packet" as a rule about the *world*, not about the process. Singleplayer differs in more than pausing, too. Beyond the pause and the distances following `Options`, `IntegratedServer` caps the player list at eight, owns LAN publishing and the `LanServerPinger`, drops the chat and command spam thresholds to zero where a dedicated server defaults to ten, takes native transport from the client's own option rather than a server property, and answers the operator-permission questions differently. ## Questions players ask **Does a dedicated server pause?** Yes — an empty server stops ticking on its own ([the server tick](../server/server-tick.md#an-empty-server-stops-ticking) has the counter and what still runs). Pausing is not a singleplayer concept; only the client-decides-it half is. **Is twenty ticks a second a constant?** No, it is a server field. `ServerTickRateManager`, over the shared `TickRateManager`, owns the nanoseconds-per-tick, the freeze and the sprint state that `/tick` manipulates, and the client mirrors it in `ClientLevel` so the `DeltaTracker` can freeze too. **Does a busy server skip work?** Less than the budget's name suggests. `MinecraftServer.haveTime` travels from `MinecraftServer.tickServer` down through every level, and what it actually gates is a short list that does not include loading or generating a chunk. [The server tick](../server/server-tick.md#what-the-budget-actually-gates) has that list, and the sprint's inverted effect on it. **What happens when something throws?** It is collected, not thrown. Both loops catch everything and wrap it in a `CrashReport`; a background thread that dies has its report parked for a loop to pick up ([how a server dies](../server/how-a-server-dies.md#the-crash-that-saves) owns that relay, and [the client loop](../client/the-client-loop.md#starting-and-the-three-ways-of-stopping) the client's own three exits). The Part I consequence is the asymmetry: only a loop constructed to propagate crashes rethrows a parked report, and `IntegratedServer` is not one — so a worker that dies in singleplayer surfaces on the *client*, not on the server thread whose work it was doing. **Which entry point starts all this?** One of five. `client/main/Main` for the client, `server/Main` for the dedicated server, `data/Main` for the data generator, `client/data/Main` for the generated client assets — models, atlases, equipment assets, waypoint styles — and `gametest/Main` for `GameTestServer`. The tree holds a sixth *main*, `SnbtDatafixer`, which converts files and starts nothing. Each parses its own command line — the client's into a `GameConfig` the `Minecraft` constructor is built from — and then reads its own settings file: *options.txt* through `Options` on the client, *server.properties* through `DedicatedServerProperties` on the dedicated server, and *version.json* through `SharedConstants` on both. The two generator entry points are build-time programs and [what this book skips](what-this-book-skips.md#the-data-generators-and-why-data-driven-is-both-true-and-misleading) says how far that is true. ## Where to look `client/main/Main` · `GameConfig` · `Minecraft` · `DeltaTracker` · `MinecraftServer` · `IntegratedServer` · `server/Main` · `DedicatedServer` · `GameTestServer` · `BlockableEventLoop` · `ReentrantBlockableEventLoop` · `Util` (the executors) · `PacketProcessor` · `PacketUtils` · `EventLoopGroupHolder` · `ServerConnectionListener` · `Connection` · `PreferredGraphicsApi` · `GpuBackend` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # What this book skips > Verified against **Minecraft 26.2** · Part I · A reader opens the atlas, sees fourteen packages hatched, and asks what is in them and why they are not taught. Open the atlas and part of the jar is drawn hatched. That hatching is this page. Java Minecraft is 7,055 classes and 719,302 lines, and the parts do not reach all of it. Some of what is left out is excluded on purpose by [rule three](../../introduction.md) — save migration is version-difference code, and a book that documents only the current version has nothing to say about it. Some is out of scope because it is a client for a service this book cannot read. And one hatched box is not skipped code so much as skipped *ground*: `net/minecraft/data` is the program that writes vanilla's own content as a data pack, it ships in the dedicated server jar — all 163 classes of it — and the running game compiles against it and calls into it. `Blocks` names `TreeFeatures` keys while it constructs mushroom blocks; `MinecraftServer` reaches for a `MiscOverworldFeatures` key for the bonus chest; the F3 screen's biome line runs through `NoiseRouterData.peaksAndValleys` into `TerrainProvider`. The boundary this page draws is honest, and part of drawing it honestly is showing where it leaks.
*(figure: packages-treemap.svg — a generated SVG, not reproduced here)*
The jar by package, area by lines of decompiled source. Hatched boxes are the packages this page tours and the parts do not. Click to enlarge.
## The sizes, and which jar ships them Each entry below says what the thing is, roughly how big it is, whether the dedicated server ships it, one fact worth knowing, and where to start reading. The treemap deliberately does not hatch `net/minecraft/gametest`: Part XIII covers it, so it is a gap that closed rather than a skip. The counts in the table are files, so a *package-info.java* counts as a class there and not in the prose below. | package | classes | lines | side | |---|---:|---:|---| | `net/minecraft/util/datafix` | 396 | 26,372 | both | | `net/minecraft/util/filefix` | 57 | 3,544 | both | | `net/minecraft/client/telemetry` | 18 | 1,221 | client | | `net/minecraft/util/profiling` | 70 | 4,260 | both | | `net/minecraft/server/jsonrpc` | 65 | 4,094 | dedicated server | | `net/minecraft/server/rcon` | 9 | 839 | dedicated server | | `com/mojang/realmsclient` | 127 | 13,217 | client | | `net/minecraft/realms` | 4 | 203 | client | | `net/minecraft/stats` | 10 | 873 | both | | `net/minecraft/gizmos` | 15 | 569 | both | | `net/minecraft/references` | 5 | 1,434 | both | | `net/minecraft/data` | 163 | 15,587 | both — see below | | └ `net/minecraft/data/worldgen` | 56 | 5,369 | both — see below | | `net/minecraft/client/data` | 28 | 6,176 | client | | `com/mojang/blaze3d/audio` | 12 | 1,013 | client | | `net/minecraft/client/multiplayer/chat/report` | 12 | 952 | client | The oracle for "server or client" throughout is the list of classes the dedicated server jar ships, which lives beside the decompile. It answers exactly one question — *does the dedicated server have this class* — so it can prove "client-only" and it can prove "both jars", and it cannot prove "dedicated server only". Two rows are labelled that way on the strength of a different check: nothing under `net/minecraft/client` or `com/mojang/blaze3d` references them. *rcon* is reached from `DedicatedServer` alone; *jsonrpc* from six files, and two of them — `BuiltInRegistries` and `Registries` — are classes the client loads at bootstrap, so the package's *types* are on the client and the server it configures is not. ## Save migration, and the fixer that moves files **`net/minecraft/util/datafix`** — the largest thing on this page and the most explicitly out of scope. `DataFixers` is one static class whose whole body is the migration history of the game written out longhand: three hundred schema registrations and four hundred-odd fixes, from schema 99 up to the current world version. The rewriting machinery itself is Mojang's external DataFixerUpper library; what lives here is the vanilla catalogue — `util/datafix/schemas` describing the *shape* of the data at each version, and `util/datafix/fixes` doing the individual rewrites. A version number becomes a chain of fixes through `DataFixTypes`, an enum of about thirty type references (level, chunk, player, entity chunk, POI chunk, options, stats, advancements, and a long tail of saved-data kinds). `DataFixTypes.updateToCurrentVersion` takes the data version as an argument — every one of its thirteen callers reads the version itself — and asks the fixer to compose every rule from there to now. `DataFixTypes.wrapCodec` is the one that reads the version *out of the tag*: it wraps an ordinary codec so decoding pulls the data version, runs the chain, and encoding stamps the current version back in. It is the rarer door — two callers, `PlayerAdvancements` and `DebugScreenEntryList` — while [chunk storage](../world/chunk-storage.md) and player data take the first one and read the version themselves. The rules are pre-compiled on a dedicated bootstrap thread, and that thread is built with some care to cost nothing: one thread, daemon, at minimum priority, with a single caller in the client's entry point, optimising exactly one type (the level-summary schema, so the world list opens fast). The dedicated server never asks for it at all. **`net/minecraft/util/filefix`** does what the other cannot. A data fixer rewrites the *contents* of a tag after it has been read, so it can never move, rename, split or delete a file. `FileFixerUpper` operates on the world **directory**: its operations are moves, regex moves, group moves, deletions, content modifications and one composite that scopes a nested list of operations to matching folders, and the concrete fixes do things like relocate dimension storage, split player storage and pull data out of *level.dat* into saved data ([level data and rules](../../reference/level-data-and-rules.md)). It stays safe by working somewhere else: the whole upgrade runs against a **custom copy-on-write file system** rooted at a scratch directory, and the result is swapped in at the end. Exactly how safe depends on the filesystem underneath. Where hard links are available it uses them. Where they are not, it writes one file, *upgrade_in_progress.json*, recording the moves, and an interrupted upgrade resumes from it while an aborted one reverts. And where atomic move is unavailable it refuses to run at all rather than risk a half-moved world. The client does not grey out a world that needs the upgrade — it relabels the button. `LevelSummary.primaryActionMessage` turns Play into *Upgrade and Play* while leaving it active; what is disabled is Edit and Recreate, which would otherwise touch a directory the fixer is about to rearrange. ## Telemetry writes to disk before it writes to the network **`net/minecraft/client/telemetry`**, client-only. Exactly seven event types: world loaded, world unloaded, graphics capabilities (which now carries the backend name and the reason a backend failed — see [Blaze3D](../rendering/blaze3d.md)), and four opt-in ones covering performance metrics, world load times, advancements and game load times. `TelemetryProperty` is the vocabulary; each property carries both an internal name and a different export key. Opting out is two-tier and neither tier is a plain checkbox. `Minecraft.allowsTelemetry` reads an *account-level* flag the game only reports; the in-game control only chooses whether the four opt-in events are sent, and is only offered when the account carries the flag that allows it. Everything sent is **also written locally** as a JSON event log with a seven-day expiry — and the send is nested *inside* the log write, so a failed log suppresses the send. A player can read their own outgoing telemetry, though not in the game: the telemetry screen renders the *catalogue* of event types and their properties, and a button next to it opens the log directory in the platform's file manager. Start at `ClientTelemetryManager`, `TelemetryEventType`. ## Four profilers, two of them in this package **`net/minecraft/util/profiling`** holds four profiling systems, though only two of them are self-contained here. The **tick profiler** is the familiar one: `Profiler` is a thread-local holder of a `ProfilerFiller`, `ActiveProfiler` records the push/pop tree of named sections that every page in this book quotes, and `/debug start` drives it. **Tracy** is the surprise, and it is one class bridging out to Mojang's Tracy binding. `TracyZoneFiller` implements the same interface, and `Profiler.get` falls back to the Tracy filler rather than the inactive one when Tracy is available — and `Profiler.decorateFiller` *combines* the two, so an attached Tracy build and a running `/debug start` both see every section. With a Tracy build attached, every profiler section in the game streams out with no command run. Tracy reaches outside this package too, into Blaze3D's frame capture and GPU profiler and into the executor wrappers. **JFR** (`util/profiling/jfr`) registers ten custom flight-recorder events under a Minecraft category — chunk generation, region reads and writes, packets sent and received, network summaries, server tick time, client FPS, structure generation, world load. Packet events are emitted straight from the packet codecs, so a recording gives a per-packet-type byte breakdown that this book's [packet reference](../../reference/packets.md) cannot. Start with *--jfrProfile* or `/jfr start`. **Metrics** (`util/profiling/metrics`) is `/perf`: sampling by nine `MetricCategory` values — pathfinding, event loops, consecutive executors, the tick loop, JVM, chunk rendering, chunk-rendering dispatch, CPU and GPU — written out as CSVs that `PerfCommand` zips. ## The management server is not RCON **`net/minecraft/server/jsonrpc`**, dedicated server only, and genuinely new. It is JSON-RPC 2.0 over a WebSocket, served by its own Netty bootstrap with an HTTP codec, an authentication handler, the WebSocket handshake and optional TLS. It is disabled by default; when enabled, TLS is on unless explicitly turned off, and the server refuses to start without a forty-character alphanumeric secret, generating one if absent. What it exposes is the administrator's surface, not the game's: allow-list, bans and IP bans, players and kicks, operators, game rules, server status, save and stop, system messages, and a family of live server settings — including the idle-pause window, whose actual behaviour is [the server tick](../server/server-tick.md#an-empty-server-stops-ticking)'s, because a dedicated server pauses too. Implementations sit behind service interfaces so the wire layer never touches the server object directly, and an executor service marshals calls onto the server thread. The description of the API cannot drift from the handlers, because it is derived from them: every method is registered with a description and typed parameter and response schemas, and a discovery method returns an **OpenRPC 1.3.2** document built by walking the two method registries and filtering on a per-method discoverable flag. There is also an outgoing direction for server-initiated notifications. The audience is panel and hosting operators. Start at `JsonRpc`, `ManagementServer`. ## RCON, query, and the pre-1.7 ping that removes itself **`net/minecraft/server/rcon`** is seven classes of pre-Netty blocking socket code on its own threads. `RconThread` speaks Valve's Source RCON framing; commands execute as a `RconConsoleSource`, a command source that accumulates output into a string rather than a chat feed ([Brigadier and commands](../commands/brigadier-and-commands.md)). `QueryThreadGs4` speaks the GameSpy4 UDP query protocol with a challenge-token handshake and a five-second response cache. The **pre-1.7 ping is not in that package**. `LegacyQueryHandler` sits in the server's network package and is installed into the Netty pipeline *before* the length-prefix splitter and the packet codec, right after the read timeout ([the connection](../networking/the-connection.md)). It peeks at the first byte; if it is the legacy ping marker it answers in the old format and closes, and otherwise it resets the reader index, **removes itself from the pipeline**, and re-fires the bytes downstream. It costs one byte comparison per connection and then vanishes. The same encoding is used client-side so the server list can still ping ancient servers. ## Realms is a client for a server nobody here can read **`com/mojang/realmsclient`**, client-only, 127 classes and 13,217 lines — about the size of the whole packet catalogue in `network/protocol`. Roughly sixty per cent is screens and the records behind them — subscriptions, world slots, templates, invites, backups, minigames, upload and download — and the rest is a task framework, the world-upload pipeline and the HTTP layer, a list of REST paths with a small request wrapper. Three classes and a *package-info.java* in `net/minecraft/realms` are the only part of vanilla the Realms UI extends. Out of scope because it is a service client: its behaviour is defined by a server this book cannot read. One fact anyway. The environment is chosen from an environment variable falling back to a system property, defaulting to production, in a static final field of the release client — and there are three environments, not two, because the third points at *localhost*. ## Statistics, the scoreboard, and the recipe book **`net/minecraft/stats`** is nine classes covering two concerns, plus a link into a third package that is the reason it is worth a paragraph. **Statistics**: `Stats` declares eight registry-backed stat types — mined, crafted, used, broken, picked up, dropped, killed, killed by — plus a custom type holding the seventy-odd hand-declared counters (play time, distances by every mode of travel, damage dealt and blocked), each bound to a formatter that affects display only. A stat type is a lazily-populated map over a registry, so stat objects are interned; `ServerStatsCounter` adds the per-player file and a dirty set, and only dirty stats are sent. Statistics are one of **two** parts of the save that go through the data fixer as *JSON* rather than NBT — the other is advancement progress ([advancements](../commands/advancements.md)), and they are the only two. **The scoreboard link** is why the package is worth a paragraph — and note that the class doing the linking is not in it: `ObjectiveCriteria` lives in `net/minecraft/world/scores/criteria`, which is why a scoreboard objective can name a statistic and why this package is reachable from a command at all ([scores, teams and stored data](../commands/scoreboard-and-data.md#what-a-criterion-can-be-which-is-nearly-anything) is how the name is parsed). **The recipe book** is the second concern, and it is in this package for historical reasons rather than architectural ones — `RecipeBook`, `RecipeBookSettings` and `ServerRecipeBook` are not skipped, they are [recipes](../items/recipes.md)', with [advancements](../commands/advancements.md) reaching in from the other side. The address is the only thing surprising about them. ## Two packages nobody will recognise **`net/minecraft/gizmos`** is a **debug-drawing API**, in the game-engine sense of the word: the immediate-mode "draw me a box in the world for one frame" facility most engines have and Minecraft did not. `Gizmos` is a static façade over a thread-local `GizmoCollector`; calling a shape method outside a collector scope throws. The shapes are small records; a style is a stroke and fill; the returned handle can pin a shape on top, persist it for a duration or fade it out. Every one of the debug renderers — chunk borders, hitboxes, pathfinding, brains, points of interest, raids, light sections — is now written against it. The part that surprises is that it is **server-side too**: there are four collectors — three on the client (per-tick, the extract pass, the render thread) and one on the integrated server, which wraps its whole packet-and-tick step in a collector scope and publishes the result for the client to drain. Server tick code can draw into the singleplayer world; a dedicated server installs no collector at all, so the same calls there would throw. A headless test server installs a no-op collector so the same calls cost nothing. **`net/minecraft/references`** is not "references" in the data-fixer sense. It is a set of **id-constant tables**, and the split is not the one the package names suggest: `BlockIds` holds the keys for blocks with **no item form** (water, lava, wall torches, piston heads, wall signs), `ItemIds` the items with no block, and `BlockItemIds` — seven times `BlockIds` and not quite twice `ItemIds` — the pairs. Look for stone in `BlockIds` and it is not there. They exist to break a class-initialisation cycle: exactly **ten** files outside the package name it, and they are precisely the ones that need to name a block or item *before* the block and item classes are loaded — `Blocks` and `Items` themselves, `GrassBlock` and `MyceliumBlock`, which name another block during that initialisation, `DecoratedPotPatterns` beside them, and the five tag providers. A resource key is a registry plus an [identifier](../foundations/identifiers-and-registries.md), so it can be built with nothing loaded. Practically, it is the canonical machine-readable list of *block and item* ids, and a better starting point than the block and item holder classes if that is what you want — but not the id list: five sibling tables for entity types, block-entity types, potions, fluids and atlases live outside the package, in the trees they belong to. ## The data generators, and why "data-driven" is both true and misleading Most of **`net/minecraft/data`** is a build-time program: a second entry point with its own options, a generator that groups providers into packs, and a hash cache that skips unchanged files. `net/minecraft/client/data` is its client half, generating block and item models and the atlas definitions. The significance is a genuine paradox worth stating plainly. **Vanilla's own content is a data pack.** `net/minecraft/data/worldgen` is the entire vanilla worldgen data pack written as Java — the biome feature lists, the surface rules, the noise settings, the carvers, the jigsaw pools, the structures and structure sets, the processor lists — and the loot, recipe, tag and advancement packages do the same for their domains, all serialised through the *same* codecs the game uses to read a pack. So "Minecraft is data-driven" is true: the running game only ever sees JSON parsed by codecs, with no vanilla-specific path ([the resource system](../foundations/resource-system.md)). And "you cannot change it without a data pack" is *nearly* true — which is the more useful statement, because the exceptions are load-bearing and a reader who believes the absolute version will misread three other pages. **The package is not build-time only, and the dedicated server ships all 163 classes of it.** Three kinds of exception: - **Plain id tables.** `AtlasIds` is read at runtime by the model manager, the atlas manager, the map, sky, painting and particle renderers, and by a chat component. Nothing build-time about it. - **The bootstrap interface itself.** `BootstrapContext` — in `net/minecraft/data/worldgen` — is what every vanilla registry bootstrap in the game is written against, from damage types and enchantments to chat types, dialogs and world clocks. It is the most-imported type in the package by a wide margin. - **Constants and math the running game calls.** `Blocks` itself names `TreeFeatures` and `CaveFeatures` keys while constructing mushroom and fungus blocks; `MinecraftServer` reaches for a `MiscOverworldFeatures` key for the bonus chest; a jigsaw block entity defaults to a `Pools` key; `NoiseRouterData` and `NoiseGeneratorSettings`, both shipped worldgen classes, are compiled against `TerrainProvider` and `SurfaceRuleData`; and the F3 screen's biome line calls `NoiseRouterData.peaksAndValleys`, one line that delegates straight into `TerrainProvider` ([density functions](../worldgen/density-functions.md)). Vanilla's density functions and noise settings still reach the running game as JSON. `NoiseRouterData.bootstrap` and `NoiseGeneratorSettings.bootstrap` are collected by `VanillaRegistries`, which the data-generator entry point runs and which `Commands.validate` borrows for its ambiguity check; the game itself reads the generated files out of the built-in pack. Editing `TerrainProvider` changes terrain by changing what that generator writes. So `net/minecraft/data` holds a build-time program *and* a handful of tables and functions the shipped game compiles against and executes. The generator half really is inert at runtime, and it is the half worth reading — it is the fastest way to understand what a vanilla biome or structure declares, because it is typed and cross-referenced where the JSON is not, a point [biomes](../worldgen/biomes.md) and [structure placement](../worldgen/structure-placement.md) both depend on. And the report providers are how you get machine-readable dumps of exactly the tables this book's own [reference layer](../../reference/README.md) covers. ## The audio backend lives in Blaze3D, and is not skipped **`com/mojang/blaze3d/audio`** is the one package in this tour that is hatched for its *address* rather than for being unread. It wraps OpenAL, and it sits inside Blaze3D, beside the GPU abstraction, rather than in the client's sound package where the engine, the manager, the channel pool and the Ogg decoding live. That is the boundary fact: Blaze3D is the platform layer for both devices, not only the graphics one, and a reader looking for the sound code under `client/sounds` will not find the half that talks to the driver. Everything the package does — the device and context, the channel pools, binaural rendering, hot-plugging a headset mid-game — is taught, in Part X, by [the sound engine](../client/sound-engine.md). ## Player reporting **`net/minecraft/client/multiplayer/chat/report`**, client-only. `ReportingContext` holds the sender, the environment (which server or realm), a log of the last thousand-odd received messages, and at most one draft report. There are three report kinds — chat, skin, name — and an eleven-value reason enum. The piece worth naming is the context builder: a chat report does not send just the offending line, it walks the log backwards to assemble surrounding **signed** context, which is what makes the report verifiable at the other end. The report machinery is the consumer of the chat-signing system that [chat and signing](../networking/chat-and-signing.md) documents. Neither the transport nor the policy is in the game — both come from the account service library. ## Gaps, and the ruling on each These were never excluded on principle — they were simply not written when the book reached them. Each carries one of four rulings: **covered** (a page now owns it), **absorbed** (a paragraph or a section on a page that already exists), **reframed** (the gap was described wrongly, and the description is what changed), or **declined** with a reason. A decline is a promise that a reader will not miss it, not a shrug. | what | size | ruling | where | |---|---|---|---| | `net/minecraft/gametest` | 47 classes, 5,514 lines | covered, which is why the treemap does not hatch it | [game tests](../commands/game-tests.md) | | the debug cluster | four packages' worth | covered: the F3 entry registry, and the server-push subscriptions, sample loggers and debug renderers | [the HUD](../client/hud.md), [debugging the running game](../client/debugging-the-running-game.md) | | `com/mojang/blaze3d/platform` | 29 classes, 3,896 lines | covered | [the window](../rendering/the-window.md) | | `PostChain`, `PostChainConfig`, `PostPass`, `UniformValue` | 4 classes, 996 lines, and six shipped chains | covered — it was the only place in the game where user-authored shaders are first class | [post-processing](../rendering/post-processing.md) | | `net/minecraft/client/renderer/item`, its item-properties subtree included | 63 classes | covered as a section rather than a page: the trace starts at an `ItemStack` but everything it touches is Part XI's | [models and atlases](../rendering/models-and-atlases.md#how-an-item-picks-its-model) | | the scoreboard, teams and command storage | 32 classes, ~3,830 lines | covered — it was the largest coherent system in the book with no page at all | [scores, teams and stored data](../commands/scoreboard-and-data.md) | | `net/minecraft/util/parsing` | 29 classes, 1,879 lines | absorbed — Mojang's own packrat parser-combinator framework, and the question it answers, why the client can complete mid-token, is that page's question | [Brigadier and commands](../commands/brigadier-and-commands.md), and [codecs, NBT and JSON](../foundations/codecs-nbt-json.md) for its largest consumer, the SNBT reader | | `net/minecraft/client/animation` | 23 classes, 509 lines | absorbed for its five framework classes, declined for the sixteen pure-keyframe definitions | [entity rendering](../rendering/entity-rendering.md) | | `net/minecraft/server/packs` | 55 classes, 4,975 lines | absorbed — mostly covered already, two corners owed a sentence each | [the resource system](../foundations/resource-system.md) | | `net/minecraft/client/resources` | 101 classes, 7,612 lines | reframed — it is not one system, and five pages own its parts | [models and atlases](../rendering/models-and-atlases.md), [what makes a sound happen](../client/what-makes-a-sound.md), [entity rendering](../rendering/entity-rendering.md), [the HUD](../client/hud.md), [the resource system](../foundations/resource-system.md) | | `com/mojang/blaze3d/vulkan` | 40 classes, 7,477 lines | declined — a faithful second implementation of an interface already documented, and the abstraction is the lecture | [Blaze3D](../rendering/blaze3d.md) | | `net/minecraft/client/data` | 28 classes, 6,176 lines | declined — build-time model and atlas generators, the same category as the generator half of `net/minecraft/data`, but big enough that a reader trips over it | named here and nowhere else | | the catalogues | ~230 mob models, ~73 particles, 101 render states, 50 render layers, 16 animation definitions, 61 of 63 worldgen features, 50 tree kits, the entity sub-predicates | declined — each is one shape repeated, and the shape is on the page that owns the framework | [the reference layer](../../reference/README.md) | | `client/quickplay`, `client/profiling`, `client/renderer/gizmos` | a few classes each | declined — no mechanism a lecture needs | — | | `net/minecraft/data/worldgen` as content | 56 classes, 5,369 lines | declined *as content*: it is the datagen bootstrap that emits vanilla's JSON | the runtime exceptions named above, which are not a decline | Three of those rows need a sentence more. `client/animation` comes with a warning owed to anyone who measures it, because *lines* is the wrong unit for that package: 509 lines and 674 KB, with one file whose single longest line is thirty thousand characters, because the decompiler renders each animation as one builder chain. Four things inside `blaze3d/vulkan` are not backend detail and are named before the decline — `GlslCompiler` and the `vulkan/glsl` shaderc and spirv-cross pair, because Minecraft still authors GLSL and cross-compiles it to SPIR-V, which is the whole reason one shader source can feed two backends; `DestructionQueue`, the deferred-free discipline OpenGL needs no equivalent of, which is the clearest illustration of what the device seam hides; and `vulkan/checkpoints`, vendor breadcrumb extensions for GPU crash reports. The interiors of `blaze3d/opengl` are declined on the same grounds. The `client/resources` reframing is the third. The old entry called it "the client reload" with "no page owning the client half end to end", and that overstates it: models and atlases, sound instances, skins, waypoint styles, and the pack source, splashes, language and metadata all have owners. What no page walks is the client *reload* as one sequence, which is a question about the shape of the documentation rather than a hole in it. The one substantively uncovered corner is `client/resources/server`, the server-resource-pack prompt and download flow — and it pairs with the two corners of `net/minecraft/server/packs` that the resource-system page owes a sentence each: *linkfs*, a synthetic read-only file system that lets a development checkout's scattered directories present as one pack root, and `DownloadQueue` with `DownloadCacheCleaner`, the server-resource-pack download queue and its cache eviction. **Named, and not yet written.** These are real systems with real lectures in them, found by the coverage sweeps and not covered by any ruling above: the carver tunnel walk; the dragon fight (`EnderDragonFight`); the advancements screen; and `client/multiplayer`'s joining-a-server tail. They are named here so that a reader who wants one knows the book knows it is missing, and knows where to start. ## Where to look If you need one of these the entry points are named in each section; if you want a *list* rather than a system, start at `net/minecraft/references` for ids and the report providers in `net/minecraft/data` for everything else, and the [atlas](../../maps/packages.md) for the shape of the whole jar. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # II · Foundations > Verified against **Minecraft 26.2** · Part II · The machinery every later part assumes: how anything becomes data, gets a name and a number, is loaded, and reaches into code. Part II is the vocabulary the other twelve parts speak without pausing to define it. Nothing here is a thing a player does; everything here is what happens underneath the things a player does. A player recognises this part by its symptom: the square brackets after an item name in a `/give`, the `#minecraft:logs` in a recipe file, the *type* line at the top of most of the JSON files in a data pack, and the fact that a world made of JSON loads in seconds. ## The shape of the part Part II is not a stack but a fan. Codecs and registries are underneath everything else here, and the five pages above them are largely independent of one another — which is why the figure has two roots and no single column. Watch it bottom-up all the same: every arrow points from the machinery to what takes it for granted, and the last page is the pattern the rest exists to make possible. ```mermaid flowchart BT C["Codecs, NBT and JSON: one object, four formats"] R["Identifiers and registries: a name, a number, a Holder, and the freeze"] S["The resource system: a stack of packs, a snapshot, prepare then apply"] T["Tags: data-pack JSON reaching into hard-coded behaviour"] D["Data components: a prototype on the registry entry, a patch on the stack"] X["Text components: text as data, worded on the client"] P["The data-driven type pattern: a type field is a lookup in a registry packs cannot extend"] C -- "every data-pack registry element is decoded by a codec" --> R R -- "dynamic registries are loaded from the pack stack" --> S S -- "tags are read from the same stack, before the listeners" --> T R -- "the tag table is swapped on a registry already frozen" --> T R -- "prototypes bind onto Holder.Reference at reload" --> D C -- "every persistent component value has a codec" --> D C -- "ComponentSerialization holds the whole text matrix in one class" --> X T -- "a tag-shaped HolderSet in any file" --> P D -- "a component type is a key in a file, not a kind" --> P R -- "the type registry is built-in, the elements come from the packs" --> P ``` ## Before you start [Anatomy](../anatomy/anatomy.md#four-threads-worth-memorising), for the threads: registries are frozen before either program exists, data-pack loading runs on the worker pool with hops back to the owning thread, and a reload's *apply* phase runs on whichever thread owns the state being replaced. ## Watch in this order 1. [Codecs, NBT and JSON](codecs-nbt-json.md) — one `ItemStack` written four ways: into a chunk file, into a packet, as a checksum in a click, and out of the text of a `/give`. The click sends no component data at all, only hashes. 2. [Identifiers and registries](identifiers-and-registries.md) — how `minecraft:diamond_sword` becomes an `Item` before the game exists, and how a data-pack biome becomes a `Holder` the client is told about. The wire id of a sword is the line number of its registration. 3. [The resource system](resource-system.md) — F3+T as a pipeline: a stack of packs, a snapshot of the list, every listener preparing at once, applying in order; `/reload` as the same pipeline on the server. On the client a failed reload deselects every pack, not the bad one. 4. [Tags](tags.md) — `#minecraft:logs` from a JSON file to the set a parrot checks before it perches. A frozen registry's contents never change, and yet `/reload` changes what the tag contains. 5. [Data components](data-components.md) — the prototype an item type supplies and the patch a stack carries. The prototype is built on every reload, with the world's registries in hand, not in the constructor. 6. [Text components](text-components.md) — a death message built on the server, sent as a translation key, and worded by the client's language file. The client receives it before anyone knows what it says. 7. [The data-driven type pattern](data-driven-types.md) — the *type* field at the top of a data-pack file is a lookup in a built-in registry of kinds, and the fifty-six registries of that shape are why a pack can compose the game's behaviours without adding one. ## Reference this part uses [Math and primitives](../../reference/math-and-primitives.md) — the coordinate spaces, packings, shapes and random sources every page assumes; it was a Part II page and is now looked up, not watched. [Registries](../../reference/registries.md) — every registry key: built-in, data-pack, synced. [Data components](../../reference/components.md) — every `DataComponentType`. [Naming drift](../../reference/naming-drift.md) — `Identifier` was *ResourceLocation*. [Diagram lanes](../../reference/lanes.md). The [glossary](../../reference/glossary.md) is worth more here than in any other part: this is where most of the book's vocabulary is defined, and it is the page to check when a later part uses one of these words in a second sense. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Codecs, NBT and JSON > Verified against **Minecraft 26.2** · Part II · One `ItemStack` written four ways: into a chest's chunk file, into a container packet, as a checksum in a click, and out of the text of a `/give`. A player types `/give @s diamond_sword[damage=5]`, drops the sword into a chest, logs out, comes back and clicks the slot. In those few seconds the same sword has been written four times into four different shapes: parsed out of the square brackets, written into the chunk file the chest lives in, sent down a socket as bytes, and sent back up when the slot was clicked. The fourth is the one worth stopping on. **The click carries no component data at all.** It names the item and the count in the clear — `HashedStack.ActualItem` is a `Holder`, an int and a hashed patch — and for each component on the sword it sends one 32-bit checksum, produced by running that component's own codec into a `DynamicOps` whose output is a hash rather than a document — `HashOps`. And it is the same codec every time. The codec that hashed the damage value is the codec that wrote it into the chunk file and the codec that parsed it out of the square brackets. One description of a type, and the format is an argument. ## The cast | class | what it decides | thread | |---|---|---| | `Codec` | DataFixerUpper's description of a type: encode to and decode from *any* format, given the ops for it | any | | `DynamicOps` | what a format's map, list, string and number are made of — the argument a codec takes | any | | `NbtOps` | tags: the binary tree on disk, and what SNBT text parses into | any | | `RegistryOps` | the registry lookup a `Holder`-valued codec needs, wrapped around another ops | any | | `HashOps` | the format whose finished document is a hash; nothing is serialised on the way | Render on the client, Server on the comparison | | `StreamCodec` | the exception: hand-laid bytes on a Netty `ByteBuf`, written once, read once | Netty | | `TagValueOutput` · `TagValueInput` | the only `ValueOutput` and `ValueInput` there are — a `CompoundTag` plus its ops, and what save code actually sees | whichever thread saves | | `ProblemReporter` | that a codec failure inside a save is a logged path, not an exception in the tick | the failing thread | All of it ships in both jars. The thread column is the four of [anatomy](../anatomy/anatomy.md#four-threads-worth-memorising), and which one holds a codec matters only where the same codec runs on two of them. ## The four paths, side by side | | into the chunk file | onto the wire | back as a checksum | out of the text | |---|---|---|---|---| | **who starts it** | `ChestBlockEntity.saveAdditional`, inside chunk serialisation | `ClientboundContainerSetSlotPacket`, from `AbstractContainerMenu.broadcastChanges` | `MultiPlayerGameMode.handleContainerInput`, on the click | `GiveCommand` through `ItemArgument` | | **the ops** | `RegistryOps` over `NbtOps` | none — a `RegistryFriendlyByteBuf` and nothing else | `RegistryOps` over `HashOps.CRC32C_INSTANCE` | `RegistryOps` over `NbtOps`, held by a `TagParser` | | **the codec** | `ItemStack.MAP_CODEC`, inside `ItemStackWithSlot.CODEC` | `ItemStack.OPTIONAL_STREAM_CODEC` | each component's own codec, through `TypedDataComponent.encodeValue` | `DataComponentType.codecOrThrow`, one component at a time | | **what is carried** | a document — *id*, *count*, *components* | a count, an item id, then a `DataComponentPatch` | one int per added component, and the bare names of the removed ones | SNBT text, then a `Tag` | | **the thread** | Server, then an IO worker for the file itself | Netty | Render on the client, Server on the comparison | Server | | **when it fails** | a problem is recorded on a `ProblemReporter` and logged when the scope closes | the decoder throws, `Connection.exceptionCaught` sees it, and the connection drops | the hash disagrees, and the server sends the slot back | a `CommandSyntaxException` with the cursor position in it | Four columns, four diagrams. Read them as four answers to the same question. ### Disk: a chest writes a list of slots ```mermaid sequenceDiagram participant CBE as ChestBlockEntity participant CHelp as ContainerHelper participant TVO as TagValueOutput participant NbtIo as NbtIo Note over CBE: the server thread, inside chunk serialisation CBE->>TVO: saveWithFullMetadata, createWithContext with a ProblemReporter and the registries CBE->>CHelp: saveAdditional hands the ValueOutput straight on CHelp->>TVO: list Items with ItemStackWithSlot.CODEC, one entry per occupied slot TVO->>TVO: ItemStack.MAP_CODEC writes id, count and components TVO-->>CBE: buildResult gives a CompoundTag, and ScopedCollector.close logs any problem Note over NbtIo: an IO worker, later CBE->>NbtIo: SerializableChunkData through RegionFileStorage.write ``` `ItemStack` has no NBT method — there is no *save* and no *parse* on it. `ChestBlockEntity.saveAdditional` receives a `ValueOutput` and calls `ContainerHelper.saveAllItems`, which opens a typed list under *Items* with `ItemStackWithSlot.CODEC`, a record of slot plus the stack's own `ItemStack.MAP_CODEC` fields inlined. That map codec writes *id*, *count* (defaulting to 1) and *components*, the last being `DataComponentPatch.CODEC`, which spells a removal as *!minecraft:foo*. `NbtUtils.addCurrentDataVersion` stamps the data version on the way out. Load is the mirror: `BlockEntity.loadStatic` reads the id, `ChestBlockEntity.loadAdditional` calls `ContainerHelper.loadAllItems` over `ValueInput.listOrEmpty`. ### Wire: a count, an id and a patch ```mermaid sequenceDiagram participant PEnc as PacketEncoder participant IStack as ItemStack participant DCP as DataComponentPatch participant PDec as PacketDecoder Note over PEnc: Netty, clientbound PEnc->>IStack: OPTIONAL_STREAM_CODEC, a varint count where anything non-positive means empty IStack->>DCP: Item.STREAM_CODEC for the id, then STREAM_CODEC for the patch DCP->>PEnc: added count, removed count, each type id with its value, then the removed ids Note over PDec: Netty, serverbound, the creative slot alone PDec->>IStack: validatedStreamCodec over OPTIONAL_UNTRUSTED_STREAM_CODEC IStack->>IStack: re-encode through ItemStack.CODEC into NullOps, keeping only the errors ``` Nothing on this path is a `Codec`. `ItemStack.OPTIONAL_STREAM_CODEC` writes a varint count where anything non-positive means empty and nothing else follows, then a registry id that resolves because the buffer is a `RegistryFriendlyByteBuf`, then `DataComponentPatch.STREAM_CODEC`. `ItemStack.STREAM_CODEC` is the same codec that refuses an empty stack. Serverbound is a different animal: `ServerboundSetCreativeModeSlotPacket` uses `ItemStack.validatedStreamCodec` over `ItemStack.OPTIONAL_UNTRUSTED_STREAM_CODEC`, where `DataComponentPatch.DELIMITED_STREAM_CODEC` length-prefixes every component value, and the decoded stack is then re-encoded through `ItemStack.CODEC` into `NullOps` — output thrown away, only the errors kept — to prove that the persistent codec would have accepted it. ### Checksum: a hash instead of a stack ```mermaid sequenceDiagram participant CPL as ClientPacketListener participant HS as HashedStack participant ACM as AbstractContainerMenu Note over CPL: the client, once, when configuration ends CPL->>CPL: createSerializationContext over HashOps.CRC32C_INSTANCE, a RegistryOps whose document is a hash Note over HS: the render thread, on the click CPL->>HS: create, one int per added component through TypedDataComponent.encodeValue HS->>ACM: ServerboundContainerClickPacket, the changed slots and the cursor Note over ACM: the server thread, after the click has been re-run ACM->>ACM: RemoteSlot.Synchronized re-hashes the server's own stack and compares ``` `HashOps` is a `DynamicOps` like any other, and a codec cannot tell the difference: it builds maps and lists and strings as usual, and what comes back at the end is a hash code rather than a tree. There is no intermediate byte form to hash. Only one instance is ever built, `HashOps.CRC32C_INSTANCE`, and **both sides wrap it in a `RegistryOps`** — `ClientPacketListener` builds one from the registries it received during configuration, `ServerPlayer` from the server's own — because a component value can name a registry entry, and a hash of an unresolvable name is no hash at all. The server's is behind a 256-entry cache keyed on the `TypedDataComponent`, so the common components are hashed once per player and then looked up. Removals are not hashed: `HashedPatchMap` is a map of added component type to int plus a plain set of removed types. What the server does with the comparison — the either-or in `RemoteSlot.Synchronized`, and the promotion to a concrete stack when the hash agrees — is [containers and menus](../items/containers-and-menus.md). ### Text: square brackets into a `Tag` ```mermaid sequenceDiagram participant IP as ItemParser participant TagP as TagParser Note over IP: the server thread, while Brigadier parses the command line IP->>TagP: create, over this parser's own RegistryOps on NbtOps IP->>TagP: parseAsArgument at the opening bracket TagP-->>IP: a Tag, read no further than its own closing brace IP->>IP: DataComponentType.codecOrThrow parses that Tag into a DataComponentPatch.Builder ``` `ItemArgument` hands `GiveCommand` an `ItemInput`, and `ItemParser` is what builds it. The parser holds a `RegistryOps` over `NbtOps.INSTANCE` and a `TagParser` created *for that ops*, so `[damage=5]` becomes a `Tag` and then goes through the very codec the chunk file used, reached by `DataComponentType.codecOrThrow`. A leading `ItemParser.SYNTAX_REMOVED_COMPONENT` is the command-line spelling of the same removal the disk codec writes. Data packs are the JSON twin of this path: `SimpleJsonResourceReloadListener.scanDirectory` takes whatever `DynamicOps` it is handed, registry-aware or bare, and an `ItemStack` in a loot table goes through `ItemStack.CODEC` exactly as it does on disk. ## One abstraction, and the ops that are not formats A `Codec` is a description of a type and nothing else; it does not know what it is writing into. The format is the `DynamicOps` handed to it at the call, so the same object describes NBT on disk, JSON in a data pack, and SNBT typed at a command line. Most of the game's codecs are assembled from the combinators in `ExtraCodecs`, a thousand lines of vocabulary in `net/minecraft/util`; the [class index](../../reference/class-index.md) is where to look one of them up. Two of the game's ops are not formats at all. `HashOps`, in the cast above, answers every question a codec asks and returns a checksum instead of a document. `NullOps`, which the cast does not list, returns `Unit`: it encodes to nothing, and exists so that a codec can be *run for its errors alone*, which is exactly what `ItemStack.validatedStreamCodec` does to a creative-mode stack. `StreamCodec` is the genuine exception, and it is not a `Codec` at all. A `StreamCodec` pairs an encoder and a decoder over a `ByteBuf` directly, with `StreamCodec.composite` building one out of field codecs; the catalogue of primitives is `ByteBufCodecs`. A packet is written once, read once and must be small, so it gets hand-laid bytes rather than a document in some format. The two worlds meet at `ByteBufCodecs.fromCodec` and `ByteBufCodecs.fromCodecWithRegistries`, which run an ordinary `Codec` into NBT and put the tag on the wire, and at `IdDispatchCodec`, the packet-id table itself. ## Where the registry context comes from A codec that names an entry of a **dynamic** registry cannot resolve it on its own. `RegistryFileCodec`, `RegistryFixedCodec` and `HolderSetCodec` all demand a `RegistryOps`, a `DelegatingOps` carrying a `RegistryOps.RegistryInfoLookup` beside whatever real ops it wraps. There are two routes worth knowing: `HolderLookup.Provider.createSerializationContext`, which is what nearly every caller uses, and `RegistryDataLoader.createContext`, used during registry loading itself, when the registries are still being built ([identifiers and registries](identifiers-and-registries.md#when-a-world-opens)). Both end at `RegistryOps.create`, which a handful of callers reach directly. A codec over a **built-in** registry is a different case: `Registry.holderByNameCodec` — and so `Item.CODEC` — resolves against the registry instance captured inside the codec, and works on bare `NbtOps.INSTANCE`. The distinction is not academic. It decides which paths must build a context before they can decode anything, and in practice almost every real path must: `TagValueOutput.createWithoutContext` exists and is called by nothing at all, and there is no context-free `ValueInput` even in principle. On the network the same context arrives as a decorator. `RegistryFriendlyByteBuf` adds `RegistryFriendlyByteBuf.registryAccess` to a `FriendlyByteBuf`, and `RegistryFriendlyByteBuf.decorator` is bound when the protocol switches from configuration to play. Configuration packets have no registry context — which is why registry data and tags are sent in that phase as NBT and ids, and why the `ByteBufCodecs.holder` family is play-only ([protocol phases](../networking/protocol-phases.md)). ## What NBT actually is `Tag` is a **sealed** interface, and the scalar leaves are records. | branch | members | notes | |---|---|---| | `CompoundTag` | — | a final class; the only keyed shape | | `CollectionTag` | `ListTag`, `ByteArrayTag`, `IntArrayTag`, `LongArrayTag` | the arrays are final classes, `ListTag` is a list | | `PrimitiveTag` | `StringTag` and the six `NumericTag` records | records of one value each | | `EndTag` | — | the terminator | Because the leaves are records, the old *getAsInt* family is gone: `Tag.asInt` and `Tag.asString` return `Optional`, `CompoundTag.getInt` is `Optional` and `CompoundTag.getIntOr` is its defaulted form, `CompoundTag.get` is still nullable, and the two-argument *contains* taking a type id no longer exists. Three things about the binary form surprise people. **A mixed-type list is boxed on the way out.** Binary NBT stores one element type per list, so `ListTag.write` promotes a heterogeneous list to compounds and wraps each element in a one-entry compound under the empty key, with `ListTag.addAndUnwrap` the exact inverse on read. This is not a legacy artefact left lying around — it is written today, and it is the whole reason mixed lists are legal at all. **A numeric array stays an array, but nothing turns a list into one.** `NbtOps.createCollector` hands back a `NbtOps.GenericListCollector` for an empty list and only reaches for `NbtOps.ByteListCollector`, `NbtOps.IntListCollector` or `NbtOps.LongListCollector` when it is handed an existing `ByteArrayTag`, `IntArrayTag` or `LongArrayTag` — and those degrade back to the generic collector the moment an element does not fit. A codec building a fresh list gets a `ListTag`, whatever is in it; the arrays on disk are written by the codecs that asked for arrays. **A whole file need not be read to answer a question about it.** `StreamTagVisitor` and the visitors beside it let `NbtIo.parse` pull a named handful of fields out of a region chunk without materialising the chunk: a `CollectFields` is built from the `FieldSelector`s the caller wants, two for the `IOWorker` reading a chunk's data version and its blending data, three for `StructureCheck`, which is how it and the world-list screen answer without loading a world. Every read that came from outside carries a budget. `NbtAccounter` is charged as the per-type read strategy in `TagType` walks the stream, with `NbtAccounter.DEFAULT_NBT_QUOTA` at 2 MiB, `NbtAccounter.UNCOMPRESSED_NBT_QUOTA` at 100 MiB and a depth cap of `NbtAccounter.MAX_STACK_DEPTH`; overrunning any of them raises an `NbtException`. Region compression is a separate global: `RegionFileVersion.configure` sets one process-wide selection from *region-file-compression*, a `RegionFile` captures it once for **writing**, but every chunk carries its own version byte and reads honour that — so one world can hold chunks in several compressions at once. `RegionFileVersion.VERSION_CUSTOM` (id 127) is the marker for a chunk written with a compression the game does not name; the marker for a chunk that grew too big and lives in its own external file is a different one — `RegionFile`'s external-stream flag, 128, set in the version byte. And `NbtIo.writeCompressed` (GZIP) is for the standalone files, *level.dat* and player data. The text form is `TagParser`, and it is generic over its *output* ops rather than producing tags: SNBT can decode straight into any format's target without a `CompoundTag` in between. `TagParser.parseCompoundFully` is the plain-string entry point; `TagParser.FLATTENED_CODEC` accepts an SNBT string only, and `TagParser.LENIENT_CODEC`, built as an alternative of the two, is the one that takes a string *or* an object interchangeably. Under it, `SnbtGrammar` is a packrat grammar and `SnbtOperations` supplies the built-in *bool* and *uuid* functions. ## Save code never sees a `CompoundTag` `ValueOutput` and `ValueInput` are the façade every `BlockEntity` and `Entity` writes through — `ValueOutput.store` with a codec, `ValueOutput.child`, `ValueOutput.list`, and typed getters with defaults on the way back in (`ValueInput.getIntOr`, `ValueInput.getStringOr`). The only implementations are `TagValueOutput` and `TagValueInput`, which wrap a `CompoundTag` and a `DynamicOps`, with `ValueInputContextHelper` holding the shared provider and the empty instances. The `CompoundTag`-returning methods on `BlockEntity` are final shells that build one of these, and they differ only in how much metadata they add: `BlockEntity.saveCustomOnly` none, `BlockEntity.saveWithoutMetadata` the block entity's own *components* as a full `DataComponentMap`, and `BlockEntity.saveWithFullMetadata` the id and x, y and z. `BlockEntity.saveWithId` adds the id too, but only in the `ValueOutput` form — it has no `CompoundTag` shell. The point of the façade is that **an encode failure is reported, not thrown**. Everything goes through a `ProblemReporter`, and `ProblemReporter.ScopedCollector` logs the whole collected tree of problems when it closes, rooted at `BlockEntity.problemPath` or `Entity.problemPath`; `TagValueOutput.EncodeToFieldFailedProblem` and its siblings are what a bad codec produces. A component that will not serialise costs you the component, not the tick. The deliberate exceptions to the façade are `CustomData` and `TypedEntityData`, the two components that carry a `CompoundTag` verbatim so that data packs have an escape hatch ([data components](data-components.md#the-key-datacomponenttype)). Migration is the one thing that runs before any of this. `DataFixTypes` is the enum of every kind of file the game owns — `DataFixTypes.CHUNK`, `DataFixTypes.PLAYER`, `DataFixTypes.LEVEL`, `DataFixTypes.OPTIONS` and the rest — and each calls `DataFixTypes.updateToCurrentVersion` on the DataFixerUpper `DataFixer` from `DataFixers.getDataFixer` before the codec is shown the tag, using the data version `NbtUtils.addCurrentDataVersion` stamped when the file was written. The fixes themselves are out of scope here. ## Trusted, untrusted and validated "Trusted" on this wire means *the server wrote it*, and it is a statement about the read budget, not about direction. `ByteBufCodecs.TRUSTED_COMPOUND_TAG` reads with an unlimited accounter and has exactly one call site, `ClientboundBlockEntityDataPacket`; plain `ByteBufCodecs.COMPOUND_TAG` reads under the 2 MiB default and carries `CustomData` and predicates. `ByteBufCodecs.TRUSTED_TAG` also exists and has no call sites at all, so build no mental model on it. Serverbound defence is layered rather than singular, and the three layers sit on three different packets. Full re-validation through `ItemStack.validatedStreamCodec` is unique to the creative-mode slot. `ServerboundCustomClickActionPacket` builds its own, much tighter `NbtAccounter` and length-prefixes its payload. And the ordinary container click sends no component data at all, only the hashes above — the strongest defence of the three, because there is nothing to validate. JSON is the format with the smallest footprint. It is the data packs, and on the wire it survives in exactly two places, both outside the play phase: `ClientboundStatusResponsePacket` and `ClientboundLoginDisconnectPacket`, sent through `ByteBufCodecs.lenientJson`. That is why the game ships two JSON parsers — `LenientJsonParser` for the wire and `StrictJsonParser` for data packs. Chat text itself is NBT by the time it reaches the play phase: `ComponentSerialization` holds that whole matrix in one class ([text components](text-components.md#serialisation-one-codec-three-shapes)). ## Questions players ask **Why does an item look identical and not stack?** Because equality is prototype plus patch, and the patch is what these codecs round-trip. Two stacks that took different routes to the same components compare equal; one that kept a component the other dropped does not, however the tooltip reads. **Why can a data pack put arbitrary NBT on an item at all?** Because `CustomData` and `TypedEntityData` are components whose value *is* a `CompoundTag`, passed through verbatim. Everything else on a stack has a real codec and is checked by it. **Why does a corrupt item vanish instead of crashing the world?** Because the save façade reports rather than throws. The bad field becomes a problem on a `ProblemReporter` and is logged when the scope closes, and the chunk saves without it. **Why is a creative-mode item the one thing the server double-checks?** Because it is the only packet on which a client authors a whole stack. Everywhere else the client either receives stacks or asserts hashes about stacks the server already sent it. **Why does a `/give` accept the same square brackets a chunk file uses?** Because it is the same codec. `ItemParser` parses the text into a `Tag` with `TagParser` and hands that tag to `DataComponentType.codecOrThrow` — the codec `ItemStack.MAP_CODEC` reaches for when it writes *components* to disk. **Why can a world hold chunks in two different compressions?** Because the compression setting is captured per `RegionFile` for writing only, and every chunk stores the version byte it was written with. Reads honour the byte. ## Where to look `Codec` · `DynamicOps` · `ExtraCodecs` · `RegistryOps` · `HolderLookup.Provider.createSerializationContext` · `Tag` · `CompoundTag` · `ListTag` · `NbtOps` · `NbtIo` · `NbtAccounter` · `TagParser` · `StreamTagVisitor` · `HashOps` · `NullOps` · `ValueOutput` · `ValueInput` · `TagValueOutput` · `ProblemReporter` · `BlockEntity` (the save shells) · `ContainerHelper` · `ItemStackWithSlot` · `ItemStack` (the codec fields) · `StreamCodec` · `ByteBufCodecs` · `RegistryFriendlyByteBuf` · `PacketEncoder` · `HashedPatchMap` · `ItemParser` · `DataFixTypes` · `RegionFileVersion` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Identifiers and registries > Verified against **Minecraft 26.2** · Part II · A player types `/give @s minecraft:diamond_sword`, and the sword reaches their inventory as the number of the line it was registered on. A player types `/give @s minecraft:diamond_sword`. The string on the right is parsed into an `Identifier`, the identifier is paired with `Registries.ITEM` to make a `ResourceKey`, and the key is looked up in a table the server froze at startup, before any world existed — `BuiltInRegistries.ITEM`. The stack that then lands in the player's inventory does not carry the name. `Item.STREAM_CODEC` is `ByteBufCodecs.holderRegistry` over `Registries.ITEM`, and it writes an integer. That integer is a line number. `MappedRegistry.byId` is an insertion-ordered list, `MappedRegistry.register` appends to it, and an entry's numeric id is its position — so the wire id of a diamond sword is where its line falls in `Items`, reordering two lines in `Blocks` changes a block's wire id, and a resource pack cannot. A dynamic registry, a biome from a data pack, gets its numbers the other way: its elements are decoded in parallel but registered in sorted order of their ids, so the server's numbering does not depend on which file finished first. The client does not re-derive those numbers at all — it registers what the server sent, in the order the packet lists them. ## The cast | class | what it decides | thread | |---|---|---| | `Identifier` | the name — namespace and path — and which strings are legal ones | any | | `ResourceKey` | the name paired with the registry it belongs to, interned so equal keys are one object | any | | `Registry` · `MappedRegistry` | the table: key to object to integer, the frozen flag, the two tag tables | written on the launching thread or a load task's worker; read from anywhere | | `Holder` | a reference to an entry that may be handed out before the entry exists (`Holder.Reference`), or an inline value that belongs to no registry (`Holder.Direct`) | any | | `HolderLookup.Provider` · `RegistryAccess` | the read-only view a codec resolves against: all the registries it may name | any | | `BuiltInRegistries` | the static registries — created empty at class init, filled and frozen by `Bootstrap.bootStrap` | the launching thread, before any server or client object exists | | `RegistryDataLoader` | the dynamic registries — one load task per registry, JSON from the packs on the server, NBT from the wire on the client | `Util.backgroundExecutor` | | `LayeredRegistryAccess` | which layer may see which: four `RegistryLayer`s on the server, two `ClientRegistryLayer`s on the client | the server thread owns `MinecraftServer.registries`; the client thread owns its own stack | All of this is server *and* client: `MappedRegistry`, `BuiltInRegistries` and `RegistryDataLoader` ship in the dedicated server jar. Client-only, of the classes this page names, are `ClientRegistryLayer`, `RegistryDataCollector` and `KnownPacksManager` — and, less surprisingly, `ClientPacketListener`, `ClientConfigurationPacketListenerImpl` and `IntegratedServer`. ## The name `Identifier`, in `net/minecraft/resources`, is a namespace and a path, and it is the class a 1.21-era reader knows as *ResourceLocation*. It is not a record: a final class with a private constructor whose validation is an assertion on the trusted path and a real check on the parsing paths (`Identifier.isValidNamespace`, `Identifier.isValidPath`). The two character sets differ, and the difference bites: a **path** may contain `/`, a **namespace** may not, and `..` is rejected outright as a namespace. `Identifier.DEFAULT_NAMESPACE` is *minecraft*, and `Identifier.withDefaultNamespace` supplies it — as does parsing a string with no separator at all, or one that starts with the separator. `Identifier.parse` throws `IdentifierException` (which lives in `net/minecraft`, not beside `Identifier`) where `Identifier.tryParse` returns null. `Identifier.read` exists twice, once returning a `DataResult` for `Identifier.CODEC` and once over a Brigadier `StringReader`, with `Identifier.readNonEmpty` beside it, and `Identifier.resolveAgainst` carries a path-traversal guard. The one that surprises people: **`Identifier.compareTo` orders by path first and namespace second**, so a sorted list of ids is not grouped by mod. A `ResourceKey` is an `Identifier` paired with the `Identifier` of the registry it belongs to, and keys are **interned** through a weak map keyed by `ResourceKey.InternKey`, so two keys for the same registry and id are literally the same object. `Registries` holds the 148 `ResourceKey`s *of registries* (`Registries.ITEM`, `Registries.BIOME` …) — 147 distinct objects, for a reason the questions at the end explain — and `ItemIds` and `BlockItemIds` (`net/minecraft/references`) hold the per-element keys the static initialisers use. ## The table `Registry` is the read interface — `Registry.getValue`, `Registry.getKey`, `Registry.getId`, `Registry.getTags`, and the codecs `Registry.byNameCodec` and `Registry.holderByNameCodec` — and it extends `IdMap`, so every registry is also an int-to-object table. `WritableRegistry` adds `WritableRegistry.register` and `WritableRegistry.bindTags`; `DefaultedRegistry` answers a default entry — *air* for items and blocks — instead of null. `MappedRegistry` is the one real implementation, with `DefaultedMappedRegistry` its only subclass. It holds the same entries in four indexes at once and every lookup direction is one of them: `MappedRegistry.byKey` and `MappedRegistry.byLocation` from a name, the insertion-ordered `MappedRegistry.byId` from a number, and `MappedRegistry.byValue` from the object itself — an identity map that is what answers `Registry.getKey`, with `MappedRegistry.toId` the parallel identity map to the number. It carries the `MappedRegistry.frozen` flag that `MappedRegistry.validateWrite` checks on every mutation. A `Holder` is the seam between a registry and the code that names its entries. It is a sealed interface with two kinds (`Holder.Kind`). `Holder.Reference` is an entry *in* a registry — and is itself `non-sealed`: it knows its `HolderOwner`, its key, its tags and its components, and any of those may be unbound until the registry binds them (`Holder.Reference.bindValue`, `Holder.Reference.bindTags`, `Holder.Reference.bindComponents`). A reference is a promise: `Holder.Reference.value` throws until `Holder.Reference.bindValue` has run, codecs hand these out freely during a load, and the freeze is what makes every promise kept — or fails loudly. `Holder.Direct` is a record wrapping an inline value and a `DataComponentMap` that belongs to no registry: it has no key, is in no tag, and serialises inline. A `HolderSet` is a set of holders — `HolderSet.Named` is a tag, `HolderSet.Direct` a literal list. What a codec sees is a read-only view. `HolderGetter`, `HolderLookup` and `HolderOwner` are those views; `HolderLookup.Provider` is "all the registries I may resolve against" and `HolderLookup.RegistryLookup` is one of them. `RegistryAccess` is a `HolderLookup.Provider` over a set of registries, and `RegistryAccess.Frozen` is a bare marker for the finished kind. Every static initialiser writes through `Registry.register`; every codec that names a *dynamic* entry — `RegistryFileCodec`, `RegistryFixedCodec`, `RegistryCodecs.homogeneousList`, `HolderSetCodec` — resolves through a `RegistryOps` ([codecs, NBT and JSON](codecs-nbt-json.md#where-the-registry-context-comes-from)); and at runtime `MinecraftServer.registryAccess` and `ClientPacketListener.registryAccess` are where anything that must resolve a key goes. ## Before the game exists ```mermaid sequenceDiagram participant Main participant Boot as Bootstrap participant BIR as BuiltInRegistries participant Items as Items participant Item as Item participant DMR as DefaultedMappedRegistry Note over Main,DMR: the launching thread, before any server or client object exists Main->>Boot: bootStrap, early, after argument parsing: isBootstrapped is set before any registry is touched Boot->>BIR: class init: one empty registry per built-in key, 95 of the 148 in Registries, each registered into WRITABLE_REGISTRY, each with a loader in LOADERS Boot->>BIR: bootStrap, then createContents: run every loader BIR->>Items: class init (the ITEM loader touches Items.AIR) Items->>Items: registerItem(ItemIds.DIAMOND_SWORD, properties): Item.Properties.setId stores the key Items->>Item: new Item(properties) Item->>DMR: createIntrusiveHolder: a Holder.Reference with a value but no key yet Items->>DMR: Registry.register, then WritableRegistry.register(key, item, BUILT_IN): bindKey, numeric id = byId.size() BIR->>BIR: freeze: the root first, then every registry: bindBootstrappedTagsToEmpty, MappedRegistry.freeze DMR->>DMR: freeze: bindValue on every holder, refuse if any holder or declared tag is unbound, build componentLookup BIR->>BIR: validate: an empty registry logs, a DefaultedRegistry without its default throws Note over Main,DMR: components are still unbound here, they are bound at the first reload, and tags at world load ``` Both `Main` classes do this early — after argument parsing and crash-report preload, and after a handful of non-registry bootstraps ([anatomy](../anatomy/anatomy.md#from-main-to-a-world)) — on the launching thread, before any server or client object exists. `Bootstrap.bootStrap` calls `BuiltInRegistries.bootStrap`, and after it returns every built-in registry is frozen and any `WritableRegistry.register` throws. **Registries exist before their contents.** `BuiltInRegistries` class init creates every registry empty and records the loader that fills it in `BuiltInRegistries.LOADERS`, an insertion-ordered map. `Bootstrap.bootStrap` then runs `BuiltInRegistries.createContents` — the loaders in that order. By then `Items`, `Blocks` and `EntityTypes` are already initialised: `Bootstrap.bootStrap` reaches `FireBlock.bootStrap`, `EntityTypes.PLAYER` and `CauldronInteractions.bootStrap` before it calls `BuiltInRegistries.bootStrap`, and each of those touches its catalogue. `Bootstrap.checkBootstrapCalled` is the guard that makes "touched `Blocks` from a static initialiser" a crash rather than a silent empty registry; it works because the bootstrap flag is set *before* the registries are touched, not after. **The key travels in the properties.** `Items.registerItem` takes a `ResourceKey` from `ItemIds` and calls `Item.Properties.setId` before constructing, so an `Item` knows its own key at construction time. Blocks do the same with `BlockItemIds` and `BlockBehaviour.Properties.setId`. **Five registries hand the object its own holder.** `BuiltInRegistries.BLOCK`, `BuiltInRegistries.ITEM`, `BuiltInRegistries.FLUID`, `BuiltInRegistries.ENTITY_TYPE` and `BuiltInRegistries.BLOCK_ENTITY_TYPE` are created with intrusive holders: the constructor asks the registry for a `Holder.Reference` (`MappedRegistry.createIntrusiveHolder`) that wraps the object before it has a key, and stores it in `Item.builtInRegistryHolder`. Registration then binds the key to *that* holder rather than creating a new one, so `Item.builtInRegistryHolder` and the registry's own holder are the same object, and a tag check on a block or item is a set lookup on the holder's own bound tag set with no registry hop ([tags](tags.md#from-json-to-a-parrots-decision)). `Holder.Reference.createIntrusive` is marked deprecated — the mechanism is load-bearing but not encouraged. **The numeric id is the line number.** `MappedRegistry.register` appends to `MappedRegistry.byId`, and every static registration carries `RegistrationInfo.BUILT_IN`. The wire id of an item is the position of its line in `Items`, and `Item.STREAM_CODEC` encodes that integer. **Freeze is a proof**, stated in full below. `BuiltInRegistries.freeze` freezes the root registry first, then every registry it holds, and `BuiltInRegistries.validate` closes the bootstrap: an empty registry is logged, a `DefaultedRegistry` whose default key is missing throws. ## When a world opens The server keeps its registries as a `LayeredRegistryAccess` in `MinecraftServer.registries`, one layer per `RegistryLayer` — `RegistryLayer.STATIC`, `RegistryLayer.WORLDGEN`, `RegistryLayer.DIMENSIONS`, `RegistryLayer.RELOADABLE`, in that order — with `MinecraftServer.registryAccess` the flattened view. `LayeredRegistryAccess.getAccessForLoading` is everything *before* a layer, which is what that layer's JSON may reference; `LayeredRegistryAccess.compositeAccess` is everything. The client mirrors this with two layers, `ClientRegistryLayer.STATIC` and `ClientRegistryLayer.REMOTE`. On both sides the STATIC layer is `RegistryAccess.fromRegistryOfRegistries` over `BuiltInRegistries.REGISTRY` — a live view of the frozen root registry, not a copy of it. ```mermaid sequenceDiagram participant WL as WorldLoader participant RDL as RegistryDataLoader participant RMRLT as ResourceManagerRegistryLoadTask participant LRA as LayeredRegistryAccess participant SCPL as ServerConfigurationPacketListenerImpl participant CCPL as ClientConfigurationPacketListenerImpl participant RDC as RegistryDataCollector Note over WL,LRA: world load, on the worker pool WL->>WL: RegistryLayer.createRegistryAccess: STATIC filled from BuiltInRegistries.REGISTRY, three empty layers WL->>RDL: load(resources, the lookups built from getAccessForLoading(WORLDGEN), WORLDGEN_REGISTRIES, backgroundExecutor) RDL->>RMRLT: one RegistryLoadTask per RegistryData, every task's ConcurrentHolderGetter visible to every other RMRLT->>RMRLT: FileToIdConverter.registry lists data/*/worldgen/biome/*.json, decode in parallel, register sorted by id, load and bind this registry's tags RMRLT->>RMRLT: freezeRegistry, then the RegistryData's RegistryValidator WL->>LRA: one replaceFrom(WORLDGEN, worldgen layer, dimensions layer): the dimensions layer is the WorldDataSupplier's finalDimensions Note over WL,RDC: later, a client logs in and reaches the configuration phase: the server thread on the left, the client thread on the right SCPL->>CCPL: ClientboundSelectKnownPacks: which packs do you already have? CCPL->>SCPL: ServerboundSelectKnownPacks: accepted all-or-nothing SCPL->>CCPL: ClientboundRegistryDataPacket, one per synced registry: RegistrySynchronization.packRegistries, entries from a known pack carry no data SCPL->>CCPL: ClientboundUpdateTagsPacket: every static registry's tags plus the synced dynamic ones, as registry ints SCPL->>CCPL: ClientboundFinishConfigurationPacket CCPL->>RDC: collectGameRegistries: rebuild REMOTE with NetworkRegistryLoadTasks, missing data re-read from the local pack, static tags applied in place RDC->>CCPL: a RegistryAccess.Frozen, into CommonListenerCookie.receivedRegistries CCPL->>SCPL: ServerboundFinishConfigurationPacket: play may begin ``` `WorldLoader.load` runs `RegistryDataLoader.load` on `Util.backgroundExecutor`, returning to the main thread for resource-manager creation and the final assembly; this is where the `RegistryLayer.WORLDGEN`, `RegistryLayer.DIMENSIONS` and `RegistryLayer.RELOADABLE` layers are filled. The configuration phase is the third moment: `SynchronizeRegistriesTask` sends the dynamic registries on the server thread, and the client rebuilds its `ClientRegistryLayer.REMOTE` layer in `RegistryDataCollector.collectGameRegistries` — decoding on the worker pool, joined on the client thread — before it will accept play packets. **Layers load against the layers before them.** `RegistryDataLoader.load` is given lookups built from `LayeredRegistryAccess.getAccessForLoading` — built by `TagLoader.buildUpdatedLookups`, so that the static registries' freshly read tags are visible to the worldgen codecs before they are applied ([tags](tags.md#the-four-moments-tags-are-loaded)) — so a biome JSON may reference a placed feature (same layer) or a sound event (`RegistryLayer.STATIC`) but never a level stem (`RegistryLayer.DIMENSIONS`, which loads after). The lists `RegistryDataLoader.WORLDGEN_REGISTRIES`, `RegistryDataLoader.DIMENSION_REGISTRIES` and `RegistryDataLoader.SYNCHRONIZED_REGISTRIES` say which keys belong to which step and which subset the client is told about. Both worldgen and dimensions are installed in a *single* `LayeredRegistryAccess.replaceFrom` call, and the dimensions layer that wins is the world data's, not necessarily the one just decoded — a saved world's dimension set survives. **Loading is a task graph.** Each registry is one `RegistryLoadTask` owning a fresh `MappedRegistry` and a lock-guarded `ConcurrentHolderGetter`. `RegistryDataLoader.createContext` hands every task's getter to every other, so `Biome.DIRECT_CODEC` decoding on one worker can ask for a configured carver that another worker is still registering — the getter returns an unbound `Holder.Reference`, and the reference is bound when that registry freezes. Forward references cost nothing; cycles are impossible because layers order the registries. Fourteen of the forty-seven dynamic registries also carry a `RegistryValidator` in their `RegistryDataLoader.RegistryData`, run after the freeze — thirteen of them entity-variant registries running the same check, `RegistryValidator.nonEmpty`, and the fourteenth `Registries.TIMELINE`, whose `Timeline.validateRegistry` is its own. **Provenance is recorded per entry, and it is coarser than it looks.** `ResourceManagerRegistryLoadTask` gives each element a `RegistrationInfo` naming the `KnownPack` it came from and a `Lifecycle`. The rule is *presence*, not vanilla-ness: an element from **any** pack that reports a `KnownPack` is stable, and only an element from a pack with no known-pack info is experimental. `KnownPack.isVanilla` is computed on that path and then discarded. Anything received over the network is experimental, and the whole `RegistryLayer.RELOADABLE` layer is constructed experimental; the registry's own lifecycle is the merge of its entries', and that merge is what the "experimental features" warning on world open reads. **The client is told what it does not already have.** `SynchronizeRegistriesTask` first asks the client which `KnownPack`s it has (`ClientboundSelectKnownPacks`). The comparison is **all or nothing**: the client's answer must equal the request exactly, or every element of every synced registry is sent in full. On a match, `RegistrySynchronization.packRegistries` sends every element's *id* but leaves the NBT payload empty for entries from those packs, and the client's `RegistryLoadTask.PendingRegistration.findAndLoadFromResource` re-decodes the JSON from its own jar. A modified biome from a custom data pack is sent in full through `Biome.NETWORK_CODEC` — the network codec, which omits the generation and mob-spawn settings the client never needs. **The client rebuilds one layer and patches the other.** `RegistryDataCollector` accumulates the packets, and `RegistryDataCollector.collectGameRegistries` runs when configuration finishes. The `ClientRegistryLayer.REMOTE` layer is rebuilt wholesale and frozen — but the **static** registries cannot be rebuilt, so their tags are applied in place, through the mechanism [tags](tags.md#the-four-moments-tags-are-loaded) owns, and when no registry data arrived at all the collector takes a tags-only path that patches and returns the original registries untouched. The result is a `RegistryAccess.Frozen` in `CommonListenerCookie` that every `RegistryFriendlyByteBuf` in the play phase decodes against. **Singleplayer throws most of that away.** When an `IntegratedServer` exists, `ClientConfigurationPacketListenerImpl.handleConfigurationFinished` substitutes the server's own registries for the ones the client just built, and the memory connection suppresses re-applying static tags and components client-side. Both halves then share the same registry objects — which is worth remembering whenever a page says "the client's copy". ## The freeze rule, stated A frozen registry's **contents** never change. Its **tags** and its elements' **components** do. Everything in this part that looks like an exception to the first sentence is one of the two things in the second. `MappedRegistry.freeze` is a proof, not a switch. It binds every holder's value and throws if any holder is still unbound, if any intrusive holder was created but never registered, or if any tag declared by a `TagKey` was never bound. The tag half of that proof works because a registry keeps its *declared* tags apart from its *bound* ones, which is the split [tags](tags.md#a-tag-is-a-key-and-a-file) is built on: the freeze checks the declared table and installs the bound one. For the static registries the real tags do not exist until a data pack is read, so `BuiltInRegistries.freeze` first binds the tags the bootstrap actually asked for to empty (`MappedRegistry.bindAllTagsToEmpty`) and the proof passes on empty sets. The freeze also builds `MappedRegistry.componentLookup`. After it, the `MappedRegistry.frozen` flag makes `MappedRegistry.validateWrite` throw on every ordinary write. Two things still change: `MappedRegistry.prepareTagReload` *requires* the frozen flag, and the component prototypes are rebound beside the tags ([data components](data-components.md)). What changes afterwards changes through two doors. **Tags:** a world load swaps the tag tables of the static registries, and `/reload` does more than refill the `RegistryLayer.RELOADABLE` layer — it re-reads and re-applies tags for **every** registry in the server's composite access. How a frozen registry's tags are swapped is the pay-off of [tags](tags.md#from-json-to-a-parrots-decision), and the mechanics of the reload itself belong to [the resource system](resource-system.md#reload-the-same-pipeline-on-the-server). **Components:** every registry element's `DataComponentMap` is bound after the freeze by `Holder.Reference.bindComponents`, and `/reload` rebinds every one of them; [data components](data-components.md#the-prototype-and-why-it-is-built-at-reload) owns how. ## Feature flags: the same registry, narrowed A frozen registry's contents never change — but what a *lookup* will show you can be narrower than what the registry holds, and that is the whole of the feature-flag mechanism. `FeatureFlagSet` is a bitmask: a 64-bit *long* and the `FeatureFlagUniverse` it belongs to, with `FeatureFlagSet.MAX_CONTAINER_SIZE` at 64 flags. There is one universe, *main*, and `FeatureFlags` declares four flags in it — `FeatureFlags.VANILLA` and the three experiments, `FeatureFlags.TRADE_REBALANCE`, `FeatureFlags.REDSTONE_EXPERIMENTS` and `FeatureFlags.MINECART_IMPROVEMENTS`. A set that is not a subset of `FeatureFlags.VANILLA_SET` is what `FeatureFlags.isExperimental` reports, and what puts the warning on a world. What carries a flag is a registry element. `FeatureElement` is an interface with one method, `FeatureElement.requiredFeatures`, implemented by exactly seven types — `Item`, `BlockBehaviour`, `EntityType`, `GameRule`, `MenuType`, `Potion` and `MobEffect` — and `FeatureElement.FILTERED_REGISTRIES` names the seven registries those live in. `HolderLookup.RegistryLookup.filterFeatures` is the filter: handed the world's enabled set, it returns the same lookup untouched for any registry not in that set of seven, and a delegating lookup that hides the disabled elements for one that is. The registry underneath is not touched, and neither is its numbering — a disabled item keeps its wire id. That is why an experiment is enabled per world and not per install. The set is `WorldDataConfiguration.enabledFeatures`, read from *level.dat*, and the filtered lookup is what `CommandBuildContext` hands every argument type, so `/give` cannot name a disabled item and `/setblock` cannot name a disabled block; `GameRules` builds its map from the filtered `BuiltInRegistries.GAME_RULE`, so a disabled rule is not merely refused but absent; and `LevelReader.holderLookup` filters for anything that asks a level for a registry. Where the flags come *from* is the pack stack — a feature pack is a built-in pack carrying a `FeatureFlagsMetadataSection`, and turning on an experiment is enabling a data pack ([the resource system](resource-system.md#discover-the-repository-and-its-packs)). ## What crosses the wire, and where the files are Built-in registry *elements* never cross the network — both sides ran the same static initialisers — but their *tags* do, and dynamic elements do: `ClientboundSelectKnownPacks` and `ServerboundSelectKnownPacks`, then `ClientboundRegistryDataPacket` (one per synchronised registry, entries as `RegistrySynchronization.PackedRegistryEntry`) in the configuration phase, then `ClientboundUpdateTagsPacket`, which is a **common** packet and arrives again mid-play after a server `/reload`; and every registry element, built-in or dynamic, crosses inside other packets as a bare varint id resolved against the buffer's registry access. Only one variant shifts that numbering: `ByteBufCodecs.holder` reserves 0 for an inline `Holder.Direct` and writes every registry id one higher, where `ByteBufCodecs.holderRegistry` — which `Item.STREAM_CODEC` uses — writes the raw id. On disk, every key in `RegistryDataLoader.WORLDGEN_REGISTRIES` and `RegistryDataLoader.DIMENSION_REGISTRIES` reads `data///*.json` (`Registries.elementsDirPath`) through `FileToIdConverter.registry` over a `ResourceManager` ([the resource system](resource-system.md#snapshot-the-manager)), its tags live under `Registries.tagsDirPath` — there is a third path builder, `Registries.componentsDirPath`, but it names a *reports* directory the data generator writes and nothing in the running game reads — and the reloadable set (loot tables, predicates, item modifiers) comes through `ReloadableServerRegistries`. Which registry is which kind is [reference/registries](../../reference/registries.md). ## Questions players ask **Are `Registries.DIMENSION` and `Registries.LEVEL_STEM` two registries?** They are the same object. Both are created from the string "dimension", and because `ResourceKey` interns, the two fields hold one interned key under two names and two (unchecked) element types. `Registries.LEVEL_STEM` is the data-pack registry the `RegistryLayer.DIMENSIONS` layer loads; `Registries.DIMENSION` keys the `ServerLevel`s; the conversion helpers between them are identity functions at runtime. That is why `Registries` declares 148 keys and holds 147 objects. **Does interning matter?** Where identity is used, and only there. `MappedRegistry.byKey` and `MappedRegistry.byLocation` are ordinary hash maps. What genuinely depends on interned keys is `MappedRegistry.registrationInfos`, an identity map, and `Holder.Reference.is` for a `ResourceKey`, which is a reference comparison. **Where does the number come from?** Two different places. For `BuiltInRegistries` it is an accident of source order — `MappedRegistry.byId` insertion order, so reordering two lines in `Blocks` changes a block's wire id and a resource pack cannot. For a **dynamic** registry it is the element ids in sorted order: `ResourceManagerRegistryLoadTask` decodes in parallel but registers sorted, which is exactly why the client can rebuild the same ids from the same element list. `MappedRegistry.toId` is keyed by *value* identity and returns −1 for anything it has never seen, including an equal-but-distinct object. **Are components part of the freeze?** No. `Holder.Reference.bindComponents` attaches a per-entry `DataComponentMap` built by `BuiltInRegistries.DATA_COMPONENT_INITIALIZERS` — on the server during a reload, on the client at the end of configuration. Do not confuse that with `MappedRegistry.componentLookup`, which is a `DataComponentLookup` built *at* freeze: a lazily-populated **reverse** index answering "which elements have this component value?", used by things like finding the spawn egg for an entity type ([data components](data-components.md#the-reverse-index-datacomponentlookup)). **What does a `RegistrationInfo` say?** Per entry, a `Lifecycle` and the `KnownPack` it came from; `RegistrationInfo.BUILT_IN` is what every static registration gets. **Why does a holder from one world refuse to be written by another?** `HolderOwner` exists for one question — `HolderOwner.canSerializeIn` — and that is it: a holder answers whether the context asking to serialise it is its own owner. **Is the vanilla data built at runtime?** No. `RegistrySetBuilder`, `BootstrapContext` and `VanillaRegistries` are the data generator that *writes* the JSON in the jar; the running game only ever reads JSON. A 1.21 reader who remembers biomes being registered in code is remembering datagen. **Is `Block.BLOCK_STATE_REGISTRY` a registry?** No. `IdMapper` is the standalone `IdMap` used for `BlockState` ids and similar palettes; the two share an interface and nothing else. ## Where to look `Identifier` · `ResourceKey` · `Registries` · `Registry` · `MappedRegistry.register` · `MappedRegistry.freeze` · `DefaultedMappedRegistry` · `Holder` · `HolderSet` · `HolderLookup` · `Bootstrap.bootStrap` · `BuiltInRegistries.bootStrap` · `RegistryLayer` · `LayeredRegistryAccess` · `WorldLoader.load` · `RegistryDataLoader.load` · `RegistryLoadTask` · `ResourceManagerRegistryLoadTask` · `RegistryValidator` · `RegistrySynchronization.packRegistries` · `SynchronizeRegistriesTask` · `RegistryDataCollector.collectGameRegistries` · `NetworkRegistryLoadTask` · `ClientConfigurationPacketListenerImpl.handleConfigurationFinished` · `RegistryOps` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The resource system > Verified against **Minecraft 26.2** · Part II · A player presses F3+T, the screen goes to the logo and a bar, and every texture, model, sound and font is rebuilt from a stack of packs without the game stopping. A player presses F3+T. The screen goes red, the Mojang Studios logo comes up, and a white bar creeps across under it while the old world keeps rendering behind. What is happening is one pipeline that everything the game reads from a file goes through — textures, models, sounds, language strings, recipes, advancements, loot tables, tags, worldgen JSON: a **stack of packs** is discovered, merged into a **resource manager** that is a snapshot of the stack, and a list of **reload listeners** each rebuild their world from it, every one of them reading on the shared worker pool at once ([anatomy](../anatomy/anatomy.md#four-threads-worth-memorising)) and swapping their live state on the owning thread in the order they were registered. The client's stack is resource packs (`PackType.CLIENT_RESOURCES`, the *assets* tree); the server's is data packs (`PackType.SERVER_DATA`, the *data* tree) — same classes, two instances, two directories, and `/reload` is the same pipeline run by the server. The surprising part is the end. A reload that fails does not find the offending pack. `Minecraft.rollbackResourcePacks` deselects *every* resource pack, clears the options lists, saves, and reloads again — and if vanilla was the only selected pack it rethrows and crashes instead. ## The cast | class | what it decides | thread | |---|---|---| | `PackRepository` · `Pack` | which packs exist, which are selected, and in what order | whoever asks: Render on the client, Server on the server | | `MultiPackResourceManager` · `FallbackResourceManager` | which pack's copy of a file wins, one stack per namespace | built on the asking thread, read from workers | | `ReloadableResourceManager` | the client's long-lived façade: the current snapshot and the listener list | Render | | `PreparableReloadListener` · `SimplePreparableReloadListener` · `SimpleJsonResourceReloadListener` | what to read off-thread and what to swap on-thread | prepare on the worker pool, apply on the owner | | `SimpleReloadInstance` | the schedule: every prepare at once, every apply in order behind a barrier | built on the caller's thread, barriers resolved on the owner | | `LoadingOverlay` | when the client's reload is done, and what to do if it failed | Render | | `ReloadableServerResources` | the server's three listeners, and the registries that replaced the rest | Server | ## The pipeline ```mermaid flowchart LR D["discover: PackRepository.reload re-runs every RepositorySource and rebuilds the selection"] --> S["snapshot: a new MultiPackResourceManager over the opened packs, a snapshot of the list, not of the bytes"] S --> P["prepare: every listener reads on the worker pool at once"] P --> A["apply: each listener swaps its live state on the owning thread, in registration order, behind a PreparationBarrier"] A --> F["finish: checkExceptions, then the level re-extracted or the server's managers installed"] A --> R["roll back: every pack deselected, the reload run again"] ``` Five stages, and the rest of the page is one section per stage: what comes in, what is decided, what goes out. F3+T is the grounding trace; `/reload` is the coda, as a table of where the server's run of the same pipeline differs. ## Discover: the repository and its packs What comes in is a set of `RepositorySource`s (`server/packs/repository`), each a place packs are found. `ClientPackSource` and `ServerPacksSource` are the built-ins, both extending `BuiltInPackSource`, which also lists the packs bundled *inside* the vanilla pack — the art packs, the accessibility packs and every feature pack (`BuiltInPackSource.TESTS_ID` is declared and referenced nowhere, so the *tests* pack is a development leftover); `FolderRepositorySource` is a directory of user packs; `DownloadedPackSource` is server-sent packs, client only. `PackRepository.reload` re-runs every source into the *available* map and then rebuilds the *selected* list: prior choices are kept, and a pack whose `Pack.isRequired` is true is force-inserted at its `Pack.getDefaultPosition`. Reading the order out of *options.txt* is a startup step — `Options.loadSelectedResourcePacks` runs once in the `Minecraft` constructor, not on every F3+T. A `Pack` is a discoverable pack: a `PackLocationInfo` (id, title, `PackSource`, optional `KnownPack`), a `Pack.ResourcesSupplier` that can open it, its `Pack.Metadata` (description, `PackCompatibility`, requested feature flags, overlays) and a `PackSelectionConfig` — required, default `Pack.Position`, fixed. `Pack.Position` owns the insertion algorithm that makes a fixed pack stick: `Pack.Position.BOTTOM` inserts at the front of the list, past any pack already fixed there, and `Pack.Position.TOP` at the back. The last pack in the list wins (next section), which is why vanilla is BOTTOM and why "higher in the UI" means "later in the list". The client's vanilla pack cannot be deselected and the server's can: `ClientPackSource` marks vanilla required and bottom; `ServerPacksSource` marks it bottom but optional. What a pack *is* on disk is `PackResources` (`server/packs`): the raw file source with `PackResources.getResource`, `PackResources.listResources` and `PackResources.getNamespaces`. `VanillaPackResources` is the jar's own assets and data; `FilePackResources` a zip; `PathPackResources` a directory; `CompositePackResources` a pack plus its *overlays* subdirectories, which the `Pack.ResourcesSupplier` assembles for zip and folder packs (the vanilla pack never produces one). Discovery is guarded: `DirectoryValidator`, `ForbiddenSymlinkInfo` and `PackDetector` decide what a folder is allowed to be, `allowed_symlinks.txt` is parsed into a `DirectoryValidator` by `LevelStorageSource.parseValidator`. Two corners of `server/packs` are worth a sentence each. `packs/linkfs` is a synthetic read-only file system — `LinkFileSystem`, `LinkFSProvider` and a `LinkFSPath` that is a name in a tree rather than a name on disk — which lets a development checkout's scattered directories present as one pack root, so the game can open a pack that was never assembled. And `DownloadQueue` is the client's cache for server-sent packs: one directory per pack UUID under a cache root, downloads run one at a time on a `ConsecutiveExecutor` over `Util.nonCriticalIoPool`, every attempt appended to a `JsonEventLog` beside them, and the constructor calls `DownloadCacheCleaner.vacuumCacheDir` to trim the root to `DownloadQueue.MAX_KEPT_PACKS` — twenty files, newest kept, one per directory before any directory's second. A server you visited twenty packs ago has been evicted. ### What *pack.mcmeta* says The file is read as `ResourceMetadata` sections: `PackMetadataSection` (description and a `PackFormat` range), `FeatureFlagsMetadataSection`, `OverlayMetadataSection`, `ResourceFilterSection`. Compatibility is a range, not a number. `PackMetadataSection` carries an inclusive range of `PackFormat` major/minor pairs and `PackCompatibility` reports too old, too new, unknown or compatible against the game's own — resource **88.0** and data **107.1** in 26.2. Above `PackFormat.lastPreMinorVersion` (64 for assets, 81 for data) the *min_format* / *max_format* fields are mandatory and the old integer *pack_format* is not enough. A *pack.mcmeta* the strict codec rejects gets one more chance through a description-only fallback so the pack can at least be listed as incompatible. Overlays are versioned sub-packs: `OverlayMetadataSection` maps a `PackFormat` range to an overlays subdirectory that `CompositePackResources` layers on top of the pack itself, so one zip can carry variants for several game versions. Feature flags are packs, but not auto-selected ones. A feature pack is a built-in pack carrying a `FeatureFlagsMetadataSection`, and its `PackSource` deliberately reports that it must *not* be added automatically. `MinecraftServer.enableForcedFeaturePacks` force-selects the ones matching the world's forced features, and the world's flag set is the selected packs' requested flags joined with those forced ones. Turning on an experiment is enabling a data pack — through a different door than ordinary packs use. What the resulting `FeatureFlagSet` then *gates* is a registry lookup, and that is [identifiers and registries](identifiers-and-registries.md#feature-flags-the-same-registry-narrowed). What goes out of the stage is `PackRepository.openAllSelected`: the selected `Pack`s opened into a list of `PackResources`, in order. ## Snapshot: the manager `MultiPackResourceManager` (`server/packs/resources`) is built from that list. It is a **snapshot of the pack list, not of the bytes**: it asks each pack for its namespaces and builds one `FallbackResourceManager` per namespace, each a stack searched from the **last** selected pack down. The old world stays up until the last apply, but the old files do not: on the client, `ReloadableResourceManager.createReload` closes the previous `MultiPackResourceManager` — with every file handle it held — before building the new one. What is frozen is *which packs are in the stack*; a `Resource` still opens its file when it is read, so a **folder** pack edited on disk mid-reload is observable. A zip is not: `FilePackResources` holds its zip open for the life of the pack. A `Resource` is what a lookup returns: its source pack, an `IoSupplier` for the bytes — opened lazily, at read time — and a lazily-read `ResourceMetadata` found beside it. A `.mcmeta` is looked for in the winning pack or those above it, never in one below, so a pack overriding a texture without its `.mcmeta` loses the animation. `ResourceFilterSection` lets a pack *hide* lower packs' files by pattern without providing replacements: `MultiPackResourceManager` reads each pack's filter section and pushes it onto the namespace stacks as a filter, and a lookup that reaches a filtered entry stops there. Some loaders want every copy, not the winner: `ResourceManager.getResourceStack` and `ResourceManager.listResourceStacks` return all packs' copies **bottom-first**, which is how languages, tags and atlas sources merge instead of overriding. Two more managers frame this one. `ReloadableResourceManager` is the long-lived client façade that holds the current snapshot and the `PreparableReloadListener` list; the server has no façade — each reload is a fresh `MultiPackResourceManager` inside `MinecraftServer.ReloadableResources`. `ResourceManager.Empty` is the do-nothing manager handed to code that must run without packs. What goes out is one `ResourceManager`, wrapped in a `PreparableReloadListener.SharedState`, and a `ReloadInstance` that has already started. ## Prepare: every listener at once A `PreparableReloadListener.reload` takes the shared state (which carries the `ResourceManager`), a background executor, a `PreparableReloadListener.PreparationBarrier` and a main-thread executor. `SimplePreparableReloadListener` splits that into `SimplePreparableReloadListener.prepare` (background) and `SimplePreparableReloadListener.apply` (main thread); `SimpleJsonResourceReloadListener` is the "every JSON file in a directory through one codec" specialisation, using `FileToIdConverter` to map *data/ns/recipe/foo.json* to *ns:foo*; `ResourceManagerReloadListener` is the apply-only shape. `SimpleReloadInstance` is the schedule, and this is what it does, read from `SimpleReloadInstance.prepareTasks`. It wraps both executors in counters. It calls `PreparableReloadListener.prepareSharedState` on every listener first, synchronously. Then it walks the listener list once, calling each listener's `PreparableReloadListener.reload` and handing it a barrier chained to the *previous* listener's returned future (the first listener's barrier is chained to the initial task). The barrier's `PreparableReloadListener.PreparationBarrier.wait` does two things: it posts a task to the main-thread executor that removes the listener from the set still preparing and completes the all-preparations future when that set empties, and it returns that future combined with the previous listener's. So listener N's apply runs only after *every* listener has reached its barrier *and* listener N−1 has finished entirely — apply included — and a listener that never reaches its barrier holds every apply behind it. The futures are sequenced fail-fast: the first listener to throw fails the reload as a whole. It does not stop the others — `Util.sequenceFailFast` completes the outer future exceptionally and leaves every prepare running; `Util.sequenceFailFastAndCancel`, which would cancel them, is not the one used here. What never happens is the applies. ```mermaid flowchart LR subgraph PREP["prepare, on the worker pool, all at once"] TMp["TextureManager: read every ReloadableTexture"] AMp["AtlasManager: stitch every atlas, completing the futures published under PENDING_STITCH"] MMp["ModelManager: load models and block states, then join the block and item stitches from PENDING_STITCH"] end ALL["all preparations: every listener has reached its barrier"] TMp --> ALL AMp --> ALL MMp --> ALL AMp -. "shared state" .-> MMp subgraph APP["apply, on the owning thread, in registration order"] TMa["TextureManager apply: swap texture contents"] AMa["AtlasManager apply: upload the atlases"] MMa["ModelManager apply: install the baked models"] end ALL --> TMa ALL --> AMa ALL --> MMa TMa --> AMa AMa --> MMa ``` Three of the client's twenty listeners, the ones registered between them elided. Every apply waits on the all-preparations node; each apply also waits on the apply before it; and the one dotted edge is the only way one listener's prepare depends on another's. ### The shared-state channel `PreparableReloadListener.prepareSharedState` is a separate first pass for a reason: it is the one place a listener can publish something for *another* listener's prepare to consume, keyed by a `PreparableReloadListener.StateKey`. The game declares exactly one — `AtlasManager.PENDING_STITCH`. `AtlasManager` publishes a future per atlas there before any prepare starts; `ModelManager` and `ParticleResources` pull the pending sprite futures out of it and join them **inside their own prepare**, so model baking overlaps atlas stitching rather than queueing behind it. This is why the model/atlas dependency is *not* an apply-order dependency, and why reasoning about it from the registration list gets the wrong answer. Which thread runs that first pass depends on who started the reload: the Render thread on the client, but a worker on the server, because the server's reload instance is created from inside an already-async chain. Prepare never touches live state. That is the whole contract, and it is one-directional: a listener that reads from the manager in `SimplePreparableReloadListener.apply` is reading the *new* snapshot and that is fine (`TextureManager` does exactly this, from its own `PreparableReloadListener.reload`), while one that mutates live state in `SimplePreparableReloadListener.prepare` is racing the Render thread. Nothing a listener owns is torn down when a reload starts; the client keeps rendering with the old atlases while the new ones bake. ## Apply: registration order Registration order is apply order. The client registers, in order, `LanguageManager`, `TextureManager`, `ShaderManager`, `SoundManager`, `AtlasManager`, `FontManager`, the three colour listeners (`GrassColorReloadListener`, `FoliageColorReloadListener`, `DryFoliageColorReloadListener`), `ModelManager`, `EquipmentAssetManager`, `EntityRenderDispatcher`, `BlockEntityRenderDispatcher`, `ParticleResources`, `LevelExtractor`, the cloud renderer, `GpuWarnlistManager`, a `PeriodicNotificationManager`, then `SplashManager` from `Gui` and `WaypointStyleManager` from `Hud` — twenty in all. On the client `ReloadableResourceManager.createReload` is called with `Util.backgroundExecutor` (named *resourceLoad*) and `Minecraft` itself as the main-thread executor, so apply runs on the Render thread, interleaved with frames. On the server `ReloadableServerResources.loadResources` is called with `Util.backgroundExecutor` and `MinecraftServer`, so apply runs on the Server thread. The counters `SimpleReloadInstance` wrapped the executors in are where the progress bar's numbers come from: `ReloadInstance.getActualProgress` weighs prepare and apply tasks double and listeners-completed single, and the overlay smooths it. ## Finish, or roll back On the client `LoadingOverlay` is a poll. It draws the logo from the vanilla pack *outside* the reload (via `VanillaPackResources.asProvider`) and a smoothed bar from `ReloadInstance.getActualProgress`; a manual reload fades it in over half a second and it will not fade out until a full second has passed. Each tick, once `ReloadInstance.isDone`, it calls `ReloadInstance.checkExceptions` and hands the result to its finish callback. Success runs `LevelExtractor.allChanged`, which is why every chunk section rebuilds after F3+T, then `ResourceLoadStateTracker.finishReload`, `DownloadedPackSource.onReloadSuccess` and `Minecraft.onResourceLoadFinished`. Failure runs `Minecraft.rollbackResourcePacks`, which does **not** find the offending pack — it deselects *every* resource pack, clears the options lists, saves, and reloads again, and if vanilla was the only selected pack it rethrows and crashes instead. That recovery reload bypasses the one-at-a-time guard, skips the fade, and if *it* fails the client abandons recovery: `Minecraft.abortResourcePackRecovery` drops the overlay, disconnects any level and returns to the title screen with a failure toast. `ShaderManager` is constructed with `Minecraft.triggerResourcePackRecovery` for exactly this, so a shader that fails at runtime rather than at load takes the same road. Throughout, `ResourceLoadStateTracker` records what kind of reload this was and with which packs, so a crash report can say. On the server there is no overlay; the finish is a continuation on the server thread, and the coda below lists it. ## F3+T, end to end ```mermaid sequenceDiagram participant KH as KeyboardHandler participant MC as Minecraft participant PR as PackRepository participant RRM as ReloadableResourceManager participant SRI as SimpleReloadInstance participant Worker as Worker participant LO as LoadingOverlay KH->>MC: handleDebugKeys matches keyDebugReloadResourcePacks, reloadResourcePacks MC->>PR: reload, then openAllSelected: rediscover, keep the selection, open it MC->>RRM: createReload: close the old MultiPackResourceManager, build the new snapshot MC->>LO: setOverlay, in the same statement: logo and a smoothed bar from getActualProgress RRM->>SRI: create: prepareSharedState on every listener, then reload on each, in order SRI->>Worker: every listener's prepare, all at once Worker-->>MC: each barrier resolved on the Render thread once every prepare is in and the previous listener has applied MC->>MC: apply, one listener per registration slot, between frames Note over LO: a later tick LO->>MC: isDone, checkExceptions, then allChanged on success or rollbackResourcePacks on failure ``` The key does nothing but ask. `KeyboardHandler.handleDebugKeys` matches `Options.keyDebugReloadResourcePacks` and calls `Minecraft.reloadResourcePacks`. If a reload is already showing an overlay, the request is parked in `Minecraft.pendingReload` and drained from `Minecraft.runTick`, and a second request while one is parked simply returns the same future. The one path that bypasses the guard is a *recovery* reload after a failure. The rest is the five stages above: `PackRepository.reload` and `PackRepository.openAllSelected` on the Render thread, `ReloadableResourceManager.createReload` closing the old snapshot and building the new one, `SimpleReloadInstance` fanning prepares out to the *resourceLoad* pool and marshalling applies back through `Minecraft`, and `LoadingOverlay.tick` polling for the end. The first reload of the game's life runs the same way from the `Minecraft` constructor, tagged `ResourceLoadStateTracker.ReloadReason.INITIAL` rather than manual; a world being opened builds its own first snapshot through `WorldLoader.load`, whose `WorldLoader.PackConfig.createResourceManager` runs `MinecraftServer.configurePackRepository` and opens the packs; and a server-sent pack is just one more `RepositorySource`, so `DownloadedPackSource` triggers an ordinary `Minecraft.reloadResourcePacks`. ## `/reload`, the same pipeline on the server | | F3+T (client) | `/reload` (server) | |---|---|---| | who starts it | `KeyboardHandler.handleDebugKeys` → `Minecraft.reloadResourcePacks`, parked in `Minecraft.pendingReload` if an overlay is already up | `ReloadCommand`, at `Commands.LEVEL_GAMEMASTERS` → `MinecraftServer.reloadResources` | | discovery | `PackRepository.reload` keeps the current selection; required packs are force-inserted | `ReloadCommand.discoverNewPacks` runs `PackRepository.reload` and then selects every available pack not in the world's disabled list — which is how a datapack dropped into the folder is picked up | | where the packs are opened | on the Render thread, before the overlay goes up | on the *server thread* first, one `Pack.open` per selected id, before any background work starts | | the manager | a façade swap: `ReloadableResourceManager.createReload` closes the old `MultiPackResourceManager` and holds the new one | a fresh `MultiPackResourceManager` inside a new `MinecraftServer.ReloadableResources`; the old one is closed only when the new one is installed, and the new one is closed if the reload fails | | which thread applies, and whether it blocks | the Render thread, between frames; nothing blocks | the Server thread; if `/reload` is issued *from* the server thread the method blocks it with `BlockableEventLoop.managedBlock` until done — `/reload` stalls the tick | | how many listeners | twenty, in registration order | three — `RecipeManager`, `ServerFunctionLibrary`, `ServerAdvancementManager` (`ReloadableServerResources.listeners`) | | what is a registry instead | nothing; the client's registries arrive over the wire | tags are read *before* the reload instance by `TagLoader.loadTagsForExistingRegistries` and applied after it ([tags](tags.md#the-four-moments-tags-are-loaded)); loot tables, predicates and item modifiers load as the `RegistryLayer.RELOADABLE` layer in `ReloadableServerRegistries.reload` ([identifiers and registries](identifiers-and-registries.md#when-a-world-opens)); item component prototypes rebind through `BuiltInRegistries.DATA_COMPONENT_INITIALIZERS` ([data components](data-components.md#the-prototype-and-why-it-is-built-at-reload)) | | when success is reported | when the overlay's poll finds the instance done with no exception | **before** the reload runs — the success message is sent first, and a failure arrives later, asynchronously | | what happens on completion | `LevelExtractor.allChanged` · `ResourceLoadStateTracker.finishReload` · `DownloadedPackSource.onReloadSuccess` · `Minecraft.onResourceLoadFinished` | close the old `MinecraftServer.ReloadableResources` · install the new · `PackRepository.setSelected` · write the new `WorldDataConfiguration` into level data · `ReloadableServerResources.updateComponentsAndStaticRegistryTags` · `RecipeManager.finalizeRecipeLoading` · `PlayerList.saveAll` · `PlayerList.reloadResources` — which re-reads every player's advancements and broadcasts `ClientboundUpdateTagsPacket` and `ClientboundUpdateRecipesPacket` · `ServerFunctionManager.replaceLibrary` · `StructureTemplateManager.onResourceManagerReload` · a rebuilt fuel table | | what happens on failure | `Minecraft.rollbackResourcePacks` | the new manager is closed, the old resources stay installed, and the command source is told | | timing | `ProfiledReloadInstance` only when the logger is at debug | the same | Two of those rows are worth a second look. The command tree is rebuilt by every `/reload` — a new `Commands` inside the new `ReloadableServerResources` — but nothing re-sends it, so connected clients complete against the tree they were given until they reconnect. And the reload is debug-timed only: the "Resource reload finished after N ms" line, the per-listener timings and the total-blocking-time figure all come from `ProfiledReloadInstance`, selected only when the logger is at debug. ## Across the wire A server pushes a pack with `ClientboundResourcePackPushPacket` (id, URL, hash, required, prompt) and withdraws one with `ClientboundResourcePackPopPacket`, sent by `ServerResourcePackConfigurationTask` in the configuration phase and by `ServerPackCommand` (*/serverpack push|pop*) at any time in play; the client answers with a `ServerboundResourcePackPacket` and its `ServerboundResourcePackPacket.Action`. Packs are keyed by UUID and stack, and a server-sent pack pins itself to the top of the selection. `ServerCommonPacketListenerImpl.handleResourcePackResponse` disconnects a client that declines — but the "required" it consults is `MinecraftServer.isResourcePackRequired`, a **server-wide** *server.properties* setting, not the flag on the individual pack, so a declined */serverpack push* can disconnect you on a server whose properties pack is required. The system is data-driven by *pack.mcmeta* (`PackMetadataSection`, with *min_format* / *max_format* replacing the integer *pack_format*), *options.txt* (`Options.resourcePacks`), *level.dat*'s `WorldDataConfiguration` (`DataPackConfig` enabled/disabled plus the `FeatureFlagSet`), *server.properties* for the server-sent pack, and *allowed_symlinks.txt* via `DirectoryValidator`. ## Questions players ask **Why does my pack's animated texture stop animating when another pack overrides the image?** Because the `.mcmeta` is looked for in the winning pack or those above it, never below. The override won the image and brought no metadata. **Why does a datapack I dropped into the folder appear after `/reload` but a resource pack I dropped in does not after F3+T?** `ReloadCommand.discoverNewPacks` selects every newly available pack; `PackRepository.reload` on the client only re-discovers and keeps the selection you had. **Why did F3+T turn all my packs off?** A listener threw. The rollback does not know which pack did it, so it clears them all and reloads with vanilla alone. **Why does `/reload` freeze the server?** When it is issued from the server thread, `MinecraftServer.reloadResources` blocks that thread with `BlockableEventLoop.managedBlock` until the reload is done. **Why can I disable the vanilla data pack but not the vanilla resource pack?** `ClientPackSource` marks it required; `ServerPacksSource` does not. ## Where to look `PackType` · `PackResources` · `Pack` · `PackRepository` · `PackCompatibility` · `PackFormat` · `ServerPacksSource` · `ClientPackSource` · `BuiltInPackSource` · `FolderRepositorySource` · `MultiPackResourceManager` · `FallbackResourceManager` · `ReloadableResourceManager` · `PreparableReloadListener` · `SimplePreparableReloadListener` · `SimpleJsonResourceReloadListener` · `SimpleReloadInstance` · `ResourceLoadStateTracker` · `Minecraft.reloadResourcePacks` · `LoadingOverlay` · `ReloadCommand` · `MinecraftServer.reloadResources` · `ReloadableServerResources` · `WorldLoader` · `DownloadedPackSource` · `ServerPackManager` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Tags > Verified against **Minecraft 26.2** · Part II · A parrot flies down through the canopy looking for a log to perch on, a moment after a data pack put a new block in the logs tag. A parrot is looking for somewhere to sit. `Parrot.ParrotWanderGoal` scans the blocks around it and asks the block state beneath each one a single question, *is this state in `BlockTags.LOGS`* — a leaves block it recognises by class, a log only by tag. Nothing in that goal names oak, or spruce, or the block a data pack added an hour ago; the tutorial toast that tells a new player to punch a tree asks the same question. A tag is a named set of registry entries, defined in data-pack JSON and tested against by key, and it is how data reaches into hard-coded behaviour without the behaviour naming any specific block. [The registries page](identifiers-and-registries.md#the-freeze-rule-stated) ended on a promise: a frozen registry's *contents* never change. Its tags do. Type `/reload` with a data pack that adds a block to *logs* and the parrot perches on it a moment later — with `BuiltInRegistries.BLOCK` frozen since before the title screen. The tag table is one of the two things a frozen registry still lets you swap — the other is the component prototype on each `Holder.Reference`, applied on the very next line of `ReloadableServerResources.updateComponentsAndStaticRegistryTags` — and it goes through `Registry.PendingTags` in three ordered steps with no lock: `MappedRegistry.prepareTagReload` builds the new table off to the side, and `Registry.PendingTags.apply` binds each `HolderSet.Named`, swaps the `MappedRegistry.TagSet`, then rebinds every holder's tag set. It is safe because one thread runs it start to finish with nothing else looking, not because it is atomic. ## The cast | class | what it decides | thread | |---|---|---| | `TagKey` | the name: a registry key and an `Identifier`, interned so equal keys are one object | any | | `TagFile` · `TagEntry` | the on-disk shape: a list of entries, each an element or a reference to another tag, and the *replace* flag | — | | `TagLoader` | reads every pack's copy of every file and resolves them in dependency order; what an id resolves to is its `TagLoader.ElementLookup` | Worker at world load, Server on `/reload` | | `MappedRegistry` | the tag half: `MappedRegistry.frozenTags`, one `HolderSet.Named` per freeze-time key, and `MappedRegistry.allTags`, the bound `MappedRegistry.TagSet` | Server; Render on the client | | `Registry.PendingTags` | a loaded table not yet installed, with a `Registry.PendingTags.lookup` that answers as if it were | built on a worker at world load, on the server thread for `/reload`, on the client's game thread from the packet; applied on the owning thread | | `HolderSet.Named` | one tag's contents, rebound in place on every reload | — | | `Holder.Reference` | one element's own `Set` of `TagKey`s — the thing a membership test reads | — | | `TagNetworkSerialization` | the wire form: tag id to a list of registry ints | Server encodes, client decodes | ## A tag is a key and a file In code a tag is a `TagKey`: a record of the registry's `ResourceKey` and an `Identifier`, interned through a weak interner so that `TagKey.create` always returns the canonical instance and a membership test is a set-contains on identity. The keys are declared once, in catalogues that hold no contents — `BlockTags`, `ItemTags`, `EntityTypeTags`, `BiomeTags`, `FluidTags` and their siblings, twenty files in `net/minecraft/tags` including `DamageTypeTags`, `EnchantmentTags`, `StructureTags`, `PoiTypeTags` and the two 26.2 arrivals `FeatureTags` and `TimelineTags`. A key that a block and its item share is declared once as a `BlockItemTagId` in `BlockItemTags` and projected into both catalogues: `BlockTags.LOGS` is the block half of `BlockItemTags.LOGS`. On disk a tag is a `TagFile`: a list of `TagEntry` and a *replace* flag. Each entry is an element id or a *#*-prefixed reference to another tag, with a *required* flag that defaults to true. The file lives at *data/\/tags/\/\.json* — `Registries.tagsDirPath` builds that string in exactly one place, and there is no plural-name fallback — so *tags/block*, *tags/item*, *tags/entity_type*, *tags/worldgen/biome*. Vanilla's own files are written by the data generator (`TagsProvider`, `TagBuilder`), which the running game never calls, and players reach tags through the *#tag* syntax of `ResourceOrTagArgument` and `ResourceOrTagKeyArgument` (Part XIII). `ResourceSelectorArgument`, beside them, is a glob over ids and takes no tag at all. Between the two sits `TagLoader`, generic over what an id resolves to: a `Holder` for a registry, a `CommandFunction` for function tags. It has two instance steps. `TagLoader.load` reads every pack's copy of every file into lists of `TagLoader.EntryWithSource`; `TagLoader.build` resolves them in dependency order, `TagLoader.tryBuildTag` doing one tag at a time through the `TagLoader.ElementLookup` the loader was built with. Its output, a `TagLoader.LoadResult`, is resolved but bound to nothing. Everything in `net/minecraft/tags` ships in both jars. There is no *TagManager* in 26.2 and no reload listener for registry tags: loading is static functions on `TagLoader`, called from `WorldLoader`, `MinecraftServer.reloadResources`, `ReloadableServerRegistries` and the registry load tasks. Function tags are the exception — `ServerFunctionLibrary` genuinely is a reload listener and runs its own `TagLoader` inside it. Inside a `MappedRegistry` a tag is two things. `MappedRegistry.frozenTags` holds one canonical `HolderSet.Named` per key for the tags that existed when the registry froze, and `MappedRegistry.allTags` is a `MappedRegistry.TagSet`, the bound view, which starts out as `MappedRegistry.TagSet.unbound`, where every read throws. Beside the registry, each `Holder.Reference` carries its own `Set` of `TagKey`s, bound by `Holder.Reference.bindTags`; that set, not the registry, is what `Holder.Reference.is` reads. ## The four moments tags are loaded World load comes first, on the worker pool — the shared one ([anatomy](../anatomy/anatomy.md#four-threads-worth-memorising)). `TagLoader.loadTagsForExistingRegistries` runs *before* the reload listeners, over the `RegistryLayer.STATIC` layer, and produces a `Registry.PendingTags` for every static registry that has at least one tag file. The pending tables are made visible to worldgen and loot loading through `TagLoader.buildUpdatedLookups`, and applied, one registry at a time, in `ReloadableServerResources.updateComponentsAndStaticRegistryTags` once every listener has finished. For the whole of a load the old tags are what `Registry.getTags` answers and the new tags are what the loading codecs see. `/reload` is the same call on the server thread, handed the composite access of `MinecraftServer.registries` — so it re-reads and re-applies tags for the **dynamic** worldgen registries too, not only the static ones. Of the loading paths only loot re-runs; a worldgen registry keeps its elements and gets new tags. A data-pack registry loads its tags inside its own load task. `ResourceManagerRegistryLoadTask` reads them after its elements, with `TagLoader.ElementLookup.fromGetters`, and `RegistryLoadTask.registerTags` binds them under the registry's write lock before it freezes. The reloadable layer reads tags too — `ReloadableServerRegistries` calls `TagLoader.loadTagsForRegistry` for every `LootDataType` — but that is the *void* overload, which throws the result away: nothing binds them, so a loot registry answers empty for every tag key. The fourth moment is the client's. One `ClientboundUpdateTagsPacket` covering every synced registry is sent by `SynchronizeRegistriesTask` after the registry data. In configuration the client merely *buffers* it — `ClientConfigurationPacketListenerImpl.handleUpdateTags` hands it to `RegistryDataCollector.appendTags`, and nothing resolves until configuration finishes. After a server `/reload`, `PlayerList.reloadResources` broadcasts the same packet into the play phase, and `ClientPacketListener.handleUpdateTags` applies that one at once. ## From JSON to a parrot's decision ```mermaid sequenceDiagram participant WL as WorldLoader participant TL as TagLoader participant MR as MappedRegistry participant RSR as ReloadableServerResources participant CCPL as ClientConfigurationPacketListenerImpl participant CPL as ClientPacketListener participant Parrot as Parrot Note over WL,MR: world load, on the worker pool WL->>TL: loadTagsForExistingRegistries over the STATIC layer TL->>TL: load, every pack's tags/block/logs.json through FileToIdConverter.listMatchingResourceStacks, a replace flag clears what lower packs contributed TL->>TL: build, DependencySorter orders oak_logs before logs_that_burn before logs, tryBuildTag resolves ids through ElementLookup.fromFrozenRegistry TL->>MR: prepareTagReload with the LoadResult, a Registry.PendingTags, nothing visible yet Note over WL,RSR: worldgen and loot codecs resolve the logs tag through PendingTags.lookup, via buildUpdatedLookups Note over MR,RSR: the thread driving the load — launching thread on a dedicated server, Render thread on the client — after the last reload listener has applied RSR->>MR: PendingTags.apply, bind every HolderSet.Named, swap allTags, refreshTagsInHolders rebinds the tag set of every Block holder Note over RSR,CCPL: configuration, a client joins, SynchronizeRegistriesTask sends ClientboundUpdateTagsPacket after the registry data, registry ints not names CCPL->>CCPL: handleUpdateTags, RegistryDataCollector.appendTags, buffered until handleConfigurationFinished Note over CPL: play, after a server /reload, PlayerList.reloadResources broadcasts the packet again CPL->>MR: handleUpdateTags, prepareTagReload always, apply unless the connection is in memory Note over MR,Parrot: a server tick, the parrot's wander goal Parrot->>MR: state.is(BlockTags.LOGS), TypedInstance.is, Block.builtInRegistryHolder, Holder.Reference.is, a Set.contains ``` **Every pack's file, lowest first.** `TagLoader.load` asks [the resource system](resource-system.md#snapshot-the-manager) for resource *stacks* — a `FileToIdConverter.json` over the tag directory, listed with `FileToIdConverter.listMatchingResourceStacks` — so every copy of *tags/block/logs.json* across the enabled packs is visited in priority order and merged, and a *replace* in a higher pack discards what the lower packs contributed to that id. A pack whose file fails to parse is logged and skipped, never fatal. **Tags of tags resolve in dependency order, and a tag with a hole is dropped whole.** The vanilla *logs* file names no block at all: it is three tag references, *logs_that_burn*, *crimson_stems* and *warped_stems*, and *logs_that_burn* is in turn nine references, *oak_logs* among them, before *oak_logs* finally lists four blocks. `TagLoader.build` feeds every tag reference into a `DependencySorter` and resolves the leaves first. A tag with **any** failing entry — a missing required element as much as a missing required tag reference — is dropped whole, not loaded minus the entry, and is then absent from `Registry.getTags`, so neither the network payload nor a lookup will find it. An optional entry (*required: false*) resolves to nothing and the tag still builds: `TagEntry.build` answers *not required* on a miss, whichever lookup it was handed. What the two lookups differ on is **where a required id is looked up** — `TagLoader.ElementLookup.fromGetters`, used by data-pack registries, sends a required id through the registration lookup and an optional one through the immutable lookup, while `TagLoader.ElementLookup.fromFrozenRegistry`, used for a static registry, asks the frozen registry either way. **Prepared, then applied.** This is where the hook pays off. `MappedRegistry.prepareTagReload` refuses a registry that is not frozen and builds the new table, reusing existing `HolderSet.Named` objects where it can; nothing is visible yet, and the `Registry.PendingTags.lookup` it hands back answers as if the new table were installed, which is what the worldgen and loot codecs are given while they load. `Registry.PendingTags.apply` is then **three ordered steps**: bind each `HolderSet.Named`, swap the `MappedRegistry.TagSet`, then rebind every holder's tag set. There is no lock and no single-reference swap; it is safe because one thread runs it start to finish with nothing else looking, not because it is atomic. Which thread depends on the occasion: at world load the Server thread does not exist yet, so the apply runs on whichever thread is driving the load — the launching thread on a dedicated server, through `Util.blockUntilDone`, and the Render thread on the client. Only `/reload` applies on the Server thread. **The client gets integers.** `TagNetworkSerialization.serializeTagsToNetwork` walks `RegistrySynchronization.networkSafeRegistries` and writes each tag as a list of registry ids, dropping any registry whose payload came out empty. That set is *every* `RegistryLayer.STATIC` registry, unconditionally, concatenated with the synced dynamic ones — `RegistrySynchronization.isNetworkable` filters only the second group. Ids for dynamic registries are meaningful only once both sides have built the same registry in the same order, which is why `SynchronizeRegistriesTask` sends the registry data first. In the configuration phase that ordering is a *send*-order constraint rather than a handling one: the client buffers both packets and resolves everything at the end. The play-phase packet resolves immediately against the live registry access, and the client then rebuilds its fuel table and the creative-inventory search tree from the new tags. **Singleplayer skips only what it already has.** On the play path `ClientPacketListener.handleUpdateTags` always prepares, and skips only the *apply* on a memory connection, because the integrated server's apply already rebound the `BuiltInRegistries` both halves share. In configuration the suppression is narrower still: only the non-networkable (static) registries' tags are skipped, and the client still binds tags on its own copies of the remote dynamic registries. **The check is a field read.** `BlockBehaviour.BlockStateBase` is a `TypedInstance`; `TypedInstance.is` asks the type holder — for a block, `Block.builtInRegistryHolder`, the intrusive holder from [identifiers-and-registries](identifiers-and-registries.md#before-the-game-exists) — and `Holder.Reference.is` is set-contains on an interned `TagKey`. No registry is consulted. `Parrot` (the perch search), `TrunkPlacer` (worldgen) and the client's `PunchTreeTutorialStepInstance` all ask this way; `FluidState`, `Entity` and `ItemStack` go through the same interface. ## The other way tags cross the wire `ClientboundUpdateTagsPacket` is not the only one. `ByteBufCodecs.holderSet` encodes a `HolderSet.Named` as a marker plus the tag's `Identifier`, and decodes it by looking the tag up in the receiving side's registry — so any packet or data component carrying a tag-shaped `HolderSet` **hard-fails on a client that does not have that tag**. That, more than the id numbering, is why the tags packet must reach the client before play traffic does. The same idea appears in data: `TagKey.hashedCodec`, `HolderSetCodec` and `RegistryCodecs` are what turn *"#minecraft:logs"* in an ordinary JSON field — a recipe ingredient, a loot condition, a placement predicate — into a `HolderSet` without any of those files being tag files. ## Questions players ask **Is a tag empty or broken before a world is open?** Empty, not fatal. `BuiltInRegistries` binds to empty only the tags the bootstrap actually asked for through its registration lookup; every other tag is simply absent from the table, so `Registry.get` for it answers empty and the same `BlockBehaviour.BlockStateBase.is` answers **false**. The window in which a tag read genuinely *throws* is narrower than it looks: it is during `BuiltInRegistries.bootStrap` itself, before `MappedRegistry.freeze` installs a bound `MappedRegistry.TagSet`. The throws even come from different places — an unbound `Holder.Reference` complains that tags are not bound, while `MappedRegistry.TagSet.unbound` guards the registry-level lookups. **Can a tag name something from another registry, or something that does not exist yet?** Never the first: a `TagEntry` carries only an `Identifier`, and the registry is fixed by the directory the file is in. The second, on one path: a data-pack tag *can* name an element that has not loaded yet, because the required path creates a placeholder `Holder.Reference` through `MappedRegistry.createRegistrationLookup`, and the registry's freeze fails with unbound values if the element never arrives. That escape hatch exists only on the data-pack path, never for a static registry. **Is the `HolderSet` I captured still the right object after `/reload`?** Only if the tag existed when the registry froze. `MappedRegistry.prepareTagReload` reuses a `HolderSet.Named` from `MappedRegistry.frozenTags` when it finds one, but a tag that first appears *after* the registry froze is created fresh into the pending map and never written back — so it gets a brand-new `HolderSet.Named` on every subsequent reload. A recipe ingredient that captured a vanilla tag at load time is correct after `/reload` without re-lookup; one that captured a data-pack tag may be holding a stale object. **What happens to a tag a reload deleted?** It keeps its old contents. It is absent from the pending map, so apply neither rebinds nor clears it. Anything still holding that `HolderSet.Named` will *iterate* the old list while `HolderSet.Named.contains` — which delegates to the holder's refreshed tag set — answers false, and `Registry.get` for the key answers empty. **What if two tags reference each other?** Both are dropped, and both are logged. `DependencySorter.addDependencyIfNotCyclic` drops the edge that would close the cycle, so an order exists and the sort does not hang — but whichever tag is built first asks for a tag that is not in the new table yet, gets nothing, and fails as a missing required reference; the second then asks for the first, which failed, and fails the same way. Two *Couldn't load tag* lines, and neither tag exists. **Which tags does the client never hear about?** Those of `RegistryLayer.RELOADABLE`-layer registries (loot tables, predicates) and of non-synced worldgen registries (configured features, structures). The reloadable ones are not even kept — see below. On the receiving side, ids the client's registry does not know are dropped from the payload silently. **Is picking a random element from a tag deterministic?** Yes, per pack stack. Duplicate entries collapse and file order is preserved — `TagLoader.tryBuildTag` collects into an insertion-ordered set — so iterating a tag, or picking from it with `HolderSet.getRandomElement`, gives the same sequence for the same packs. **Why do function tags look different from every other kind?** Because they are. `ServerFunctionLibrary` runs its own `TagLoader` over `CommandFunction`s, inside a real reload listener, with no registry involved; the well-known keys `ServerFunctionManager.TICK_FUNCTION_TAG` and `ServerFunctionManager.LOAD_FUNCTION_TAG` live on the manager, not the library (see Part XIII). ## Where to look `TagKey` · `BlockItemTags` · `BlockTags` · `TagFile` · `TagEntry` · `TagLoader` · `MappedRegistry` (the tag half) · `Registry.PendingTags` · `HolderSet` · `DependencySorter` · `WorldLoader` · `ReloadableServerResources` · `TagNetworkSerialization` · `ClientboundUpdateTagsPacket` · `ClientConfigurationPacketListenerImpl` · `ClientPacketListener` · `RegistryDataCollector` · `ByteBufCodecs` · `TypedInstance` · `Parrot.ParrotWanderGoal` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Data components > Verified against **Minecraft 26.2** · Part II · A player types `/give @s diamond_sword[enchantments={sharpness:3}]`, and later rolls Sharpness onto a plain sword at the enchanting table: what the square brackets are, and how the client finds out. A player types `/give @s diamond_sword[enchantments={sharpness:3}]`. The part in square brackets is a **patch**: one keyed, typed value laid over the sword. A data component is exactly that — one typed, keyed piece of data attached to an item stack: its damage, its enchantments, its lore, the food it is, the armour slot it goes in. The item *type* supplies a **prototype** map of components; a stack carries only a patch against that prototype. Everything that used to be "the NBT on an item" is a component with a codec, and most of what used to be a subclass of `Item` carrying behaviour (a sword, a piece of armour) is now a kit of components on a plain `Item`. The surprise is where the prototype comes from. It is not built in the item's constructor: `Item.Properties` only *records* an initializer, and the map is built again on every reload with the world's registries in hand — `DataComponentInitializers.build` on the reload worker, `Holder.Reference.bindComponents` on the owning thread. That is why a data pack can change what a jukebox plays without touching the item, and why a stack cannot even be decoded before the first reload: `Item.CODEC_WITH_BOUND_COMPONENTS` guards on `Holder.areComponentsBound`. ## The cast | class | what it decides | thread | |---|---|---| | `DataComponentType` | the key: which codec writes the value to disk, which to the wire, and whether it is saved at all | static, registered at bootstrap | | `DataComponentMap` | an immutable, identity-keyed map — the prototype's shape | any; never mutated | | `PatchedDataComponentMap` | what an `ItemStack` actually owns: a shared prototype, a patch, and whether the patch is still someone else's | whichever thread owns the stack | | `DataComponentPatch` | the serialisable form of the patch: additions and removals | Netty for packets; server and workers for saves | | `DataComponentInitializers` | builds every registry element's prototype, from the recorded initializers, once the registries exist | reload worker | | `Holder.Reference` | holds the bound prototype for one registry element, and throws until it has one | bound on the server thread after a reload; on the client at the end of configuration | | `ItemStack` | the writes (`ItemStack.set`, `ItemStack.update`, `ItemStack.remove`, `ItemStack.copyFrom`) and the reads it inherits from `DataComponentHolder` | whichever thread owns the stack | | `DataComponentLookup` | the reverse index: which elements of a registry carry this component value | built at freeze, populated lazily | Everything here ships in both jars; in the trace below only `ClientPacketListener` is client-only. ## The shape of a stack ```mermaid flowchart LR DCI["DataComponentInitializers.build, on the reload worker, with the registries in hand"] --> HR subgraph ITEM["Item, one per registry element"] HR["Holder.Reference, bindComponents at reload"] --> PROTO["the prototype: a DataComponentMap, immutable and identity-keyed"] end subgraph STACK["ItemStack"] PDM["PatchedDataComponentMap"] --> PATCH["patch: type to Optional value, empty meaning removed from the prototype"] PDM --> COW["copyOnWrite: the map is still shared with the stack it was copied from"] end PDM -. "prototype, shared by every stack of the item" .-> PROTO PATCH -- "asPatch and fromPatch" --> DCP["DataComponentPatch: added values and removed keys, the form on disk and on the wire"] KEY["DataComponentType: the key, with a persistent codec, a network codec, or only the latter"] --> PATCH KEY --> PROTO ``` Read it left to right. The item's prototype is built by `DataComponentInitializers` and bound onto the item's `Holder.Reference`. A stack points at that shared prototype and owns only a patch over it, whose values are `Optional` — an empty value is a removal. The patch's serialisable twin is a `DataComponentPatch`, and the key of every entry in all of them is a `DataComponentType`. The rest of the page is a tour of those objects, each grounded in one small trace: Sharpness III arriving on a sword at the enchanting table. ## The key: `DataComponentType` A type is built by `DataComponentType.Builder`. `DataComponentType.Builder.persistent` gives the disk and JSON codec; `DataComponentType.Builder.networkSynchronized` a hand-written wire codec. A type with no persistent codec is **transient** (`DataComponentType.isTransient`): never saved, only sent. `DataComponentType.Builder.networkSynchronized` is not a gate — without it, `DataComponentType.Builder.build` derives a wire codec from the persistent codec (NBT over the wire), and with neither it throws. The only real switch is transient-versus-persistent, and it gates *saving*. Three types exist only on the wire: `DataComponents.CREATIVE_SLOT_LOCK`, `DataComponents.ADDITIONAL_TRADE_COST`, `DataComponents.MAP_POST_PROCESSING`. **111** — vanilla types, registered in `DataComponents` into `BuiltInRegistries.DATA_COMPONENT_TYPE`; 29 of them have slash-shaped ids (*villager/variant* and its siblings). The catalogue is [reference/components](../../reference/components.md). Component *types* are code, in a registry data packs cannot extend; what data packs reach is the prototype (below) and `CustomData`, which carries arbitrary NBT for them to use. Two more flags live on the builder. `DataComponentType.Builder.cacheEncoding` routes a type's encodes through `EncoderCache` (`DataComponents.ENCODER_CACHE`). `DataComponentType.Builder.ignoreSwapAnimation` is set on exactly one type, `DataComponents.DAMAGE` — so durability ticking down does not replay the held-item swap on the client. Underneath, `DataComponentType.PERSISTENT_CODEC` and `DataComponentType.VALUE_MAP_CODEC` are the shared dispatch machinery that `DataComponentMap.CODEC` and the predicates are built on. ## The maps `DataComponentMap` is immutable and identity-keyed; `DataComponentMap.Builder` builds one and can carry a `DataComponentMap.Builder.addValidator`, which is where the prototype-time structural rule lives (below). `DataComponentMap.EMPTY` is the map every registry element gets when nothing declared one. `DataComponentMap.composite` is dead API: it exists, and nothing in 26.2 calls it; the layering that actually happens is `PatchedDataComponentMap`'s prototype-plus-patch. `PatchedDataComponentMap` is the map an `ItemStack` actually owns: a `PatchedDataComponentMap.prototype` shared with every stack of that item, a `PatchedDataComponentMap.patch` whose values are `Optional` (empty means *removed from the prototype*), and a `PatchedDataComponentMap.copyOnWrite` flag. It sanitises on every write: `PatchedDataComponentMap.set` stores nothing when the value equals the prototype's, and `PatchedDataComponentMap.remove` stores a removal marker only if the prototype had the key. In the trace, the sword's prototype carries an *empty* `ItemEnchantments` (every item's does, through `DataComponents.COMMON_ITEM_COMPONENTS`), so Sharpness III differs from the prototype and the patch gains one entry. Set the enchantments back to empty and the entry vanishes rather than becoming an explicit default. **The first write pays for the copy.** `PatchedDataComponentMap.ensureMapOwnership` clones the backing map only when `PatchedDataComponentMap.copyOnWrite` is set; `ItemStack.copy`, `PatchedDataComponentMap.asPatch` and `ItemStack.transmuteCopy` all alias the same map and set the flag, so copying a stack is O(1) until someone writes. `ItemStack.applyComponentsAndValidate` relies on that: it snapshots with `PatchedDataComponentMap.asPatch`, applies, runs `ItemStack.validateStrict`, and on failure `PatchedDataComponentMap.restorePatch`. **Equality is prototype plus patch.** `ItemStack.isSameItemSameComponents` bottoms out in `PatchedDataComponentMap.equals`. Because setting a value equal to the prototype default *removes* it from the patch, two stacks that reached the default by different routes compare equal. ## The patch, on the wire and on disk `DataComponentPatch` is the serialisable form: additions and removals. `DataComponentPatch.CODEC` writes removals as `!minecraft:foo`; `DataComponentPatch.STREAM_CODEC` writes two counts then the entries; `DataComponentPatch.DELIMITED_STREAM_CODEC` is the length-prefixed variant for untrusted input; and `DataComponentPatch.split` is the added/removed decomposition that the hashing and block-entity paths are both built on. `TypedDataComponent` — a type with its value — has its own "type id then value" stream codec, distinct from the patch encoding, for the places one value travels alone. **The wire patch never contains defaults — for a real stack.** Because `PatchedDataComponentMap` sanitises on every write, a value equal to the prototype's is dropped rather than sent. `ItemStackTemplate` is the exception: it holds a raw `DataComponentPatch` straight from the builder, which does no such comparison, and sends it verbatim. On the network the patch travels inside every `ItemStack` in `ClientboundContainerSetSlotPacket`, `ClientboundContainerSetContentPacket`, `ClientboundSetCursorItemPacket`, `ClientboundSetPlayerInventoryPacket` and `ClientboundSetEquipmentPacket`; the client answers ordinary clicks with hashes, not stacks, and the creative slot alone sends a full, re-validated stack — [codecs, NBT and JSON](codecs-nbt-json.md#the-four-paths-side-by-side) owns the four serialisations of a stack, and [containers and menus](../items/containers-and-menus.md) owns what the server does with a hash. On disk an item is saved as a *patch* (`ItemStack.MAP_CODEC`'s "components", with transient types silently dropped), but a block entity as a full `DataComponentMap` — the two are not symmetric. ## The prototype, and why it is built at reload An `Item` constructor registers its initializer in `BuiltInRegistries.DATA_COMPONENT_INITIALIZERS`. `Item.Properties.component` and its convenience methods (`Item.Properties.durability`, `Item.Properties.food`, `Item.Properties.equippable`, `Item.Properties.sword`, …) only *record* a `DataComponentInitializers.Initializer`; nothing is a map yet. `Item.Properties.delayedComponent` and `Item.Properties.delayedHolderComponent` are the reason it must be deferred: they name registry entries that do not exist until a world's registries do — an item's `DataComponents.JUKEBOX_PLAYABLE` or `DataComponents.DAMAGE_RESISTANT` names an entry a data pack can change. The maps are *built* with full registry context — tags, damage types, jukebox songs resolvable — by `DataComponentInitializers.build` on the reload worker on the server, and *installed* with `Holder.Reference.bindComponents` on the owning thread: on the server in `ReloadableServerResources.updateComponentsAndStaticRegistryTags` (server thread, after every reload including `/reload`), on the client in `RegistryDataCollector` at the end of configuration. A `/reload` therefore rebinds every item's prototype. Every registry element gets a component map (`DataComponentMap.EMPTY` if it had no initializer), so `EntityType` holders have one too — with one asymmetry, and it runs the way round you would not guess. `ClientConfigurationPacketListenerImpl` passes `Connection.isMemoryConnection` into `RegistryDataCollector.collectGameRegistries`, which negates it, so a **singleplayer** client binds only the registries `RegistrySynchronization.isNetworkable` accepts and a **multiplayer** client binds every one. Singleplayer can skip the rest because it shares `BuiltInRegistries` with the integrated server, whose own apply has already bound them. Before binding, reading a registry element's components throws, and the failure is a null-check, not a friendly error: `Item.components` merely delegates, the throw comes from `Holder.Reference.components`, and the non-throwing question is `Holder.areComponentsBound`. `Item.CODEC_WITH_BOUND_COMPONENTS` guards on it and refuses to decode a stack until then. **Ten** — entries in `DataComponents.COMMON_ITEM_COMPONENTS`, the map every item's prototype starts from. Notably it puts an *empty* `ItemEnchantments` on every item, which is what `ItemStack.isEnchantable` depends on: it gates first on `DataComponents.ENCHANTABLE` being present, and *then* on `DataComponents.ENCHANTMENTS` being present and empty. **There are two structural rules, at two different times.** At prototype-build time a validator installed by `Item.Properties` rejects an item that is both damageable and stackable. At stack time `ItemStack.validateStrict` rejects a `DataComponents.MAX_DAMAGE` alongside a `DataComponents.MAX_STACK_SIZE` above one, a count above the stack's own maximum, an over-weight bundle, and contained items whose counts exceed their own limits. That last check reaches exactly **one** level into containers, bundles and charged projectiles and does not re-run the full validation there — nesting is not followed. ## The reverse index: `DataComponentLookup` Every frozen `MappedRegistry` builds one (`Registry.componentLookup`): a lazily-populated reverse index answering "which elements carry this component value?", which is how the game finds the spawn egg for an entity type or the item for a dye colour. It reads the same bound prototypes the holders carry, so it too is meaningless before the first reload. ## The readers and the predicates `DataComponentGetter` reads one component; `DataComponentHolder` reads one and has a map, and is implemented only by `ItemStack`, which is where `DataComponentHolder.get`, `DataComponentHolder.getOrDefault` and `DataComponentHolder.has` come from. `ItemInstance` is the read-only face over `ItemStack` and `ItemStackTemplate` (item, count, patch — a record) that predicates and recipes take. Beyond item behaviour, the callers are loot functions (`CopyComponentsFunction`) and the `/give` and `/item` commands. The predicates come in two strengths. `DataComponentExactPredicate` requires every listed component to equal. The partial `DataComponentPredicate` family under `core/component/predicates` — `DataComponentPredicates`, **15** kinds, in their own `BuiltInRegistries.DATA_COMPONENT_PREDICATE_TYPE` registry — matches a shape rather than a value, and `DataComponentMatchers` joins the two for `ItemPredicate`. ## The values, by package | package | value types | |---|---| | `world/item/component` | `Consumable`, `Tool`, `Weapon`, `BlocksAttacks`, `ItemLore`, `CustomData`, `TooltipDisplay`, `ItemContainerContents`, `BundleContents`, `TypedEntityData` … | | `world/item/equipment` | `Equippable` | | `world/item/enchantment` | `ItemEnchantments`, `Enchantable`, `Repairable` | `Item` subclasses no longer carry combat. `Weapon`, `BlocksAttacks`, `KineticWeapon`, `PiercingWeapon`, `AttackRange`, `SwingAnimation` and `Tool` are components; `Item.Properties.sword`, `Item.Properties.spear` and `Item.Properties.humanoidArmor` build whole kits, and *SwordItem* is gone. But the tools that act *on a block* are not: `AxeItem`, `ShovelItem` and `HoeItem` still exist as classes, purely for stripping, path-making and tilling — their combat and mining live in components like everything else. ## The trace: Sharpness at the enchanting table ```mermaid sequenceDiagram participant EM as EnchantmentMenu participant IStack as ItemStack participant PDM as PatchedDataComponentMap participant ACM as AbstractContainerMenu participant CPL as ClientPacketListener Note over EM: server thread, ServerboundContainerButtonClickPacket has arrived EM->>IStack: transmuteCopy(Items.ENCHANTED_BOOK) if the input is a book, same patch over a new prototype EM->>IStack: enchant(holder, level) for each chosen EnchantmentInstance IStack->>IStack: EnchantmentHelper.updateEnchantments, then set of STORED_ENCHANTMENTS for a book, ENCHANTMENTS otherwise IStack->>PDM: set: ensureMapOwnership clones the shared map, the value differs from the prototype's empty set, so patch.put Note over PDM: patch is now {minecraft:enchantments to {sharpness: 3}} Note over ACM: still inside the packet handler, which calls broadcastChanges itself once the click is accepted ACM->>ACM: broadcastChanges, RemoteSlot.Synchronized.matches fails for this slot ACM->>CPL: ClientboundContainerSetSlotPacket: count, item id, DataComponentPatch.STREAM_CODEC CPL->>CPL: decode: new ItemStack(holder, count, patch), fromPatch against the client's own bound prototype CPL->>CPL: handleContainerSetSlot, AbstractContainerMenu.setItem, tooltip via ItemEnchantments.addToTooltip ``` **The menu owns the mutation.** `EnchantmentMenu.clickMenuButton` runs under `ContainerLevelAccess.execute` on the server thread. For a book it first calls `ItemStack.transmuteCopy` — a new stack with the *same patch* applied to the enchanted book's prototype — then `ItemStack.enchant` per chosen `EnchantmentInstance`. `ItemStack.enchant` hands the edit to `EnchantmentHelper.updateEnchantments`, which reads the current `ItemEnchantments`, edits a mutable copy and writes the immutable result back with `ItemStack.set`; the enchanting rules, the lapis, the seed and `/enchant` are [Part VII's](../items/enchanting.md). What matters here is that enchantments are a value, not a list on the stack: one component, one write. **One write, one patch entry.** `ItemStack.set` is `PatchedDataComponentMap.set`. The map the sword owned was shared with whatever it was copied from, so `PatchedDataComponentMap.ensureMapOwnership` clones it now; the new `ItemEnchantments` differs from the prototype's empty one, so the patch gains its single entry. A book that was transmuted a moment earlier carries the same patch over a different prototype, which is the whole meaning of `ItemStack.transmuteCopy`. **The menu compares, and here it does not wait for the tick.** `AbstractContainerMenu.broadcastChanges` compares every slot against what the client was last told (`RemoteSlot.Synchronized`). It runs from `ServerPlayer.tick` in the ordinary case — but a menu-button click is not the ordinary case: `ServerGamePacketListenerImpl.handleContainerButtonClick` calls it directly, in the same handler, as soon as `AbstractContainerMenu.clickMenuButton` accepts. The sword's slot no longer matches, so `ClientboundContainerSetSlotPacket` goes out, and only the patch crosses: `ItemStack.OPTIONAL_STREAM_CODEC` writes the count, `Item.STREAM_CODEC` (a registry id) and the patch. The client answers later clicks with a `HashedStack` of CRC32C checksums rather than stacks ([codecs, NBT and JSON](codecs-nbt-json.md#checksum-a-hash-instead-of-a-stack) for the hashing, [containers and menus](../items/containers-and-menus.md) for the click protocol). **The client rebuilds against its own prototype.** The decoder constructs `ItemStack` from holder, count and patch, and that constructor is `PatchedDataComponentMap.fromPatch` against the client's *own* bound prototype — the map `RegistryDataCollector` bound at the end of configuration. That is the reason components must be bound on the client before the play phase: an unbound prototype would throw in a packet decoder on a Netty thread. `ClientPacketListener.handleContainerSetSlot` then hands the stack to `AbstractContainerMenu.setItem`, and the tooltip's purple line is `ItemEnchantments.addToTooltip` reading the same component. ## Components on things that are not items **Block entities, both directions.** Placing runs `BlockEntity.applyComponentsFromItemStack`, which hands subclasses a *recording* `DataComponentGetter`: whatever `BlockEntity.applyImplicitComponents` reads is forgotten from the patch (`DataComponentPatch.forget`) and only the leftovers persist as opaque `BlockEntity.components`. Two types are pre-seeded into that forget set regardless of whether anything reads them — `DataComponents.BLOCK_ENTITY_DATA` and `DataComponents.BLOCK_STATE` — and only the *added* half of the resulting patch is kept, so removals are discarded. Breaking or picking runs the reverse, `BlockEntity.collectComponents` over `BlockEntity.collectImplicitComponents`, with `BlockEntity.removeComponentsFromTag` de-duplicating what was promoted; `BlockItem.setBlockEntityData` is the write path for the opaque blob. **Entities, read-only.** `Entity` implements `DataComponentGetter` with no patch of its own: `Entity.get` answers `DataComponents.CUSTOM_NAME` and `DataComponents.CUSTOM_DATA` by hand, lets subclass overrides (`Sheep`, `Wolf`, `Villager` …) answer the variant-shaped types, and otherwise falls through to its `EntityType` holder's bound prototype — the same map every registry element gets. `Entity.applyComponentsFromItemStack` is the write path, and it is not only spawn eggs: any item-to-entity spawn, an arrow picking up its stack, a lingering potion's cloud and `BlockItem` all take it. ## Where to look `DataComponentType` · `DataComponents` · `DataComponentMap` · `PatchedDataComponentMap` · `DataComponentPatch` · `DataComponentInitializers` · `Holder.Reference` · `DataComponentLookup` · `Item.Properties` · `ItemStack` · `ItemInstance` · `ItemStackTemplate` · `EnchantmentMenu` · `AbstractContainerMenu` · `BlockEntity` · `Entity` (the getter half) --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Text components > Verified against **Minecraft 26.2** · Part II · A player dies: the message that names the killer is built on the server, crosses the wire as a translation key, and is worded on the client. An arrow lands and a player drops. A line appears in everyone's chat, and on the victim's screen the death screen says who did it. The server built that message on its own thread, inside `CombatTracker.getDeathMessage`, and sent it twice: once in `ClientboundPlayerCombatKillPacket` to the victim, once as system chat to everybody. But the server never wrote the sentence. What it built was a `Component` whose contents are a translation key — *death.attack.arrow* — and two arguments, the victim's name and the killer's, each of them another `Component` carrying a hover card and a click action. The packet crosses the wire as NBT. On the client the packet is decoded, handed to a `DeathScreen`, and still nobody has read the key. **The client receives the death message before anyone knows what it says.** The words are chosen on the first frame that draws it, when `TranslatableContents` asks the client's own `Language` for the template behind the key, so two players watching the same death read two different sentences from one packet — and the server, which logs the message too, only ever reads it in English. ## The cast | class | what it decides | thread | |---|---|---| | `Component` | the interface: contents, style, siblings, and a walk over them in logical order | built wherever text is made — a server tick, a command, the Render thread | | `MutableComponent` | the only implementation; `MutableComponent.append` and `MutableComponent.withStyle` are how a tree is built | as above | | `ComponentContents` · `TranslatableContents` | what a node *says*: seven kinds, of which the translatable kind is the one that waits for a `Language` | worded on whichever thread first visits it | | `Style` | the eleven inheritable fields, immutable, `Style.EMPTY` the shared blank | any | | `ComponentSerialization` | the one recursive codec, and the NBT stream codecs built over it | Netty, in `PacketEncoder` and `PacketDecoder`; Server or Render for data on disk | | `ComponentUtils` | `ComponentUtils.resolve`: selectors, scores and NBT paths into text, against a `ResolutionContext` | Server, during command execution | | `Language` | a key to a template; the client swaps the instance on every resource reload | Render thread reads it, `Language.inject` swaps it | | `ClickEvent` | what a click on styled text may do, and the one action a server may not send | Render thread, when the click lands | ## A component is three things ```mermaid flowchart TD Comp["Component: an interface with four abstract members"] --> MComp["MutableComponent: the only implementation"] MComp --> Contents["one ComponentContents"] MComp --> Style["one Style: eleven nullable fields, null means inherit"] MComp --> Siblings["an ordered list of siblings, each a Component"] Siblings -. "recursion" .-> MComp subgraph kinds ["the seven kinds"] Text["PlainTextContents"] Trans["TranslatableContents: key, fallback, arguments"] Key["KeybindContents"] Score["ScoreContents"] Sel["SelectorContents"] Nbt["NbtContents"] Obj["ObjectContents: a sprite in the text"] end Contents --> kinds MComp --> CS["ComponentSerialization.CODEC, and STREAM_CODEC over NBT"] ``` `Component` is an interface with four abstract members — `Component.getStyle`, `Component.getContents`, `Component.getSiblings` and `Component.getVisualOrderText`, the last of which is why `MutableComponent` caches a laid-out form — and exactly **one** implementation, `MutableComponent`. A component is a triple: one `ComponentContents`, one `Style`, and an ordered list of sibling components. Style inheritance happens during traversal, not in storage: `Component.visit` applies a node's own style over its parent's with `Style.applyTo` and hands the result down to the contents and then to each sibling in turn, so a red parent with a plain child draws the child red, and nothing in the child records it. The mutability is in the name. `Component.literal`, `Component.translatable` and the other factories return a `MutableComponent`, and a tree is built by `MutableComponent.append` and `MutableComponent.withStyle`, which replace the style field with a fresh `Style` each time. `Component.copy` is a shallow copy — the sibling list is new, the siblings are shared. The walk is the only way to read one. `Component.getString` visits every node and concatenates what it says; `Component.getString` with a limit stops at that many characters, which is how a death message too long to send is cut to 256 for its replacement. Everything from the walk onwards — the codepoint stream, bidirectional reordering, glyphs — belongs to [text and fonts](../client/text-and-fonts.md). ## The seven kinds of contents Each kind has a static factory on `Component`: | kind | class | made by | |---|---|---| | text | `PlainTextContents`, with `PlainTextContents.LiteralContents` | `Component.literal` | | translatable | `TranslatableContents` | `Component.translatable` | | keybind | `KeybindContents` | `Component.keybind` | | score | `ScoreContents` | `Component.score` | | selector | `SelectorContents` | `Component.selector` | | nbt | `NbtContents` | `Component.nbt` | | object | `ObjectContents` | `Component.object` | ### What each kind says, and when **Text** says its string and nothing more; the empty string is the shared `PlainTextContents.EMPTY`, which is what `Component.empty` and `CommonComponents.EMPTY` hold. **Translatable** is a key, an optional fallback and an array of arguments. An argument is a number, a boolean, a string or another `Component` — `TranslatableContents.isAllowedPrimitiveArgument` is the test, and `Component.translatableEscape` turns anything else into its string form before it can reach the codec. The template the key names is read by `TranslatableContents.decompose`, which accepts only `%s`, `%n$s` and `%%`: any other specifier is a `TranslatableFormatException`, and the component then shows the raw template string instead. The decomposition is cached against the identity of the `Language` that produced it (`TranslatableContents.decomposedWith`), so a language switch re-words every component the next time it is visited, without anything being re-sent. **Keybind** names a key binding and asks `KeybindResolver.keyResolver` for its current name; the default resolver answers with the name itself, and the client installs `KeyMapping.createNameSupplier` in the `Minecraft` constructor, so *key.jump* reads as whatever key is bound on a client and as *key.jump* anywhere else. **Score**, **selector** and **nbt** are the three kinds that say nothing until a server resolves them. A score names a holder — an entity selector, a literal name, or `ScoreHolder.WILDCARD` for the context's own entity — and an objective. A selector holds a compiled `EntitySelector` and an optional separator; visited unresolved, it shows the selector's source text. An nbt component holds an `NbtPathArgument.NbtPath`, a separator, and one of three `DataSource`s — `EntityDataSource`, `BlockDataSource`, `StorageDataSource` — and either prints what it finds through `TextComponentTagVisitor` or, with *interpret*, parses each match as a component; *interpret* and *plain* are refused together. Visited without resolution, score and nbt contents say nothing at all. **Object** puts a picture inside text. Its `ObjectInfo` is an `AtlasSprite` (an atlas and a sprite, the block atlas by default) or a `PlayerSprite` (a `ResolvableProfile` and whether to draw the hat). When visited with a style it emits a single placeholder character, U+FFFC, with the style's font set to the info's `FontDescription`; the font system draws the sprite where a glyph would go. Visited without style — `Component.getString`, a log line — it says its fallback, or `ObjectInfo.defaultFallback`: *[sprite]* for an atlas sprite, *[name head]* for a player. ## Style, and the click that never crosses `Style` holds eleven nullable fields: `Style.color` (a `TextColor`), `Style.shadowColor`, the five booleans — `Style.bold`, `Style.italic`, `Style.underlined`, `Style.strikethrough`, `Style.obfuscated` — `Style.clickEvent`, `Style.hoverEvent`, `Style.insertion` and `Style.font` (a `FontDescription`). Null means *inherit*, which is what makes `Style.applyTo` a merge: a field the child sets wins, a field it leaves null falls through to the parent. A setter returns a new `Style` only when the value changes — set bold to what it already is and the same object comes back — and the codec and the setters collapse a style with nothing left set to the shared `Style.EMPTY`, so `Style.isEmpty` is an identity check. `ChatFormatting` is the legacy vocabulary — `Style.applyFormat` sets one boolean or, for a colour code, `TextColor.fromLegacyFormat`, and `ChatFormatting.RESET` returns `Style.EMPTY` outright. A `TextColor` is 24 bits, masked on construction, and serialises as one of its sixteen names or as *#RRGGBB*; `TextColor.parseColor` accepts either. The shadow is different: `Style.shadowColor` is a 32-bit ARGB integer, `Style.NO_SHADOW` is zero, and `Style.withoutShadow` is how a component asks to be drawn flat. `Style.font` is a `FontDescription`, which may be a `FontDescription.Resource` naming a font file, or one of the two sprite shapes that `ObjectContents` uses — it need not name a font at all. ### Eight clicks, three hovers, one refusal `ClickEvent` and `HoverEvent` are interfaces implemented by nested records — closed by convention and by their `Action` dispatch codec, not by the language. There are eight click actions and three hover actions: | `ClickEvent.Action` | record | carries | a server may send it | |---|---|---|---| | `ClickEvent.Action.OPEN_URL` | `ClickEvent.OpenUrl` | a URI, through `ExtraCodecs.UNTRUSTED_URI` | ✓ | | `ClickEvent.Action.OPEN_FILE` | `ClickEvent.OpenFile` | a path on the viewer's disk | **no** | | `ClickEvent.Action.RUN_COMMAND` | `ClickEvent.RunCommand` | a command string | ✓ | | `ClickEvent.Action.SUGGEST_COMMAND` | `ClickEvent.SuggestCommand` | a string for the chat box | ✓ | | `ClickEvent.Action.SHOW_DIALOG` | `ClickEvent.ShowDialog` | a `Holder` of a `Dialog` | ✓ | | `ClickEvent.Action.CHANGE_PAGE` | `ClickEvent.ChangePage` | a positive page number | ✓ | | `ClickEvent.Action.COPY_TO_CLIPBOARD` | `ClickEvent.CopyToClipboard` | a string | ✓ | | `ClickEvent.Action.CUSTOM` | `ClickEvent.Custom` | an `Identifier` and an optional NBT payload, for servers to define | ✓ | What keeps `ClickEvent.Action.OPEN_FILE` out of a server's hands is `ClickEvent.Action.filterForSerialization`, applied as a validation on `ClickEvent.Action.CODEC` and therefore biting in **both** directions and in **every** format: a data pack cannot write one either, and the client cannot encode one it built itself. The private flag behind it is `ClickEvent.Action.allowFromServer`. The unfiltered `ClickEvent.Action.UNSAFE_CODEC` exists and nothing outside the enum reads it; an open-file click is something only client code constructs in memory — `Screenshot`'s notice, a debug dump's path in `KeyboardHandler`, a profiler result in `Minecraft` — for its own chat. `HoverEvent.Action` has the identical machinery and nothing to filter — all three of its values are allowed: `HoverEvent.ShowText` wraps a component, `HoverEvent.ShowItem` an `ItemStackTemplate`, `HoverEvent.ShowEntity` an `HoverEvent.EntityTooltipInfo` of type, UUID and optional name. ## Serialisation: one codec, three shapes **Serialisation** is one recursive codec, `ComponentSerialization.CODEC`, whose shape is a three-way choice: a bare string becomes a literal, a list becomes its first element with the rest appended, and an object is the full record. Contents are matched by an explicit *type* field if one is present and otherwise by trying each contents codec in turn — which is why an untyped component still round-trips. `Component.tryCollapseToString` is what lets a plain unstyled literal encode as a bare string. The full record is flat. `Style.Serializer.MAP_CODEC` is inlined, so the style's keys — *color*, *shadow_color*, *bold*, *italic*, *underlined*, *strikethrough*, *obfuscated*, *click_event*, *hover_event*, *insertion*, *font* — sit beside the contents' own keys, and the siblings are a non-empty list under *extra*. The seven contents codecs are registered in an `ExtraCodecs.LateBoundIdMapper` under the names in the table above, and `ComponentSerialization.createLegacyComponentMatcher` builds the matcher that the object and data-source kinds reuse for their own *object* and *source* fields. In JSON and NBT, encoding never writes a *type*: each kind is written with its own distinguishing key (*text*, *translate*, *keybind*, *score*, *selector*, *nbt*, and a sprite's *sprite* or *player*), and a decoder recognises it by that key. A translatable argument that decodes to a plain unstyled literal is collapsed back to a string argument, so the argument list round-trips by meaning rather than by shape. ### On the wire: NBT, and two budgets On the wire, **components travel as NBT, not JSON**: `ComponentSerialization.STREAM_CODEC` is built over the NBT ops — it is `ByteBufCodecs.fromCodecWithRegistries`, which encodes through `NbtOps` with the buffer's `RegistryOps` and writes the resulting `Tag`, and on decode reads a `Tag` under an `NbtAccounter` and parses it back. The accounter is the difference between the two families: `NbtAccounter.defaultQuota` allows two mebibytes at depth 512, and the trusted variants — `ComponentSerialization.TRUSTED_STREAM_CODEC` and its siblings, built with `ByteBufCodecs.fromCodecWithRegistriesTrusted` over `NbtAccounter.unlimitedHeap` — lift the NBT budget, and **every clientbound chat packet uses them** ([packets and stream codecs](../networking/packets-and-stream-codecs.md)). So does everything else a server authors: the death packet, entity custom names through `EntityDataSerializers.OPTIONAL_COMPONENT`, score displays, painting titles, command-suggestion tooltips. The budgeted codec is what the two component-typed data components use — `DataComponents.CUSTOM_NAME` and `DataComponents.ITEM_NAME` are synchronised with it — and those are the components a creative player's stack carries serverbound. A third shape, `ComponentSerialization.TRUSTED_CONTEXT_FREE_STREAM_CODEC`, needs no registries and carries the texts that must decode before a registry exists: `ClientboundDisconnectPacket`, the MOTD in `ClientboundServerDataPacket`, a resource-pack prompt, server links. And `ComponentSerialization.flatRestrictedCodec` caps a component by the length of its JSON form; `WrittenBookContent.CONTENT_CODEC` uses it at 32,767 for a page. ## Resolution: what a server does to a component before it sends it Resolution — turning selectors, scores and NBT paths into text — is `ComponentUtils.resolve` against a `ResolutionContext`, which carries the command source, a depth limit and a `ResolutionContext.LimitBehavior`. The walk copies the tree: `ComponentContents.resolve` returns a copy by default and is overridden by the score, selector, nbt and object kinds, and by the translatable kind, which resolves each component argument; siblings are resolved after the contents, and a `HoverEvent.ShowText` inside a style is resolved too. Past the depth limit — 100 by default — `ResolutionContext.LimitBehavior.STOP_PROCESSING_AND_COPY_REMAINING` copies the rest untouched and `ResolutionContext.LimitBehavior.DISCARD_REMAINING` puts `CommonComponents.ELLIPSIS` in its place. A context with no command source resolves score, selector and nbt contents to empty. The context also carries an `ObjectInfo` validator: `ResolutionContext.validate` swaps a rejected sprite for its fallback, which is how `ServerStatusPinger` sanitises a server-list MOTD on the *client* — depth 16, discard past it, and no player heads. **Ordinary chat never resolves anything**: the content of a `ServerboundChatPacket` is a plain string all the way to `Component.literal`. Commands are the exception — `MessageArgument.Message.toComponent` expands entity selectors inside a message argument, behind a permission, which is why `/say @a` names people and a chat line saying the same thing does not. The resolved text becomes the message's *unsigned* content. The mechanics: `MessageArgument.Message.parseText` refuses more than 256 characters and, if the source may use selectors at all (`EntitySelectorParser.allowSelectors`), tries every `@` as a selector and keeps the ones that parse as a `MessageArgument.Part` — an `@` that fails on a missing or unknown selector type is left as ordinary text; at execution the permission is `Permissions.COMMANDS_ENTITY_SELECTORS`, and each part becomes `EntitySelector.joinNames` of what it finds. A raw component argument — `/tellraw`'s — is a `ComponentArgument`, parsed with the full codec from SNBT and resolved by `ComponentArgument.getResolvedComponent` with the target player as the scoreboard entity. ## The death message ```mermaid sequenceDiagram participant SP as ServerPlayer participant CT as CombatTracker participant CS as ComponentSerialization participant CPL as ClientPacketListener participant DScr as DeathScreen participant TrC as TranslatableContents participant Language as Language Note over SP,CT: the server tick in which the player dies SP->>CT: die: getDeathMessage, if show_death_messages CT->>CT: the last CombatEntry's DamageType.deathMessageType, then DamageSource.getLocalizedDeathMessage CT-->>SP: translatable death.attack.arrow, arguments: the victim's display name, the killer's SP->>CS: ClientboundPlayerCombatKillPacket to the victim, TRUSTED_STREAM_CODEC SP->>CS: the same component in ClientboundSystemChatPacket to everyone, and getString for the log Note over CS: the Netty thread, PacketEncoder CS->>CS: encode through NbtOps: translate, with, and each argument a compound with hover_event and insertion Note over CS,CPL: the client's Netty thread decodes, then ensureRunningOnSameThread CS->>CPL: handlePlayerCombatKill, a MutableComponent nobody has read CPL->>DScr: new DeathScreen with the packet's message, Render thread Note over DScr: the next frame DScr->>TrC: visitText, then Component.visit reaches the contents TrC->>Language: decompose: Language.getInstance, getOrDefault(death.attack.arrow) Language-->>TrC: the template from the client's language stack, en_us then the selected code TrC-->>DScr: the victim's name, " was shot by ", the killer's name, each argument visited in turn TrC->>Language: the killer's name, if it is a mob, is entity.minecraft.zombie, worded the same way ``` ### Built on the server, in no language `ServerPlayer.die` runs on the server thread. If `GameRules.SHOW_DEATH_MESSAGES` is on it asks the `CombatTracker` for the message; `CombatTracker.getDeathMessage` takes the last `CombatEntry`, reads its `DamageType.deathMessageType`, and for the default kind hands to `DamageSource.getLocalizedDeathMessage`, which picks a key from the damage type's `DamageType.msgId` — *death.attack.* plus the id, with *.player* when the source names no entity but `LivingEntity.getKillCredit` still finds one (the last player, or failing that the last mob, to hurt the victim), with *.item* when the killer's held item has a `DataComponents.CUSTOM_NAME` — and builds `Component.translatable` with the victim's and the killer's display names as arguments. `DeathMessageType.FALL_VARIANTS` and `DeathMessageType.INTENTIONAL_GAME_DESIGN` take two more branches, the second of them attaching a `ClickEvent.OpenUrl` to a bracketed link — and the fall branch only when `CombatTracker.getMostSignificantFall` actually found one, so a fall-typed source with no recorded fall drops back to the ordinary message. A fourth branch comes first: an empty combat log is *death.attack.generic*. The key is a line in *en_us.json*; the tracker never sees the line. The killer's name is itself a component, and often a translatable one. `Entity.getDisplayName` is `PlayerTeam.formatNameForTeam` over `Entity.getName` — the custom name if there is one, otherwise `Entity.getTypeName`, which is `EntityType.getDescription`, a `Component.translatable` of *entity.minecraft.zombie* — with a `HoverEvent.ShowEntity` and the UUID as insertion. `Player.getDisplayName` adds a `ClickEvent.SuggestCommand` of */tell name* and the name as insertion. A team wraps the name in its prefix, suffix and colour. So the argument list carries components inside a component, styles inside styles, and a mob killer's name is a second key the client will look up after the first. The packet is sent twice over. `ClientboundPlayerCombatKillPacket` goes to the victim, with a `PacketSendListener.exceptionallySend` fallback: if the send fails, a replacement carries *death.attack.even_more_magic* with the first 256 characters of the real message in a hover, so the death screen never goes blank. The same component goes to everyone as system chat — `PlayerList.broadcastSystemMessage`, or the team-scoped `PlayerList.broadcastSystemToTeam` / `PlayerList.broadcastSystemToAllExceptTeam` when the team's `Team.Visibility` says so — each as a `ClientboundSystemChatPacket`. With the game rule off, the kill packet still goes, carrying `CommonComponents.EMPTY`, and nothing is broadcast. Both packets use `ComponentSerialization.TRUSTED_STREAM_CODEC`, so what crosses is a compound with *translate* and *with*, the arguments compounds of their own. The server reads the message once, for its log: `PlayerList.broadcastSystemMessage` starts with `MinecraftServer.sendSystemMessage`, which logs `Component.getString`, and the walk visits the translatable contents and asks `Language.getInstance`. That is the ordinary path only. A victim on a team whose death-message visibility is not `Team.Visibility.ALWAYS` goes through `PlayerList.broadcastSystemToTeam` or `PlayerList.broadcastSystemToAllExceptTeam` instead, neither of which logs — and `Team.Visibility.NEVER` matches neither branch, so the message reaches nobody and is never read at all. On a server that is `Language.DEFAULT_INSTANCE`, loaded once from the *en_us.json* on the classpath, and nothing on the server ever calls `Language.inject`. A named mob's death is logged the same way from `LivingEntity.die`. The console is in English whatever the players speak. ### Worded on the client, on the first frame On the client, `ClientPacketListener.handlePlayerCombatKill` hops to the Render thread and constructs a `DeathScreen` with `ClientboundPlayerCombatKillPacket.message`, or respawns at once if `LocalPlayer.shouldShowDeathScreen` is off. Nothing has read the key. The next frame's `DeathScreen.visitText` hands the component to the text collector, and the walk reaches `TranslatableContents.decompose`, which asks the current `Language` for the template. That `Language` is a `ClientLanguage`, built by `LanguageManager.onResourceManagerReload` from `ClientLanguage.loadFrom` over a stack of two codes — *en_us* first, then the selected language if its pack declares it — reading *lang/code.json* from every namespace of every enabled pack, merged in stack order. A key missing from the chosen language falls to English; a key missing from both is shown as itself, by `Language.getOrDefault`, unless the component carried a fallback from `Component.translatableWithFallback`. `Language.loadFromJson` rewrites a *%d* or *%f* specifier to *%s* as it loads, which is why translators' number specifiers do not crash the decomposer. The template's arguments are visited in their turn — the killer's name, if it is *entity.minecraft.zombie*, goes back to the same `Language` — and the sentence exists, in this client's language, for the first time. The chat line took the same path through `ChatListener.handleSystemMessage`. ## What this page does not own Signing and chat delivery — the `PlayerChatMessage`, the session, the *Not Secure* tag — is [chat and signing](../networking/chat-and-signing.md). Everything after `Component.visit` — `StringDecomposer`, `FormattedCharSequence`, wrapping, reordering, glyphs — is [text and fonts](../client/text-and-fonts.md). What a `Codec` is, and how NBT and JSON differ under one, is [codecs, NBT and JSON](codecs-nbt-json.md#one-abstraction-and-the-ops-that-are-not-formats). ## Questions players ask **Why did my friend's death message say something different from mine?** Because neither client received a sentence. Both received *death.attack.arrow* and two name components, and each client's `Language` supplied its own template on the first frame that drew it. **Why does `/tellraw` with a selector name people when chat with an `@` does not?** Chat is a literal, always: nothing in the chat path calls `ComponentUtils.resolve`. A command's message argument parses `@` into `MessageArgument.Part`s and expands them behind `Permissions.COMMANDS_ENTITY_SELECTORS`; a `/tellraw` component is resolved whole by `ComponentArgument.getResolvedComponent`. **Why does a message sometimes show as a raw key like *death.attack.foo*?** The key is in neither the selected language nor *en_us*, and the component has no fallback, so `Language.getOrDefault` returned the key. A server or data pack that invents a key without shipping a resource pack for it shows the key on every client. **Why can a server show me a link but not open a file?** `ClickEvent.Action.OPEN_FILE` fails `ClickEvent.Action.filterForSerialization`, so it cannot be encoded into any packet, data pack or book by either side. Only client code that constructs the `ClickEvent.OpenFile` in memory — the screenshot notice, a debug dump's path — can present one. **What does an object component actually put in the text?** One U+FFFC placeholder whose style names a sprite font — an atlas sprite or a player head — so the font system draws the picture where a glyph would go. Anything that reads the text as a string sees *[sprite]* or *[name head]* instead. **Why does the server console show death messages in English?** The server's `Language` is `Language.DEFAULT_INSTANCE`, the bundled *en_us.json*; `Language.inject` is called only by the client's `LanguageManager`. ## Where to look `Component` · `MutableComponent` · `ComponentContents` · `PlainTextContents` · `TranslatableContents.decompose` · `KeybindContents` · `ScoreContents` · `SelectorContents` · `NbtContents` · `ObjectContents` · `Style.applyTo` · `TextColor` · `ClickEvent.Action.filterForSerialization` · `HoverEvent` · `ComponentSerialization.CODEC` · `ComponentSerialization.STREAM_CODEC` · `ByteBufCodecs.fromCodecWithRegistries` · `ComponentUtils.resolve` · `ResolutionContext` · `MessageArgument.Message.toComponent` · `CombatTracker.getDeathMessage` · `DamageSource.getLocalizedDeathMessage` · `ServerPlayer.die` · `ClientboundPlayerCombatKillPacket` · `ClientPacketListener.handlePlayerCombatKill` · `DeathScreen.visitText` · `Language.getOrDefault` · `ClientLanguage.loadFrom` · `LanguageManager.onResourceManagerReload` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The data-driven type pattern > Verified against **Minecraft 26.2** · Part II · A data pack's loot table says *"function": "minecraft:set_count"*, and the game turns that string into an object it never named in code. A data-pack author writes a chest loot table, gives one entry a function whose *function* field says *minecraft:set_count* and whose *count* is a range, and drops the file into *data/mypack/loot_table/chests/*. Nothing in the pack says `SetItemCountFunction`. Nothing in the jar says *mypack*. When the server reloads, `ReloadableServerRegistries.reload` scans the directory, hands the file to `LootTable.DIRECT_CODEC`, and somewhere inside that codec the string *minecraft:set_count* is looked up in `BuiltInRegistries.LOOT_FUNCTION_TYPE` — a registry that was filled by a static initialiser and frozen before any world existed — and the `MapCodec` it finds there reads the rest of the object. The result is a `SetItemCountFunction`, and the first time a player opens that chest, `SetItemCountFunction.run` calls `ItemStack.setCount` on every stack the pool emits. The same move is made in fifty-six places. That is why *type* is the most important key in a data pack: **every file that has one is a lookup in a registry data packs cannot add to**, so a pack can compose the game's behaviours endlessly and never add a new one. ## The cast | class | what it decides | thread | |---|---|---| | `MapCodec` (DataFixerUpper) | how to read one kind's fields out of a JSON object that also carries a type key; the registry element in the bare spelling | — | | `Registry` | `Registry.byNameCodec`: a string to an element, with the error *Unknown registry key* and the element's registration lifecycle attached | — | | `BuiltInRegistries` | the registries of kinds — filled at `BuiltInRegistries.bootStrap`, frozen, identical on client and server | main thread, at `Bootstrap` | | `ReloadableServerRegistries` | the three loot registries (`LootDataType.TABLE`, `LootDataType.MODIFIER`, `LootDataType.PREDICATE`) rebuilt on every reload | the background executor | | `RegistryOps` | the ops that let a codec resolve a `Holder` to another data-pack element while it decodes | wherever the codec runs | | `LootItemFunctions` | `LootItemFunctions.TYPED_CODEC`, the dispatch codec for one instance of the pattern; `LootItemFunctions.compose`, the list of functions folded into one | — | | `SetItemCountFunction` | the kind traced below: conditions, a `NumberProvider`, an *add* flag | Server, when the loot rolls | | `Holder` | how everything else refers to the loaded element — by key, bound later | — | ## The idea, stated once A codec built by `Codec.dispatch` reads one field of a JSON object — *type* unless the caller names another — decodes it with a codec for the *kind*, and asks that kind for a `MapCodec` to read the remaining fields. The class that does it is DataFixerUpper's `KeyDispatchCodec` — a `Codec` and a `DynamicOps` being the two halves of every read in this part ([codecs, NBT and JSON](codecs-nbt-json.md#one-abstraction-and-the-ops-that-are-not-formats)) — which is why the `MapCodec` a kind supplies is a *map* codec: it reads a set of fields from the same object the type key came from, so the file looks flat. When the kind codec is `Registry.byNameCodec` over a registry in `BuiltInRegistries`, the set of kinds is whatever the jar registered at `Bootstrap`, and a pack can reach every one of them by name and none it did not ship. The element that comes out is then either **registered** — a `RegistryDataLoader` registry such as `Registries.STRUCTURE`, or one of the three `ReloadableServerRegistries` registries — or **inline**, a value inside a larger element that has no id of its own, such as the `PlacementModifier` list in a `PlacedFeature`. A registered element is referred to everywhere else by `Holder`: a `RegistryFileCodec` reads either an id or the inline object, and the identifiers page explains how the reference is handed out before the entry exists ([identifiers and registries](identifiers-and-registries.md)). ```mermaid flowchart LR F["a data-pack file with a type key"] --> D["Codec.dispatch over Registry.byNameCodec"] D --> K["a built-in registry of kinds, frozen at Bootstrap"] K --> M["that kind's MapCodec reads the remaining fields"] M --> O["an object of a class the file never named"] O --> R["registered by RegistryDataLoader or ReloadableServerRegistries, or inline in a larger element"] R --> H["referred to by Holder from other files and from the wire"] ``` The pattern has two spellings, and they differ only in what the registry holds. In the **bare** spelling the element *is* the `MapCodec`: `BuiltInRegistries.LOOT_FUNCTION_TYPE` is a `Registry` of `MapCodec`, `LootItemFunctions.bootstrap` registers `SetItemCountFunction.MAP_CODEC` under *set_count*, and `LootItemFunction.codec` is how a live object names its own kind for encoding. In the **type-object** spelling the element is a small interface or record that wraps the codec: `PlacementModifierType` is an interface with one method, `PlacementModifierType.codec`, its constants such as `PlacementModifierType.COUNT` are registered into `BuiltInRegistries.PLACEMENT_MODIFIER_TYPE`, and `PlacementModifier.CODEC` dispatches on `PlacementModifier.type`. The type object exists so that a kind can carry something beside its codec — `RecipeSerializer`, `ConsumeEffect.Type` and `RecipeDisplay.Type` are records of a `MapCodec` and a `StreamCodec`, one for the file and one for the wire. There is a **third** spelling with two members, in which the type object is the behaviour itself: a `Feature` is registered into `BuiltInRegistries.FEATURE`, `Feature.place` is what it does, and what the file supplies is only a *config* — `Feature.configuredCodec` wraps the feature's configuration codec under that key, and `ConfiguredFeature.DIRECT_CODEC` dispatches to it. `WorldCarver` and `ConfiguredWorldCarver.DIRECT_CODEC` are the same shape. Seven of the instances accept a bare value in place of the object: `IntProviders.CODEC`, `FloatProviders.CODEC` and `NumberProviders.CODEC` read a plain number as a constant, `DensityFunctions.DIRECT_CODEC` reads a plain number as `DensityFunctions.Constant`, a height provider reads a bare anchor, and `NbtProviders.CODEC` and `ScoreboardNameProviders.CODEC` read a bare string as the context form. Three accept a bare **list**: `LootItemFunctions.ROOT_CODEC` tries `LootItemFunctions.TYPED_CODEC` and falls back to `SequenceFunction.INLINE_CODEC`, so a JSON array where one function was expected is a sequence of them; `LootItemCondition` does the same through `AllOfCondition.INLINE_CODEC`, and `SlotSources` through `GroupSlotSource.INLINE_CODEC`. > **For a 1.21-era reader.** The loot package has no type-object class any > more: there is no *LootItemFunctionType* record wrapping a `MapCodec`, > and `BuiltInRegistries.LOOT_FUNCTION_TYPE`, > `BuiltInRegistries.LOOT_CONDITION_TYPE` and the provider registries hold > the `MapCodec` itself. The worldgen registries > kept their type objects. Both spellings dispatch identically. ## Fifty-six of them **Fifty-six** — registries in `BuiltInRegistries` that a codec dispatches on from the **value** of a field, counted at the dispatch sites: thirty-one bare, twenty-three type-object, two where the type is the behaviour. The dispatch key is *type* unless the row says otherwise. The criterion is the value *in the data this book is about*, not `Registry.byNameCodec` itself: four more registries dispatch through it and are not here. `BuiltInRegistries.GAME_RULE` and `BuiltInRegistries.STAT_TYPE` spell the registry name as the *key* of a map rather than the value of a field. `BuiltInRegistries.ENVIRONMENT_ATTRIBUTE` and `BuiltInRegistries.DATA_COMPONENT_TYPE` do both: they are keys everywhere a data pack meets them, and each also backs exactly one field dispatch — `EnvironmentAttributeCheck.MAP_CODEC` on *attribute*, and one client item-model property on *component*. All four are among the exceptions below. ### The bare spelling: the registry holds a `MapCodec` | registry | element | key | where the elements live | taught in | |---|---|---|---|---| | `BuiltInRegistries.LOOT_POOL_ENTRY_TYPE` | `LootPoolEntryContainer` | | inline in loot tables | [loot tables](../items/loot-tables.md) | | `BuiltInRegistries.LOOT_FUNCTION_TYPE` | `LootItemFunction` | *function* | `Registries.ITEM_MODIFIER` (reloadable), and inline in tables, pools and entries | [loot tables](../items/loot-tables.md) | | `BuiltInRegistries.LOOT_CONDITION_TYPE` | `LootItemCondition` | *condition* | `Registries.PREDICATE` (reloadable), and inline | [loot tables](../items/loot-tables.md) | | `BuiltInRegistries.LOOT_NUMBER_PROVIDER_TYPE` | `NumberProvider` | | inline; a bare number is a constant | [loot tables](../items/loot-tables.md) | | `BuiltInRegistries.LOOT_NBT_PROVIDER_TYPE` | `NbtProvider` | | inline in loot functions | [loot tables](../items/loot-tables.md) | | `BuiltInRegistries.LOOT_SCORE_PROVIDER_TYPE` | `ScoreboardNameProvider` | | inline in loot conditions | [loot tables](../items/loot-tables.md) | | `BuiltInRegistries.SLOT_SOURCE_TYPE` | `SlotSource` | | inline in `SlotLoot` entries and container-modifying functions | [loot tables](../items/loot-tables.md) | | `BuiltInRegistries.INT_PROVIDER_TYPE` | `IntProvider` | | inline in feature configs and elsewhere; a bare integer is a constant | [features and placement](../worldgen/features-and-placement.md) | | `BuiltInRegistries.FLOAT_PROVIDER_TYPE` | `FloatProvider` | | inline; a bare float is a constant | [features and placement](../worldgen/features-and-placement.md) | | `BuiltInRegistries.DENSITY_FUNCTION_TYPE` | `DensityFunction` | | `Registries.DENSITY_FUNCTION`, and inline; a bare number is a constant | [density functions](../worldgen/density-functions.md) | | `BuiltInRegistries.MATERIAL_CONDITION` | `SurfaceRules.ConditionSource` | | inline in `Registries.NOISE_SETTINGS` | [terrain](../worldgen/terrain.md) | | `BuiltInRegistries.MATERIAL_RULE` | `SurfaceRules.RuleSource` | | inline in `Registries.NOISE_SETTINGS` | [terrain](../worldgen/terrain.md) | | `BuiltInRegistries.BIOME_SOURCE` | `BiomeSource` | | inline in `Registries.LEVEL_STEM` | [biomes](../worldgen/biomes.md) | | `BuiltInRegistries.CHUNK_GENERATOR` | `ChunkGenerator` | | inline in `Registries.LEVEL_STEM` | [terrain](../worldgen/terrain.md) | | `BuiltInRegistries.STRUCTURE_PROCESSOR` | `StructureProcessor` | *processor_type* | `Registries.PROCESSOR_LIST` | [jigsaw and templates](../worldgen/jigsaw-and-templates.md) | | `BuiltInRegistries.POOL_ALIAS_BINDING_TYPE` | `PoolAliasBinding` | | inline in jigsaw structures | [jigsaw and templates](../worldgen/jigsaw-and-templates.md) | | `BuiltInRegistries.ENCHANTMENT_LEVEL_BASED_VALUE_TYPE` | `LevelBasedValue` | | inline in `Registries.ENCHANTMENT` | [enchantments](../items/enchantments.md) | | `BuiltInRegistries.ENCHANTMENT_ENTITY_EFFECT_TYPE` | `EnchantmentEntityEffect` | | inline in `Registries.ENCHANTMENT` | [enchantments](../items/enchantments.md) | | `BuiltInRegistries.ENCHANTMENT_LOCATION_BASED_EFFECT_TYPE` | `EnchantmentLocationBasedEffect` | | inline in `Registries.ENCHANTMENT` | [enchantments](../items/enchantments.md) | | `BuiltInRegistries.ENCHANTMENT_VALUE_EFFECT_TYPE` | `EnchantmentValueEffect` | | inline in `Registries.ENCHANTMENT` | [enchantments](../items/enchantments.md) | | `BuiltInRegistries.ENCHANTMENT_PROVIDER_TYPE` | `EnchantmentProvider` | | `Registries.ENCHANTMENT_PROVIDER` | [enchantments](../items/enchantments.md) | | `BuiltInRegistries.SPAWN_CONDITION_TYPE` | `SpawnCondition` | | inline in entity variants, through `SpawnPrioritySelectors.CODEC` | [entity lifecycle](../entities/entity-lifecycle.md) | | `BuiltInRegistries.TEST_ENVIRONMENT_DEFINITION_TYPE` | `TestEnvironmentDefinition` | | `Registries.TEST_ENVIRONMENT` | [game tests](../commands/game-tests.md) | | `BuiltInRegistries.TEST_INSTANCE_TYPE` | `GameTestInstance` | | `Registries.TEST_INSTANCE` | [game tests](../commands/game-tests.md) | | `BuiltInRegistries.DIALOG_TYPE` | `Dialog` | | `Registries.DIALOG` | [dialogs](../commands/dialogs.md) | | `BuiltInRegistries.DIALOG_ACTION_TYPE` | `Action` | | inline in dialogs | [dialogs](../commands/dialogs.md) | | `BuiltInRegistries.DIALOG_BODY_TYPE` | `DialogBody` | | inline in dialogs | [dialogs](../commands/dialogs.md) | | `BuiltInRegistries.INPUT_CONTROL_TYPE` | `InputControl` | | inline in dialogs, as a `MapCodec` (`Codec.dispatchMap`) | [dialogs](../commands/dialogs.md) | | `BuiltInRegistries.PERMISSION_TYPE` | `Permission` | | inline in a permission check | [Brigadier and commands](../commands/brigadier-and-commands.md) | | `BuiltInRegistries.PERMISSION_CHECK_TYPE` | `PermissionCheck` | | only written, by `ArgumentUtils` into the command-tree report | [Brigadier and commands](../commands/brigadier-and-commands.md) | | `BuiltInRegistries.BLOCK_TYPE` | `Block` | | nothing loads it: every block is Java, and `BlockTypes.CODEC` is read by no one and written only by `BlockListReport` | [blocks and states](../blocks/blocks-and-states.md) | ### The type-object spelling: the registry holds a type that carries a `MapCodec` | registry | type object | element | key | where the elements live | taught in | |---|---|---|---|---|---| | `BuiltInRegistries.PLACEMENT_MODIFIER_TYPE` | `PlacementModifierType` | `PlacementModifier` | | inline in `Registries.PLACED_FEATURE` | [features and placement](../worldgen/features-and-placement.md) | | `BuiltInRegistries.HEIGHT_PROVIDER_TYPE` | `HeightProviderType` | `HeightProvider` | | inline in placements; a bare anchor is a constant | [features and placement](../worldgen/features-and-placement.md) | | `BuiltInRegistries.BLOCK_PREDICATE_TYPE` | `BlockPredicateType` | `BlockPredicate` | | inline in features and placements | [features and placement](../worldgen/features-and-placement.md) | | `BuiltInRegistries.BLOCKSTATE_PROVIDER_TYPE` | `BlockStateProviderType` | `BlockStateProvider` | | inline in feature configs | [features and placement](../worldgen/features-and-placement.md) | | `BuiltInRegistries.TRUNK_PLACER_TYPE` | `TrunkPlacerType` | `TrunkPlacer` | | inline in tree configs | [trees](../worldgen/trees.md) | | `BuiltInRegistries.FOLIAGE_PLACER_TYPE` | `FoliagePlacerType` | `FoliagePlacer` | | inline in tree configs | [trees](../worldgen/trees.md) | | `BuiltInRegistries.ROOT_PLACER_TYPE` | `RootPlacerType` | `RootPlacer` | | inline in tree configs | [trees](../worldgen/trees.md) | | `BuiltInRegistries.TREE_DECORATOR_TYPE` | `TreeDecoratorType` | `TreeDecorator` | | inline in tree configs | [trees](../worldgen/trees.md) | | `BuiltInRegistries.FEATURE_SIZE_TYPE` | `FeatureSizeType` | `FeatureSize` | | inline in tree configs | [trees](../worldgen/trees.md) | | `BuiltInRegistries.STRUCTURE_TYPE` | `StructureType` | `Structure` | | `Registries.STRUCTURE` | [structure placement](../worldgen/structure-placement.md) | | `BuiltInRegistries.STRUCTURE_PLACEMENT` | `StructurePlacementType` | `StructurePlacement` | | inline in `Registries.STRUCTURE_SET` | [structure placement](../worldgen/structure-placement.md) | | `BuiltInRegistries.STRUCTURE_POOL_ELEMENT` | `StructurePoolElementType` | `StructurePoolElement` | *element_type* | inline in `Registries.TEMPLATE_POOL` | [jigsaw and templates](../worldgen/jigsaw-and-templates.md) | | `BuiltInRegistries.RULE_TEST` | `RuleTestType` | `RuleTest` | *predicate_type* | inline in processor lists | [jigsaw and templates](../worldgen/jigsaw-and-templates.md) | | `BuiltInRegistries.POS_RULE_TEST` | `PosRuleTestType` | `PosRuleTest` | *predicate_type* | inline in processor lists | [jigsaw and templates](../worldgen/jigsaw-and-templates.md) | | `BuiltInRegistries.RULE_BLOCK_ENTITY_MODIFIER` | `RuleBlockEntityModifierType` | `RuleBlockEntityModifier` | | inline in processor rules | [jigsaw and templates](../worldgen/jigsaw-and-templates.md) | | `BuiltInRegistries.RECIPE_SERIALIZER` | `RecipeSerializer` | `Recipe` | | the `RecipeMap` that `RecipeManager` builds from *data/<ns>/recipe/* — a reload listener, not a registry | [recipes](../items/recipes.md) | | `BuiltInRegistries.RECIPE_DISPLAY` | `RecipeDisplay.Type` | `RecipeDisplay` | | inline, mostly on the wire to the recipe book | [recipes](../items/recipes.md) | | `BuiltInRegistries.SLOT_DISPLAY` | `SlotDisplay.Type` | `SlotDisplay` | | inline in recipe displays | [recipes](../items/recipes.md) | | `BuiltInRegistries.CONSUME_EFFECT_TYPE` | `ConsumeEffect.Type` | `ConsumeEffect` | | inline in the `Consumable` and `DeathProtection` components | [data components](data-components.md) | | `BuiltInRegistries.TRIGGER_TYPES` | `CriterionTrigger` | `Criterion` | *trigger*, with the fields under *conditions* (`ExtraCodecs.dispatchOptionalValue`) | advancements, loaded by `ServerAdvancementManager` — a reload listener, not a registry | [advancements](../commands/advancements.md) | | `BuiltInRegistries.PARTICLE_TYPE` | `ParticleType` | `ParticleOptions` | | inline in biome ambient particles (`AmbientParticle`) and area effect clouds | [particles](../rendering/particles.md) | | `BuiltInRegistries.NUMBER_FORMAT_TYPE` | `NumberFormatType` | `NumberFormat` | | inline in `Objective` and `Score` — save data and commands, no pack | [scoreboard and data](../commands/scoreboard-and-data.md) | | `BuiltInRegistries.POSITION_SOURCE_TYPE` | `PositionSourceType` | `PositionSource` | | `VibrationParticleOption`, and one enchantment effect (`SpawnParticlesEffect`) — save data and the wire, no pack | [game events and vibrations](../world/game-events-and-vibrations.md) | The first nine rows are the sub-objects of a configured or placed feature, and the trace on [features and placement](../worldgen/features-and-placement.md) walks a tree through all of them. ### The type is the behaviour: the file supplies only a config | registry | type object | element | where the elements live | taught in | |---|---|---|---|---| | `BuiltInRegistries.FEATURE` | `Feature` | `ConfiguredFeature` | `Registries.CONFIGURED_FEATURE` | [features and placement](../worldgen/features-and-placement.md) | | `BuiltInRegistries.CARVER` | `WorldCarver` | `ConfiguredWorldCarver` | `Registries.CONFIGURED_CARVER` | [terrain](../worldgen/terrain.md) | Three of the registered destinations are not in `RegistryDataLoader` at all. `Registries.LOOT_TABLE`, `Registries.ITEM_MODIFIER` and `Registries.PREDICATE` are built by `ReloadableServerRegistries`, which `DatapackStructureReport` calls stable dynamic registries, while `Registries.RECIPE` and `Registries.ADVANCEMENT` it calls pseudo-registries: keys exist for them, directories are named after them, and no `Registry` is ever constructed. The elements that reach the client are the ones in `RegistryDataLoader.SYNCHRONIZED_REGISTRIES`, re-encoded with the same direct codec, which is why `BuiltInRegistries` must be identical on both sides: the client runs the same dispatch on the same kinds ([protocol phases](../networking/protocol-phases.md)). ## One instance traced: *set_count* ```mermaid sequenceDiagram participant RSReg as ReloadableServerRegistries participant LT as LootTable participant LIF as LootItemFunctions participant BIR as BuiltInRegistries participant SICF as SetItemCountFunction participant CBE as ChestBlockEntity Note over RSReg: a reload, on the background executor RSReg->>RSReg: reload builds a RegistryOps over JsonOps, then scanDirectory per LootDataType RSReg->>LT: DIRECT_CODEC parses data/mypack/loot_table/chests/mine.json LT->>LIF: a functions entry, ROOT_CODEC then TYPED_CODEC reads the function key LIF->>BIR: LOOT_FUNCTION_TYPE.byNameCodec looks up minecraft:set_count BIR-->>LIF: SetItemCountFunction.MAP_CODEC, out of a frozen registry LIF->>SICF: MAP_CODEC reads conditions, count and add, the object exists SICF-->>LT: compose folds the list into one BiFunction LT-->>RSReg: registered in a fresh MappedRegistry, validated, the RELOADABLE layer replaced Note over CBE: a later tick, on the server thread, a player opens the chest CBE->>RSReg: unpackLootTable asks reloadableRegistries for the key RSReg-->>CBE: the LootTable, or LootTable.EMPTY for an unknown key CBE->>LT: fill, then getRandomItems with a CHEST context LT->>SICF: decorate wraps the output, every emitted stack passes through apply SICF->>SICF: the conditions pass, run calls ItemStack.setCount with count.getInt SICF-->>CBE: the stack lands in a slot ``` **The reload half.** `MinecraftServer.reloadResources` — and `WorldLoader.load` on first start — calls `ReloadableServerResources.loadResources`, whose first act is `ReloadableServerRegistries.reload` on the background executor. It builds a `RegistryOps` over `JsonOps.INSTANCE` from a `HolderLookup.Provider` that already carries the updated tags, so a loot condition can name an item tag while it decodes ([tags](tags.md#the-four-moments-tags-are-loaded)). For each of the three `LootDataType`s it creates a new `MappedRegistry` and calls `SimpleJsonResourceReloadListener.scanDirectory` — the every-JSON-file-in-a-directory listener shape ([the resource system](resource-system.md#prepare-every-listener-at-once)) — whose lister is `FileToIdConverter.registry` over `Registries.elementsDirPath` — the directory *is* the registry's path, *loot_table* — and which parses every file with the type's codec. A file that fails to parse is logged and skipped, and a duplicate id is an error, so one bad table costs one table. Inside `LootTable.DIRECT_CODEC` the *functions* list is `LootItemFunctions.ROOT_CODEC`; the entry in question is an object, so `LootItemFunctions.TYPED_CODEC` runs: `Registry.byNameCodec` on `BuiltInRegistries.LOOT_FUNCTION_TYPE` reads *minecraft:set_count*, finds `SetItemCountFunction.MAP_CODEC`, and that codec reads *conditions* (the `LootItemConditionalFunction.commonFields` every conditional function shares, itself a list dispatched on `BuiltInRegistries.LOOT_CONDITION_TYPE`), *count* through `NumberProviders.CODEC` — a third dispatch, on `BuiltInRegistries.LOOT_NUMBER_PROVIDER_TYPE` — and the optional *add*. Three built-in registries were consulted to build one function, and the file named none of them. A misspelt kind fails at the first of them with an *Unknown registry key* error that names the registry of kinds, and the whole file is dropped. The `LootTable` constructor then calls `LootItemFunctions.compose`, which folds each list of functions into a single function; a list of one is the function itself. After all three registries are built, `ReloadableServerRegistries.createUpdatedRegistries` replaces the `RegistryLayer.RELOADABLE` layer through `LayeredRegistryAccess.replaceFrom` and `ReloadableServerRegistries.validateLootRegistries` runs `LootDataType.runValidation` over every element: `SetItemCountFunction.validate` checks its count provider's references against the finished lookup. Validation **warns** — problems are logged, the element stays registered. Every element in these three registries is `Lifecycle.experimental`. **The run half.** `RandomizableContainerBlockEntity.getItem` — like the other container methods on that class — calls `RandomizableContainer.unpackLootTable`, which asks `MinecraftServer.reloadableRegistries` for the table by `ResourceKey`; an unknown key is `LootTable.EMPTY`, never an exception. `LootTable.fill` rolls `LootTable.getRandomItems` with a `LootContextParamSets.CHEST` context and `LootTable.shuffleAndSplitItems` spreads the result over the slots. On the way out, each level wraps the consumer: `LootTable.getRandomItemsRaw` decorates the output with the table's composite function, `LootPool.addRandomItems` with the pool's, and `LootPoolSingletonContainer.EntryBase` with the entry's, each through `LootItemFunction.decorate`. `LootItem.createItemStack` makes a stack of one, and it passes through `LootItemConditionalFunction.apply`, which tests the conditions and, if they pass, calls `SetItemCountFunction.run` — `ItemStack.setCount` with `NumberProvider.getInt`, added to the current count if *add* was set. The object the pack described by a string is now a method call on a stack in a chest. ## What does not follow the pattern Not every registry in `BuiltInRegistries` whose name ends in *type* is a registry of kinds, and most of the ones that are not fall into three groups. A few fall outside them altogether — `BuiltInRegistries.TICKET_TYPE`, `BuiltInRegistries.MAP_DECORATION_TYPE`, `BuiltInRegistries.POINT_OF_INTEREST_TYPE` and `BuiltInRegistries.VILLAGER_TYPE` are registries of ordinary things whose names happen to end in *type*, dispatching nothing, and `BuiltInRegistries.ATTRIBUTE_TYPE` has a `Registry.byNameCodec` — `AttributeTypes.CODEC` — that nothing in the tree reads; the attribute name a file actually uses as a key belongs to `BuiltInRegistries.ENVIRONMENT_ATTRIBUTE`. **A key, not a kind.** `BuiltInRegistries.DATA_COMPONENT_TYPE`, `BuiltInRegistries.ENCHANTMENT_EFFECT_COMPONENT_TYPE`, `BuiltInRegistries.GAME_RULE` and `BuiltInRegistries.ENVIRONMENT_ATTRIBUTE` each hold objects that carry a codec for their *value*, and a file uses them as JSON **keys**: `GameRuleMap.CODEC` and `DataComponentPredicate.CODEC` are `Codec.dispatchedMap`, a map whose key codec is `Registry.byNameCodec` and whose value codec depends on the key. There is no *type* field because the name of the field is the type. `BuiltInRegistries.ENTITY_SUB_PREDICATE_TYPE` is the same shape and holds a plain `Codec` rather than a `MapCodec`, and `EntityPredicate` reads it as a dispatched map too. `BuiltInRegistries.STAT_TYPE` is a key whose value codec is derived from `StatType.getRegistry` rather than stored, which is what `PlayerPredicate.StatMatcher` builds on. `BuiltInRegistries.MEMORY_MODULE_TYPE` is a key in a brain's saved memories, `MemoryMap.CODEC`, the same way. **A type object with no codec.** `BuiltInRegistries.RECIPE_TYPE` is the one a modder reaches for and the wrong one: `RecipeType.CRAFTING` groups recipes for lookup, and the kind a recipe file names — the field `Recipe.CODEC` dispatches on through `Recipe.getSerializer` — is a `RecipeSerializer`, in `BuiltInRegistries.RECIPE_SERIALIZER`. `BuiltInRegistries.STRUCTURE_PIECE` holds `StructurePieceType`, a loader from NBT with a `StructurePieceSerializationContext`, for the pieces of a started structure saved in the chunk — save data, not a pack. `BuiltInRegistries.ENTITY_TYPE`, `BuiltInRegistries.BLOCK_ENTITY_TYPE`, `BuiltInRegistries.MENU`, `BuiltInRegistries.SENSOR_TYPE` and `BuiltInRegistries.COMMAND_ARGUMENT_TYPE` are registries of type objects that no codec dispatches on: they are looked up by name and construct or describe things in Java. **A registry of kinds with nothing to load.** `BuiltInRegistries.BLOCK_TYPE` is a complete instance of the bare spelling — `BlockTypes.CODEC` dispatches on it through `Block.codec` — that no data pack and no loader ever reads. Its one caller is the data generator's `BlockListReport`, which encodes every block with it. It is the pattern applied for the sake of the report. ## Questions players ask **Can a data pack add a new loot function, placement modifier or dialog kind?** No. Every kind is an entry in a `BuiltInRegistries` registry, and those are frozen at `Bootstrap`. A pack composes kinds; only the jar adds one. **Why does one typo break the whole file and not the whole pack?** `SimpleJsonResourceReloadListener.scanDirectory` parses each file on its own and logs the ones that fail, so a loot table that names *minecraft:set_cuont* is simply missing, and the chest that names the table gets `LootTable.EMPTY`. `RegistryDataLoader` is stricter: it collects every error by key and fails the whole load, which is why a broken biome stops the world from opening and a broken loot table does not. **Why is the same kind name accepted in a pool, an entry and a table?** Because the field is decoded by the same `LootItemFunctions.ROOT_CODEC` in all three places, and the three composite functions wrap the output consumer one inside the other. A function on the table runs last. **Why do worldgen files sometimes take a number where an object was expected?** `Codec.either` in front of the dispatch: a bare number is a constant for int, float and number providers and for density functions. ## Where to look `Codec.dispatch` · `KeyDispatchCodec` · `Registry.byNameCodec` · `BuiltInRegistries` · `LootItemFunctions.TYPED_CODEC` · `LootItemFunctions.bootstrap` · `SetItemCountFunction.MAP_CODEC` · `PlacementModifier.CODEC` · `PlacementModifierType` · `ConfiguredFeature.DIRECT_CODEC` · `Feature.configuredCodec` · `LootDataType` · `ReloadableServerRegistries.reload` · `SimpleJsonResourceReloadListener.scanDirectory` · `RegistryFileCodec` · `RegistryDataLoader.WORLDGEN_REGISTRIES` · `LootTable.fill` · `LootItemFunction.decorate` · `SetItemCountFunction.run` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # III · The server > Verified against **Minecraft 26.2** · Part III · The program that owns the world: one thread, one loop, twenty times a second, from the command line that starts it to the exception that ends it. Everything a player thinks of as *the world* — the blocks, the mobs, the weather, the hunger bar — is state owned by one object on one thread, and this part is that thread doing a full lap. Part I named the Server thread; this is the first place you watch it work. A player recognises this part by its clock: nothing in the world happens continuously. A furnace advances in steps of a twentieth of a second, a hopper moves one item per eight of them, and when the console prints *Can't keep up! Is the server overloaded?* some of those steps did not happen at all and never will. ## The shape of the part Part III is a line into a loop and out again. Two pages are the loop itself — the server tick and the one step of it that is a whole lecture — and the other three are the beginning, the population and the end. ```mermaid flowchart LR Start["Starting a server: java -jar to the word Done"] Tick["The server tick: 50 ms on the Server thread"] Level["The level tick: one dimension advances"] Players["Players and sessions: who is in the loop"] Death["How a server dies: three endings"] Start -- "the Server thread is spun, the levels are built" --> Tick Tick -- "tickChildren calls ServerLevel.tick, overworld first" --> Level Level -- "the packets the tick decided to send" --> Tick Tick -- "the connection phase, after the levels" --> Players Players -- "a join, a respawn, a disconnect" --> Tick Tick -- "the loop's finally, which two of the three endings reach" --> Death ``` ## Before you start [Anatomy](../anatomy/anatomy.md), and specifically its *two loops* figure. This part assumes you know that the Server thread is an event loop as well as a game loop, that a packet is decoded on a Netty thread and handled on this one, and that the client's frame loop is a separate clock. Part II's [codecs](../foundations/codecs-nbt-json.md) and [registries](../foundations/identifiers-and-registries.md) are assumed wherever something is written to disk or sent on the wire, and [the resource system](../foundations/resource-system.md) is assumed once, by *starting a server*, which runs its staged load for server data. Two pages from a later part are assumed, and they are cut two different ways. [Tickets and loading](../world/tickets-and-loading.md) owns what *entity-ticking* and *block-ticking* range mean, and [the level tick](server-level-tick.md) defines both in one sentence before it uses them, so that one keeps until Part IV. [Environment attributes and timelines](../world/environment-attributes-and-timelines.md) does not: it owns the per-position system whose cache `ServerLevel.tick` throws away before it touches anything else — before the border, before the weather — and out of which `Level.updateSkyBrightness` later reads the sky light rather than deriving it from the time of day. That is the one page worth watching out of order before this part; everything else in Part IV can wait. ## Watch in this order 1. [The server tick](server-tick.md) — one 50 ms lap: the deadline that moves before the work starts, every packet since last time handled at once, every dimension advanced, and the two writes per client the tick leaves behind. *"Can't keep up!"* is not a warning that the server is about to skip ticks — it is the skip. 2. [The level tick](server-level-tick.md) — one step of that lap, which is the whole world changing: weather, scheduled ticks, mob spawns, random ticks, every entity, every block entity. The block changes go out *before* the entities move, so a change a piston makes reaches you a tick later than one a player's command makes — with falling sand the exception that sends its own packet, and a console command the one that is as late as the piston. 3. [Players and sessions](players-and-sessions.md) — a join from the end of the login handshake to a player standing in a world with chunks on the way, and then the four ways that session changes: death, a dimension, a disconnect, and a debug command that sends the player back to the configuration phase. Dying replaces your player object; the Nether does not. 4. [Starting a server](starting-a-server.md) — *java -jar server.jar* to the word *Done*: the EULA, the lock on `session.lock`, the packs and registries, the thread, the levels. The step that loads the world's chunks loads none of them on an ordinary world. 5. [How a server dies](how-a-server-dies.md) — three endings compared: `/stop`, a crash in the tick loop, and the watchdog. A crash saves your world. The watchdog does not. ## Reference this part uses [Threads](../../reference/threads.md) — the Server thread, the worker pool and the dedicated server's five side threads, with who makes each. [Game rules](../../reference/gamerules.md) — the rules the tick consults, which is most of them. [Packets](../../reference/packets.md) — everything the tick sends. [Diagram lanes](../../reference/lanes.md). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The server tick > Verified against **Minecraft 26.2** · Part III · One 50 ms tick on the Server thread, from the moment the clock says *now* to the moment the thread parks again. Twenty times a second the Server thread wakes, hands every packet that arrived since it last looked to the code that answers it, advances every dimension by one step, pushes what each player needs to know onto the wire, and then spends whatever is left of its 50 ms on work other threads posted back before parking until the next beat. Everything a player calls *the world* — blocks, mobs, weather, the contents of a chest — is computed in plenty of places and **committed** in this one, on this one thread. When a lap runs long the console eventually prints *Can't keep up! Is the server overloaded?*, and that line is not a warning that the server is about to start missing ticks. `MinecraftServer.runServer` logs it and advances the deadline past the whole backlog inside the same *if*, so the message **is** the skip; because the log and the skip share one condition, a server that complained recently keeps running behind rather than skipping at all. And the ticks it does skip are simply gone — nothing runs them later, so game time ends up that many ticks younger than the wall clock and stays there. ## The cast | class | what it decides | thread | |---|---|---| | `MinecraftServer` | the loop, the thread and the event loop in one object: the deadline, the budget, and the order everything happens in | Server | | `ServerTickRateManager` | how long this tick is, whether game elements advance at all, and whether the loop is sprinting | Server | | `TickTask` | one queued runnable plus the `MinecraftServer.tickCount` it was submitted on — the age the budget reads | any thread submits, Server runs | | `PacketProcessor` | which serverbound packets are waiting, and that all of them are handled at the top of a tick | Netty fills it, Server empties it | | `ServerLevel` | one dimension's step — [a lecture of its own](server-level-tick.md) | Server | | `ServerConnectionListener` | which connections are still alive, and when each one's player takes its own tick | Server | | `ServerCommonPacketListenerImpl` | whether a packet handed to a player is written, or written *and* flushed | Server, mostly | | `ServerChunkCache.MainThreadExecutor` | what the leftover milliseconds buy, one queue per level | Server | ## One lap, ending on the wire ```mermaid sequenceDiagram participant PP as PacketProcessor participant MS as MinecraftServer participant SL as ServerLevel participant Conn as Connection participant PCS as PlayerChunkSender participant Wire as the network Note over MS: runServer moves nextTickTimeNanos forward, past the backlog if it warns MS->>PP: processQueuedPackets PP->>MS: every serverbound packet Netty queued since the last drain, handled now Note over MS,Conn: tickChildren begins, suspendFlushing on every player connection MS->>SL: tick(haveTime), each dimension in turn, overworld first SL->>Conn: send, written to the channel and not flushed MS->>Conn: ServerConnectionListener.tick walks the list, Connection.tick each Conn->>Conn: flushQueue, then ServerGamePacketListenerImpl.tick, the player's own tick Conn->>Wire: flush one, the levels and the player tick MS->>PCS: sendNextChunks, per player PCS->>Conn: the chunk batch, written and not flushed MS->>Conn: resumeFlushing Conn->>Wire: flush two, the chunks and everything sent after the connection phase Note over MS: waitUntilNextTick runs all tasks, then managedBlock parks until the deadline Note over PP,Wire: the next tick begins ``` ### The deadline moves before the work starts `MinecraftServer.runServer` re-reads this tick's length every iteration from `TickRateManager.nanosecondsPerTick` and adds it to `MinecraftServer.nextTickTimeNanos` *before* calling anything. Being behind means the wall clock has already passed that field. The overload branch fires when it has passed by more than `MinecraftServer.OVERLOADED_THRESHOLD_NANOS` (one second) plus `MinecraftServer.OVERLOADED_TICKS_THRESHOLD` ticks' worth — two seconds at the default rate — **and** the last warning is at least `MinecraftServer.OVERLOADED_WARNING_INTERVAL_NANOS` (ten seconds) plus `MinecraftServer.OVERLOADED_TICKS_WARNING_INTERVAL` ticks' worth behind it, fifteen seconds at the default rate. Both effects — the log line, and `MinecraftServer.nextTickTimeNanos` jumping over the missed ticks — are statements of that one branch. Lateness therefore has to *accumulate* before either happens: a server running every lap ten percent long says nothing while the shortfall piles up, then warns and drops the pile in one step. And because `MinecraftServer.lastOverloadWarningNanos` is set to the new deadline, the second gate is fifteen seconds of the server's *own* scheduled time — which on an overloaded server is rather more than fifteen seconds of yours. In between, the backlog is real and every tick of it is run. Sprinting takes the other arm of the same *if*: when the server is not paused, `ServerTickRateManager.isSprinting` is true and `ServerTickRateManager.checkShouldSprintThisTick` consents, this tick is declared **zero nanoseconds long** and `MinecraftServer.nextTickTimeNanos` is set to *now*. `TickRateManager.nanosecondsPerTick` still reads 50 ms throughout — a sprint changes the length of *this tick*, gives the overload check nothing to measure, and sends the loop straight back for another. Sprinting also unfreezes the game — `ServerTickRateManager.requestGameToSprint` remembers the old state in `ServerTickRateManager.previousIsFrozen` — and restores it when `ServerTickRateManager.finishTickSprint` reports the measured rate. ### Every packet since last time, in one drain `MinecraftServer.processPacketsAndTick` opens the Tracy frame, then calls `PacketProcessor.processQueuedPackets` — before `MinecraftServer.tickServer`, so the frame a profiler shows includes the packets. Each entry in that `ConcurrentLinkedQueue` is a `PacketProcessor.ListenerAndPacket`: a Netty thread decoded the packet, the handler called `PacketUtils.ensureRunningOnSameThread`, and that queued the pair here and aborted the Netty-side call by throwing `RunningOnDifferentThreadException` ([anatomy](../anatomy/anatomy.md) has the crossing). This is where most player input enters the world, but not all of it: the handlers that never call `PacketUtils.ensureRunningOnSameThread` hop by the other door instead. `ServerGamePacketListenerImpl.handleChat` and both command packets run their work through `MinecraftServer.execute`, and filtered sign and book text comes back on a `CompletableFuture` completed against the server — so chat and commands arrive as *tasks*, drained by the event loop below, and not with the packets. Two gates sit on each queued pair. `PacketListener.shouldHandleMessage` is asked again at handling time, so a player who disconnected between arrival and now is dropped with a debug line rather than handled into a dead session. And a handler that throws does not end the tick: `ServerPacketListener` overrides `PacketListener.onPacketError` to log *"suppressing error"* and return, and `ServerCommonPacketListenerImpl.onPacketError` additionally files the throwable through `MinecraftServer.reportPacketHandlingException` into the `SuppressedExceptionCollector` that the next crash report dumps. The one escape is a `ReportedException` wrapping an *OutOfMemoryError*, which `PacketUtils.makeReportedException` rethrows. ### An empty server stops ticking Before anything else `MinecraftServer.tickServer` compares `MinecraftServer.emptyTicks` against `MinecraftServer.pauseWhenEmptySeconds` times twenty — the *pause-when-empty-seconds* property, default 60 in `DedicatedServerProperties`, and zero on the base class, which disables the feature. The counter advances only while nobody is online *and* the loop is not sprinting; on the tick it first reaches the threshold the server logs, autosaves once, and from then on runs `MinecraftServer.tickConnection` alone and returns. `MinecraftServer.tickCount` does not advance, so a paused server is stopped in every sense that matters and still answers pings. The integrated server pauses on a different signal. `IntegratedServer.tickServer` sets `IntegratedServer.paused` from `Minecraft.isPaused` or an empty player list, saves once on the way in, runs `IntegratedServer.tickPaused` — connections plus one statistic — instead of the real tick, and re-syncs the world time on the way out. It also copies the client's render and simulation distance into the `PlayerList` on every unpaused tick, which is why singleplayer has no separate view-distance setting. ### What `MinecraftServer.tickChildren` runs, and in what order `MinecraftServer.tickChildren` is the tick, and the rows below are its order. All but the first are a profiler section of their own; suspending the flush has none, and the debug row is three: | in order | what it does | skipped when | |---|---|---| | suspend flushing | `ServerCommonPacketListenerImpl.suspendFlushing` on every player's connection | never | | command functions | `ServerFunctionManager.tick` runs `ServerFunctionManager.LOAD_FUNCTION_TAG` once after a reload, then `ServerFunctionManager.TICK_FUNCTION_TAG` | frozen | | clocks | `ServerClockManager.tick` advances the world clocks, a `SavedData` kept in the *world_clocks* file | frozen, or `GameRules.ADVANCE_TIME` is off | | time sync | `MinecraftServer.forceGameTimeSynchronization` broadcasts a `ClientboundSetTimePacket` | not a multiple of 20 ticks | | levels | `MinecraftServer.updateEffectiveRespawnData`, then `ServerLevel.tick` for each dimension in `MinecraftServer.getAllLevels` order, overworld first | never | | connection | `MinecraftServer.tickConnection` — every `Connection`, and each playing client's own tick | never | | players | `PlayerList.tick` broadcasts a latency-only `ClientboundPlayerInfoUpdatePacket` | its own counter has not passed 600 — so every 601st call, not every 600th tick | | debug, game tests, tickables | `ServerDebugSubscribers.tick`, `GameTestTicker.tick`, the dedicated server GUI's refresh through `MinecraftServer.addTickable` | game tests alone, when frozen | | send chunks | `PlayerChunkSender.sendNextChunks`, then `ServerCommonPacketListenerImpl.resumeFlushing`, per player | never | A throwable out of `ServerLevel.tick` is caught, filled with the level's details as *"Exception ticking world"* and rethrown as a `ReportedException` — which is how one bad dimension ends the whole server. Nothing else in the list is wrapped. Two things a reader looks for here are elsewhere: the `/schedule` queue is a `TimerQueue` the server owns and persists (`MinecraftServer.getScheduledEvents`) but ticks from inside `ServerLevel.tickTime`, with the dimension's own game time, and the last statement of `MinecraftServer.tickChildren` is `ServerActivityMonitor.tick`, a rate-limited nudge to the `NotificationManager` rather than anything the world can see. ### Where a player's own tick actually happens The connection phase runs *after* every level. `ServerConnectionListener.tick` walks its synchronized list and, for each live `Connection`, calls `Connection.tick`: flush the deferred send queue, tick the `TickablePacketListener`, drop the connection if it has died, flush the channel, and every twentieth tick recompute the packet-rate averages. For a playing client that listener is `ServerGamePacketListenerImpl`, whose `ServerGamePacketListenerImpl.tick` acknowledges pending block changes, runs `ServerPlayer.doTick` through `ServerGamePacketListenerImpl.tickPlayer`, and then, only if that returns without having kicked anyone, three more things in this order: the fifteen-second keep-alive (`ServerCommonPacketListenerImpl.LATENCY_CHECK_INTERVAL`), the three spam throttles, and the idle-timeout check. So a movement packet is applied to the player before any level ticks, and the player *entity* takes its step after all of them. Entities see the player where the packets put her; the player then ticks against a world that has already moved. A throw out of `Connection.tick` disconnects that client with *"Internal server error"* — except on an in-memory connection, where it is rethrown as *"Ticking memory connection"* and takes the integrated server down with it. On a dedicated server `DedicatedServer.tickConnection` adds `DedicatedServer.handleConsoleInputs`, which is how a command typed at the console reaches the Server thread. RCON does not come this way: `DedicatedServer.runCommand` puts the command on the task queue with `BlockableEventLoop.executeBlocking` and waits for the answer, so it runs wherever the queue next drains. [Players and sessions](players-and-sessions.md) is what happens inside that phase; [the connection](../networking/the-connection.md) is the channel underneath it. ### The two writes each client gets `ServerCommonPacketListenerImpl.suspendFlushing` at the top of `MinecraftServer.tickChildren` sets a flag that turns `ServerCommonPacketListenerImpl.send` into a channel *write* with no flush — but only for sends made on the Server thread, because the flag is tested together with `BlockableEventLoop.isSameThread`, so anything sent from another thread flushes on its own as before. Two things then empty the buffer. `Connection.tick` flushes the channel unconditionally at the end of the connection phase, carrying everything the levels and the player's own tick produced. `ServerCommonPacketListenerImpl.resumeFlushing`, after the chunk batch, both clears the flag and calls `Connection.flushChannel` itself, carrying the player-list update, the debug subscribers, the game-test ticker, the server's own tickables and last the chunks. **Two** — writes to the socket per client per tick: one after the levels, one after the chunks. The pacing of that second write is [tickets and loading](../world/tickets-and-loading.md)'s subject. `PlayerChunkSender` answers to the client's own acknowledgements, so a slow client throttles its own chunks without slowing the tick. ### The bookkeeping at the bottom `MinecraftServer.tickServer` closes with three ledgers. The cached `ServerStatus` is rebuilt when the old one is more than `MinecraftServer.STATUS_EXPIRE_TIME_NANOS` (five seconds) old, so a ping never costs a walk of the player list. `MinecraftServer.ticksUntilAutosave` counts down to `MinecraftServer.autoSave`. And the tick's own duration replaces its slot in the hundred-entry `MinecraftServer.tickTimesNanos` ring, updates `MinecraftServer.aggregatedTickTimesNanos`, and folds into `MinecraftServer.smoothedTickTimeMillis` at `MinecraftServer.AVERAGE_TICK_TIME_SMOOTHING`. That ring is what `/tick query` reads, through `MinecraftServer.getAverageTickTimeNanos` and `MinecraftServer.getTickTimesNanos`. The debug screen's TPS chart is a different pipe: a `SampleLogger` with one slot per `TpsDebugDimensions` value, written from three points of the loop. `MinecraftServer.logTickMethodTime` records the tick method, `MinecraftServer.finishMeasuringTaskExecutionTime` the scheduled-task and idle slots after the wait, and `MinecraftServer.logFullTickTime` at the very bottom both flushes the four-slot sample and measures the **whole iteration**, tick plus wait. `IntegratedServer` logs it always; a dedicated server only while a client is subscribed to `DebugSubscriptions.DEDICATED_SERVER_TICK_TIME`. The loop is instrumented in three more places, none of them that ring. Every iteration gets a fresh `ProfilerFiller` from `MinecraftServer.createProfiler`, composing the `MetricsRecorder`'s profiler with a `SingleTickProfiler`, and `/debug start` arms `MinecraftServer.TimeProfiler` at the *top* of an iteration through `MinecraftServer.debugCommandProfilerDelayStart`. `MinecraftServer.tickFrame` is a `DiscontinuousFrame` from `TracyClient`, opened before the packet drain and closed after the tick. `JvmProfiler` is fed `MinecraftServer.smoothedTickTimeMillis` after the wait, on the last line of the iteration — where `MinecraftServer.isReady` is also set, every lap rather than once. ## The event loop, and what a tick's spare time buys `MinecraftServer` extends `ReentrantBlockableEventLoop` of `TickTask`: it is an `Executor` whose queue drains on the Server thread, and every other thread that needs to touch server state submits to it and waits. This section is where the rest of the book sends you for that machinery. ```mermaid flowchart TD P["BlockableEventLoop.pollTask peeks the head of the queue"] --> E{"anything queued"} E -- "a task" --> B{"blocking depth above zero"} B -- "inside managedBlock" --> RUN["run it, and report true"] B -- "not blocked" --> S{"MinecraftServer.shouldRun"} S -- "queued more than MAX_TICK_LATENCY ticks ago" --> RUN S -- "otherwise, ask the budget" --> H{"MinecraftServer.haveTime"} H -- "a task is already running" --> RUN H -- "in the slack, now is before delayedTasksMaxNextTickTimeNanos" --> RUN H -- "inside the tick, now is before nextTickTimeNanos" --> RUN H -- "out of time" --> L["leave it queued"] E -- "nothing" --> C{"only now does pollTaskInternal offer every level's chunk source a turn, when sprinting or blocked or in time"} L --> C C -- "one of them had work" --> RUN C -- "none did" --> W["report false. Inside managedBlock, waitForTasks parks, and a schedule unparks it early"] ``` ### Every runnable becomes a `TickTask` `MinecraftServer.wrapRunnable` stamps the current `MinecraftServer.tickCount` onto whatever is handed to the server, from whichever thread. That stamp is the whole of a task's identity to the scheduler: `MinecraftServer.shouldRun` lets a task run when there is time left, *or* when it is older than `MinecraftServer.MAX_TICK_LATENCY` (three) ticks — so a saturated server still drains its queue, late but in order and without unbounded growth. Submitting from the Server thread does not mean running inline: `ReentrantBlockableEventLoop.scheduleExecutables` reports true while another task is running, so re-entrant work queues instead of nesting. Once the server has stopped, `MinecraftServer.scheduleExecutables` reports false and a submitted task runs *inline on the caller's thread*; it is the separate `MinecraftServer.executeIfPossible` door that refuses with a *RejectedExecutionException*, and `PacketProcessor.scheduleIfPossible` that refuses a late packet the same way. A task that throws is not the loop's problem either. `BlockableEventLoop.doRunTask` logs the failure under the fatal marker and returns, rethrowing only what `BlockableEventLoop.isNonRecoverable` calls unrecoverable — an *OutOfMemoryError* or a `StackOverflowError`, unwrapped through any `ReportedException` around it. A worker thread that dies surfaces here too. `Util`'s uncaught-exception handler and `GenerationChunkHolder.applyStep` both park the report through `BlockableEventLoop.relayDelayCrash`, and the next `BlockableEventLoop.pollTask` on a loop built to propagate crashes rethrows it. The dedicated server is such a loop and the integrated server is not: in singleplayer it is `Minecraft`'s loop that propagates, and `IntegratedServer.onServerCrash` relays the server's own crash into the same slot so the client picks it up. What happens next belongs to [how a server dies](how-a-server-dies.md) — the crash report, the shutdown the loop's *finally* performs, and the watchdog that reads `MinecraftServer.getNextTickTime` from outside the loop and halts the JVM without saving. ### The budget, and where it stops applying `MinecraftServer.haveTime` is the `BooleanSupplier` the whole tick is handed. It is true unconditionally while a task is running (`ReentrantBlockableEventLoop.runningTask`), and otherwise compares the clock against `MinecraftServer.delayedTasksMaxNextTickTimeNanos` or `MinecraftServer.nextTickTimeNanos` depending on `MinecraftServer.mayHaveDelayedTasks` — which `MinecraftServer.runServer` sets true, together with the slack deadline, immediately after the tick, and which every subsequent `MinecraftServer.pollTask` overwrites with *was there more*. It stops applying the moment the thread blocks. Inside `BlockableEventLoop.managedBlock` the blocking depth is non-zero, so `BlockableEventLoop.shouldRunAllTasks` is true and `BlockableEventLoop.pollTask` never consults `MinecraftServer.shouldRun`: every queued task runs, budget and age irrelevant. That is what lets a level block on a chunk mid-tick without deadlocking — the wait *is* the drain, and the thread doing the waiting is the thread that completes the thing it waits for. `MinecraftServer.waitUntilNextTick` is the same mechanism used deliberately: `BlockableEventLoop.runAllTasks`, then `BlockableEventLoop.managedBlock` on *no time left*. `MinecraftServer.waitForTasks` parks with `LockSupport` until `MinecraftServer.nextTickTimeNanos` — or 100 µs at a time when the loop is not waiting on a tick — and `BlockableEventLoop.schedule` unparks it, so a submitted task cuts the park short. `PacketProcessor.scheduleIfPossible` pointedly does not: a packet landing in the slack waits for the next drain. ### What the budget actually gates **Three** — the things `MinecraftServer.haveTime` decides, once it has travelled from `MinecraftServer.tickServer` through `MinecraftServer.tickChildren`, `ServerLevel.tick`, `ServerChunkCache.tick` and `ChunkMap.tick`. They are `ChunkMap.processUnloads` (the unload queue, which drains anyway while it holds more than two thousand entries), `ChunkMap.saveChunksEagerly` (at most twenty chunks a tick, and only under 128 outstanding writes) and `SectionStorage.tick` by way of `PoiManager.tick` (the dirty village-point sections being written out). Loading a chunk, generating one, propagating tickets and ticking chunks take no supplier and are not gated at all. A late server does not load fewer chunks, then; it postpones unloading and saving them, and its memory grows while it is behind. ### The slack, and the sprint that inverts it After the tick, `MinecraftServer.waitUntilNextTick` spends the remaining milliseconds. `MinecraftServer.pollTaskInternal` polls the server's own queue first and, only if that queue had nothing to run, offers every level's `ServerChunkCache.MainThreadExecutor.pollTask` a turn — when the loop is sprinting, or blocked, or still in time. The levels get the leftovers of the leftovers. That executor keeps a policy of its own: its `ServerChunkCache.MainThreadExecutor.shouldRun` is unconditionally true, with no age rule, and its poll runs `ServerChunkCache.runDistanceManagerUpdates` first and returns at once if that did any work. The first thing leftover milliseconds buy is chunks changing status; the light schedule and the one queued chunk task only happen on a poll where the graphs were already quiet. Sprinting inverts the arithmetic. `MinecraftServer.processPacketsAndTick` hands `MinecraftServer.tickServer` a constant *false* instead of `MinecraftServer.haveTime`, so unloading, eager saving and section flushing stop for the length of the sprint — and yet `ServerTickRateManager.isSprinting` is the *first* term of the condition guarding the chunk-source poll, so every level's queue is drained on every poll regardless. A sprint therefore does more chunk work per wall-clock second than an ordinary server, not less, while doing almost none of the housekeeping that would let the results reach the disk — the exception being the unload queue, which the two-thousand rider drains whether there is time or not. ## Questions players ask **Does freezing stop the server?** It stops the *world*. `/tick freeze` sets `TickRateManager.isFrozen`, and `TickRateManager.tick` turns that into this tick's `TickRateManager.runGameElements` — unless `/tick step` left `TickRateManager.frozenTicksToRun` above zero, which it also decrements. The loop still runs, `MinecraftServer.tickCount` still increments, connections still tick, and `TickRateManager.isEntityFrozen` exempts players and anything carrying one. Everything that consults `TickRateManager.runsNormally` — functions, clocks, weather, block and fluid ticks, other entities, game tests — stops. **Why does lowering the tick rate not delay my autosave?** `MinecraftServer.ticksUntilAutosave` starts at `MinecraftServer.AUTOSAVE_INTERVAL` (6000) and is thereafter `MinecraftServer.computeNextAutosaveInterval`: the tick rate times 300, floored at `MinecraftServer.MIMINUM_AUTOSAVE_TICKS` (100 — the typo is Mojang's). An autosave is five wall-clock minutes. `MinecraftServer.onTickRateChanged` re-derives it whenever `/tick rate` changes, but only ever *shortens* the pending countdown. While sprinting it uses the measured rate from `MinecraftServer.getAverageTickTimeNanos`, so a sprint saves at the speed it is really running. **Is the tick rate settable to anything?** Between `TickRateManager.MIN_TICKRATE` (1.0) and `TickCommand.MAX_TICKRATE` (10000). Clients are told: `ServerTickRateManager.setTickRate` and `ServerTickRateManager.setFrozen` broadcast a `ClientboundTickingStatePacket`, `ServerTickRateManager.stepGameIfPaused` a `ClientboundTickingStepPacket`, and `ServerTickRateManager.updateJoiningPlayer` sends both to anyone arriving. A sprint is not announced as such — a client sees the unfreeze before it and the refreeze after. **Why is an empty Nether nearly free?** Because a dimension that nothing holds a simulation ticket in stops ticking entities and block entities after 300 ticks of `ServerLevel.emptyTime`, while still running its chunk-source work. That is [the level tick](server-level-tick.md)'s rule, and [tickets and loading](../world/tickets-and-loading.md) owns which ticket resets it. The loop itself is written once. `IntegratedServer` and `DedicatedServer` override pieces of the tick, never the loop: `DedicatedServer.tickServer` adds the JSON-RPC `ManagementServer.tick`, `DedicatedServer.tickConnection` the console, `IntegratedServer.tickServer` the pause. [Starting a server](starting-a-server.md) is how the thread that runs `MinecraftServer.runServer` comes to exist. ## Where to look `MinecraftServer.runServer` · `MinecraftServer.processPacketsAndTick` · `MinecraftServer.tickServer` · `MinecraftServer.tickChildren` · `MinecraftServer.waitUntilNextTick` · `MinecraftServer.haveTime` · `MinecraftServer.shouldRun` · `MinecraftServer.pollTask` · `TickTask` · `ReentrantBlockableEventLoop` · `BlockableEventLoop` · `PacketProcessor` · `PacketUtils` · `TickRateManager` · `ServerTickRateManager` · `TickCommand` · `ServerConnectionListener.tick` · `Connection.tick` · `ServerCommonPacketListenerImpl.send` · `ServerChunkCache.MainThreadExecutor` · `ChunkMap.processUnloads` · `IntegratedServer` · `DedicatedServer` · `ServerClockManager` · `SampleLogger` · `TpsDebugDimensions` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The level tick > Verified against **Minecraft 26.2** · Part III · A player stands still for one twentieth of a second while a whole dimension advances — weather, scheduled ticks, spawns, every entity, every block entity. Nobody moves. A wheat crop is one random tick short of ripe, a creeper is walking a fence line, rain has been falling for four minutes and a piston somewhere is halfway through extending. Then `ServerLevel.tick` runs, once, for this dimension, and all of it advances: the border interpolates, the weather counts down, the scheduled block ticks fire, mobs are counted and spawned, chunks near players get their random ticks, every entity in range runs its `Entity.tick`, block entities run their tickers, and the block changes that all of that produced are handed to the clients. It is one method on the Server thread, called once per dimension from `MinecraftServer.tickChildren` ([the server tick](server-tick.md)), overworld first. The order inside it is the whole lecture, because one of the steps is in a place nobody expects: **the block-change broadcast runs before the entities tick**. `ServerChunkCache.broadcastChangedChunks` comes before `ChunkMap.tick` and long before `EntityTickList.forEach`, so a block a command changed reaches your screen this tick and a block a piston changed reaches it on the next one. ## Three ranges, before we need them Three phrases run through everything below and they are all one number line. A chunk is *loaded* when it has a `ChunkHolder` at all; it is **block-ticking** at level 32 or below (`ChunkLevel.BLOCK_TICKING_LEVEL`) and **entity-ticking** at 31 or below (`ChunkLevel.ENTITY_TICKING_LEVEL`), and those last two answers come from the simulation graph, through `DistanceManager.inBlockTickingRange` and `DistanceManager.inEntityTickingRange`. How a chunk gets its level — which tickets put it there and which graph they feed — is Part IV's [tickets and loading](../world/tickets-and-loading.md); for this page it is enough that block-ticking reaches one chunk further out than entity-ticking, and that both are decided fresh, inside this tick, before anything ticks. ## The cast | class | what it decides | thread | |---|---|---| | `ServerLevel` | the order of the tick, and every gate in it — one instance per dimension | Server | | `ServerChunkCache` | the chunk half of the tick: ticket purging, distance updates, spawning, random ticks, the broadcast | Server | | `ChunkMap` | which chunks are candidates for spawning, which are entity-ticking, and every player's view of every entity | Server | | `ChunkHolder` | one chunk's pending block and light changes, and which packet shape they become | Server | | `LevelTicks` | the two scheduled-tick queues, one `LevelChunkTicks` per chunk, drained by priority | Server | | `EntityTickList` | which entities are ticked, and a stable view of that set while it is being walked | Server | | `PersistentEntitySectionManager` | which entities exist and which of them are ticking — the tick list's only editor | Server; its inbox is filled by IO threads | | `TickRateManager` | whether this is a normal tick at all, through `TickRateManager.runsNormally` | Server | ## The whole tick, and its three gates The tick is one method calling its own private methods, so its shape is not a conversation — it is a column with guards down the side. There are three guards, and every step is behind one of them, a combination of them, or nothing: *running* (`TickRateManager.runsNormally`, false while `/tick freeze` holds and no step is pending), *not a debug world* (`Level.isDebug`), and *the dimension is not empty* (`ServerLevel.emptyTime` below `ServerLevel.EMPTY_TIME_NO_TICK`, 300). ```mermaid flowchart TD START["MinecraftServer.tickChildren calls ServerLevel.tick, overworld first"] ENV["handlingTick goes true, EnvironmentAttributeSystem.invalidateTickCache — no gate"] WB["WorldBorder.tick, then advanceWeatherCycle and its game-event packets — running"] SLEEP["the sleep check: move the clock, wake the players, reset the weather — no gate"] SKY["Level.updateSkyBrightness, read out of the environment attributes — no gate"] TIME["ServerLevel.tickTime: gameTime and the schedule queue, overworld only — running"] SCHED["ServerLevel.blockTicks then ServerLevel.fluidTicks, 65536 apiece — running, and not a debug world"] RAID["Raids.tick — running"] SCC["ServerChunkCache.tick, handed the server's time budget — no gate"] PURGE["TicketStorage.purgeStaleTickets — running"] DIST["ServerChunkCache.runDistanceManagerUpdates: which chunks tick is settled here — no gate"] CHUNKS["mob counts, spawning chunks, thunder, spawns, random ticks, custom spawners — running, and not a debug world"] CAST["ServerChunkCache.broadcastChangedChunks: block, block-entity and light packets — not a debug world"] TRACK["ChunkMap.tick: chunk tracking, and the movement of everything that moved last tick — no gate"] UNLOAD["ChunkMap.tick with haveTime: POI saving and chunk unloads, until the budget is spent — no gate"] EVENTS["ServerLevel.runBlockEvents, then handlingTick goes false — running"] EMPTY["hasActiveTickets resets emptyTime, otherwise it rises — and it rises only while running"] DRAGON["EnderDragonFight.tick, the End only — running, and the dimension is not empty"] ENT["EntityTickList.forEach: each entity, then its riders — the dimension is not empty"] BE["Level.tickBlockEntities — the dimension is not empty, and each ticker fires only while running"] EM["PersistentEntitySectionManager.tick: the loading inbox, then the unload set — no gate"] DBG["LevelDebugSynchronizers.tick — no gate"] START --> ENV --> WB --> SLEEP --> SKY --> TIME --> SCHED --> RAID --> SCC SCC --> PURGE --> DIST --> CHUNKS --> CAST --> TRACK --> UNLOAD UNLOAD --> EVENTS --> EMPTY --> DRAGON --> ENT --> BE --> EM --> DBG ``` Read the gates and most of the page's surprises fall out of the figure. Sleeping through the night works with the game frozen. A frozen world still loads, sends and unloads chunks — and stops expiring its tickets. A debug world keeps its entities and drops its block updates. And the last two steps run on a dimension with nobody in it. ## The cache that is dropped before the border The first thing the tick does to the world, before the border and before the weather, is `EnvironmentAttributeSystem.invalidateTickCache`: last tick's resolved environment attributes are thrown away. That system ([environment attributes](../world/environment-attributes-and-timelines.md)) is where the old per-dimension and per-biome constants went, and `Level.updateSkyBrightness` later in this same tick reads `EnvironmentAttributes.SKY_LIGHT_LEVEL` out of it rather than deriving sky light from the time of day. `ServerClockManager` invalidates the same cache on every level whenever a clock moves, so the level is not its only owner — it is the first reader of the tick, and it starts clean. Then `WorldBorder.tick` advances the interpolated extent, and `ServerLevel.advanceWeatherCycle` counts the clear, rain and thunder timers down under `GameRules.ADVANCE_WEATHER`, resampling each from `ServerLevel.RAIN_DELAY`, `ServerLevel.RAIN_DURATION`, `ServerLevel.THUNDER_DELAY` and `ServerLevel.THUNDER_DURATION` as it expires, and fading `Level.rainLevel` and `Level.thunderLevel` by 0.01 a tick, which is why a downpour arrives as a five-second ramp. The countdowns belong to the *server*: `ServerLevel.getWeatherData` delegates to `MinecraftServer.getWeatherData`, one `WeatherData` shared by every dimension, and the only per-level parts are those two floats and the `Level.canHaveWeather` test that decides whether this dimension acts on any of it. Every move of a float is a `ClientboundGameEventPacket` (`ClientboundGameEventPacket.RAIN_LEVEL_CHANGE`, `ClientboundGameEventPacket.THUNDER_LEVEL_CHANGE`) to this dimension's players, and a start or a stop goes to every player in every dimension. ## Sleeping is the one thing a freeze cannot stop `SleepStatus.areEnoughSleeping` and `SleepStatus.areEnoughDeepSleeping` (against `GameRules.PLAYERS_SLEEPING_PERCENTAGE`) decide the night skip, and they are checked outside every gate. If the dimension type has a default clock and `GameRules.ADVANCE_TIME` is on, `ServerClockManager.moveToTimeMarker` jumps that clock to `ClockTimeMarkers.WAKE_UP_FROM_SLEEP`; `ServerLevel.wakeUpAllPlayers` gets everyone out of bed, and `ServerLevel.resetWeatherCycle` clears the storm. The `ClientboundSetTimePacket` that follows is sent by the clock manager, not by the level — day time is not the level's state in 26.2 at all. What the level does own is *gameTime*, and only in the overworld: `ServerLevel.tickTime` advances it when the level was built with its `ServerLevel.tickTime` flag set, which `MinecraftServer` does for the overworld alone, and every other dimension reads that number. The same call advances the server-wide `TimerQueue` behind `/schedule` (`MinecraftServer.getScheduledEvents`), so a scheduled function is timed off overworld *gameTime* and stands still while the world is frozen. ## Scheduled ticks, twice, and a promise to the same block `LevelTicks.tick` is called twice — `ServerLevel.blockTicks`, then `ServerLevel.fluidTicks` — each with the current *gameTime* and a budget of `ServerLevel.MAX_SCHEDULED_TICKS_PER_TICK`, 65536, and both skipped entirely in a debug world. Each call collects every `LevelChunkTicks` container whose head is due and whose chunk passes `ServerLevel.isPositionTickingWithEntitiesLoaded`, drains them in `ScheduledTick.INTRA_TICK_DRAIN_ORDER` — priority, then submission order, with no time term, because a container is only collected once its head is already due, and `ScheduledTick.DRAIN_ORDER`, which does compare times, orders each chunk's own queue — and hands each drained tick to `ServerLevel.tickBlock` or `ServerLevel.tickFluid`. Both of those check that what is at the position is *still* what the tick was scheduled for: `ServerLevel.tickBlock` the `Block`, before `BlockBehaviour.BlockStateBase.tick`; `ServerLevel.tickFluid` the `Fluid`, before `FluidState.tick`. That check is the whole cancellation mechanism: replace a block and its pending ticks evaporate, with nothing anywhere removing them. The queue itself, and what schedules into it, is [scheduled ticks](../world/scheduled-ticks.md). ## The chunk source does five things in one call `ServerChunkCache.tick` receives the server's `MinecraftServer.haveTime` supplier — the level never looks at it, it only passes it on — and does five things in order. It purges stale tickets, but only while running, so a frozen world holds on to expired portal and pearl tickets indefinitely. It runs `ServerChunkCache.runDistanceManagerUpdates`, ungated, which is where chunks change ticking state: the reason an entity starts or stops ticking this tick is decided here, several steps before the entity loop reads it. Then, if this is not a debug world, it does the spawning and random-ticking work below and broadcasts the block changes; it updates entity tracking; and finally it spends whatever time is left on POI saving and chunk unloads. That last step is the only part of the whole level tick that yields to the clock, and even it does not yield completely: `ChunkMap.processUnloads` force-drains anything over two thousand queued unloads whatever the budget says. ### Two chunk sets, and two different mob caps `NaturalSpawner.createState` walks `ServerLevel.getAllEntities` — every entity in the dimension, skipping mobs that require persistence — and counts them per `MobCategory`, using the chunk each one stands in to charge a spawn-potential field and to feed a `LocalMobCapCalculator`. That count is then read two different ways. Globally, `NaturalSpawner.SpawnState.canSpawnForCategoryGlobal` allows a category while its count is under `MobCategory.getMaxInstancesPerChunk` × `DistanceManager.getNaturalSpawnChunkCount` ÷ 289 (`NaturalSpawner.MAGIC_NUMBER`, 17²) — the mob cap everyone argues about. Locally, `LocalMobCapCalculator.canSpawn` applies the raw, unscaled `MobCategory.getMaxInstancesPerChunk` per player close enough to the chunk, so a category can sit well under the server-wide cap and still refuse to spawn beside one crowded player. Persistent categories — the animals — are considered only on a tick where *gameTime* divides by 400, and the whole spawning half is behind `GameRules.SPAWN_MOBS`. Two chunk sets are then walked, and they are not the same set. `ChunkMap.collectSpawningChunks` gathers the **spawning chunks**: the radius-8 tracker's candidates that have a ticking chunk and at least one non-spectating player within 128 blocks (`ChunkMap.playerIsCloseEnoughForSpawning`). They are shuffled, and each then gets `ChunkAccess.incrementInhabitedTime`, then `ServerLevel.tickThunder` *if it is also entity-ticking* — a 1-in-100000 roll per chunk per tick while raining and thundering, whose bolt prefers a lightning rod, then a mob that can see the sky, then the heightmap, and which brings a trap skeleton horse along at effective difficulty × 1 % — and then `NaturalSpawner.spawnForChunk` if `ServerLevel.canSpawnEntitiesInChunk`. The second set is `ChunkMap.forEachBlockTickingChunk`, which despite its name walks `DistanceManager.forEachEntityTickingChunk` — the entity-ticking set, level 31 and below. Each of those chunks gets `ServerLevel.tickChunk`. ### Random ticks are counted per section, and empty sections are free `ServerLevel.tickChunk` does two things with one number, `GameRules.RANDOM_TICK_SPEED` (default 3). It rolls that many 1-in-48 chances at `ServerLevel.tickPrecipitation` — the ice and snow layer, using `Biome.shouldFreeze`, `Biome.shouldSnow` and `GameRules.MAX_SNOW_ACCUMULATION_HEIGHT` — and then, for every `LevelChunkSection` in the chunk that reports `LevelChunkSection.isRandomlyTicking`, picks that many random positions and rolls both `BlockBehaviour.BlockStateBase.randomTick` and the fluid's at each. `LevelChunkSection.isRandomlyTicking` is a counter maintained on every block change, so a section of solid stone costs nothing at all: the loop over sections is a loop over the chunk's *interesting* height. Set the rule to zero and both halves stop, ice and snow included. The position itself does not come from `Level.random`. `Level.getBlockRandomPos` advances `Level.randValue`, a plain linear congruential generator, and unpacks x, y and z out of one integer. Everything the block then *does* with that position — the crop-growth roll, fire spreading, every implementation of `BlockBehaviour.BlockStateBase.randomTick` — takes `Level.random`, which is also what the ice and snow rolls, the lightning roll and the spawning-chunk shuffle use. The custom spawners come last inside this step. `ServerLevel.tickCustomSpawners` runs the overworld's `PhantomSpawner`, `PatrolSpawner`, `CatSpawner`, `VillageSiege` and `WanderingTraderSpawner` — a list only the overworld is constructed with. Three of the five carry a game rule of their own (`GameRules.SPAWN_PHANTOMS`, `GameRules.SPAWN_PATROLS`, `GameRules.SPAWN_WANDERING_TRADERS`); cats and sieges answer only to `GameRules.SPAWN_MOBS`, which gates the whole call. ### The broadcast, which is why entities are a tick behind Almost nothing in the game sends a block update at the moment a block changes — a landing `FallingBlockEntity` is the exception worth knowing, and it is dealt with at the end of this section. `ServerLevel.sendBlockUpdated` calls `ServerChunkCache.blockChanged`, which finds the `ChunkHolder`, records the position in that holder's per-section set, and — the first time a holder gains a changed section — adds it to `ServerChunkCache.chunkHoldersToBroadcast`. Then it returns. The packets are built once, later, in the chunk source's step. ```mermaid sequenceDiagram participant SL as ServerLevel participant SCC as ServerChunkCache participant CH as ChunkHolder participant CM as ChunkMap participant Wire as the network Note over SL,CM: before the tick — a command changed a hundred blocks SL->>SCC: sendBlockUpdated, once per block SCC->>CH: blockChanged, one entry in that section's set CH-->>SCC: nothing leaves, the holder is only marked Note over SL,Wire: this tick, inside ServerChunkCache.tick SCC->>CH: broadcastChangedChunks walks every marked holder CH->>Wire: ClientboundLightUpdatePacket first, to the border players only CH->>Wire: ClientboundBlockUpdatePacket for a section with one change CH->>Wire: ClientboundSectionBlocksUpdatePacket for a section with several CH->>Wire: BlockEntity.getUpdatePacket beside any changed position that has one SCC->>CM: ChunkMap.tick, the movement of everything that moved last tick Note over SL,Wire: still this tick, several steps later SL->>SL: tickBlockEntities, PistonMovingBlockEntity finishes a push and changes a block SL->>SCC: sendBlockUpdated, marked, and it waits for the next tick ``` `ChunkHolder.broadcastChanges` asks two different questions of two different audiences, and it asks the light one first: if either light filter has anything in it, `ChunkHolder.PlayerProvider.getPlayers` is called with the border-only flag set and one `ClientboundLightUpdatePacket` goes to the players on the edge of their tracked area, before a single block packet is built. The blocks are then emitted **per 16³ section**, to everyone tracking the chunk, and the shape depends on how many blocks in that section changed: one `ClientboundBlockUpdatePacket` for a single change, one `ClientboundSectionBlocksUpdatePacket` for several, plus `BlockEntity.getUpdatePacket` for any changed position that carries a block entity. A hundred blocks changed by one command are therefore a handful of packets, one per affected section — not a hundred. Then `ChunkMap.tick` runs — chunk tracking for each player, and `ChunkMap.TrackedEntity` for each entity, which is where `ServerEntity.sendChanges` turns last tick's movement into packets. Blocks first, entities second, and the entity loop only after both. The ordering is visible from a client: a player's `/setblock` lands in the tick the command was typed in, because a command packet is handled before `MinecraftServer.tickChildren` even starts, while a piston head lands in the tick after the one that moved it. Falling sand is the exception that proves the rule, and it is deliberate. `FallingBlockEntity` calls `Level.setBlock` and then, on the very next line, `ChunkMap.sendToTrackingPlayers` with a `ClientboundBlockUpdatePacket` of its own — so the block the client sees appear is sent in the same tick the entity that placed it vanished, rather than a tick behind it. A handful of other places do the same thing to one player rather than to everyone tracking the chunk. What the client does with all of this is [what the client is told](../networking/what-the-client-is-told.md). ## Block events close the handlingTick window `ServerLevel.runBlockEvents` drains `ServerLevel.blockEvents` — the note-block plays, piston pushes and chest-lid counts raised anywhere in this tick — completely, calling `BlockBehaviour.BlockStateBase.triggerEvent` after re-checking that the block at the position is still the one the event was raised for, the same promise a scheduled tick makes. When the block returns true, a `ClientboundBlockEventPacket` goes to players within 64 blocks. Because the set is a linked hash set, two identical events in one tick collapse into one; and an event whose chunk is not block-ticking is parked on `ServerLevel.blockEventsToReschedule` and re-queued rather than dropped. `ServerLevel.handlingTick` went true at the very top of the tick and goes false here, so the whole entity half runs outside that window. Its one reader in the game is `PistonBaseBlock`, which uses it to tell a piston update raised inside the tick from one raised outside it. ## An empty dimension skips exactly three things `ServerChunkCache.hasActiveTickets` — really `TicketStorage.shouldKeepDimensionActive`, which is the players' *simulation* tickets and a handful of others — resets `ServerLevel.emptyTime`. Otherwise the counter rises, and it rises only while running, so a frozen dimension never falls asleep. Past `ServerLevel.EMPTY_TIME_NO_TICK`, 300 ticks, the level skips the dragon fight, the entity loop and the block entities. That is the entire skip: the weather, the scheduled ticks, the chunk source, the block events, the entity manager's load and unload drain and the debug feed all keep running on a dimension nobody has visited for fifteen seconds. ## Every entity, and then its riders `EntityTickList.forEach` walks the tick list. An entity is skipped if it has been removed, or if `TickRateManager.isEntityFrozen` says so — frozen, and neither a player nor something carrying one. Otherwise it gets `Entity.checkDespawn`, and then ticks only if it is a `ServerPlayer` or its chunk answers `DistanceManager.inEntityTickingRange`. A passenger whose vehicle is alive and still lists it is passed over here and ticked by the vehicle instead; a stale link is broken with `Entity.stopRiding`. `ServerLevel.tickNonPassenger` records the old position, bumps `Entity.tickCount` and calls `Entity.tick`, then `ServerLevel.tickPassenger` runs `Entity.rideTick` for each rider that is a `Player` or in the tick list, recursively down the stack. `Level.guardEntityTick` wraps each one in a crash report titled *Ticking entity*, so a mob that throws names itself. The list stays stable under all of that by construction. Membership changes during the loop — a spawner's mob, a fired arrow, a lightning bolt — arrive through `ServerLevel.EntityCallbacks.onTickingStart` immediately. `EntityTickList` allows exactly one `EntityTickList.forEach` at a time and, on any add or remove while it is iterating, copies into its `EntityTickList.passive` map and swaps that with `EntityTickList.active`: the running loop keeps walking the view it started with, and the new entity waits for the next tick. Which entities are in the list at all is `PersistentEntitySectionManager`'s answer, through `Visibility.fromFullChunkStatus` — only `FullChunkStatus.ENTITY_TICKING` maps to a ticking visibility — with one override, `Player.isAlwaysTicking`, which keeps a player ticking whatever its chunk is doing and makes the `ServerPlayer` test inside the loop a second, redundant guard. Entities are Part VI's subject: [entity lifecycle](../entities/entity-lifecycle.md). ## Block entities reach one chunk further than mobs `Level.tickBlockEntities` walks `Level.blockEntityTickers`, dropping removed tickers as it goes and running the rest — but only those whose position passes `ServerLevel.shouldTickBlocksAt`, which is the **block**-ticking range, and only while `TickRateManager.runsNormally`. That single condition is why a furnace keeps smelting one chunk further out than a zombie keeps walking. A block entity created mid-tick — a chest a piston just pushed — lands on `Level.pendingBlockEntityTickers` instead, because `Level.tickingBlockEntities` is true, and is merged in at the top of the next tick. [Block entities](../blocks/block-entities.md) has the rest. ## The two steps that always run `PersistentEntitySectionManager.tick` drains `PersistentEntitySectionManager.loadingInbox`, a concurrent queue that chunk storage fills from IO threads — entities from freshly loaded chunks join the world here — and then processes `PersistentEntitySectionManager.chunksToUnload`. The `ServerLevel.EntityCallbacks` it fires (`ServerLevel.EntityCallbacks.onTickingStart`, `ServerLevel.EntityCallbacks.onTickingEnd`) are exactly what add and remove entries in the tick list the previous step walked. Last, `LevelDebugSynchronizers.tick` pushes this tick's neighbour updates, brains, paths, POIs and raids to any client subscribed through `DebugSubscriptions` — and, just before it, arms or clears the neighbour listener on `CollectingNeighborUpdater` depending on whether anyone is subscribed to `DebugSubscriptions.NEIGHBOR_UPDATES` at all. It is the one step of the level tick whose entire output is diagnostic, and it is outside every gate. ## What leaves the level, and when | the packet | the step that sends it | to whom | |---|---|---| | `ClientboundGameEventPacket`, rain and thunder levels | `ServerLevel.advanceWeatherCycle` | this dimension's players | | `ClientboundGameEventPacket`, start and stop raining | the same step, on a transition | every player in every dimension | | `ClientboundSetTimePacket` | `ServerClockManager.moveToTimeMarker`, from the sleep skip | everyone, sent by the clock manager | | `ClientboundBlockUpdatePacket` · `ClientboundSectionBlocksUpdatePacket` | `ChunkHolder.broadcastChanges`, per 16³ section | everyone tracking the chunk | | `BlockEntity.getUpdatePacket` | the same walk, beside a changed position that has one | everyone tracking the chunk | | `ClientboundLightUpdatePacket` | the same walk, before the blocks | the players on their tracked area's border | | entity add, move and remove | `ChunkMap.TrackedEntity`, inside `ChunkMap.tick` | whoever tracks that entity | | `ClientboundBlockEventPacket` | `ServerLevel.runBlockEvents` | players within 64 blocks | None of them go out when they are written. Every one is queued behind the suspended flush that `MinecraftServer.tickChildren` opens around all the levels, and leaves on the wire at the end of the server tick ([the server tick](server-tick.md)). ## Questions players ask **Does `/tick freeze` stop the server doing work?** Barely, on the chunk side. Distance updates, the block-change broadcast, entity tracking and chunk unloads all run every tick regardless. Two things inside the chunk source *are* frozen and are easy to miss: the spawning and random-ticking step, and `TicketStorage.purgeStaleTickets` — so a frozen world accumulates expired tickets and never releases the chunks they hold. **Why does my furnace keep going after the mobs around it stop?** Two thresholds, one chunk apart. Block entities are gated on block-ticking range (`ChunkLevel.BLOCK_TICKING_LEVEL`, 32) and entities on entity-ticking range (`ChunkLevel.ENTITY_TICKING_LEVEL`, 31), and a chunk on the boundary is in one and not the other. **Why did replacing a block cancel its scheduled tick?** Because a scheduled tick is a promise to *that* block: `ServerLevel.tickBlock` compares the `Block` at the position with the one scheduled, and a mismatch runs nothing. **Why does `/weather rain` in the Nether change the overworld?** There is one `WeatherData` on the `MinecraftServer` and every dimension advances the same countdowns. Only the fade of `Level.rainLevel` and `Level.thunderLevel` is per level, and only where `Level.canHaveWeather`. **Why does a mob that spawns this tick not move until the next one?** `EntityTickList` swapped its maps the moment the mob was added, so the loop that is running kept the view it started with. The mob is in the list — it is just not in *this* walk of it. **Why is a dimension with nobody in it still burning CPU?** Because going empty skips three things and nothing else: past 300 ticks with no active ticket the dragon fight, the entity loop and the block entities stop, and the weather, scheduled ticks, chunk source, block events and entity manager carry on. **Where did the day–night cycle go?** Out of the level. Time is a set of `WorldClock`s owned by `ServerClockManager` and ticked by the server; `ServerLevel.tickTime` advances *gameTime* only, only in the overworld, and every other dimension reads the overworld's number. ## Where to look `ServerLevel.tick` · `ServerLevel.advanceWeatherCycle` · `ServerLevel.tickTime` · `ServerLevel.tickBlock` · `ServerLevel.tickChunk` · `ServerLevel.tickThunder` · `ServerLevel.tickCustomSpawners` · `ServerLevel.runBlockEvents` · `ServerLevel.tickNonPassenger` · `ServerLevel.tickPassenger` · `ServerChunkCache.tick` · `ServerChunkCache.tickChunks` · `ServerChunkCache.broadcastChangedChunks` · `ServerChunkCache.blockChanged` · `ChunkHolder.broadcastChanges` · `ChunkMap.collectSpawningChunks` · `ChunkMap.forEachBlockTickingChunk` · `ChunkMap.tick` · `ChunkMap.processUnloads` · `DistanceManager.inEntityTickingRange` · `ChunkLevel.fullStatus` · `LevelTicks.tick` · `NaturalSpawner.createState` · `LocalMobCapCalculator.canSpawn` · `EntityTickList.forEach` · `PersistentEntitySectionManager.tick` · `Level.tickBlockEntities` · `Level.getBlockRandomPos` · `WeatherData` · `ServerClockManager.moveToTimeMarker` · `SleepStatus.areEnoughSleeping` · `EnvironmentAttributeSystem.invalidateTickCache` · `LevelDebugSynchronizers.tick` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Players and sessions > Verified against **Minecraft 26.2** · Part III · One connection, four events: a player joins, dies, walks into a Nether portal, and logs out. A player clicks a server in the list, watches a progress bar, and is standing in a world. An hour later they fall in lava, press *Respawn*, walk through a portal and log off for the night. On the server those are four events on one object graph, and they disagree about what survives. Dying **destroys** the `ServerPlayer` and builds a new one; a trip to the Nether keeps the same object and moves it. Both keep the player's entity id, and both keep the very same `ServerGamePacketListenerImpl` — a respawn does not rebuild the connection, it repoints it, in one field assignment, at the new player. Which is why anyone still holding the old `ServerPlayer` after a respawn is holding a corpse: an object flagged removed, out of every list, that will keep answering questions all day. ## The cast | class | what it decides | thread | |---|---|---| | `PlayerList` | who is admitted, who is in the tab list, and the exact order of the join burst | Server | | `ServerPlayer` | one player's world state, and what a new one inherits from an old one | Server | | `ServerGamePacketListenerImpl` | the session: the player it points at, the flush suspension, the load gate, the kicks | Netty to decode, Server to handle | | `ServerConfigurationPacketListenerImpl` | the strictly serial task queue a join is prepared in | Netty, with four handlers hopping to Server | | `PrepareSpawnTask` | where the player will stand, and when the `ServerPlayer` is finally constructed | Server | | `PlayerDataStorage` | the `.dat` file, its *.dat_old* twin and its corrupt-copy rescue | Server | | `PlayerChunkSender` | how many chunks a client is trusted with, starting at almost none | Server | | `CommonListenerCookie` | the four facts that survive a phase change: profile, latency, client options, transferred | — | The phases this trace passes through — login, configuration, play, and the terminal packets between them — belong to [protocol phases](../networking/protocol-phases.md). This page starts where that one hands over: with `PlayerList` deciding whether the login is allowed at all. ## Admission is a `Component` or nothing Identity here is a `NameAndId`: a record of a UUID and a name, made from the authenticated `GameProfile`, and the key that every stored-user list, every op lookup and every save file is addressed by. `UserNameToIdResolver`, reached through `MinecraftServer.services`, is the cache that maps between the two halves, and comparing its remembered name against the profile's is how the server notices a player has been renamed since their last visit. `PlayerList.canPlayerLogin` returns the reason to refuse, or null. It asks four questions in order — the ban list, the whitelist, the IP ban list, then capacity — and the two ways past it are not the ones a reader expects. The whitelist is bypassed by being an **op**: `PlayerList.isWhiteListed` is satisfied by presence in the op list, and `DedicatedPlayerList.isWhiteListed` overrides it to route through `PlayerList.isOp`, which also asks whether the identity is the singleplayer owner — a branch that never fires here, because `DedicatedServer.isSingleplayerOwner` returns false for everyone. The *bypassesPlayerLimit* flag in *ops.json* is a separate thing entirely and reaches exactly one of the four questions: `ServerOpListEntry.bypassesPlayerLimit` is read only by `DedicatedPlayerList.canBypassPlayerLimit`, and only the capacity test calls it. A banned op is still banned. The gate runs **twice**, and the second run is not a repeat of the first. `ServerLoginPacketListenerImpl.verifyLoginAndFinishConnectionSetup` runs it during login; `ServerConfigurationPacketListenerImpl.handleConfigurationFinished` runs it again when the client says configuration is over, because a ban or a newly full server can land in the seconds a configuration takes. Duplicate logins differ between the two. At login the newcomer wins: `PlayerList.disconnectAllPlayersWithProfile` kicks every session holding that UUID with `PlayerList.DUPLICATE_LOGIN_DISCONNECT_MESSAGE`, and the login parks until the old connection is really gone. At the second check the newcomer loses — an existing player with that id is a flat rejection, since by then there is a prepared spawn to throw away rather than a session to evict. ## Preparing a place to stand ```mermaid sequenceDiagram participant SLPL as ServerLoginPacketListenerImpl participant PL as PlayerList participant SCPL as ServerConfigurationPacketListenerImpl participant PST as PrepareSpawnTask participant PDS as PlayerDataStorage participant SL as ServerLevel SLPL->>PL: canPlayerLogin, then disconnectAllPlayersWithProfile SLPL->>SCPL: handleLoginAcknowledgement builds the listener, startConfiguration Note over SCPL: one task at a time, startNextTask refuses to overlap two SCPL->>SCPL: SynchronizeRegistriesTask, then a code of conduct or a resource pack SCPL->>PST: returnToWorld appends PrepareSpawnTask, then JoinWorldTask PST->>PDS: load, decoding SavedPosition out of the whole datafixed file PST->>SL: a PLAYER_SPAWN ticket at radius 3, then wait Note over SCPL,SL: every tick until the chunks land, the client still in configuration SL-->>PST: the load future completes, Preparing becomes Ready SCPL->>SCPL: JoinWorldTask sends ClientboundFinishConfigurationPacket SCPL->>PL: handleConfigurationFinished re-checks duplicates and canPlayerLogin SCPL->>PST: spawnPlayer ``` Nothing in that queue overlaps. `ServerConfigurationPacketListenerImpl.startNextTask` throws rather than start a task while another is unfinished, so the registry transfer completes before `PrepareSpawnTask` reads a byte, and the two optional tasks sit between them. What *does* overlap is the chunk load and the client's remaining work: once the ticket is placed the task simply reports *not finished* from `ConfigurationTask.tick` each tick, and the client spends that time in configuration with no idea a world is being assembled for it. The ticket needs re-arming. `TicketType.PLAYER_SPAWN` is registered with a timeout of twenty ticks, so `PrepareSpawnTask.keepAlive` — called from `ServerConfigurationPacketListenerImpl.tick` — re-adds it at the same radius every tick once the task has reached `PrepareSpawnTask.Ready`. Without that, a client slow to acknowledge the finish packet would arrive to find its spawn chunks expired underneath it. A player with no save file gets a search instead of a position. `PlayerSpawnFinder.findSpawn` walks up to `PlayerSpawnFinder.ABSOLUTE_MAX_ATTEMPTS` candidates — a thousand and twenty-four, or fewer if `GameRules.RESPAWN_RADIUS` or the world border says so — in a coprime-strided order from a random offset, loading each candidate's chunk under a one-tick `TicketType.SPAWN_SEARCH` ticket and returning a future. The `ChunkLoadCounter` beside it feeds only the server's `LevelLoadListener`, through `LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS` — the client's progress bar on an integrated server, and nothing a remote player ever sees on a dedicated one, where the listener is a `LoggingLevelLoadListener` that boot already closed. ## The save file is read twice, and both reads are the whole file `PrepareSpawnTask.start` reads the `.dat` and decodes `ServerPlayer.SavedPosition` from it — three optional fields, dimension, position and rotation, and nothing else. That is all it *decodes*. It is not all it does: `PlayerDataStorage.load` reads the compressed tag with an unlimited accounter and runs `DataFixTypes.PLAYER` over the entire document before any codec sees it ([codecs, NBT and JSON](../foundations/codecs-nbt-json.md)). So the narrow read saves decoding, not I/O, and a join pays the migration cost twice — once here, and once in `PrepareSpawnTask.Ready`, where the file is loaded again for the full `Entity.load`. Between the two reads the player is built. `ServerLevel.waitForEntities` blocks the Server thread until the entities in the spawn chunks have finished loading — a horse to remount has to exist before a rider can be attached to it — and then the `ServerPlayer` constructor runs, pulling its `ServerStatsCounter` and `PlayerAdvancements` out of `PlayerList` on the way past and defaulting `ServerPlayer.requestedViewDistance` to 2 until the client's `ClientInformation` says otherwise. The second read fills the object in, the player is snapped to the prepared position, `PlayerList.placeNewPlayer` runs, and only afterwards do `ServerPlayer.loadAndSpawnEnderPearls` and `ServerPlayer.loadAndSpawnParentVehicle` put back what the player was carrying and sitting on when they left. One rescue is wired into the read. `PlayerList.loadPlayerData` checks whether the joining identity is the singleplayer owner and, if the world records a *singleplayer_uuid* in its level data, loads **that** file rather than the one named for the joining id. It works once: the save writes under the current id and `MinecraftServer.saveAllChunks` stamps the current owner's id into the level data, so the old file is read on one join and orphaned by the first save afterwards. ## `PlayerList.placeNewPlayer` sends a world in one write ```mermaid sequenceDiagram participant PL as PlayerList participant SGPL as ServerGamePacketListenerImpl participant SL as ServerLevel participant CM as ChunkMap participant Wire as the network Note over PL,Wire: in the scheduled packet processing at the top of a tick PL->>SGPL: new listener, inbound protocol to play, suspendFlushing PL->>SGPL: ClientboundLoginPacket, difficulty, abilities, held slot, recipes PL->>SGPL: the permission level as an entity event, then the command tree PL->>SGPL: recipe book, scoreboard, join message, teleport, server status PL->>SGPL: everyone already here, then the joiner to everyone PL->>SGPL: sendLevelInfo, border, clocks, spawn, rain, LEVEL_CHUNKS_LOAD_START PL->>SL: addNewPlayer SL->>CM: onTrackingStart, updatePlayerStatus, the first ChunkTrackingView CM->>SGPL: the chunks in view marked pending on PlayerChunkSender PL->>SGPL: boss events, active effects, initInventoryMenu, resumeFlushing SGPL->>Wire: one write Note over SL,Wire: at the end of the same tick, the first chunk batch, and only one ``` The whole method sits inside one suspension. `ServerCommonPacketListenerImpl.suspendFlushing` sets a flag that every send from the Server thread consults, queuing packets unflushed, and `ServerCommonPacketListenerImpl.resumeFlushing` clears it and calls `Connection.flushChannel` once. Everything above — a login packet, a command tree, a scoreboard, a tab list, a world border — leaves as a single write. It is the same bracket [the server tick](server-tick.md) puts around every client every tick, opened by hand here because a join is handled in the scheduled packet processing that runs *before* `MinecraftServer.tickChildren` opens the tick's own. `ClientboundLoginPacket` is where the client learns the entity id it is about to be given, the hardcore flag, every dimension key the server has, the view and simulation distances, whether the server authenticates, and a `CommonPlayerSpawnInfo` from `ServerPlayer.createCommonSpawnInfo` carrying the dimension type, the obfuscated seed, both game modes, the last death location, the portal cooldown and the sea level. Three game rules are pinned into it as plain booleans and never re-sent by this path: `GameRules.REDUCED_DEBUG_INFO`, `GameRules.LIMITED_CRAFTING`, and `GameRules.IMMEDIATE_RESPAWN` inverted into *show the death screen*. The permission level arrives twice over, as a `ClientboundEntityEventPacket` carrying one of five event ids and as the whole command tree from `Commands.sendCommands`, both from `PlayerList.sendPlayerPermissionLevel` resolving a `LevelBasedPermissionSet` out of `MinecraftServer.getProfilePermissions`. The model behind that set is [Brigadier and commands](../commands/brigadier-and-commands.md); all a join needs to know is that both halves are re-sent whenever `PlayerList.op` or `PlayerList.deop` changes it. The tab list goes out in a deliberate order: the joiner is sent everyone already present, *then* added to `PlayerList.players`, *then* everyone — themselves included — is sent the joiner. And the join message is chosen a few lines earlier than it is sent, because *multiplayer.player.joined.renamed* is used when the name in the profile differs from the one the name cache remembers, and the cache is overwritten at the top of the method. Entering the level is the step that starts the terrain, though it is not the last: the boss bars, the active effects, the inventory menu and the join notification all follow it, and `ServerCommonPacketListenerImpl.resumeFlushing` closes the single write. `ServerLevel.addNewPlayer` hands the player to `PersistentEntitySectionManager.addNewEntity`, whose callback adds it to `ServerLevel.players` and to the chunk source; `ChunkMap.updatePlayerStatus` registers the player with `DistanceManager`, resets its chunk tracking to `ChunkTrackingView.EMPTY` and computes the real one, and every chunk inside that view is marked pending on the connection's `PlayerChunkSender`. So there are two player lists, written by different systems: `PlayerList.players` is *who is on the server* and `PlayerList` alone writes it, while `ServerLevel.players` is *whose entity is in this level* and only the entity manager's tracking callbacks write it. A player halfway through a dimension change is in the first and in neither copy of the second. A joining client is trusted with one batch. `PlayerChunkSender` starts with a budget of a single unacknowledged batch and a guess of nine chunks a tick; the first `ServerboundChunkBatchReceivedPacket` raises the budget to ten (`PlayerChunkSender.MAX_UNACKNOWLEDGED_BATCHES`) and replaces the guess with the client's own measured rate, clamped between `PlayerChunkSender.MIN_CHUNKS_PER_TICK` and `PlayerChunkSender.MAX_CHUNKS_PER_TICK`. Batches go out nearest chunk first, in the *send chunks* step at the very end of `MinecraftServer.tickChildren`. [Tickets and loading](../world/tickets-and-loading.md) picks up from there. ## Loaded is something the client says The player is in the world before the client can play. `ServerGamePacketListenerImpl.hasClientLoaded` gates movement, block breaking, item use, sprinting and sneaking, and it answers false until either the client sends `ServerboundPlayerLoadedPacket` or a sixty-tick timer runs out — `ServerGamePacketListenerImpl.CLIENT_LOADED_TIMEOUT_TIME`, started in the listener's constructor and counted down from `ServerPlayer.tick`, which is to say from the level's entity loop rather than from the connection. Death re-arms the same gate by a different mechanism. `ServerGamePacketListenerImpl.markClientUnloadedAfterDeath`, called at the end of `ServerPlayer.die`, sets a flag that no timer clears: a dead player's client counts as unloaded indefinitely, and only `ServerGamePacketListenerImpl.restartClientLoadTimerAfterRespawn` — reached from the respawn, or from a brand-new listener — clears the flag and starts the sixty ticks again. The death screen is held open by the same field the *Joining world* screen is. That the countdown ticks from `ServerPlayer.tick` while the food, health and stat sync tick from `ServerPlayer.doTick` is the whole reason a player is ticked from two places every tick; [player anatomy](../player/player-anatomy.md) is the lecture on that. The fact worth carrying out of here is that the level's entity loop ticks players *regardless* of entity-ticking range, which is how a player standing in an otherwise idle chunk still moves. ## Four ways the session changes The join is one story; what happens afterwards is a comparison. Three of these a player will meet tonight and the fourth exists for one debug command. They are not quite every way out of a `ServerLevel` — the end credits are a fifth, and `ServerPlayer.showEndCredits` removes the player with `Entity.RemovalReason.CHANGED_DIMENSION` on its way to the respawn below — but they are the four a session is built out of, and they disagree about almost everything. | | respawn | dimension change | disconnect | `ServerGamePacketListenerImpl.switchToConfig` | |---|---|---|---|---| | **who runs it** | `ServerGamePacketListenerImpl.handleClientCommand` | `ServerPlayer.teleport` | `Connection.handleDisconnection` | `DebugConfigCommand` | | **same `ServerPlayer`?** | no — `PlayerList.respawn` constructs a new one | yes | yes, briefly | yes, then a new one on the way back | | **same entity id?** | yes, copied over by `Entity.setId` | yes | — | no, the rebuilt player gets a fresh one | | **same connection listener?** | yes, `ServerPlayer.connection` is reassigned | yes | — | no, a new one from `PlayerList.placeNewPlayer` | | **what carries over** | whatever `ServerPlayer.restoreFrom` copies | everything, because nothing is copied | — | whatever the `.dat` holds | | **is the `.dat` written?** | no | no | yes, inside `PlayerList.remove` | yes, the same call | | **the client is told** | `ClientboundRespawnPacket`, keeping nothing, or attribute modifiers | `ClientboundRespawnPacket`, `ClientboundRespawnPacket.KEEP_ALL_DATA` | nothing, the channel is already gone | `ClientboundStartConfigurationPacket` | | **removal reason** | `Entity.RemovalReason.KILLED` | `Entity.RemovalReason.CHANGED_DIMENSION` | `Entity.RemovalReason.UNLOADED_WITH_PLAYER` | the same | ### The object, and the reference that outlives it `PlayerList.respawn` builds the new `ServerPlayer` from the old one's profile and client information, then performs three assignments that make the difference invisible from outside: `ServerPlayer.connection` is set to the old player's listener, `Entity.setId` copies the entity id, and the listener's own player field is reassigned by its caller. To every other client on the server nothing has happened — same id, same UUID, same tab list row, and no `ClientboundPlayerInfoUpdatePacket` is sent at all. To anything inside the server holding the old object, everything has happened: it was removed from its level before the new one existed, and it is not in `PlayerList.players` any more. Only two of the four paths take a `TeleportTransition`, and they are the two that move you. `ServerPlayer.findRespawnPositionAndUseSpawnBlock` turns the saved `ServerPlayer.RespawnConfig` — respawn data plus a *forced* flag — into one: a respawn anchor is found and a charge spent unless the point is forced, a bed is found and costs nothing, `TeleportTransition.missingRespawnBlock` says the block is gone, and `TeleportTransition.createDefault` falls back to the world spawn. Both of those last two are the expensive branch, not just the fallback: each builds its position with `TeleportTransition.findAdjustedSharedSpawnPos`, so each runs `ServerPlayer.adjustSpawnLocation` — the same `PlayerSpawnFinder` search a join runs asynchronously — and blocks the Server thread on the future with `BlockableEventLoop.managedBlock`. ### What comes across when you die `ServerPlayer.restoreFrom` has two branches, and the interesting one is not the one people name it for. Its *restore everything* branch — permanent attribute modifiers, health, hunger, every active effect, the inventory, the portal state — is reached only when `PlayerList.respawn` is called with `Entity.RemovalReason.CHANGED_DIMENSION`, which happens in exactly one place: a player pressing *Respawn* on the end credits, with `ServerPlayer.wonGame` set. That is the End-portal return, not *keepInventory*. An ordinary death takes the other branch. Health is reset to maximum, effects are gone, and `GameRules.KEEP_INVENTORY` — or having died as a spectator — decides only whether `ServerPlayer.transferInventoryXpAndScore` runs, moving the inventory, the experience and the score and nothing else. Outside the branch, and so true of every death, a long tail is copied unconditionally: the ender chest, the enchantment seed, both game modes, base attribute values, the recipe book, the warden spawn tracker, the chat session, the skin customisation, the last death location. The ender chest surviving death is not a game rule; it is a field assignment. ### Why the Nether keeps your potion effects Because nothing is copied. `ServerPlayer.teleport` across a dimension boundary sets `ServerPlayer.isChangingDimension`, sends the client a `ClientboundRespawnPacket` marked `ClientboundRespawnPacket.KEEP_ALL_DATA`, removes the entity from the old level with `Entity.RemovalReason.CHANGED_DIMENSION` — a reason that neither destroys the entity nor writes it into a chunk — immediately calls `Entity.unsetRemoved`, points it at the new level with `ServerPlayer.setServerLevel`, and adds it back with `ServerLevel.addDuringTeleport`. It is one object the whole way. The effects survive because they were never touched, and what the server then re-sends — level info, the inventory through `PlayerList.sendAllPlayerInfo`, the active effects, the abilities, the permission level — is not a restoration but a **re-sync**, because the client threw its own world away the moment the respawn packet arrived. A same-dimension teleport is not this path at all. It is `ServerGamePacketListenerImpl.teleport` followed by `ServerGamePacketListenerImpl.resetPosition`, with no client-side world discarded and no respawn packet sent. ### Where your llama goes when you log out The channel closes on a Netty thread and nothing happens until `ServerConnectionListener.tick`, in the connection step of the server tick, notices and calls `Connection.handleDisconnection`. That reaches `ServerGamePacketListenerImpl.removePlayerFromWorld`: the leave message, `ServerPlayer.disconnect` to eject passengers and stop sleeping, and `PlayerList.remove`. The order inside `PlayerList.remove` is the answer to the question. The `.dat` is written **first**, with the stats and advancements JSON beside it, and `ServerPlayer.saveParentVehicle` writes the entire root vehicle into it under *RootVehicle* — but only if that vehicle has exactly one player passenger. The same test then decides whether the vehicle chain is removed from the world with `Entity.RemovalReason.UNLOADED_WITH_PLAYER`, the removal reason that neither destroys an entity nor saves it into a chunk, which is exactly what you want for something already written into a different file. In-flight ender pearls go the same way, saved into the file and removed from the world. So the llama leaves the world with you, rides in your save, and is put back by `ServerPlayer.loadAndSpawnParentVehicle` after your next `PlayerList.placeNewPlayer` — unless somebody else was riding it too, in which case it stays where it is and you land beside it. On an integrated server one further thing happens: `ServerCommonPacketListenerImpl.onDisconnect` sees that the leaver is the singleplayer owner and halts the server ([how a server dies](how-a-server-dies.md), which owns `PlayerList.saveAll` and `PlayerList.removeAll` at shutdown). `ServerGamePacketListenerImpl.switchToConfig` is that same removal with the connection left alive. It runs `ServerGamePacketListenerImpl.removePlayerFromWorld` — leave message, saved file, tab-list removal and all — sends `ClientboundStartConfigurationPacket` and swaps the outbound protocol back; the acknowledgement builds a fresh `ServerConfigurationPacketListenerImpl`, and `ServerConfigurationPacketListenerImpl.returnToWorld` exists separately from `ServerConfigurationPacketListenerImpl.startConfiguration` precisely so the second visit re-queues the spawn and join tasks without re-sending registries. It is also why `CommonListenerCookie` carries the transferred flag and the client's options across at all. ### What everyone else is told Very little, and only on the way out. A respawn and a dimension change send nothing to other clients about the player, because the identity a tab list is keyed on never changed. A disconnect sends one `ClientboundPlayerInfoRemovePacket` to everybody, and `PlayerList.tick` broadcasts a latency-only update for the whole list on every six hundred and first call, counted by `PlayerList` rather than off the tick number. The rest of what other players see is entity tracking in `ChunkMap`, and the removal reasons in the table above have already told it what to do. ### The three kicks that come from the tick `ServerGamePacketListenerImpl.tick` ends sessions of its own accord. An idle player is disconnected after `MinecraftServer.playerIdleTimeout` minutes with no action, unless they are sitting on the end credits. A player whose client reports itself airborne for longer than `ServerGamePacketListenerImpl.MAXIMUM_FLYING_TICKS` — eighty ticks, scaled up when gravity is reduced and disabled entirely when gravity is zero — is kicked for flying, and a floating vehicle is counted separately from a floating rider. And keep-alive is a strict pair: `ServerCommonPacketListenerImpl.keepConnectionAlive` sends one every `ServerCommonPacketListenerImpl.LATENCY_CHECK_INTERVAL` milliseconds and disconnects with `ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE` if the previous one is still unanswered, while an answer carrying the wrong id disconnects immediately rather than being ignored. The round trip it measures is smoothed three parts old to one part new, so a tab list lags a genuine latency change by several pings. The singleplayer owner is exempt from the keep-alive, and from that alone: it is the only one of the three that asks `ServerCommonPacketListenerImpl.isSingleplayerOwner`, and the host can be kicked for idling or for flying like anyone else. > **For a 1.21-era reader.** Identity is a `NameAndId` record, not a > `GameProfile`, everywhere below the login handshake — the ban list, the op > list, the whitelist, the save file and the name cache all key on it. And a > permission is no longer an integer: `ServerOpListEntry` holds a > `LevelBasedPermissionSet`, and the number in *ops.json* is a spelling of > one. ## Where to look `PlayerList.canPlayerLogin` · `PlayerList.placeNewPlayer` · `PlayerList.respawn` · `PlayerList.remove` · `PlayerList.loadPlayerData` · `PlayerList.sendLevelInfo` · `PrepareSpawnTask` · `JoinWorldTask` · `PlayerSpawnFinder` · `PlayerDataStorage` · `ServerPlayer.restoreFrom` · `ServerPlayer.teleport` · `ServerPlayer.findRespawnPositionAndUseSpawnBlock` · `ServerPlayer.die` · `ServerPlayer.doTick` · `ServerGamePacketListenerImpl.removePlayerFromWorld` · `ServerGamePacketListenerImpl.switchToConfig` · `ServerGamePacketListenerImpl.hasClientLoaded` · `ServerCommonPacketListenerImpl` · `PlayerChunkSender` · `ChunkMap.updatePlayerStatus` · `TeleportTransition` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Starting a server > Verified against **Minecraft 26.2** · Part III · Dropping the jar in an empty folder, typing *java -jar server.jar*, and waiting for the line that says *Done*. The first run writes two files and exits: you have not agreed to the EULA. The second gets as far as *Preparing level "world"* and, a second or two later, *Done (1.284s)! For help, type "help"*. By the time the second of those lines prints, the server has opened a data pack stack, built every registry the world is made of, taken an operating-system lock on the save directory, rewritten `level.dat`, constructed a `ServerLevel` for every dimension the packs declare and started listening on 25565 — and only the levels were built between the two lines. Everything else was over before the first one printed, which is why the elapsed time it reports is so short. And the step in the middle whose name promises the most — `MinecraftServer.prepareLevels`, the one whose progress line still reads *Preparing spawn area* — does the least: **on an ordinary world it loads no chunks at all.** All it does is re-arm the tickets the last shutdown wrote down, and exactly two of the nine ticket types are ever written down. ## The cast | class | what it decides | thread | |---|---|---| | `server/Main` | everything decided before a second thread exists: the flags, the properties, the EULA, which directory is the world, and when to hand off | JVM main | | `DedicatedServerSettings` | the live copy of `server.properties` — and the rewrite that happens every time a setting changes | main, then Server | | `LevelStorageSource.LevelStorageAccess` | one open world: its directory layout, its `session.lock`, and every read and write of `level.dat` | whoever holds it | | `WorldLoader` | the packs, the registries and the datapack-driven resources, assembled into a `WorldStem` | main and `Util.backgroundExecutor` | | `DedicatedServer` | what a dedicated server has and singleplayer does not: the console, the port, the legacy conversions, RCON, query, the watchdog | Server, but constructed on main | | `MinecraftServer` | the levels, the saves and the loop — and `MinecraftServer.spin`, the line where the second thread begins | Server | | `ServerLevel` | one dimension: its chunk source, its saved data, its ticket store | Server | | `LevelLoadListener` | what boot progress looks like: log lines on a dedicated server, a progress bar on a client | the caller's | ## From the command line to the Server thread ```mermaid sequenceDiagram participant Main as Main participant LSA as LevelStorageSource.LevelStorageAccess participant WL as WorldLoader participant Worker as Worker participant DS as DedicatedServer participant MS as MinecraftServer participant SL as ServerLevel Main->>Main: tryDetectVersion, the flags, CrashReport.preload, Bootstrap.bootStrap and validate, the timer hack thread Main->>Main: DedicatedServerSettings reads server.properties and writes it straight back, then Eula. Without agreement main returns here Main->>Main: JsonRpc.create binds the ManagementServer on its own daemon Netty group, with no world open yet Main->>LSA: validateAndCreateAccess. session.lock is taken in the constructor LSA-->>Main: level.dat parsed, or level.dat_old restored into its place Main->>WL: load, wrapped in Util.blockUntilDone, so this thread is now an executor WL->>Main: createResourceManager opens the packs, handed to the main-thread executor WL->>Worker: static tags, worldgen then dimension registries, ReloadableServerResources Worker-->>WL: the stages that must be single-threaded come back to main WL-->>Main: a WorldStem Main->>LSA: saveDataTag rewrites level.dat now, upgrade or no upgrade Main->>MS: spin builds the Thread object and sets priority 8 above four processors MS->>DS: the factory it was handed runs the constructor here, on this thread, and can throw MS->>MS: only once there is a server to run does spin start the thread Note over Main,MS: main registers the shutdown hook and returns. Everything below is the Server thread MS->>DS: runServer calls initServer DS->>DS: the console daemon thread, the properties into fields, the key pair DS->>DS: startTcpServerListener binds the port, then convertOldUsers DS->>MS: loadLevel MS->>SL: createLevels builds the overworld first, then one level per LevelStem on DerivedLevelData MS->>SL: prepareLevels re-arms the persisted tickets and waits in 10 ms slices SL-->>MS: nothing pending. On an ordinary world nothing was ever asked for MS-->>DS: loadLevel returns DS->>DS: Done is logged here, before the loop is ever entered DS->>DS: query, RCON, the watchdog, JMX, one flush save, serverStarted DS-->>MS: initServer returns true MS->>MS: the icon and the status response are built, then the tick loop begins ``` Read the note bar as a wall: above it, one thread does all the work and the server object does not exist for most of it; below it, *main* has returned and everything that remains is the Server thread and the daemons arranged around it. [Anatomy](../anatomy/anatomy.md) draws the same hand-off from the client's side, where the thread that spins the server is the one drawing frames. ## Everything *main* does before there is a second thread `Main.main` runs on the thread the JVM handed it and stays there for all of what follows. `SharedConstants.tryDetectVersion` reads *version.json* out of the jar first, so everything downstream knows what version it is. Then the flags are parsed — *--nogui*, *--port*, *--universe*, *--world*, *--forceUpgrade*, *--recreateRegionFiles*, *--safeMode*, *--initSettings*, *--pidFile* and the rest — and `CrashReport.preload` runs: `MemoryReserve` sets a block of heap aside and one throwaway report is formatted and discarded, so that the crash-report path is warm and has memory of its own even when the reason for the crash is that there is none left. `Bootstrap.bootStrap` and `Bootstrap.validate` build and freeze the static registries ([identifiers and registries](../foundations/identifiers-and-registries.md)), and `Util.startTimerHackThread` starts a daemon that sleeps forever and touches nothing. Only then does the server read its own configuration. `DedicatedServerSettings` parses `server.properties`, and `DedicatedServerSettings.forceSave` writes it straight back — which is why a properties file carried over from an older jar comes back with the new keys filled in. `RegionFileVersion.configure` takes the region compression out of it before any chunk file is ever opened ([chunk storage](../world/chunk-storage.md)). And `Eula` reads `eula.txt`, or writes the default and reports it missing. That is the gate: `Eula.hasAgreedToEULA` false means one log line and *main* returns. No world has been opened, no server exists, and everything *main* has started so far is a daemon — which does not hold the JVM open, so the process ends with the return. *--initSettings* returns one step earlier still, having written both files on purpose. ### The management port opens before the world does With the EULA agreed, `Services.create` builds the authentication services and the name cache in the universe directory, and `JsonRpc.create` starts a `ManagementServer` if *management-server-enabled* is set — and throws, ending the boot, if it is set and the secret is not forty alphanumeric characters rather than quietly going without one: a Netty WebSocket listener with its own event-loop group named *Management server IO*, TLS on by default, and a `JsonRpcNotificationService` registered on the `NotificationManager` that everything later in the boot reports through. It binds before `session.lock` is taken, and `DedicatedServer.onServerExit` stops it last, so the management protocol is reachable at both ends of the server's life — including while there is no world to ask it about ([how a server dies](how-a-server-dies.md)). ### Taking the lock, and fixing `level.dat` twice `LevelStorageSource.createDefault` points at the universe directory — the working directory unless *--universe* says otherwise — and `LevelStorageSource.validateAndCreateAccess` opens the world named by *--world* or by *level-name*. The validation is a symlink check; the lock is neither optional nor deferred. `DirectoryLock.create`, called from the `LevelStorageSource.LevelStorageAccess` constructor, opens `DirectoryLock.LOCK_FILE`, writes a snowman into it and takes a `FileChannel.tryLock` on it. That is an operating-system advisory lock, not a value in the file: a second server on the same directory fails at once with `DirectoryLock.LockException`, a crashed JVM releases it with nothing to clean up, and a world folder copied out from under a running server carries a `session.lock` that means nothing. The client's world list greys out a world that `DirectoryLock.isLocked` reports held. If the directory already holds world data, *main* parses `level.dat` once and runs the result through the datafixers twice. `LevelStorageSource.LevelStorageAccess.getUnfixedDataTagWithFallback` reads the file and, on a parse failure, falls back to `level.dat_old` and restores it over the original. That raw tag goes through `LevelStorageSource.LevelStorageAccess.fixAndGetSummaryFromTag` for a `LevelSummary`, which is enough to answer `LevelSummary.requiresManualConversion` and `LevelSummary.isCompatible` — two gates that each end the boot with one explanatory line — and separately through `DataFixers.getFileFixer` for the fully upgraded tag the world is actually built from. ### The world load turns the main thread into an executor `ServerPacksSource.createPackRepository` builds the repository over the world's *datapacks/* folder and `WorldLoader.load` does the rest: the staged load that [the resource system](../foundations/resource-system.md) describes, run here for server data. `WorldLoader.PackConfig.createResourceManager` selects and opens the packs, `TagLoader.loadTagsForExistingRegistries` collects tags for the static registries, `RegistryDataLoader` loads `RegistryDataLoader.WORLDGEN_REGISTRIES` and then `RegistryDataLoader.DIMENSION_REGISTRIES`, the `WorldLoader.WorldDataSupplier` turns the fixed `level.dat` into a `PrimaryLevelData` through `LevelStorageSource.getLevelDataAndDimensions` — or, with no world data at all, `Main.createNewWorldData` builds one out of `server.properties` — and `ReloadableServerResources.loadResources` compiles the recipes, loot tables, functions and advancements. What comes back is a `WorldStem`. The threading is the part worth noticing. `WorldLoader.load` takes two executors: `Util.backgroundExecutor` for the work, and a main-thread executor for the stages that must be single-threaded. `Util.blockUntilDone` supplies the second by handing the loader a queue's *add* method and then draining that queue until the future completes. For the length of the world load the JVM main thread is an event loop, running the pack-opening stage and the final assembly itself between bouts of waiting on the workers. Two things then happen before the server object exists. With *--forceUpgrade* or *--recreateRegionFiles*, a `WorldUpgrader` rewrites every region file while *main* polls it once a second and logs a percentage. And either way `LevelStorageSource.LevelStorageAccess.saveDataTag` writes `level.dat` back out through a temp file, rotating the previous copy into `level.dat_old`. That is unconditional: a server started and killed one second later has already rewritten its world data. ## `MinecraftServer.spin`, and the last thing *main* does `MinecraftServer.spin` takes a factory rather than a server, and its order is the point. It builds the `Thread` object first, sets priority 8 on a machine with more than four processors, calls the factory *on the calling thread*, and only then starts the thread — so the whole of the `DedicatedServer` constructor runs on the JVM main thread, and the new thread cannot begin before there is a server for it to run. That constructor is real work. `MinecraftServer`'s own opens the server-wide `SavedDataStorage`, takes the `WorldData` and `WorldGenSettings` out of the stem ([creating a world](../worldgen/creating-a-world.md) is where the stem was built, by a screen or by *server.properties*), builds the `ServerConnectionListener`, the `PlayerDataStorage`, the `GameRules`, the `StructureTemplateManager` and the `PacketProcessor`, finalises recipe loading, and refuses outright a stem whose `LevelStem` registry has no overworld. `DedicatedServer`'s adds the `ServerTextFilter`, the `ServerLinks` built from the bug-report property, and — when *enable-code-of-conduct* is set — every *.txt* file under the *codeofconduct* folder, which throws if that folder is missing. A misconfigured code of conduct kills the server on the main thread, before a Server thread exists to be killed. Back in *main*, the factory has already applied *--port*, *--demo* and *--serverId* and, unless *--nogui* or a headless JVM says otherwise, opened the Swing window through `DedicatedServer.showGui`. A *Server Shutdown Thread* hook goes on the runtime — its whole body is [one `MinecraftServer.halt` call](how-a-server-dies.md) — and *main* returns. ## The Server thread wakes up, and can still fail twice `MinecraftServer.runServer` is the Server thread's body and its first act is `DedicatedServer.initServer`, which begins with the console: a daemon thread named *Server console handler* reading `System.in` line by line. It runs nothing itself. Each line becomes a `ConsoleInput` — the text plus a `CommandSourceStack` built there on the console thread — appended to a synchronized list that `DedicatedServer.handleConsoleInputs` drains from `DedicatedServer.tickConnection`, so a typed command executes inside a tick like every other command. Then the properties become fields: online mode, the local IP, the default game type, the port. `MinecraftServer.initializeKeyPair` generates the RSA pair that login encryption uses ([players and sessions](players-and-sessions.md)), and `ServerConnectionListener.startTcpServerListener` binds the port with a Netty server bootstrap. Two things can end the boot at this point, and they end it identically. | the failure | what the console says | the check | |---|---|---| | the port is already taken | *FAILED TO BIND TO PORT!* and the exception | `ServerConnectionListener.startTcpServerListener` throws, and `DedicatedServer.initServer` returns false | | a legacy user list survived conversion | *FAILED TO START THE SERVER AFTER ACCOUNT CONVERSION!* and the files to delete by hand | `OldUsersConverter.areOldUserlistsRemoved` looks for *banned-players.txt*, *banned-ips.txt*, *ops.txt* and *white-list.txt*, and is false if any of the four is still there | The conversion itself is `DedicatedServer.convertOldUsers`, which attempts five migrations — the two ban lists, the op list, the whitelist and the player save files — retrying each up to twice more, five seconds apart, and reporting whether *any* of them did something. The gate is the separate check above, so what stops the boot is the file nobody could convert. Either failure makes `MinecraftServer.runServer` throw, which lands in `MinecraftServer.runServer`'s own catch, writes a crash report and falls into the same *finally* that `/stop` reaches — a server that failed to bind still walks the whole shutdown path and releases `session.lock` on the way out ([how a server dies](how-a-server-dies.md)). Past them, a `DedicatedPlayerList` is built, offline names are resolved in the cache, and the log says *Preparing level "world"*. ## Building the levels `MinecraftServer.loadLevel` is three calls: `MinecraftServer.createLevels`, `MinecraftServer.forceDifficulty` — empty in the base class, and overridden only by `DedicatedServer.forceDifficulty`, which pushes *server.properties*' difficulty onto the world on every boot — and `MinecraftServer.prepareLevels`. `MinecraftServer.createLevels` builds the overworld first and by name, and it is the only level that is special. It gets the custom spawners — the `PhantomSpawner`, the `PatrolSpawner`, the `CatSpawner`, the `VillageSiege` and the `WanderingTraderSpawner` — and its `ServerLevelData` is the real `PrimaryLevelData` out of `level.dat`. With it in `MinecraftServer.levels` (a `LinkedHashMap`, so it stays first for every later walk over the levels), the scoreboard, the `CommandStorage` and the `Stopwatches` come out of the server-wide saved data. Every other `LevelStem` in the registry then gets a `ServerLevel` over a `DerivedLevelData`, a view of the overworld's data — which is why the time of day, the weather, the difficulty and the world spawn are one set of numbers every dimension shares ([level data and rules](../../reference/level-data-and-rules.md)). A brand-new world takes one detour, and it is the detour that actually generates terrain at boot. When `ServerLevelData.isInitialized` is false, `MinecraftServer.setInitialSpawn` asks the biome sampler for a spawn chunk, reads the generator's spawn height, and walks a spiral over the chunks five in each direction — the value `MinecraftServer.SPAWN_POSITION_SEARCH_RADIUS` names, though the method spells it as literals rather than reading it — calling `PlayerSpawnFinder.getSpawnPosInChunk` until one of them offers a standable block. The bonus chest is placed here if *--bonusChest* asked for one. Then the flag is set, and no later boot of that world repeats any of it. ## Preparing the levels, which prepares nothing `MinecraftServer.prepareLevels` replays a list. For each level a `ChunkLoadCounter` records which chunks are already `ChunkStatus.FULL`, calls `TicketStorage.activateAllDeactivatedTickets`, runs the distance manager again and counts what is new. The tickets it replays are the ones the last shutdown parked rather than dropped, and of the nine `TicketType`s exactly two carry `TicketType.FLAG_PERSIST` — `TicketType.FORCED`, from */forceload*, and `TicketType.PORTAL` — so those two are the only entries a *chunk_tickets* file contains ([tickets and loading](../world/tickets-and-loading.md)). The world spawn is kept by nothing. **Zero** — chunks `MinecraftServer.prepareLevels` loads on a world with no forceloads and no live portal ticket. What follows is `MinecraftServer.waitUntilNextTick` with the deadline set `MinecraftServer.PREPARE_LEVELS_DEFAULT_DELAY_NANOS` — 10 ms — out, repeated while `ChunkLoadCounter.pendingChunks` is above zero: the Server thread pumping its own queue in slices, the same way a tick waits for a chunk ([the server tick](server-tick.md)). With a total of zero it runs once and leaves. `MinecraftServer.updateMobSpawningFlags` and the effective respawn data are recomputed, and the step is over. ### What the console prints while nothing is prepared The output is worth reading literally. `LoggingLevelLoadListener` logs *Loading N persistent chunks...* when `LevelLoadListener.Stage.LOAD_INITIAL_CHUNKS` starts and *Time elapsed: N ms* when it finishes, with *Preparing spawn area: N%* — still the *menu.preparingSpawn* string, its percentage computed by `LevelLoadProgressTracker` — printed at most twice a second in between. On an ordinary world there is no in between: N is zero and the percentage line never gets a chance to run. Of the four values `LevelLoadListener.Stage` declares, `LevelLoadListener.Stage.PREPARE_GLOBAL_SPAWN` fires only on a world's first boot, `LevelLoadListener.Stage.LOAD_PLAYER_CHUNKS` belongs to a player joining rather than to boot — `PrepareSpawnTask`, in the configuration phase ([players and sessions](players-and-sessions.md)) — and `LevelLoadListener.Stage.START_SERVER` is declared and fired by nothing. > **For a 1.21-era reader.** There is no *spawnChunkRadius* game rule: > `GameRuleRegistryFix` deletes it out of any save that still carries one, and > the chunks around the world spawn are kept alive by whoever stands in them > and by nothing else. *spawn-protection* in `server.properties` survives and > is unrelated — it is a permission check in > `DedicatedServer.isUnderSpawnProtection`, not a loader. ## *Done* comes before the loop `DedicatedServer.initServer` logs *Done (1.284s)! For help, type "help"* the moment `MinecraftServer.loadLevel` returns — and only then starts the optional listeners. `QueryThreadGs4.create` runs if *enable-query*, `RconThread.create` if *enable-rcon*, a *Server Watchdog* thread if `DedicatedServer.getMaxTickLength` is above zero, JMX if *enable-jmx-monitoring*. After those come one `MinecraftServer.saveEverything` with flush and force and `NotificationManager.serverStarted` on the JSON-RPC feed. `DedicatedServer.initServer` returns true, `MinecraftServer.runServer` loads `server-icon.png` — the server directory's, or failing that the world's, and it must be 64 by 64 — builds the first `ServerStatus`, and enters the tick loop. So *Done* is a claim about the world being ready rather than about the server being reachable in every sense. The play port has been open since before the level loaded, and RCON is still not listening when the line prints. `MinecraftServer.isReady` — read by the singleplayer client's loading screen and by the JSON-RPC status method — is set at the bottom of the loop's first iteration, one tick later still. ## The threads startup leaves behind Boot creates every thread on this list and then hands the process to one of them. [Threads](../../reference/threads.md) has the complete set, including the pools and the situational ones. | thread | made by | daemon | what it may touch | |---|---|---|---| | **Server thread** | `MinecraftServer.spin`, from *main* | no | everything — it is the only thread allowed to change the world | | *Server console handler* | `DedicatedServer.initServer`, first statement | yes | `System.in` and the `ConsoleInput` list. The command itself runs on the Server thread | | *Server Watchdog* | `DedicatedServer.initServer`, only if `DedicatedServer.getMaxTickLength` is above zero | yes | reads `MinecraftServer.getNextTickTime`, the game rules and `ServerLevel.getWatchdogStats` off-thread, mid-tick | | *RCON Listener*, plus one *RCON Client* per connection | `RconThread.create`, after *Done* | no | TCP accept. `DedicatedServer.runCommand` hops the command onto the Server thread with `BlockableEventLoop.executeBlocking` | | *Query Listener* | `QueryThreadGs4.create`, after *Done* | no | a UDP status protocol, read-only | | *Management server IO* | `JsonRpc.create`, back in *main* | yes | the JSON-RPC socket. `ManagementServer.tick` runs from `DedicatedServer.tickServer` | | *Timer hack thread* | `Util.startTimerHackThread`, in *main* | yes | nothing. It sleeps and is never woken | Two rows carry a consequence for the other end of the story. `RconThread` and `QueryThreadGs4` are both `GenericThread`s, created from the Server thread and never marked daemon, so they are the only non-daemon threads in this table besides the Server thread itself — `Util.ioPool`'s *IO-Worker* threads, made outside it and squarely in the boot path, are non-daemon too — and each polls its socket with a half-second timeout so that it notices `GenericThread.running` going false. And `RconThread.create` returns nothing — logging that RCON is disabled — when *rcon.password* is empty or *rcon.port* is out of range, so setting *enable-rcon* on its own starts no thread at all. One more thing outlives boot without being a thread: `server.properties` stays live. Nineteen `DedicatedServerProperties` fields are `Settings.MutableValue`s, and `Settings.MutableValue.update` rebuilds the whole properties object and `DedicatedServerSettings.update` writes the file back, so */difficulty*, */whitelist*, the spawn-protection setter and the JSON-RPC settings calls all edit `server.properties` on disk while the server runs. ## Singleplayer boots the same server with a shorter list `IntegratedServer` uses the same `MinecraftServer.spin`, `MinecraftServer.runServer` and `MinecraftServer.loadLevel`, and almost nothing else on this page. `IntegratedServer.initServer` turns authentication on, generates the key pair, calls `MinecraftServer.loadLevel`, sets the MOTD from the host's name and the level name, saves once and returns true: no console thread, no TCP bind, no legacy conversion, no RCON, no query, no watchdog and no *Done* line. Its player list is built in the constructor rather than in `DedicatedServer.initServer`, and its `LevelLoadListener` is a `LevelLoadTracker` composed with the logging one, so the client's progress bar and a dedicated server's log lines are two readings of the same callbacks. The whole of it is constructed from `Minecraft.doWorldLoad` on the Render thread, by a caller that goes back to drawing frames rather than returning from *main* — [anatomy](../anatomy/anatomy.md) is that hand-off, and its diagram is the one to read for how the two loops meet. ## Where to look `server/Main` · `CrashReport.preload` · `Bootstrap.bootStrap` · `Eula` · `DedicatedServerSettings` · `DedicatedServerProperties` · `JsonRpc.create` · `LevelStorageSource.createDefault` · `LevelStorageSource.validateAndCreateAccess` · `LevelStorageSource.LevelStorageAccess` · `DirectoryLock.create` · `WorldLoader.load` · `WorldStem` · `Util.blockUntilDone` · `WorldUpgrader` · `LevelStorageSource.LevelStorageAccess.saveDataTag` · `MinecraftServer.spin` · `MinecraftServer.runServer` · `DedicatedServer.initServer` · `OldUsersConverter.areOldUserlistsRemoved` · `MinecraftServer.loadLevel` · `MinecraftServer.createLevels` · `MinecraftServer.setInitialSpawn` · `MinecraftServer.prepareLevels` · `ChunkLoadCounter` · `TicketStorage.activateAllDeactivatedTickets` · `LevelLoadListener` · `LoggingLevelLoadListener` · `RconThread.create` · `QueryThreadGs4.create` · `ServerWatchdog` · `IntegratedServer.initServer` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # How a server dies > Verified against **Minecraft 26.2** · Part III · `/stop` typed at the console, an exception out of the tick loop, and a tick that never ends — three endings that write three different amounts of your world to disk. An admin types `/stop`. The command sets one boolean and returns, and the tick already in progress carries on to its end. Everything a player would call *shutting down* — the players written, the unloads drained, `level.dat` rotated, `session.lock` released — happens afterwards, inside the *finally* of the loop that just exited, on the same Server thread that was ticking mobs a moment ago. Which is what makes the second ending strange, and worth a lecture: **a crash saves your world and the watchdog does not.** An exception out of the tick loop lands in that same *finally*, so a server that dies of a bad block entity writes exactly what `/stop` writes. `ServerWatchdog` instead calls `System.exit`, which runs the Server Shutdown Thread hook, which calls `MinecraftServer.halt` with *wait* true and waits for the Server thread to finish — the very thread wedged in the tick that tripped the watchdog. That wait never returns. Ten seconds later the watchdog's own scheduled `Runtime.halt` ends the JVM with nothing written. ## The cast | class | what it decides | thread | |---|---|---| | `MinecraftServer` | the three booleans, the tick loop, and the *finally* that is the whole of shutdown | Server | | `StopCommand` | that `/stop` is one call to `MinecraftServer.halt` with *wait* false, at `Commands.LEVEL_OWNERS` | Server | | `DedicatedServer` | what wraps the base teardown: the JSON-RPC notification, `Util.shutdownExecutors`, and the side threads in `DedicatedServer.onServerExit` | Server | | `ServerWatchdog` | that a tick past *max-tick-time* is a dead server, and that the JVM goes with it | Server Watchdog, a daemon | | `PlayerList` | that every player is written before anyone is disconnected | Server | | `ServerChunkCache` · `ChunkMap` | when the world is quiet enough to stop draining, and what a flush save means | Server, with the writes on the IO pool | | `LevelStorageSource.LevelStorageAccess` | `level.dat` and the `DirectoryLock` on `session.lock` | Server | | `Util` | the process-wide pools, and the three-second grace each of the two it shuts down gets | any | ## Three endings, side by side | | `/stop` | a tick-loop crash | a watchdog kill | |---|---|---|---| | **what clears `MinecraftServer.running`** | `MinecraftServer.halt`, with *wait* false from `StopCommand`, the JSON-RPC state service or the singleplayer host logging out, with *wait* true from the shutdown hook and the server GUI's close button | nothing: the loop is left by the throw, not by the condition | the shutdown hook, eventually — `System.exit` runs it and it calls `MinecraftServer.halt` with *wait* true | | **does `MinecraftServer.runServer`'s *finally* run** | yes, on the Server thread | yes, on the Server thread, after the crash report | no: the Server thread never leaves the tick | | **are players saved** | yes, `PlayerList.saveAll` then `PlayerList.removeAll` | yes, identically | no | | **are chunks saved** | yes: the unload drain, then `MinecraftServer.saveAllChunks` with *flush* | yes, identically | no — only what the last autosave happened to write | | **is `level.dat` written** | yes, `LevelStorageSource.LevelStorageAccess.saveDataTag` | yes, identically | no | | **is `session.lock` released** | yes, `LevelStorageSource.LevelStorageAccess.close` drops the `DirectoryLock` | yes, identically | not by the game — the OS drops it when the process dies | | **is a crash report written** | no | yes, into *crash-reports/*, from `MinecraftServer.constructOrExtractCrashReport` | yes, into *crash-reports/*, from `ServerWatchdog.createWatchdogCrashReport`, before the exit | | **what ends the JVM** | nothing explicit: the Server thread returns and no non-daemon thread is left | the same | `Runtime.halt` from the watchdog's own timer, ten seconds after its `System.exit` | The first two columns differ in three of the eight rows: what clears the flag, when the *finally* runs relative to the crash report, and whether there is a crash report at all. The third differs from the first in all eight, and the rest of this page is why. ## `/stop`, in full ```mermaid sequenceDiagram participant SC as StopCommand participant MS as MinecraftServer participant PL as PlayerList participant SL as ServerLevel participant SCC as ServerChunkCache participant LSA as LevelStorageSource.LevelStorageAccess participant Disk SC->>MS: halt with wait false, so running becomes false Note over MS: the tick in progress finishes, then the loop condition fails MS->>MS: stopped = true, then stopServer, from runServer's finally MS->>MS: PacketProcessor.close, then ServerConnectionListener.stop MS->>PL: saveAll, then removeAll PL->>Disk: each player's dat file, stats and advancements MS->>SL: noSave cleared on every level loop while any ChunkMap.hasWork MS->>SCC: the deadline is pushed one millisecond out, then deactivateTicketsOnClosing and tick end MS->>SL: saveAllChunks with flush, reaching ChunkMap.saveAllChunks SL->>Disk: region files, entities, poi, and the chunk_tickets saved data MS->>LSA: saveDataTag, level.dat built into a temp file LSA->>Disk: the temp file replaces level.dat, the old one rotated to level.dat_old MS->>MS: savedDataStorage.saveAndJoin, after level.dat and not before MS->>SL: close, ServerChunkCache.close then the entity manager MS->>LSA: close, releasing the DirectoryLock on session.lock MS->>MS: Util.shutdownExecutors, then onServerExit stops RCON and query Note over MS: the Server thread returns, and no non-daemon thread is left ``` ### The command is a flag `StopCommand` registers one literal at `Commands.LEVEL_OWNERS`, sends *commands.stop.stopping* and calls `MinecraftServer.halt` with *wait* false. That call assigns `MinecraftServer.running` and returns. Nothing else happens on that line of the console: the tick that was running the command finishes its entities, its block entities and its packet flush, and the loop condition at the top of `MinecraftServer.runServer` fails on the next pass. Five other places on this side of the jar call the same method: the server GUI's window-close listener and `Main`'s shutdown hook, both with *wait* true, so that they block until the Server thread has finished; the JSON-RPC management API's `MinecraftServerStateServiceImpl`, which takes the flag from its caller; `ServerCommonPacketListenerImpl.onDisconnect`, which stops a singleplayer server when its host logs out; and `GameTestServer`, which halts itself when its test run is over. The client adds three more of its own. Teardown itself is the loop's *finally*. `MinecraftServer.runServer` sets `MinecraftServer.stopped` and calls `MinecraftServer.stopServer`, then calls `MinecraftServer.onServerExit` from a nested *finally*, so that a teardown which throws still stops the side threads. `DedicatedServer.stopServer` wraps the base with `NotificationManager.serverShuttingDown` before and `Util.shutdownExecutors` after. ### The front door closes, the guests do not leave `PacketProcessor.close` is first, before anything is even logged. Afterwards `PacketProcessor.scheduleIfPossible` refuses a packet a Netty thread has just decoded, and `PacketProcessor.processQueuedPackets` returns without draining — so packets already in the queue go the same way as the ones still arriving. Then `ServerConnectionListener.stop` closes the channels it *bound*, and only those: closing a Netty parent channel does not close the connections accepted through it. Live sessions are severed one step later by `PlayerList.removeAll`, with the *multiplayer.disconnect.server_shutdown* reason. A connection still in handshake, login or configuration has no `ServerPlayer` and is in neither list, so it is closed by neither, and simply dies with the process ([players and sessions](players-and-sessions.md)). `MinecraftServer.stopped` also changes how work is accepted: `MinecraftServer.executeIfPossible` rejects anything new outright, and `MinecraftServer.scheduleExecutables` reports false, so a caller reaching `BlockableEventLoop.execute` from another thread runs its task inline rather than queueing it for a loop that has stopped looping. ### Players before chunks, and never `MinecraftServer.saveEverything` Shutdown does not use the save entry point everything else uses. `MinecraftServer.saveEverything` — autosave, `/save-all`, the integrated server's pause, the JSON-RPC save call — is players *then* chunks in one call. `MinecraftServer.stopServer` does the two halves by hand: `PlayerList.saveAll` (each player's data through `PlayerDataStorage`, plus their `ServerStatsCounter` and `PlayerAdvancements`), then `PlayerList.removeAll`, and only much later `MinecraftServer.saveAllChunks`. `PlayerList.removeAll` is thinner than its name: it disconnects each connection and nothing else, so the tickets those players hold are *not* what goes with them. What lets the next step finish is `ServerChunkCache.deactivateTicketsOnClosing`, called on every level inside the drain loop itself. `ServerLevel.noSave` is then cleared on every level. `/save-off` does not survive `/stop`. ### The drain `ChunkMap.hasWork` is the question, and it is a broad one — nine things in one *or*: pending light, pending unloads, a non-empty updating map, POI work, chunks queued to drop, a non-empty unload queue, the worldgen and light dispatchers, and — the reason the loop terminates at all — `DistanceManager.hasTickets`. While any level answers yes, the server pushes the tick deadline one millisecond out, calls `ServerChunkCache.deactivateTicketsOnClosing` and `ServerChunkCache.tick` on each level, and runs `MinecraftServer.waitUntilNextTick`, which drains the main-thread queue and polls each level's chunk executor for what is left of that millisecond. **One millisecond** — each slice of the drain, so unloads and their saves proceed while the main-thread queue keeps taking chunk results. `TicketStorage.deactivateTicketsOnClosing` moves every ticket except `TicketType.UNKNOWN` into a parked map. Parked tickets stop holding chunks — `TicketStorage.hasTickets` counts only the live map, which is how the loop ends — but they are not forgotten. `TicketStorage.packTickets` writes both maps, and the types that `TicketType.persist`, forced and portal, go into the level's *chunk_tickets* saved data. On the next boot they load back parked and `TicketStorage.activateAllDeactivatedTickets` re-arms them during `MinecraftServer.prepareLevels` ([tickets and loading](../world/tickets-and-loading.md), [starting a server](starting-a-server.md)). ### The flush save `MinecraftServer.saveAllChunks` with *flush* true is the real save. The scoreboard is pushed into its saved data, then each `ServerLevel.save`: `ServerLevel.saveLevelData` joins the level's own `SavedDataStorage`, and `ServerChunkCache.save` runs the distance manager once more before `ChunkMap.saveAllChunks` in flush mode. That last one is a *loop*, not a pass — every holder that `ChunkHolder.wasAccessibleSinceLastSave`, waited on with `BlockableEventLoop.managedBlock` until `ChunkHolder.isReadyForSaving`, repeated until a whole round saves nothing new — and then `SectionStorage.flushAll` for the POI sections, the unloads processed, and `SimpleRegionStorage.synchronize` joined so that the `IOWorker` has actually put the bytes down ([chunk storage](../world/chunk-storage.md)). Entities follow, through the level's `PersistentEntitySectionManager`. `level.dat` is written the same way at every save, flush or not: `LevelStorageSource.LevelStorageAccess.saveDataTag` builds the tag from `PrimaryLevelData.createTag`, wraps it under *Data*, writes it gzipped to a temp file in the world directory with `NbtIo.writeCompressed`, and `Util.safeReplaceFile` swaps it in, rotating the previous file to `level.dat_old`. Only after that does the *server-wide* `SavedDataStorage` get its `SavedDataStorage.saveAndJoin`. There are two tiers of saved data, and they are flushed at opposite ends of this section. ### The closes, and the last thread `ServerLevel.close` is `ServerChunkCache.close` — which saves once more, then closes the level's saved data, the `ThreadedLevelLightEngine` and `ChunkMap` — followed by the entity manager. Then the server's own `SavedDataStorage` (whose `SavedDataStorage.close` is itself a final `SavedDataStorage.saveAndJoin`), the `MinecraftServer.ReloadableResources`, and last `LevelStorageSource.LevelStorageAccess.close`, which releases the `DirectoryLock`. From that moment the world is openable by anything else. `Util.shutdownExecutors` then stops `Util.backgroundExecutor` and `Util.ioPool` with a three-second grace each, inside `DedicatedServer.stopServer` and so *before* `DedicatedServer.onServerExit` — nothing may need a worker after that point. `Util.nonCriticalIoPool` is untouched, and survives only because its threads are daemons. `DedicatedServer.onServerExit` closes the text filter and the GUI and stops `RconThread`, `QueryThreadGs4` and the `ManagementServer`. There is no `System.exit` anywhere on this path, and none is needed. Every other thread the server started is a daemon — the console reader, the Netty groups, the management server's group, the watchdog — except the RCON and query threads, which `GenericThread.stop` joins here in one-second slices, and the IO pool's workers, which went a step earlier with `Util.shutdownExecutors`. So when `MinecraftServer.runServer` returns, the Server thread is the last one left, and the JVM ends because there is nothing to keep it ([the thread reference](../../reference/threads.md)). ## The crash that saves `MinecraftServer.runServer` wraps the entire loop, `DedicatedServer.initServer` included. Anything thrown out of a tick — a block entity, a mob's AI, a command, a packet handler that did not catch its own trouble — is logged, turned into a report, saved, and then falls into the same *finally*. `MinecraftServer.constructOrExtractCrashReport` walks the cause chain and keeps the *innermost* `ReportedException` it finds, using that exception's own report and noting the outer throwable under a *Wrapped in* category. A throwable with no `ReportedException` anywhere in it becomes a fresh report titled *Exception in server tick loop*. Either way `MinecraftServer.fillSystemReport` fills it in — the value of `MinecraftServer.running`, the player count and roster, the selected and available data packs, the enabled feature flags, the world-generation lifecycle, the world seed, and the contents of the server's `SuppressedExceptionCollector`, which has been quietly watching every chunk load failure, chunk save failure and packet-handler exception since boot — keeping the latest eight of them in full, and a running count of the rest. `DedicatedServer.fillServerSystemReport` adds two lines, the modded status and the words *Dedicated Server*. The file lands in *crash-reports/* under `MinecraftServer.getServerDirectory`, named by `Util.getFilenameFormattedDateTime`. `MinecraftServer.onServerCrash` is a hook the dedicated server does not override. **A crash on another thread arrives here too.** `BlockableEventLoop` keeps one static parked report. `Util.onThreadException` — the uncaught-exception handler on every `Util.backgroundExecutor` and IO-pool thread — and `GenerationChunkHolder`, when a generation step completes exceptionally, both call `BlockableEventLoop.relayDelayCrash`, which parks the report or suppresses the new one under a report already parked. The next `BlockableEventLoop.pollTask` on a loop constructed with crash propagation throws it as a `ReportedException`, and the dedicated server is constructed that way. So a worker that dies does not die silently: it dies as a tick-loop crash, on the Server thread, at whatever moment that thread next looks for a task ([the server tick](server-tick.md)). The integrated server is constructed with propagation off and hands its report to the client instead, through `IntegratedServer.onServerCrash`. ## The watchdog that does not ```mermaid sequenceDiagram participant SW as ServerWatchdog participant MS as MinecraftServer participant JVM participant Hook as Server Shutdown Thread Note over MS: wedged inside one tick, past max-tick-time SW->>MS: getNextTickTime, a deadline now far in the past SW->>SW: createWatchdogCrashReport, every thread dumped, the Server thread's stack grafted on SW->>MS: fillSystemReport, read off-thread while the tick is still running SW->>JVM: schedule Runtime.halt for ten seconds from now SW->>JVM: System.exit JVM->>Hook: run the shutdown hooks Hook->>MS: halt with wait true, running becomes false Hook->>MS: then waits for the Server thread, which is the wedged one MS-->>Hook: nothing, because the tick never returns Note over JVM: ten seconds later, Runtime.halt, nothing written ``` `ServerWatchdog` is a daemon thread started by `DedicatedServer.initServer` whenever `DedicatedServer.getMaxTickLength` is positive — that is `DedicatedServerProperties.maxTickTime`, *max-tick-time*, default sixty thousand milliseconds, and setting it to zero or less means the thread is never created and there is no backstop at all. Its loop is short: while `MinecraftServer.isRunning`, compare `Util.getNanos` with `MinecraftServer.getNextTickTime`, then sleep exactly until the earliest moment a violation could be true. What it compares matters. `MinecraftServer.getNextTickTime` is the *deadline* the loop set for the tick it is running, not a timestamp of when that tick began, and the tick loop advances it before each tick and again when catching up after an overload. The watchdog fires when the server is that far past where it promised to be. The report comes first, and it is the good part of the design. `ServerWatchdog.createWatchdogCrashReport` dumps every thread in the JVM, sorts them daemon-last, appends the lot as a *Thread Dump* category, and grafts the Server thread's stack trace onto a synthetic error — so the report's headline stack is the code that hung. `MinecraftServer.fillSystemReport` adds the usual, plus a *Performance stats* category holding the random-tick game rule and `ServerLevel.getWatchdogStats` for every level: players, entities by type, block entities by type, block and fluid tick counts, chunk source stats. All of it is read from another thread with no synchronisation whatsoever, off a world that is mid-tick — which is exactly the trade the class makes, because the alternative is asking a wedged thread for the answer. It goes to real stdout through `Bootstrap.realStdoutPrintln` and to *crash-reports/* like any other report. Then the deadlock. The watchdog schedules `Runtime.halt` on a timer and calls `System.exit`, which runs the registered shutdown hooks — including the "Server Shutdown Thread" that `Main` registered at boot, whose whole body is `MinecraftServer.halt` with *wait* true. That sets `MinecraftServer.running` false, which the wedged tick will never read, and then waits for the Server thread to end. It does not end. `System.exit` will not return until its hooks do, so the JVM sits there until the watchdog's timer fires. **Ten seconds** — from the watchdog's `System.exit` to its `Runtime.halt` (`ServerWatchdog.MAX_SHUTDOWN_TIME`), and the world is not touched in any of them. The watchdog is a liveness backstop, and reading it as a safe stop gets the guarantee backwards. Whether anything guards shutdown depends on how shutdown was reached, because the watchdog loops while `MinecraftServer.running` is true and only `MinecraftServer.halt` ever clears that flag. After `/stop` it is cleared before the drain begins, so a server stuck on "Saving chunks" is stuck with no watchdog left watching it. After a crash nothing clears it — the crash path never calls `MinecraftServer.halt` — so the watchdog is still counting. The drain loop survives that by resetting the deadline every pass; the flush save that follows it does not reset anything, so a slow enough save after a crash can be shot by the watchdog mid-write. ## Ctrl-C, the window, and a singleplayer world Ctrl-C at the console and a *SIGTERM* from a service manager are the same thing as far as the game is concerned: the JVM runs its shutdown hooks, and the one `Main` registered calls `MinecraftServer.halt` with *wait* true. The contrast with the watchdog is only in the health of the thread being waited for. Here the Server thread is fine, notices the cleared flag at the top of its next tick, and runs the entire `/stop` teardown while the hook thread waits. A Ctrl-C on a healthy server *is* a `/stop`, and the JVM does not exit until the world is on disk. The server GUI's window-close button does the same thing from the AWT thread. Singleplayer ends on a poll. `Minecraft.disconnect` — reached from *Save and Quit*, from a disconnect, and from `Minecraft.emergencySave` — closes the client's connection, then calls `IntegratedServer.halt` with *wait* false. That override first uses `BlockableEventLoop.executeBlocking` to remove every player who is not the host, then clears `MinecraftServer.running` and stops the LAN pinger. The client then puts up a `GenericMessageScreen` reading `Gui.SAVING_LEVEL` and calls `Minecraft.renderFrame` in a loop while `MinecraftServer.isShutdown` is false. The "Saving world" screen is not a progress bar and is not driven by the server: it is a render loop spinning on one question, *is the Server thread dead yet* ([the client loop](../client/the-client-loop.md)). Closing the game window reaches the same place by a different road: `Window.shouldClose` makes `Minecraft.runTick` call `Minecraft.stop`, which ends the frame loop, and `Main` then calls `Minecraft.exitWorldAndClose` on its way out. The client's "Client Shutdown Thread" is a JVM shutdown hook rather than that path — the backstop for a kill signal — and after an ordinary exit it finds `Minecraft.singleplayerServer` already null. The integrated server never calls `Util.shutdownExecutors`. `IntegratedServer.stopServer` tears down published state and defers to the base. The client owns those pools and shuts them down at the very end of its own life, long after the world is closed. ## Three booleans and a question `MinecraftServer.running` is volatile and is the loop condition, and it is the only one of the three that anything sets in order to stop the server. `MinecraftServer.stopped` is a plain field set in the *finally* just before teardown, read from other threads through `MinecraftServer.isStopped`, and it is what closes the task queue. `MinecraftServer.isReady` is volatile, set at the bottom of every loop iteration, and is not what prints *Done* — that is logged in `DedicatedServer.initServer`, before the loop is entered. `MinecraftServer.isShutdown` is the odd one out, and is not a field at all: it asks whether the Server thread is still alive. Nothing sets it, nothing can lie about it, and it stays false through the whole teardown whichever ending is running — which is precisely why the singleplayer client waits on it rather than on `MinecraftServer.isStopped`, which goes true at the *start* of teardown, when nothing has been saved yet. ## What you lose if you kill the process Ordinary autosave is `MinecraftServer.saveEverything` with neither *flush* nor *force*, every 6000 ticks — five minutes of game clock, floored at 100 ticks. It writes every player, every dirty chunk — `ChunkMap.saveAllChunks` clears `ChunkMap.nextChunkSaveTime` on the way in, so the ten-second per-chunk spacing that throttles ordinary saving never gates an autosave — and, unconditionally, `level.dat`, followed by a scheduled write of the level's `SavedData`. Between the two, the spawn point and the world time (in `level.dat`) and the weather, the game rules and the world clocks (each its own `SavedData` file — *weather*, *game_rules*, *world_clocks*) on disk are never more than one autosave stale, even on a server nobody ever stops cleanly. Everything else — a chest filled two minutes ago, a mob that walked into a new chunk, an inventory change — lives in the `LevelChunk` and the entity sections until something saves them. That gives an honest answer per ending. After `/stop` or a tick-loop crash, nothing is lost: the drain, the flush save and the joined `IOWorker` mean the process does not end until the bytes are down. The crash has one asterisk the clean stop does not — the watchdog is still armed all the way through it — but short of a save slow enough to trip it, both endings land the same. After a watchdog kill, or a *kill -9*, or a power cut, you lose everything since the last autosave, plus anything still queued inside the `IOWorker` — those writes run on `Util.ioPool`, and `Runtime.halt` does not wait for a pool. What you never lose is access to the world. `session.lock` is an OS advisory lock taken with `FileChannel.tryLock`, not a file whose contents mean anything, and the operating system releases it when the process dies however it dies. A world left behind by a killed server opens on the next start. Copying a world directory copies a `session.lock` that means nothing at all. Individual failures are quieter than any of this. A chunk that cannot be written calls `MinecraftServer.reportChunkSaveFailure`: logged, added to the `SuppressedExceptionCollector` that the next crash report will print, written out as its own `ReportType.CHUNK_IO_ERROR` file under *debug/*, and followed by a disk-space check. The tick does not stop, the server does not stop, and the only sign at the time is a line in the log. ## Where to look `StopCommand` · `MinecraftServer.halt` · `MinecraftServer.runServer` · `MinecraftServer.constructOrExtractCrashReport` · `MinecraftServer.stopServer` · `MinecraftServer.saveAllChunks` · `MinecraftServer.saveEverything` · `ServerLevel.save` · `ServerChunkCache.save` · `ChunkMap.saveAllChunks` · `ChunkMap.hasWork` · `TicketStorage.deactivateTicketsOnClosing` · `LevelStorageSource.LevelStorageAccess.saveDataTag` · `LevelStorageSource.LevelStorageAccess.close` · `DirectoryLock` · `Util.shutdownExecutors` · `DedicatedServer.stopServer` · `DedicatedServer.onServerExit` · `ServerWatchdog.run` · `ServerWatchdog.createWatchdogCrashReport` · `Main` (the shutdown hook) · `BlockableEventLoop.relayDelayCrash` · `IntegratedServer.halt` · `Minecraft.disconnect` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # IV · The world > Verified against **Minecraft 26.2** · Part IV · The machinery that turns a place you have walked to into a place that exists: chunks made, lit, sent, saved and forgotten, and the four side-systems that make the world they hold feel alive. Part III was one thread going round. This part is what it goes round *on*. A world is too big to hold, so the server holds a moving window of it, and almost everything in this part exists to decide the window's edge: which chunks are worth building, how far past the edge to build them, which of them tick, which of them are sent to you, and when one is finally written down and let go. A player recognises the part by its edge — the ring of half-made terrain past render distance, the mobs that stop moving when you fly away from them, the *Saving world* bar. Almost none of that edge is one number: **render distance, simulation distance and the mob-spawning radius are three different radii, answered by three different mechanisms, and only two of them are settings.** ## The shape of the part Part IV is a conveyor with a vocabulary page in front of it. Four pages are the conveyor and they hand a chunk to each other in order; the fifth page defines the thing being handed. The other five are not on the line at all — one is what the place and the hour decide, and four are about the world the conveyor delivers. ```mermaid flowchart TD CA["Chunk anatomy: sections, palettes, heightmaps"] TL["Tickets and loading: which chunk, at what level"] GP["The generation pipeline: EMPTY to FULL"] LI["Lighting: two 4-bit fields, off the tick"] CS["Chunk storage: copy, encode, sectors"] LC["a live LevelChunk in a ticking world"] EA["Environment attributes: what the place and the hour decide"] ST["Scheduled ticks: the appointment book"] FL["Fluids: the book's biggest customer"] GV["Game events and vibrations: what just happened"] PI["Points of interest: what is worth going to"] CA -- "the vocabulary every page below spends" --> TL TL -- "a holder, a ceiling, three futures" --> GP GP -- "a chunk that still needs its light finished" --> LI LI -- "sections dirtied, one packet of them" --> LC LC -- "the level rises past 44, nobody needs it" --> CS CS -- "a ticket wants it back, and it is read in" --> TL LC --> ST ST -- "one customer, big enough for its own page" --> FL LC --> GV LC --> PI EA -- "read by fluids and by the villagers, and by Part III" --> LC ``` What the part hands forward, and what it does not: [blocks and states](../blocks/blocks-and-states.md) in Part V assumes the section and palette model [chunk anatomy](chunk-anatomy.md) defines, and Part XII's terrain generation is the cargo on the conveyor [the generation pipeline](chunk-generation-pipeline.md) describes. Neither is a dependency of this part; both are parts that depend on it. ## Before you start [The server tick](../server/server-tick.md) and [the level tick](../server/server-level-tick.md). Almost everything here happens on the Server thread inside that loop, or on a worker the loop is waiting for — the IO lane below is the exception the storage page is about — and the level tick is where the chunk source is asked to do its five things. Part II's [codecs](../foundations/codecs-nbt-json.md) and [registries](../foundations/identifiers-and-registries.md) are assumed wherever a chunk is written to disk or a type is looked up by name, and [tags](../foundations/tags.md) wherever a behaviour is defined by a set the data pack owns — which is most of what the last two pages do. Nothing in this part needs Part V or beyond. ## Watch in this order Lectures two to six are the chain — nothing later in it can be watched first. The first is off the chain on purpose, and the last four can be watched in any order once you have the vocabulary page. 1. [Environment attributes and timelines](environment-attributes-and-timelines.md) — the one page here that depends on nothing else in the part, and the page [the level tick](../server/server-level-tick.md) already asked you to watch. Whether lava flows fast, what colour the sky is and when a villager goes to work are one mechanism. The night does not *set* the sky's colour — it multiplies whatever the biome produced. 2. [Chunk anatomy](chunk-anatomy.md) — what a chunk is made of, down to the bit storage. The vocabulary the rest of the part spends. A section holding two block states costs exactly what one holding sixteen costs. 3. [Tickets and loading](tickets-and-loading.md) — a player takes one step east and a column twenty-one chunks wide is asked for. Nothing ever asks whether a chunk is loaded: it asks for a *level*, and two graphs reading one ticket store answer different questions about it. 4. [The chunk generation pipeline](chunk-generation-pipeline.md) — one chunk from *EMPTY* to *FULL* through twelve statuses and a pyramid of neighbour requirements. Asking for one chunk asks for 529. 5. [Lighting](lighting.md) — a torch is placed. Two 4-bit fields flooded on a worker and published as a copy. There is no light thread, and no light phase of the tick. 6. [Chunk storage](chunk-storage.md) — a chunk nobody needs is copied, encoded and written, on three different threads, and the save path waits for none of it. Most of your world's writes are ones nobody asked for. 7. [Scheduled ticks](scheduled-ticks.md) — how anything happens *later*: an appointment book of two queues per chunk, and a dedup rule that quietly drops the second appointment even when it is sooner. 8. [Fluids](fluids.md) — a bucket of water on flat stone. Water finds a hole four blocks away because every side runs its own search, and a side the water cannot even enter still votes on where the rest of it goes. 9. [Game events and vibrations](game-events-and-vibrations.md) — a footstep reaches a sculk sensor, through a cascade of tests that is most of the lecture. The sensor hears you one tick late by design. 10. [Points of interest](points-of-interest.md) — a villager claims a bed from 48 blocks away, the moment a path to it exists. Going to sleep in it tells the index nothing, and the one behaviour that acts on the flag can only take a claim away. ## Reference this part uses [Level data and rules](../../reference/level-data-and-rules.md) — who owns the seed, the spawn, the rules, the border and the dimensions, and which file remembers each. [Game rules](../../reference/gamerules.md) — five of which this part reads. [Math and primitives](../../reference/math-and-primitives.md) — `ChunkPos` and `SectionPos`, and the packings the conveyor pages assume. [Block update flags](../../reference/block-update-flags.md) — the flag word three pages here spend. [Threads](../../reference/threads.md) — the worker pool and the IO lane. [Diagram lanes](../../reference/lanes.md). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Environment attributes and timelines > Verified against **Minecraft 26.2** · Part IV · The trace: dusk falls — one value resolved through a stack of layers, on the server and again on the client. At tick 12542 on the overworld clock the sun goes under, and three things a player would never connect happen at once: the sky over a taiga slides from its pale blue towards black, the sky over a pale garden slides from its grey towards black by the same proportion, and every mob in the open stops being in danger of burning at dawn. In 26.2 those are one mechanism. An **environment attribute** is a named, typed, registered property of the world — `EnvironmentAttributes` puts 48 of them in `BuiltInRegistries.ENVIRONMENT_ATTRIBUTE` — and the world answers one for a position and an instant by running a short stack of **layers** over the attribute's default value: the dimension, the biome, the **timelines**, the weather. The surprise is in what a timeline holds. The day timeline does not know what colour a taiga sky is and never learns: its keyframes are not values but *modifier arguments*, and the night segment of the sky track is a multiply. **Night does not set the sky's colour — it multiplies whatever the biome produced**, which is how one data-driven curve darkens every overworld biome correctly without being told about any of them. > **For a 1.21-era reader.** The gameplay booleans you would look for on > `DimensionType` are entries in `DimensionType.attributes` now: where the > nether once said *ultrawarm*, *bed_works*, *piglin_safe* and > *respawn_anchor_works*, it sets `EnvironmentAttributes.FAST_LAVA`, > `EnvironmentAttributes.WATER_EVAPORATES`, `EnvironmentAttributes.BED_RULE`, > `EnvironmentAttributes.PIGLINS_ZOMBIFY` and > `EnvironmentAttributes.RESPAWN_ANCHOR_WORKS`, while > `DimensionType.hasFixedTime` and `DimensionType.ambientLight` stayed put. > `BiomeSpecialEffects` has shrunk to the water, foliage and grass tints — > sky and fog are `Biome.getAttributes` — and the villager *Schedule* class > is `Timelines.VILLAGER_SCHEDULE`, a data-pack `Timeline` like any other. ## The cast | class | what it decides | thread | |---|---|---| | `EnvironmentAttribute` | the key: a type, a default, an `AttributeRange` and three flags. It holds no value and no state | — (registry constant) | | `EnvironmentAttributeMap` | what one dimension or one biome contributes — a modifier and an argument per attribute, never a bare value | — (loaded from data) | | `EnvironmentAttributeSystem` | the baked per-level resolver: one `EnvironmentAttributeSystem.ValueSampler` for each attribute some layer mentions | built in the level constructor, read on that level's thread | | `Timeline` | a clock, an optional period, one `AttributeTrack` per attribute, and the named instants on that clock | — (loaded from data) | | `AttributeTrackSampler` | one track baked against a clock, with a one-tick cache of the sampled argument | Server or Render | | `ServerClockManager` | **the owner of day time** — one `ServerClockManager.ClockInstance` per registered `WorldClock`, saved as *world_clocks* | Server | | `ClientClockManager` | the client's copy: free-runs each clock forward between packets | Render | | `EnvironmentAttributeProbe` | the client's smoothing layer, living on `Camera`: 216 biome samples a tick, a lerp every frame | Render | ## The stack a value falls through ```mermaid flowchart BT DEF["the attribute's own EnvironmentAttribute.defaultValue enters here"] --> DIM DIM["1 — dimension: DimensionType.attributes as EnvironmentAttributeLayer.Constant, folded into the baked base by EnvironmentAttributeSystem.bakeLayerSampler"] --> BIO BIO["2 — biome: one EnvironmentAttributeLayer.Positional for each attribute any biome in the registry mentions"] --> TLS TLS["3 — timelines: DimensionType.timelines, one EnvironmentAttributeLayer.TimeBased for each track a timeline carries"] --> WEA WEA["4 — weather: WeatherAttributes.addBuiltinLayers, added only when Level.canHaveWeather"] --> LF1 LF1["client only — the sky colour lerped toward the lightning flash colour"] --> LF2 LF2["client only — the sky light factor pinned to 1 while the flash lasts"] --> SAN SAN["EnvironmentAttribute.sanitizeValue clamps the result to the attribute's AttributeRange"] ``` `EnvironmentAttributeSystem.Builder.addDefaultLayers` stacks those four in that order and only that order. There is no priority number anywhere and no ordering data: a biome cannot run before its dimension, and weather is the last word on the server — on the client the two lightning-flash layers sit above it. The stack is *per attribute*, too — an attribute nothing in the level mentions has no `EnvironmentAttributeSystem.ValueSampler` at all, and `EnvironmentAttributeSystem.getValue` hands back its default. And it is baked once: the whole thing is built in the `ServerLevel` and `ClientLevel` constructors and never rebuilt, the only writer being `ServerLevel.setEnvironmentAttributes`, which is deprecated, marked for testing and called only from `TestEnvironmentDefinition`. A data-pack reload does not touch it. Each rung earns its shape. The dimension's is an `EnvironmentAttributeLayer.Constant`, and `EnvironmentAttributeSystem.bakeLayerSampler` walks off the front of the list folding every *leading* constant into one baked base value, so a dimension costs nothing at read time. The biome's is an `EnvironmentAttributeLayer.Positional`, added once per attribute that any biome in the whole registry mentions — in vanilla, eleven attributes across sixty-six biome files, with *visual/sky_color* in fifty-six of them. Timeline layers are `EnvironmentAttributeLayer.TimeBased`, one per track, and so is weather: one for each of the nine attributes named by `WeatherAttributes.RAIN` or `WeatherAttributes.THUNDER`, blending rain in first and thunder second — rain at `Level.getRainLevel` *minus* the thunder level, so a thunderstorm never counts twice. `Level.canHaveWeather` wants sky light, no ceiling and not the End, so a rain-free dimension does not carry a weather layer that does nothing: it carries none. `ClientLevel` adds two more after those four, both keyed on the lightning flash that `LightningBolt` sets through `Level.setSkyFlashTime`. One lerps `EnvironmentAttributes.SKY_COLOR` a fixed 22% toward a pale blue-white, the other pins `EnvironmentAttributes.SKY_LIGHT_FACTOR` to 1 outright, and both read the flash through the accessibility option *Hide Lightning Flashes*, which reports a flash time of zero. (The End's sky flash is a different thing entirely — `EndFlashState`, read by the renderers rather than through the stack; see [lightmap, fog and sky](../rendering/lightmap-fog-and-sky.md).) Two rules police what may enter. `Biome.getAttributes` is read through `EnvironmentAttributeMap.CODEC_ONLY_POSITIONAL`, which makes it a load error for a biome to name a non-positional attribute — so no biome can locally change sky light level or lava speed. And `WorldGenRegion.environmentAttributes` returns `EnvironmentAttributeReader.EMPTY`, answering everything with its default: a feature that asks about the environment during generation gets a constant, deliberately, because generation must not depend on the hour. ## Arguments, not values `EnvironmentAttributeMap` is not a map of values. It is a map of `EnvironmentAttributeMap.Entry`, and an entry is an *argument* plus an `AttributeModifier`. `EnvironmentAttributeMap.Builder.set` is sugar for `EnvironmentAttributeMap.Builder.modify` with `AttributeModifier.override`; the interesting entries multiply, blend, maximise or *or* into whatever the layer below produced. `AttributeTrack` is the same shape — a modifier plus a `KeyframeTrack` of arguments — which is why `Timeline.Builder.addTrack` is only the override case and `Timeline.Builder.addModifierTrack` is the general one, and why the day timeline can say *multiply sky light by 0.267 at night* rather than *sky light is 4 at night*. In a data pack the shorthand shows: an entry written as a bare value means override, one written as an object carries a *modifier* and an *argument*. ### What a type allows `AttributeType` is a record of a value codec, an `AttributeType.modifierLibrary` of the operations legal on that type, and **four** separate `LerpFunction`s, one for each way two values of it can meet. | lerp slot | used when | |---|---| | `AttributeType.keyframeLerp` | between two keyframes of a timeline track that overrides the value — a track of *modifier arguments* uses the modifier's own `AttributeModifier.argumentKeyframeLerp` instead | | `AttributeType.stateChangeLerp` | fading weather in and out | | `AttributeType.spatialLerp` | across a biome boundary | | `AttributeType.partialTickLerp` | between two client ticks, inside a frame | `AttributeTypes` registers fourteen types — *boolean*, *tri_state*, *float*, *angle_degrees*, *rgb_color*, *argb_color*, *integer*, *moon_phase*, *activity*, *bed_rule*, *particle*, *ambient_particles*, *background_music* and *ambient_sounds*. One built by `AttributeType.ofNotInterpolated` gets a step function in all four slots, each with its own threshold, which is how a `MoonPhase` snaps while a colour slides; `AttributeType.toFloat` is nullable, and its presence decides whether an attribute can be read as a loot number. The library is small — `BooleanModifier` is six logic gates, `FloatModifier` adds, subtracts, multiplies, minimises, maximises and alpha-blends a `FloatWithAlpha`, `ColorModifier` multiplies RGB or ARGB, alpha-blends or blends toward grey through a `ColorModifier.BlendToGray`, and `IntegerModifier` rounds it out — and `AttributeType.checkAllowedModifier` throws at build time when a track or an entry asks for an operation the type does not publish, so an illegal combination is a load error rather than a runtime surprise. Three codecs then decide who may write what: | codec | used by | effect | |---|---|---| | `EnvironmentAttributeMap.CODEC` | `DimensionType.attributes` | anything | | `EnvironmentAttributeMap.CODEC_ONLY_POSITIONAL` | `Biome.getAttributes` | **rejects non-positional attributes** | | `EnvironmentAttributeMap.NETWORK_CODEC` | `DimensionType.NETWORK_CODEC`, `Biome.NETWORK_CODEC` | drops every non-syncable entry before the wire | ## Who owns the clock `WorldClock` is a unit record. It holds nothing at all: it is an identity token in the `Registries.WORLD_CLOCK` registry, and vanilla registers two, `WorldClocks.OVERWORLD` and `WorldClocks.THE_END`. Every piece of state lives in `ServerClockManager.ClockInstance` — a total tick count, a fractional partial tick, a rate and a paused flag — and the manager holding those is a `SavedData` under `ServerClockManager.TYPE`, saved once for the whole server as *world_clocks*. **`ServerClockManager` is the owner of day time**; both [level data and rules](../../reference/level-data-and-rules.md) and [the level tick](../server/server-level-tick.md) point here for it. `MinecraftServer` calls `ServerClockManager.tick` once per server tick, inside the *clocks* profiler zone and only while the tick-rate manager runs normally; the `GameRules.ADVANCE_TIME` check sits inside the method itself, and gates every clock at once where `ServerClockManager.setPaused` gates one. Each unpaused instance then gains its rate, accumulating the fraction, so a clock at rate 0.5 gains a tick every other server tick and one at rate 1000 gains a thousand — the command accepts anything from 0.00001 to 1000. A `ClockTimeMarker` is a named instant on a clock: `ClockTimeMarkers.DAY`, *NOON*, *NIGHT*, *MIDNIGHT*, *WAKE_UP_FROM_SLEEP*, *ROLL_VILLAGE_SIEGE*. Markers are declared *inside* timelines and collected onto the clock by `ServerClockManager.init`, and `Timeline.validateRegistry` fails the whole registry load if two timelines on one clock declare the same one. The subset a player can name is the one flagged `ClockTimeMarker.showInCommands`; `ServerClockManager.isAtTimeMarker` is how `VillageSiege` asks whether the siege roll is due, and `ServerLevel.tick` calls `ServerClockManager.moveToTimeMarker` to jump the clock when enough players are asleep. `TimeCommand` registers its whole subtree twice — once directly on `/time`, against the source level's `DimensionType.defaultClock`, and once under `/time of` against a clock the player names — so *set*, *add*, *pause*, *resume*, *rate* and *query* exist in both forms. Only `/time query gametime` sits outside the clock nodes. ## The four timelines | timeline | period | what it carries | |---|---:|---| | `Timelines.OVERWORLD_DAY` | 24000 | the whole day/night curve — sun, moon and star angles, sky and fog colours, sky light, and the gameplay flags that flip at dusk | | `Timelines.MOON` | 24000 × `MoonPhase.COUNT` | the moon phase, and the surface slime spawn chance riding the same steps | | `Timelines.VILLAGER_SCHEDULE` | 24000 | `EnvironmentAttributes.VILLAGER_ACTIVITY` and `EnvironmentAttributes.BABY_VILLAGER_ACTIVITY` | | `Timelines.EARLY_GAME` | none | one ramp that *and*s `EnvironmentAttributes.CAN_PILLAGER_PATROL_SPAWN` with false until tick 120000 | All four run on `WorldClocks.OVERWORLD`; nothing in vanilla is bound to the End's clock. Which of them a dimension runs is a tag on `DimensionType.timelines`: `TimelineTags.IN_OVERWORLD` names the day, moon and early-game timelines on top of `TimelineTags.UNIVERSAL`, while `TimelineTags.IN_NETHER` and `TimelineTags.IN_END` name only the universal one, which holds the villager schedule. ## What crosses the wire The *rules* travel, never the resolved values. `Registries.TIMELINE` and `Registries.WORLD_CLOCK` are in `RegistryDataLoader.SYNCHRONIZED_REGISTRIES` — the timeline through `Timeline.NETWORK_CODEC`, so only syncable tracks go — while `Registries.ENVIRONMENT_ATTRIBUTE` and `Registries.ATTRIBUTE_TYPE` are built-in code registries that never go out at all. Clock *state* rides `ClientboundSetTimePacket`: a game time plus a `ClockNetworkState` — total ticks, partial tick, rate — per clock in its map. `ServerClockManager.createFullSyncPacket` fills that map on join and on a `GameRules.ADVANCE_TIME` change and every mutator broadcasts a one-clock update, but the routine broadcast from `MinecraftServer.forceGameTimeSynchronization`, once every twenty ticks, sends an *empty* map and nothing but the game time. `ClientClockManager.handleUpdates` adopts what arrives and `ClientClockManager.tick` free-runs the rest — which is why a paused clock travels as rate 0: the client has no paused flag to receive. ## The trace: dusk falls A mob asks whether it should be burning, and the camera asks what colour the sky is. They are the same question asked twice. ```mermaid sequenceDiagram participant SL as ServerLevel participant EAS as EnvironmentAttributeSystem participant EVS as EnvironmentAttributeSystem.ValueSampler participant ATS as AttributeTrackSampler participant SCM as ServerClockManager participant KTS as KeyframeTrackSampler participant Mob as Mob Note over SL,Mob: one server tick SL->>EAS: invalidateTickCache — before the world border, before the weather EAS->>EVS: drop the cached value, bump the cache tick id Mob->>EAS: getValue(MONSTERS_BURN, position) EAS->>EVS: getValue — is any layer of this attribute positional? EVS->>EVS: none is — start from the baked base, the default false EVS->>ATS: applyTimeBased(value, cache tick id) ATS->>SCM: getTotalTicks(the overworld clock) ATS->>KTS: sample — which keyframe segment, and how far into it KTS-->>ATS: the argument — false at 12542, true again at 23460 ATS-->>EVS: BooleanModifier.OR applied to the value EVS->>EVS: weather adds a layer for nine attributes only, and this is not one EVS-->>Mob: sanitizeValue — false, and cached for the rest of the tick ``` Read the arrows as decisions. `EnvironmentAttributeSystem.invalidateTickCache` computes nothing: it drops each sampler's cached value and bumps a counter, and that counter is the identity every downstream sampler compares against. `AttributeTrackSampler.applyTimeBased` keeps a one-entry cache of the sampled *argument* and reuses it for every reader arriving with the same tick id, so a thousand mobs asking `EnvironmentAttributes.MONSTERS_BURN` cost one keyframe sample between them. The step that reads oddly is the fifth. `EnvironmentAttributes.MONSTERS_BURN` is a positional attribute — everything is, unless a builder says `EnvironmentAttribute.Builder.notPositional` — and yet its stack is one layer deep, because in vanilla nothing but the day timeline mentions it: no dimension type, no biome. `EnvironmentAttributeSystem.ValueSampler` decides by *layers*, not by the flag, so with no positional layer present the position is ignored and the whole answer is memoised for the tick. The flag still governs where it is read: it is what `EnvironmentAttributeMap.CODEC_ONLY_POSITIONAL` checks, what makes `EnvironmentAttributeCheck` and `EnvironmentAttributeValue` declare `LootContextParams.ORIGIN` a required parameter, and what makes `EnvironmentAttributeSystem.getDimensionValue` throw in a development build if asked for a positional attribute at all. Three call sites name an attribute and read it that positionless way: `Level.updateSkyBrightness` for `EnvironmentAttributes.SKY_LIGHT_LEVEL`, and `LavaFluid.isFastLava` and `Entity` for `EnvironmentAttributes.FAST_LAVA` — the only two attributes built `EnvironmentAttribute.Builder.notPositional`, and the pair that decides [how fast lava flows](fluids.md). A fourth site names none: `EnvironmentAttributeReader` sends any non-positional attribute down this road when a loot context asks for one. `KeyframeTrackSampler.sample` is where the period matters: for a periodic track it bakes two extra segments, last keyframe to first on either side of the loop, so a value interpolates *across the wrap* — tick 0, which on this clock is dawn — instead of snapping, and it reduces the clock's total ticks with a floor-mod before choosing one. `EasingType` supplies the curve, and the day timeline's sun, moon and star angles share one symmetric cubic Bézier whose two keyframes both sit at tick 6000, so the baked segment runs noon to noon. The sun therefore turns slowest at its zenith — two thirds of the linear rate — and fastest at midnight, at about six fifths of it. That is why a Minecraft day is not two equal halves: the sun spends roughly 13,560 ticks above the horizon against 10,440 below. ### The same value on the client ```mermaid sequenceDiagram participant Camera as Camera participant EAP as EnvironmentAttributeProbe participant GS as GaussianSampler participant SAI as SpatialAttributeInterpolator participant EAS as EnvironmentAttributeSystem participant SR as SkyRenderer Note over Camera,SR: one client tick Camera->>EAP: tick(level, position) — once per client tick EAP->>GS: sample around the camera GS->>SAI: accumulate(weight, that biome's attributes) — 216 times Note over Camera,SR: between ticks, once per frame SR->>EAP: getValue(SKY_COLOR, partialTicks) EAP->>EAS: getValue(attribute, position, interpolator) EAS->>SAI: applyAttributeLayer — weighted blend of every biome in range EAP-->>SR: partialTickLerp between last tick's value and this one ``` The client resolves the *same* stack from the *same* data — it is never sent a resolved value. What it adds is two kinds of smoothing the server never does. In space, `EnvironmentAttributeProbe.tick` prunes, clears, then runs `GaussianSampler.sample` over a 6×6×6 neighbourhood of quart-resolution biome cells — 216 samples, a 1-4-6-4-1 kernel lerped by the sub-cell offset on each axis — accumulating weights into a `SpatialAttributeInterpolator`, whose `SpatialAttributeInterpolator.applyAttributeLayer` applies each contributing biome's modifier to the base value and lerps the *results* together by weight. That is only for the 21 attributes flagged `EnvironmentAttribute.isSpatiallyInterpolated`; anything else takes the single biome under the position. In time, each probed value keeps last tick's answer beside this tick's and returns `AttributeType.partialTickLerp` between them — and prunes itself, dropping any value nobody read during a tick. The probe lives on `Camera`, ticked from `Camera.tick` and emptied by `Camera.reset`, and six consumers go through it: `SkyRenderer`, `LightmapRenderStateExtractor`, `AtmosphericFogEnvironment`, `WaterFogEnvironment`, `LevelExtractor` for clouds and `Minecraft` for music. It is not a wall: the clock item reads *sun_angle* and *moon_phase* off `ClientLevel.environmentAttributes` directly, and so does `ClientLevel` itself for ambient particles. That is why [lightmap, fog and sky](../rendering/lightmap-fog-and-sky.md) never touches `EnvironmentAttributeSystem` directly. ## Questions players ask **Why does the nether have no night?** It has no day timeline: the nether's and the End's `DimensionType.timelines` both resolve to `TimelineTags.UNIVERSAL` alone. Its environment is constants instead — the dimension type pins *gameplay/sky_light_level*, *gameplay/fast_lava*, *gameplay/water_evaporates* and eleven more, which `EnvironmentAttributeSystem.bakeLayerSampler` folds into a base value that never changes again. **Does setting the time in the overworld move the End?** No: each clock keeps its own `ServerClockManager.ClockInstance`. Every mutator does invalidate the cache on *every* level at once, though — `ServerClockManager` walks `MinecraftServer.getAllLevels` on each change, because a time jump must not leave half a tick of stale sky behind. **Why do the server and the client disagree by a tick?** They invalidate at opposite ends of it. `ServerLevel.tick` calls `EnvironmentAttributeSystem.invalidateTickCache` before the world border and the weather, then runs `Level.updateSkyBrightness` later in the same method, once sleeping and weather have resolved; `ClientLevel.tick` does the reverse, `Level.updateSkyBrightness` first and invalidation last, so the client's sky-darken value comes from the previous tick's clock. **Where did the villager schedule go?** Into `Timelines.VILLAGER_SCHEDULE`. `Brain.setSchedule` takes an `EnvironmentAttribute` of `Activity` and `Villager` is the only caller — adults get `EnvironmentAttributes.VILLAGER_ACTIVITY`, babies `EnvironmentAttributes.BABY_VILLAGER_ACTIVITY`, two tracks on one timeline — and `Brain.updateActivityFromSchedule` reads it at the villager's own position, only when more than 20 game ticks have passed since it last looked. Where that activity then sends a villager is in [points of interest](points-of-interest.md). **Why do pillager patrols not show up on day one?** `Timelines.EARLY_GAME` has no period, and a timeline without one is not a cycle: its track runs once against total ticks and holds its last value forever. Its single modifier track *and*s `EnvironmentAttributes.CAN_PILLAGER_PATROL_SPAWN` with false until tick 120000 — a hundred minutes — and with true after. ## Where to look `EnvironmentAttributes` · `EnvironmentAttribute.Builder` · `AttributeTypes` · `EnvironmentAttributeMap.Entry` · `EnvironmentAttributeSystem.Builder` · `EnvironmentAttributeSystem.bakeLayerSampler` · `EnvironmentAttributeSystem.invalidateTickCache` · `WeatherAttributes.addBuiltinLayers` · `Timelines` · `Timeline.createTrackSampler` · `AttributeTrackSampler.applyTimeBased` · `KeyframeTrackSampler.sample` · `ServerClockManager.tick` · `ClientClockManager.handleUpdates` · `EnvironmentAttributeProbe.tick` · `SpatialAttributeInterpolator.applyAttributeLayer` · `GaussianSampler.sample` · `TimeCommand` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Chunk anatomy > Verified against **Minecraft 26.2** · Part IV · One block is placed, and the write travels down through the chunk until it lands in four bits of one long. You place a single block of deepslate at y −40, in a section that until now held nothing but stone and air. The click ends in `LevelChunk.setBlockState`, and the write goes down four objects: a `LevelChunkSection` sixteen blocks tall, a `PalettedContainer` of 4,096 block states, a palette that turns the state into a small integer, and a `SimpleBitStorage` that puts that integer into some fixed number of bits of one long. Everything else in this part *moves* chunks — tickets load them, the pyramid generates them, the light engine walks them, the region file stores them. This is the page that looks inside, and it is the vocabulary the rest of the part spends. Begin with the part that surprises people: **a section holding two distinct block states costs exactly what one holding sixteen costs — four bits an entry, 256 longs, on disk as well as in memory — and the block that makes it seventeen re-encodes all 4,096 entries into a wider storage before it can be written.** ## The cast | class | what it decides | thread | |---|---|---| | `ChunkAccess` | everything a chunk has whatever its shape: position, height, sections, heightmaps, block entities, structures, the two volatile flags | abstract — whichever thread owns the shape below | | `ProtoChunk` | a chunk under construction: status, carving mask, entities as NBT, the light engine it reports to | written on the worker pool, one writer at a time | | `LevelChunk` | a chunk that is part of a `Level`: block entities, tickers, tick containers, the full-status supplier | the server thread — on the client, the client's main thread | | `ImposterProtoChunk` | what a still-generating neighbour sees when the chunk it asked for is already live | the server thread | | `LevelChunkSection` | 16×16×16: two palette containers and four counters that let a whole section be skipped | whichever thread holds its permit | | `PalettedContainer` | the mapping from 4,096 (or 64) entries to values, and when to widen it | one writer at a time, enforced by `ThreadingDetector`; reads are lock-free | | `Strategy` | which palette and which bit width each entry count deserves, for block states and for biomes | immutable, shared by every container in the level | | `Heightmap` | the top of each of 256 columns, for one definition of *top* | with the chunk that owns it | ## The four shapes a chunk takes ```mermaid flowchart LR NEW["nothing on disk: ChunkMap.createEmptyChunk"] --> PC DISK["the region file"] -->|"SerializableChunkData.parse, on the worker pool"| SCD["SerializableChunkData"] SCD -->|"read, on the server thread, stored status below full"| PC SCD -->|"read, stored status full: a LevelChunk is built, then wrapped"| IPC PC["ProtoChunk: generation state, written on the worker pool"] -->|"ChunkStatusTasks.full, on the server thread"| LC LC["LevelChunk: the live chunk, owned by the server thread"] -->|"GenerationChunkHolder.replaceProtoChunk"| IPC IPC["ImposterProtoChunk: a ProtoChunk-shaped view over a LevelChunk"] -->|"ImposterProtoChunk.getWrapped"| LC MISS["a lookup that finds nothing there"] --> ELC["EmptyLevelChunk: void air, and a LevelChunk itself"] ``` `ChunkAccess` is the abstract chunk and has exactly two direct concrete lines — `ProtoChunk` (`ChunkType.PROTOCHUNK`) and `LevelChunk` (`ChunkType.LEVELCHUNK`) — with `ImposterProtoChunk` a subclass of the first and `EmptyLevelChunk` of the second. Nothing else extends it. Every one of them carries the same core. `ChunkAccess.chunkPos` says where it is, and a `LevelHeightAccessor` says how tall: `LevelHeightAccessor.getMinY` and `LevelHeightAccessor.getHeight` are the only two facts about height there are, and the overworld's −64 and 384 give `LevelHeightAccessor.getSectionsCount` of **24**, section Y −4 through 19. Beside them sit the heightmaps, the block entities in two maps (`ChunkAccess.blockEntities` live, `ChunkAccess.pendingBlockEntities` still NBT, `ChunkAccess.getBlockEntitiesPos` the union), `ChunkAccess.structureStarts` and `ChunkAccess.structuresRefences` (Mojang's spelling), the per-section `ChunkAccess.postProcessing` offsets to revisit after load (`ProtoChunk.packOffsetCoordinates` packs four bits each of x, y and z into a short), `ChunkAccess.inhabitedTime` behind local difficulty ([the level tick](../server/server-level-tick.md)), `ChunkAccess.upgradeData` and the nullable `ChunkAccess.blendingData` whose presence *is* `ChunkAccess.isOldNoiseGeneration` ([blending](../worldgen/blending.md)), and two *volatile* flags — `ChunkAccess.unsaved`, whose test-and-clear `ChunkAccess.tryMarkSaved` the saver uses, and `ChunkAccess.isLightCorrect`, saved as *isLightOn*. It is also three interfaces at once — `LightChunk`, which is what the light engine reads through `LightChunk.findBlockLightSources` and `LightChunk.getSkyLightSources`, plus `StructureAccess` and `BiomeManager.NoiseBiomeSource` — and `ChunkAccess.getPersistedStatus` is the `ChunkStatus` that goes to disk, with `ChunkAccess.getHighestGeneratedStatus` folding in `BelowZeroRetrogen.targetStatus` for a chunk still being deepened. A `ProtoChunk` adds what only generation needs: a volatile `ProtoChunk.status` (`ProtoChunk.setPersistedStatus` also retires a finished `BelowZeroRetrogen`), a `ProtoChunk.lightEngine` from `ProtoChunk.setLightEngine` that it reports to only once the status `ChunkStatus.isOrAfter` `ChunkStatus.INITIALIZE_LIGHT`, its entities as a list of `CompoundTag` (`ProtoChunk.addEntity` serialises on the spot), a `ProtoChunk.carvingMask`, and `ProtoChunkTicks` that `ProtoChunk.unpackBlockTicks` turns into `LevelChunkTicks` on promotion ([scheduled ticks](scheduled-ticks.md)). The pool that fills all of that in is [the generation pipeline](chunk-generation-pipeline.md). Ask it for a biome before `ChunkStatus.BIOMES` and `ProtoChunk.getNoiseBiome` throws *Asking for biomes before we have biomes*. A `LevelChunk` adds what only a live chunk needs: `LevelChunk.level`, `LevelChunk.setLoaded`, a supplier of `FullChunkStatus` the holder owns ([tickets](tickets-and-loading.md)), two `LevelChunkTicks` that `LevelChunk.registerTickContainerInLevel` attaches to the level's queues and `LevelChunk.unregisterTickContainerFromLevel` detaches, the ticker map, a one-shot `LevelChunk.postLoad` processor that `LevelChunk.runPostLoad` fires, the per-section `LevelChunk.gameEventListenerRegistrySections` — an `EuclideanGameEventListenerRegistry` each, built on demand and only on a server ([game events](game-events-and-vibrations.md)) — and a `LevelChunk.unsavedListener` that `LevelChunk.markUnsaved` fires **only on the false-to-true edge**, which is how the server's dirty set learns of a change without scanning. Its `LevelChunk.getPersistedStatus` is always `ChunkStatus.FULL`. `ImposterProtoChunk` exists because a neighbour still generating asks the holder for "the chunk at status X" and must be handed something `ProtoChunk`-typed even when that chunk is already live. Reads delegate to `ImposterProtoChunk.getWrapped`; writes are dropped unless *allowWrites*, which both of the two places that construct one pass as **false**, so in 26.2 every write to an imposter is dropped — heightmaps, structure starts and references and block-entity NBT unconditionally, and the rest for want of the flag. `ImposterProtoChunk.getSections` hands back the wrapped chunk's array unconditionally — only the single-section `ImposterProtoChunk.getSection` is gated — while `ImposterProtoChunk.markUnsaved` and `ImposterProtoChunk.setLightCorrect` always pass through and `ImposterProtoChunk.canBeSerialized` is false, because the `LevelChunk` under it is what gets saved. Its `ImposterProtoChunk.fixType` maps a request for a *_WG* heightmap onto the live one, but only inside `ImposterProtoChunk.getHeight`: asking it to *create* a *_WG* heightmap creates a real one on the live chunk. `EmptyLevelChunk` is the other direction: `Blocks.VOID_AIR` everywhere, `EmptyLevelChunk.isEmpty` true where `LevelChunk.isEmpty` is false, one fixed biome, and `EmptyLevelChunk.getFullStatus` a flat `FullChunkStatus.FULL`. `ClientChunkCache.emptyChunk` is one shared instance handed out for *any* client miss, and only when the caller asked to load or generate — otherwise the miss returns null; `PathNavigationRegion` builds its own for whatever a mob's pathfinder cannot see. The real client chunks live in `ClientChunkCache.Storage`, an `AtomicReferenceArray` ring whose `ClientChunkCache.Storage.onSectionEmptinessChanged` and its double-buffered added and removed sets are the renderer's feed of which sections exist. ## Sections and their four counters ```mermaid flowchart TD LC["LevelChunk: one 16 by 16 column of the whole build height"] --> ARR["ChunkAccess.sections: an array of LevelChunkSection, 24 in the overworld, never a null slot"] LC --> HM["four Heightmaps: 256 entries of 9 bits each"] ARR --> ST["LevelChunkSection.states: PalettedContainer of BlockState, 4096 entries"] ARR --> BIO["LevelChunkSection.biomes: PalettedContainerRO of Biome, 64 entries, one per 4 by 4 by 4 quart"] ARR --> CNT["four shorts: nonEmptyBlockCount, fluidCount, tickingBlockCount, tickingFluidCount"] ST --> DATA["PalettedContainer.Data: one volatile record of configuration, palette and storage"] BIO --> DATA DATA --> PAL["the palette, on the block-state ladder: one value at 0 bits, then anything from 2 to 16 values at 4 bits, 17 to 256 hashed, then the registry itself. Biomes climb a shorter ladder"] DATA --> BST["the BitStorage: ZeroBitStorage, or a SimpleBitStorage of 256 longs at 4 bits, 342 at 5, 512 at 8"] ``` The array never has a hole: `ChunkAccess.replaceMissingSections` runs in the constructor and fills every empty slot with a fresh all-air section from the level's `PalettedContainerFactory`, so nothing that walks sections checks for null. The four counters are what make a section cheap to skip. `LevelChunkSection.setBlockState` adjusts all four from the outgoing and incoming state on every single write — `LevelChunkSection.nonEmptyBlockCount` (zero is `LevelChunkSection.hasOnlyAir`, which is also how `LevelChunk.getBlockState` answers air without touching a palette), `LevelChunkSection.fluidCount`, `LevelChunkSection.tickingBlockCount` and `LevelChunkSection.tickingFluidCount` — and `LevelChunkSection.isRandomlyTicking` is the *or* of the last two. That one boolean lets `ServerLevel` skip a section of solid stone without looking at any of its 4,096 blocks. Only a load from disk recounts: `LevelChunkSection.recalcBlockCounts` runs from the two-container constructor, whose only caller is `SerializableChunkData`. Biomes share the section but are coarse and read-only. Two bits per axis, 64 entries of 4×4×4 blocks each — the number `LevelChunkSection.BIOME_CONTAINER_BITS` names, though the 2 that matters is the literal in `Strategy.createForBiomes` and no reader of the constant survives the decompile. The field is a `PalettedContainerRO` and the *published* one is never mutated: `LevelChunkSection.fillBiomesFromNoise`, `LevelChunkSection.read` and `LevelChunkSection.readBiomes` each build a replacement through `PalettedContainerRO.recreate`, fill it, and swap the reference. The block-state container is the only one anything writes to in place. Two different copies leave a section. The saver takes `LevelChunkSection.copy`, a deep copy of both containers and all four counters, on the server thread inside `SerializableChunkData.copyOf` — the IO lane never sees a live section, and even the NBT encoding of the copy runs on the background pool ([chunk storage](chunk-storage.md)). The client mesher takes something cheaper: a `SectionCopy` takes `PalettedContainer.copy` of the block-state container alone (nothing at all when the section is air) plus an immutable snapshot of the chunk's block-entity map. A worker that means to write instead brackets its work with `LevelChunkSection.acquire` and `LevelChunkSection.release`. ## The palette and the ladder it climbs A `PalettedContainer`'s whole state is one *volatile* record, `PalettedContainer.Data`, holding a `Configuration`, a `Palette` and a `BitStorage`. `PalettedContainer.get` reads the record once into a local and takes no lock, because a resize swaps in a whole new record rather than editing the old one. `PalettedContainer.read` from the wire is the exception that proves it: `PalettedContainer.createOrReuseData` hands back the *existing* record whenever the incoming bit count wants the same configuration, and the palette and the long array are then overwritten in place — which is the ordinary case on a client, where `ClientChunkCache` reuses the live `LevelChunk`. Which record a given entry count deserves is decided by a top-level `Strategy` — no longer nested inside the container — whose `Strategy.createForBlockStates` and `Strategy.createForBiomes` are called once per level by `PalettedContainerFactory.create` over `Block.BLOCK_STATE_REGISTRY` and the biome registry ([registries](../foundations/identifiers-and-registries.md)), which is why the global palette's width is a runtime number and not a constant. `Strategy.getConfigurationForBitCount` is the ladder, and block states and biomes climb different ones: | distinct values (bits needed) | block states | biomes | |---|---|---| | 1 (0 bits) | `Strategy.ZERO_BITS` → `SingleValuePalette` + `ZeroBitStorage` | the same | | 2 … 8 (1–3 bits) | `Strategy.FOUR_BITS_LINEAR` → `LinearPalette`, **already 4 bits** | `Strategy.ONE_BIT_LINEAR` / `Strategy.TWO_BITS_LINEAR` / `Strategy.THREE_BITS_LINEAR` → `LinearPalette` at its own width | | 9 … 16 (4 bits) | `Strategy.FOUR_BITS_LINEAR`, **still 4 bits** | `Configuration.Global` already | | 17 … 256 (5–8 bits) | `Strategy.FIVE_BITS_HASHMAP` … `Strategy.EIGHT_BITS_HASHMAP` → `HashMapPalette` | `Configuration.Global` — there is no hashed tier for biomes | | more | `Configuration.Global` → `GlobalPalette`, the registry's own `IdMap` | `Configuration.Global` | `SingleValuePalette` holds one value and asks for width 1 the moment a second arrives — which for block states means jumping straight to the 4-bit rung. `LinearPalette` is a flat array of *2^bits* slots scanned by identity, `HashMapPalette` a `CrudeIncrementalIntIdentityHashBiMap`, and `GlobalPalette` writes nothing on the wire, maps an unknown value to id 0 and answers `Palette.maybeHas` with an unconditional yes. Each of them calls `PaletteResize.onResize` when it fills, and the container *is* its own `PaletteResize`: `PalettedContainer.onResize` builds the next record, `PalettedContainer.Data.copyFrom` walks every entry of the old storage through the old palette into the new one, the record is published, and only then is the value that triggered the growth added — under `PaletteResize.noResizeExpected`, which throws if a second growth were somehow needed. `SimpleBitStorage` never lets an entry straddle a long: its `SimpleBitStorage.valuesPerLong` is 64 divided by the width, so 4,096 entries are 256 longs at four bits, 342 at five and 512 at eight, and the cell index comes from a multiply-shift table (`SimpleBitStorage.MAGIC`) rather than a division. `ZeroBitStorage` answers 0 for everything and shares one empty `ZeroBitStorage.RAW` array. An array of the wrong length raises `SimpleBitStorage.InitializationException`, which `PalettedContainer.unpack` turns into a `DataResult` error instead of a crash. ### What packing actually buys The two serialised forms differ. `PalettedContainer.write` is the wire: a bits byte, the palette, a fixed-size long array at exactly the in-memory width. `PalettedContainer.pack` is the disk (the *palette* and optional *data* fields of a `PalettedContainerRO.PackedData`, behind `PalettedContainer.codecRW` and `PalettedContainer.codecRO` — [codecs](../foundations/codecs-nbt-json.md)), and it re-encodes into a fresh `HashMapPalette` before asking `Strategy.getConfigurationForPaletteSize` for the width — **the same ladder memory climbs**. Packing therefore buys a smaller palette, not narrower entries: unreferenced entries are dropped, which can demote a container a whole rung, and a `Configuration.Global` container shrinks from `Configuration.bitsInMemory` to `Configuration.bitsInStorage`. `PalettedContainer.unpack` re-encodes on the way back only for `Configuration.Global`, whose `Configuration.alwaysRepack` is true — every `Configuration.Simple` rung reports one width for both, so its long array is adopted exactly as it lies on disk. ## The six heightmaps A `Heightmap` is 256 entries — one per column, indexed *x + z·16* — in a `SimpleBitStorage` whose width is `Mth.ceillog2` of height + 1, so **9 bits** for a 384-tall world, stored relative to the minimum Y. `Heightmap.getFirstAvailable` is the first free Y and `Heightmap.getHighestTaken` the one below it. `Heightmap.primeHeightmaps` fills several types in a single top-down column scan (starting from the deprecated-for-removal `ChunkAccess.getHighestSectionPosition`) into maps that `ChunkAccess.getOrCreateHeightmapUnprimed` makes on demand. `Heightmap.update` is the incremental path, raising the height when an opaque block is placed at or above it and rescanning downward only when the block that turned transparent was the top one. | type | *opaque* means | usage | saved | sent | |---|---|---|---|---| | `Heightmap.Types.WORLD_SURFACE_WG` | not air | `Heightmap.Usage.WORLDGEN` | proto only | no | | `Heightmap.Types.WORLD_SURFACE` | not air | `Heightmap.Usage.CLIENT` | yes | yes | | `Heightmap.Types.OCEAN_FLOOR_WG` | blocks motion | `Heightmap.Usage.WORLDGEN` | proto only | no | | `Heightmap.Types.OCEAN_FLOOR` | blocks motion | `Heightmap.Usage.LIVE_WORLD` | yes | **no** | | `Heightmap.Types.MOTION_BLOCKING` | blocks motion or holds fluid | `Heightmap.Usage.CLIENT` | yes | yes | | `Heightmap.Types.MOTION_BLOCKING_NO_LEAVES` | the same, but not a `LeavesBlock` | `Heightmap.Usage.CLIENT` | yes | yes | Which of the six a chunk carries follows its status: `ChunkStatus.heightmapsAfter` is the two *_WG* maps through `ChunkStatus.SURFACE` and `ChunkStatus.FINAL_HEIGHTMAPS` — the other four — from `ChunkStatus.CARVERS` on, and a `LevelChunk` is constructed with exactly those four. A `ProtoChunk` primes any of its status's maps that are missing the first time a block is written. What is *saved*, though, is not `Heightmap.Types.keepAfterWorldgen`: the saver writes whatever the chunk's **persisted** status names, so a proto chunk stored below `ChunkStatus.CARVERS` does save its two *_WG* maps. Separately and privately, `ChunkAccess.skyLightSources` is a *second* 256-entry bit storage — a `ChunkSkyLightSources` — that only the sky-light engine reads ([lighting](lighting.md)). ## What placing a block actually does `LevelChunk.setBlockState` is the one write path into a live chunk, and its order matters more than any single step in it: | in order | what happens | when it is skipped | |---|---|---| | 1 | the section is fetched and its emptiness remembered | air into an all-air section returns null immediately | | 2 | `LevelChunkSection.setBlockState` writes the palette entry and moves all four counters | — | | 3 | the old state is compared to the new | identical state returns null, and nothing below runs | | 4 | all four heightmaps take `Heightmap.update` | — | | 5 | if the section's emptiness flipped: `LevelLightEngine.updateSectionStatus` and `ChunkSource.onSectionEmptinessChanged` | when it did not flip | | 6 | if `LightEngine.hasDifferentLightProperties`: `ChunkSkyLightSources.update`, then `LevelLightEngine.checkBlock` | when opacity and emission are unchanged **and** neither state uses a shape for light occlusion | | 7 | the old block entity is dropped, preceded on the server by `BlockEntity.preRemoveSideEffects` | when the block did not change, had no block entity, or `BlockBehaviour.BlockStateBase.shouldChangedStateKeepBlockEntity` — the side effects alone are skipped on the client and under `Block.UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS` | | 8 | `BlockBehaviour.BlockStateBase.affectNeighborsAfterRemoval` | when the block did not change and the new one is no `BaseRailBlock`, off the server, or without `Block.UPDATE_NEIGHBORS` and not moved by a piston | | 9 | the section is re-read — if step 8 changed the block again, the call returns null | — | | 10 | `BlockBehaviour.BlockStateBase.onPlace` | on the client, or under `Block.UPDATE_SKIP_ON_PLACE` | | 11 | the new block entity is created or re-validated, and its ticker rebound | when the new state has no block entity | | 12 | `LevelChunk.markUnsaved` | — | Two steps there are easy to misread. Step 8 is a genuine neighbour side effect *inside* the chunk write, but it is the removed block's own clean-up — shape updates and redstone notifications belong to `Level.setBlock`, after the chunk returns ([blocks and states](../blocks/blocks-and-states.md)). Step 9 exists because step 8 runs arbitrary block code that may write the same position again, and `LevelChunk.setBlockState` will not claim a placement it no longer owns. `ProtoChunk.setBlockState` is the same idea with everything live stripped out: section write, light only past `ChunkStatus.INITIALIZE_LIGHT`, the status's heightmaps updated (primed first if absent), and no block entity, no `BlockBehaviour.BlockStateBase.onPlace`, no neighbours at all. ### The double indirection behind step 11 `LevelChunk` holds every block-entity ticker in the world's hot loop, and it does so at one remove. `LevelChunk.addAndRegisterBlockEntity` sets the entity, registers its game-event listener and asks for its ticker; a `LevelChunk.BoundTickingBlockEntity` binds the entity to its `BlockEntityTicker` and gates every tick on `LevelChunk.isTicking` (inside the world border, at `FullChunkStatus.BLOCK_TICKING` or beyond, entities loaded). That sits inside a `LevelChunk.RebindableTickingBlockEntityWrapper` held in `LevelChunk.tickersInLevel`, so `Level.blockEntityTickers` keeps one stable handle per position for the life of the chunk and removal is only a `LevelChunk.RebindableTickingBlockEntityWrapper.rebind` to `LevelChunk.NULL_TICKER`, whose `TickingBlockEntity.isRemoved` is true and lets the level's list prune itself. When the chunk first goes live, `LevelChunk.postProcessGeneration` replays the post-processing offsets, promotes every pending block entity and applies `UpgradeData.upgrade`. ## Questions players ask **Why do two threads writing one section crash the game rather than block?** Because `PalettedContainer.threadingDetector` is a detector, not a mutex. `ThreadingDetector.checkAndLock` tries a one-permit semaphore and, on failure, records itself as the loser and then blocks. It is the **winner** that notices, in `ThreadingDetector.checkAndUnlock`: it builds `ThreadingDetector.makeThreadingException` — *Accessing PalettedContainer from multiple threads*, with both stack traces — and throws it, and the loser re-throws the same report the instant it acquires the permit. Both threads die, deliberately: an interleaved section write would be a corrupt world rather than a crash. Exactly one thread writes a section at a time — the server thread for a live chunk, and on the worker pool whoever holds `LevelChunkSection.acquire`, either `NoiseBasedChunkGenerator`, which holds every section across its noise range, or the `BulkSectionAccess` that `OreFeature` — its only user — holds over every section it touches until it closes. Those hold the permit already, so they write through the unchecked five-argument `LevelChunkSection.setBlockState` and `PalettedContainer.getAndSetUnchecked`. **Why are a client chunk's ticking counters zero?** Because `LevelChunkSection.write` carries only two of the four shorts, the non-empty-block and fluid counts, and `LevelChunkSection.read` takes exactly those two and never recounts. A client section therefore starts with `LevelChunkSection.tickingBlockCount` and `LevelChunkSection.tickingFluidCount` at zero and only ever counts what has changed since the chunk arrived. Nothing notices: the only reader of `LevelChunkSection.isRandomlyTicking` is `ServerLevel`, and the client runs no random ticks. **Why does the proto chunk keep working after the level chunk exists?** Because the two share the sections but not the array. `ChunkAccess` always allocates its own array and copies the references in, so promotion leaves two chunks holding two arrays over one set of `LevelChunkSection` objects — writing through either is writing the same blocks. That is also what makes the `ImposterProtoChunk` honest: it is a third handle on the same sections. **Why does a chest in a freshly loaded chunk not exist yet?** Because it is still a `CompoundTag` in `ChunkAccess.pendingBlockEntities` ([block entities](../blocks/block-entities.md)). Any call to `LevelChunk.getBlockEntity` promotes it through `LevelChunk.promotePendingBlockEntity` on the first touch, whatever the `LevelChunk.EntityCreationType` asked for; `LevelChunk.postProcessGeneration` and `LevelChunk.registerAllBlockEntitiesAfterLevelLoad` promote the rest in bulk when the chunk goes live. Of the three creation types only `LevelChunk.EntityCreationType.IMMEDIATE` and `LevelChunk.EntityCreationType.CHECK` have callers left in the game — `LevelChunk.EntityCreationType.QUEUED` has none. **Why can a search skip a whole section without reading it?** Because `LevelChunkSection.maybeHas` puts the predicate to the palette alone, so `ChunkAccess.findBlocks` can rule out 4,096 blocks with a handful of comparisons — unless the section is on the global palette, whose answer is always *maybe*. **What does the client actually receive?** `ClientboundLevelChunkWithLightPacket`, whose `ClientboundLevelChunkPacketData` carries the heightmaps for which `Heightmap.Types.sendToClient` is true (three of the six), one buffer holding *every* section's `LevelChunkSection.write` — empty ones included — and the block-entity update tags. The writer pre-sizes that buffer from the sum of `LevelChunkSection.getSerializedSize` and throws if `ClientboundLevelChunkPacketData.extractChunkData` does not fill it to the byte, and the reader refuses anything over two megabytes. Light rides beside it in `ClientboundLightUpdatePacketData`. The client applies the lot through `ClientPacketListener.updateLevelChunk` → `ClientChunkCache.replaceWithPacketData` → `LevelChunk.replaceWithPacketData`, which clears the block entities, gives each section `LevelChunkSection.read`, installs the raw heightmaps with `ChunkAccess.setHeightmap` and rebuilds the sky-light sources. Biome-only refreshes come later as `ClientboundChunksBiomesPacket` → `LevelChunk.replaceBiomes`, and the block-entity tags travel as `ClientboundLevelChunkPacketData.BlockEntityInfo`. ## Where to look `ChunkAccess` · `LevelChunk.setBlockState` · `LevelChunk.getBlockEntity` · `ProtoChunk.setBlockState` · `ImposterProtoChunk` · `ChunkStatusTasks.full` · `LevelChunkSection.setBlockState` · `LevelChunkSection.write` · `PalettedContainer.onResize` · `PalettedContainer.pack` · `PalettedContainer.unpack` · `Strategy.createForBlockStates` · `Configuration.Global` · `SimpleBitStorage` · `ThreadingDetector.checkAndLock` · `BulkSectionAccess` · `Heightmap.Types` · `ChunkStatus.heightmapsAfter` · `ClientChunkCache.Storage` · `ClientboundLevelChunkPacketData.extractChunkData` · the [class index](../../reference/class-index.md) for every field no page names --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Tickets and loading > Verified against **Minecraft 26.2** · Part IV · A player walks one block east across a chunk boundary, and a column of chunks thirteen past the edge of view is asked for. A player standing at the eastern edge of a chunk takes one step. On the server, `ChunkMap.move` notices that the player's section changed, and before the tick is out a column of twenty-one chunks to the east has been asked for. The nearest of them will be generated, lit, sent and alive within a second or two. The furthest, thirteen chunks past the ticket that asked for it, will exist only as a `ChunkHolder` at level 44, allowed no further than `ChunkStatus.STRUCTURE_STARTS` and never becoming a `LevelChunk` at all. Nothing in that machinery ever asks for a chunk *because* it is loaded — even `ChunkLevel.isLoaded` is a question about a number. Everything asks for a chunk at a *level*, and the ticket system decides what the level means. That is the whole design, and it has one consequence a player can see: **there are two graphs reading one ticket store, and they answer different questions** — so a chunk can be `FullChunkStatus.ENTITY_TICKING` by every measure the holder knows and tick nothing, because render distance is how far you can see and simulation distance is how far the world is alive. ## The cast | class | what it decides | thread | |---|---|---| | `TicketStorage` | which tickets exist on which chunk, and which survive a restart — it is the *chunk_tickets* `SavedData` | Server | | `DistanceManager` | owns both graphs, the two player-radius trackers and the four-slot throttle | Server | | `LoadingChunkTracker` · `SimulationChunkTracker` | the two graphs: `ChunkTracker`s over `DynamicGraphMinFixedPoint`, one level higher per ring | Server | | `ChunkLevel` | the number line — which level means which `FullChunkStatus` and which generation status | static | | `ChunkHolder` | one chunk's level and its three futures | Server; futures complete on workers, confirmations hop back | | `ChunkMap` | the holders (an updating map and a visible clone), each player's `ChunkTrackingView`, unloads | Server; workers read the visible map | | `ServerChunkCache` | the level's `ChunkSource`: the tick slot, the synchronous `ServerChunkCache.getChunk`, and a `BlockableEventLoop` pinned to the server thread | Server | | `PlayerChunkSender` | the per-player batches, paced by the client's acknowledgements | Server | ## From a ticket to a future ```mermaid flowchart TD SRC["a ticket source: a player, a portal, a pearl, the dragon, /forceload, a synchronous getChunk"] --> TS["TicketStorage.addTicket"] TS -- "FLAG_LOADING" --> LCT["LoadingChunkTracker, levels 0 to 45"] TS -- "FLAG_SIMULATION" --> SCT["SimulationChunkTracker, levels 0 to 33"] LCT --> FLOOD["flood: each ring one level higher than the last, every chunk at the minimum of what reaches it"] SCT --> FLOOD2["the same flood, in its own map"] FLOOD --> UCS["ChunkMap.updateChunkScheduling: a holder exists at level 44 or below"] UCS --> UF["ChunkHolder.updateFutures compares ChunkLevel.fullStatus of the old and new level"] UF --> F33["33 and below: fullChunkFuture, the generation pipeline to FULL"] UF --> F32["32 and below: tickingChunkFuture, then postProcessGeneration, unpackTicks, send"] UF --> F31["31 and below: entityTickingChunkFuture, then EntityTickList"] FLOOD2 --> Q["DistanceManager.inBlockTickingRange and inEntityTickingRange: does anything here tick"] ``` The figure is the page. A ticket lands on one chunk; each graph the ticket's flags name floods outward from it; the loading graph's levels decide which holders exist and what futures they arm; the simulation graph's levels decide what ticks. The rest of the page is one section per decision on that path. ### What a ticket asks for A `Ticket` is a `TicketType`, a `Ticket.ticketLevel` and a countdown, `Ticket.ticksLeft`. Its identity is the pair (type, level): there is no key object and no owner, and re-adding an identical ticket only `Ticket.resetTicksLeft`. The type is a record of a timeout and a set of flags, registered in `BuiltInRegistries.TICKET_TYPE`, and the flags say what the ticket *does*: `TicketType.FLAG_LOADING` feeds the loading graph, `TicketType.FLAG_SIMULATION` the simulation graph, `TicketType.FLAG_PERSIST` writes it to disk, `TicketType.FLAG_KEEP_DIMENSION_ACTIVE` stops the level's empty-tick countdown and `TicketType.FLAG_CAN_EXPIRE_IF_UNLOADED` lets its countdown run under a chunk that has no holder yet. There are exactly nine types, and every reason a chunk is ever loaded is one of them: | type | timeout | loads | simulates | keeps dimension active | persists | who adds it | |---|---:|---|---|---|---|---| | `TicketType.PLAYER_LOADING` | — | ✓ | | | | `DistanceManager.PlayerTicketTracker`, one per chunk in view | | `TicketType.PLAYER_SIMULATION` | — | | ✓ | ✓ | | `DistanceManager.addPlayer`, the player's own chunk | | `TicketType.FORCED` | — | ✓ | ✓ | ✓ | ✓ | `/forceload` via `TicketStorage.updateChunkForced` | | `TicketType.PORTAL` | 300 | ✓ | ✓ | ✓ | ✓ | `Entity` on portal travel, radius 3 | | `TicketType.ENDER_PEARL` | 40 | ✓ | ✓ | ✓ | | `ServerPlayer`, the pearl's chunk, radius 2 | | `TicketType.DRAGON` | — | ✓ | ✓ | | | `EnderDragonFight`, radius 9 | | `TicketType.PLAYER_SPAWN` | 20 | ✓ | | | | `PrepareSpawnTask` during configuration, radius 3 | | `TicketType.SPAWN_SEARCH` | 1 | ✓ | | | | `PlayerSpawnFinder` | | `TicketType.UNKNOWN` | 1 | ✓ | | | | a synchronous `ServerChunkCache.getChunk` that may generate, at the level for its target status | Two things in that table are easy to read past. The ticket that keeps a dimension alive is the player's *simulation* ticket, not the loading tickets: `TicketStorage.shouldKeepDimensionActive` feeds `ServerChunkCache.hasActiveTickets`, which resets `ServerLevel.emptyTime`, the counter that past 300 makes a dimension skip its entity loop and its block entities ([the level tick](../server/server-level-tick.md)). And only two types come back after a restart: `TicketStorage.packTickets` writes the types that `TicketType.persist`, so forced and portal tickets are in the dimension's *chunk_tickets* file and everything else evaporates. On shutdown `TicketStorage.deactivateTicketsOnClosing` parks every ticket except `TicketType.UNKNOWN` in `TicketStorage.deactivatedTickets`, and `TicketStorage.activateAllDeactivatedTickets` replays them during `MinecraftServer.prepareLevels`. > **For a 1.21-era reader.** There is no *LIGHT*, *PLAYER*, *START* or > *POST_TELEPORT* ticket, and there is no forced-chunks file: `TicketStorage` > (in `world/level`, not `server/level`) is the saved data now. The two > graphs are separate top-level classes, not inner classes of > `DistanceManager`, and the whole `ChunkHolder` generation half is a > superclass, `GenerationChunkHolder`. ### The number line `ChunkLevel` is the scale every ticket is measured on. Its thresholds are declared and its ceiling is derived. `ChunkLevel.byStatus` gives 31 for `FullChunkStatus.ENTITY_TICKING`, 32 for `FullChunkStatus.BLOCK_TICKING` and 33 for `FullChunkStatus.FULL`. Above 33 a chunk is `FullChunkStatus.INACCESSIBLE` but still *generating*: the FULL step of `ChunkPyramid.GENERATION_PYRAMID` needs a neighbourhood of eleven (`ChunkLevel.RADIUS_AROUND_FULL_CHUNK`, computed from the pyramid), so `ChunkLevel.MAX_LEVEL` is 44 and `ChunkLevel.generationStatus` maps 34 … 44 onto ever-earlier `ChunkStatus`es — but not one per level: `ChunkStatus.INITIALIZE_LIGHT` at 34, `ChunkStatus.CARVERS` at 35, `ChunkStatus.BIOMES` at 36, and `ChunkStatus.STRUCTURE_STARTS` for all eight of 37 … 44. `ChunkStatus.NOISE` is on that list nowhere. Level 45 means no holder. Change the pyramid and the loading radius changes with it. **Thirteen** — chunks past a level-31 ticket that get a holder: two rings to reach level 33, and eleven more because FULL needs that many neighbours generated. ### Two graphs, one store Both graphs are `ChunkTracker`s over the same `TicketStorage`, but each reads only the tickets whose flags name it, and each is asked a different kind of question: | the question | who asks it | which graph answers | |---|---|---| | does a holder exist, and how far may it generate | `ChunkMap.updateChunkScheduling`, `GenerationChunkHolder.updateHighestAllowedStatus` | loading | | may this chunk be sent to a player | `ChunkMap.onChunkReadyToSend`, from the level-32 future | loading | | do its blocks and fluids tick | `ServerLevel.shouldTickBlocksAt` | simulation | | do its entities tick | the entity loop, through `DistanceManager.inEntityTickingRange` | simulation | | which chunks does the level walk for random ticks | `ChunkMap.forEachBlockTickingChunk`, a wrapper over `DistanceManager.forEachEntityTickingChunk` | simulation | | may mobs spawn here | `DistanceManager.naturalSpawnChunkCounter`, feeding `ChunkMap.anyPlayerCloseEnoughForSpawning` | neither: a fixed radius-8 tracker of its own | The last row is the third radius, and it is the only one no setting moves: the tracker's radius of 8 is a constant. It is not the only gate, though — `ChunkMap.collectSpawningChunks` keeps a candidate only if the holder has a ticking chunk and some non-spectating player is within 128 blocks of it. And the two graphs have different sizes: the loading graph runs 0 … 45 and the simulation graph 0 … 33, where 33 is not a ticking level but the tracker's word for *no simulation ticket at all* — `SimulationChunkTracker.setLevel` drops the entry at 33 and the map answers 33 for anything absent. A `TicketType.PLAYER_LOADING` ticket puts a chunk at level 31 in the loading graph — `FullChunkStatus.ENTITY_TICKING` by holder status, all three futures armed — and contributes nothing to the simulation graph, which is why the far edge of a large render distance is generated, lit, sent and inert. ## The four statuses ```mermaid stateDiagram-v2 direction LR [*] --> INACCESSIBLE : level 44 or below, a holder is made INACCESSIBLE --> FULL : level 33, fullChunkFuture succeeds FULL --> BLOCK_TICKING : level 32, tickingChunkFuture succeeds BLOCK_TICKING --> ENTITY_TICKING : level 31, entityTickingChunkFuture succeeds ENTITY_TICKING --> BLOCK_TICKING : level above 31, immediate BLOCK_TICKING --> FULL : level above 32, immediate FULL --> INACCESSIBLE : level above 33, immediate INACCESSIBLE --> [*] : level above 44, toDrop then processUnloads note right of ENTITY_TICKING : promotion waits for a future, demotion does not ``` `ChunkHolder.updateFutures` compares `ChunkLevel.fullStatus` of `ChunkHolder.oldTicketLevel` (what the futures reflect) with that of `ChunkHolder.ticketLevel` (what the graph last said). Each threshold crossed upward arms one future — `ChunkMap.prepareAccessibleChunk` at 33, `ChunkMap.prepareTickingChunk` at 32, `ChunkMap.prepareEntityTickingChunk` at 31 — and a chunk that goes from 45 to 31 in one update arms all three at once. Every one is wrapped by `ChunkHolder.scheduleFullChunkPromotion`, so that success fires `ChunkMap.onFullChunkStatusChange` on the main thread, and chained into `ChunkHolder.addSaveDependency`, so the chunk cannot be saved or unloaded mid-promotion. Under the hood each future is a `GenerationChunkHolder.scheduleChunkGenerationTask` — the [generation pipeline](chunk-generation-pipeline.md). Demotion is the asymmetry. A threshold crossed downward completes the matching future with `ChunkHolder.UNLOADED_LEVEL_CHUNK` and `ChunkHolder.demoteFullChunk` fires the status change *now*, cancelling any promotion still pending. Entities in a chunk whose level rose past 31 stop ticking in the same update; nothing waits for a worker. What each promotion means when it lands: at FULL, `PersistentEntitySectionManager.updateChunkStatus` makes the chunk's entities `Visibility.TRACKED` and queues their data to load (Part VI). At BLOCK_TICKING the continuation runs `LevelChunk.postProcessGeneration`, `ServerLevel.startTickingChunk` — which is `LevelChunk.unpackTicks`, the saved scheduled ticks becoming real — and `ChunkMap.onChunkReadyToSend`. At ENTITY_TICKING, `PersistentEntitySectionManager.startTicking` puts the entities on the `EntityTickList`. ## When the graphs run All of it on the **Server thread**, in three slots: 1. **The tick.** `ServerChunkCache.tick`, from the level tick, runs `TicketStorage.purgeStaleTickets` and then `ServerChunkCache.runDistanceManagerUpdates`. 2. **Idle time.** Whenever the server thread would otherwise wait, `MinecraftServer.pollTaskInternal` polls every level's `ServerChunkCache.MainThreadExecutor.pollTask`, which runs the distance updates *first* and, if they did any work, returns at once. The light schedule and the one queued chunk task only happen on a poll where the graphs were already settled — propagation does not share the queue with chunk work, it starves it until quiescent. 3. **A synchronous ask.** `ServerChunkCache.getChunk` from anywhere on the server thread checks a four-entry cache, then `ServerChunkCache.getChunkFutureMainThread` adds a `TicketType.UNKNOWN` ticket and, if `ServerChunkCache.chunkAbsent`, runs the distance updates synchronously so the holder exists in this call, then `BlockableEventLoop.managedBlock` until the future is done. The server thread never sleeps on a chunk: it runs chunk tasks while it waits. Off-thread callers are bounced to the main thread and joined. Inside `DistanceManager.runAllUpdates` the order is fixed: the spawn counter, the simulation tracker, the player ticket tracker, the loading tracker, and then **two passes** over `DistanceManager.chunksToUpdateFutures` — `GenerationChunkHolder.updateHighestAllowedStatus` for every changed holder first, `ChunkHolder.updateFutures` for every holder second — because a holder's range future depends on its neighbours' allowed status. Nothing here adds a ticket from a worker. The throttle's `ThrottlingChunkTaskDispatcher` is built over a `TaskScheduler` wrapping `DistanceManager.mainThreadExecutor`, so the ticket task it releases runs on the main thread; only the dispatcher's own priority-queue bookkeeping runs on the worker pool. ## The walk east View distance 10, simulation distance 10. ```mermaid sequenceDiagram participant SGPL as ServerGamePacketListenerImpl participant CM as ChunkMap participant DM as DistanceManager participant TS as TicketStorage participant CH as ChunkHolder participant PCS as PlayerChunkSender SGPL->>CM: move: the section changed CM->>DM: removePlayer(old) then addPlayer(new) DM->>TS: PLAYER_SIMULATION ticket moves, level 21 CM->>SGPL: ClientboundSetChunkCacheCenterPacket, the two crescents marked or dropped Note over DM: runAllUpdates, this tick or the next idle poll DM->>DM: simulation graph floods: entity range 10, block range 11 DM->>DM: PlayerTicketTracker: 21 chunks entered view, 21 left, four submitted at a time DM->>TS: PLAYER_LOADING added at level 31 (east), removed (west) DM->>CM: loading graph floods: updateChunkScheduling makes holders out to level 44 DM->>CH: updateFutures: 45 to 31 arms all three futures CH-->>CM: (a later tick) FULL, then BLOCK_TICKING: onChunkReadyToSend CM->>PCS: markChunkPendingToSend for every player whose view holds it CH-->>DM: ENTITY_TICKING completes, the throttle slot is released PCS->>SGPL: sendNextChunks: batch start, nearest first up to the quota, batch finished ``` The move is `ServerGamePacketListenerImpl.handleMovePlayer` → `ServerChunkCache.move` → `ChunkMap.move`, which updates every `ChunkMap.TrackedEntity` for the player and then compares `ServerPlayer.getLastSectionPos` with the new `SectionPos`. `DistanceManager.removePlayer` finds the old chunk's `DistanceManager.playersPerChunk` set empty and removes the `TicketType.PLAYER_SIMULATION` ticket; `DistanceManager.addPlayer` mirrors it at `DistanceManager.getPlayerTicketLevel`, 31 minus the simulation distance. `TicketStorage.addTicket` compares the new ticket's level against the lowest each graph the flags name already had, and tells the registered `TicketStorage.ChunkUpdated` listener only when the new one is lower; the removal path is the one that recomputes a minimum. Either way the listener only *queues* the change; the flood happens in `DistanceManager.runAllUpdates`. The simulation graph settles first and needs no futures and no IO: level 31 out to distance 10, 32 at 11, and from that moment `DistanceManager.inEntityTickingRange` and `DistanceManager.inBlockTickingRange` answer differently for the far western edge, whose block ticking has already stopped. Then `DistanceManager.PlayerTicketTracker.runAllUpdates` floods its own radius-32 graph. For each chunk newly within `DistanceManager.PlayerTicketTracker.haveTicketFor` it submits a task to the dispatcher at priority = distance; for each chunk that left, it releases the slot with a continuation that removes the `TicketType.PLAYER_LOADING` ticket. The dispatcher lets **four** through at a time; each adds a ticket at `DistanceManager.PLAYER_TICKET_LEVEL`, 31, and records the key in `DistanceManager.ticketsToRelease`. The loading graph floods from it — 31 at the chunk, 32 and 33 in the rings, then 34 … 44 eleven chunks further east — and `LoadingChunkTracker.setLevel` → `ChunkMap.updateChunkScheduling` creates a `ChunkHolder` for every chunk whose level dropped to 44 or below, or resurrects one from `ChunkMap.pendingUnloads`. Finally `ChunkMap.promoteChunkMap` publishes the new holders to the visible map and `ServerChunkCache.clearCache`. The release is what makes sprinting outrun the loader by design. `DistanceManager.runAllUpdates` does not wait for the entity-ticking future: on a pass where nothing else needed updating, it hangs a continuation on each pending key's future and clears the set. A busy tick defers every release, and the slot frees whenever the already-attached future completes, so at most four view chunks are ever loading at once and they are the four nearest. The west unloads without a timeout. The removed loading tickets raise the western column past 44 → `ChunkMap.toDrop` → the futures complete with `ChunkHolder.UNLOADED_LEVEL_CHUNK` and the demotion fires at once → the next `ChunkMap.tick` with the time supplier runs `ChunkMap.processUnloads` → `ChunkMap.scheduleUnload`, save and `ServerLevel.unload` ([chunk storage](chunk-storage.md)). If a ticket re-adopts the chunk first, `ChunkMap.updateChunkScheduling` pulls it back out of `ChunkMap.pendingUnloads` and the unload task finds nothing to do. ## What the player is sent, and when | the moment | what goes out | the gate | |---|---|---| | the player crosses a section boundary | `ClientboundSetChunkCacheCenterPacket` | only if the chunk column changed — `ChunkMap.updateChunkTracking` returns early on the same centre and view distance, and `ChunkMap.applyChunkTrackingView` sends the packet only when the centre moved | | a chunk enters the view | `ChunkMap.markChunkPendingToSend` | only if `ChunkMap.getChunkToSend` already has a ticking chunk; a fresh chunk waits for its promotion | | a chunk reaches BLOCK_TICKING | `ChunkMap.onChunkReadyToSend` → pending for every player whose view holds it | `ChunkHolder.sendSync`, which starts complete; the one thing that delays it is `ChunkMap.waitForLightBeforeSending`, whose single caller is `EnderDragonFight` after building the exit portal | | once a tick, from `MinecraftServer.tickChildren` | `ClientboundChunkBatchStartPacket`, up to the quota of `ClientboundLevelChunkWithLightPacket` nearest first, `ClientboundChunkBatchFinishedPacket` | under the acknowledgement limit: one batch until the first reply, then `PlayerChunkSender.MAX_UNACKNOWLEDGED_BATCHES`, 10 | | the client replies | `ServerboundChunkBatchReceivedPacket` carries how many chunks per tick it wants | clamped `PlayerChunkSender.MIN_CHUNKS_PER_TICK` 0.01 … `PlayerChunkSender.MAX_CHUNKS_PER_TICK` 64, starting at `PlayerChunkSender.START_CHUNKS_PER_TICK` 9 | | a chunk leaves the view | `ClientboundForgetLevelChunkPacket` | only if it was not still pending, and only to a living player — you cannot forget what was never delivered | | a block changes in a chunk not yet delivered | nothing | `ChunkMap.isChunkTracked` is false while the chunk sits in `PlayerChunkSender.pendingChunks`; the full chunk will carry it | | the settings change | `ClientboundSetChunkCacheRadiusPacket` · `ClientboundSetSimulationDistancePacket` | `PlayerList.setViewDistance` · `PlayerList.setSimulationDistance`, which swaps every simulation ticket's level through `TicketStorage.replaceTicketLevelOfType` | Two shapes hide in that table. The view is a rounded square, not a disc: `ChunkTrackingView.isWithinDistance` subtracts a buffer of two from each axis *before* squaring, so at view distance 10 it reaches eleven chunks along the axes and nine on the diagonal. And view distance shapes what is *sent*, not what is loaded: `ChunkMap.getPlayerViewDistance` clamps a player's request to the server's, but the ticket tracker's radius comes from the **server** view distance through `DistanceManager.updatePlayerTickets`. Singleplayer skips the size cap, not the pacing: `PlayerChunkSender` still wants an acknowledgement slot and a whole chunk of quota before it builds a batch, but on an in-memory connection the batch it then builds is the whole pending set. ## When a ticket dies | the ticket | dies when | |---|---| | no timeout — player, forced, dragon | its source removes it: the player leaves the chunk, `/forceload remove`, the fight ends | | timed and `TicketType.canExpireIfUnloaded` — only `TicketType.UNKNOWN` | the countdown runs every tick regardless, so it can expire before the chunk it asked for loads; `ServerChunkCache.addTicketAndLoadWithRadius` refuses such types for that reason | | timed, everything else — portal, pearl, spawn | the countdown runs only while there is **no holder at all** or the holder `ChunkHolder.isReadyForSaving`; a portal ticket never expires under a chunk still loading, and one over a chunk nothing tracks expires normally | | the server stops | every type but `TicketType.UNKNOWN` is parked and replayed on the next start; only the persisting types reach disk | `TicketStorage.purgeStaleTickets` runs from `ServerChunkCache.tick` and applies exactly those rules — once a tick, unless the level is frozen and chunk ticking is on, in which case it does not run at all. ## Questions players ask **Why does turning my render distance down not help the server?** Because the server loads to *its* view distance, not yours. Your request only clamps what you are sent. **Why does the world load in a square?** It does not, quite: the tracking view is a square with its corners cut by the buffer-of-two test above. Loading, though, follows the graph, and the graph floods in Chebyshev rings — every ring is a square. **Why do mobs spawn where I did not expect?** The spawn set is a fixed radius of eight around each player, on a tracker that reads neither graph and no setting. **Why does a portal keep its chunks after I have gone?** A `TicketType.PORTAL` ticket lasts 300 ticks, persists across restarts with its remaining `Ticket.ticksLeft`, and only counts down while its chunk is saveable. **Why does sprinting outrun chunk loading?** Four in flight, nearest first, one slot released per completed entity-ticking future. It is a throttle, not a bug. **Do spectators load chunks?** Only if `GameRules.SPECTATORS_GENERATE_CHUNKS` says so: `ChunkMap.skipPlayer` is the gate, and a skipped player is still sent chunks that exist, but places no tickets that would generate them. ## Where to look `TicketType` · `TicketStorage.addTicket` · `TicketStorage.purgeStaleTickets` · `ChunkLevel.byStatus` · `ChunkLevel.fullStatus` · `DistanceManager.addPlayer` · `DistanceManager.runAllUpdates` · `DistanceManager.PlayerTicketTracker.onLevelChange` · `LoadingChunkTracker` · `SimulationChunkTracker` · `ChunkTracker.computeLevelFromNeighbor` · `ChunkMap.updateChunkScheduling` · `ChunkMap.move` · `ChunkMap.applyChunkTrackingView` · `ChunkMap.prepareTickingChunk` · `ChunkHolder.updateFutures` · `ChunkHolder.scheduleFullChunkPromotion` · `ServerChunkCache.getChunk` · `ServerChunkCache.getChunkFutureMainThread` · `ServerChunkCache.runDistanceManagerUpdates` · `ChunkTrackingView.difference` · `PlayerChunkSender.sendNextChunks` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The chunk generation pipeline > Verified against **Minecraft 26.2** · Part IV · A ticket asks for one chunk at *FULL*, and the server claims five hundred and twenty-nine of them before it runs a single step. A player walks east, a loading ticket lands on the chunk that just entered view, and `ChunkHolder.updateFutures` asks that chunk for `ChunkStatus.FULL`. Nothing in the request mentions neighbours. But *FULL* is the last of twelve steps, and the steps below it read — and four of them write — the chunks around the one being built, so the first thing `ChunkGenerationTask.create` does is walk out to Chebyshev distance 11 and take a claim on every holder in that square: **asking for one chunk asks for 529 of them, and the eleven rings it claims will never, on this task's account, become chunks you could stand on.** Eleven is not a tuning constant. It is the index of the last entry of `ChunkStep.accumulatedDependencies` for the FULL step — a twelve-entry list, one per ring from the centre out: `ChunkLevel.RADIUS_AROUND_FULL_CHUNK` reads that radius off the pyramid, and `ChunkLevel.MAX_LEVEL`, 44, is 33 plus it. Change the pyramid and the world's loading radius changes with it. ## The cast | class | what it decides | thread | |---|---|---| | `ChunkStatus` | the twelve names and their order, and nothing else — no task, no radius, no work | static, a `BuiltInRegistries.CHUNK_STATUS` entry | | `ChunkPyramid` | the two step lists — one for generating, one for loading — that say what each status needs and what runs it | static | | `ChunkStep` | one status's direct and accumulated dependencies, its block-state write radius, and its body | static | | `ChunkGenerationTask` | one (chunk, target) walk: which layer is in flight, which pyramid it is using, and when to yield | the *worldgen* executor | | `GenerationChunkHolder` | one chunk's twelve futures, its ticket-derived ceiling, and the compare-and-set that runs each step exactly once | any — every field of it is atomic | | `ChunkMap` | makes the tasks, owns both executors, and turns the *EMPTY* step into a disk read | Server, but `ChunkMap.applyStep` runs on whatever thread reached it | | `ChunkTaskDispatcher` | which chunk's batch of work the executor gets next, and re-sorts the queue when tickets move | its own single-file queue on the worker pool | | `WorldGenRegion` | what a running step may read and what it may write, checked per call | the thread running the step | ## The pyramid, drawn ```mermaid flowchart TD EM[("EMPTY, radius 11 — the disk read: region file, parse on the pool, chunk object on the server thread")] SS["STRUCTURE_STARTS, radius 11 — inline on the worldgen executor"] SR["STRUCTURE_REFERENCES, radius 3 — inline on the worldgen executor"] BI(["BIOMES, radius 3 — forked to the worker pool as init_biomes"]) NO(["NOISE, radius 2 — forked to the worker pool as wgen_fill_noise, may write radius 0"]) SU["SURFACE, radius 2 — inline, may write radius 0"] CA["CARVERS, radius 2 — inline, may write radius 0"] FE["FEATURES, radius 1 — inline, may write radius 1"] IL(["INITIALIZE_LIGHT, radius 1 — the light executor"]) LI(["LIGHT, radius 0 — the light executor"]) SP["SPAWN, radius 0 — inline on the worldgen executor"] FU(["FULL, radius 0 — the server thread"]) ACC["accumulated for FULL: SPAWN at distance 0, INITIALIZE_LIGHT at 1, CARVERS at 2, BIOMES at 3, STRUCTURE_STARTS from 4 out to 11 — twelve entries, so a radius of 11"] EM --> SS --> SR --> BI --> NO --> SU --> CA --> FE --> IL --> LI --> SP --> FU FU -- "ChunkStep.accumulatedDependencies, counted" --> ACC ``` Read it downward: that is the whole pipeline. The rounded steps are the five that leave the *worldgen* executor, the cylinder is the one step that is not worldgen at all, and the six plain boxes run inline. The radius on each node is how wide that layer is swept **when the target is FULL and the task has decided it must generate** — `ChunkGenerationTask.getRadiusForLayer` asks the FULL step of whichever pyramid is in play for `ChunkStep.getAccumulatedRadiusOf` that status. A task aiming lower sweeps narrower rings, and a chunk that only ever reaches *STRUCTURE_STARTS* is swept at radius 0 by its own task. *EMPTY* is the node to read twice: the first sweep is the loading pyramid's radius 1, and only a chunk that turns out to need generating is swept at 11, as the load-or-generate section below explains. A `ChunkStatus` carries no work. It is a registry entry with an index, a parent, a `ChunkType` (`ChunkType.PROTOCHUNK` for the first eleven, `ChunkType.LEVELCHUNK` for `ChunkStatus.FULL`) and the heightmaps that become valid after it, `ChunkStatus.heightmapsAfter`. Everything else — the dependencies, the write radius, the body — lives in the `ChunkStep` that `ChunkPyramid` holds for that status. Each step is built from its predecessor, so every step silently requires its own parent status at distance 0 before it declares anything; `ChunkStep.Builder.addRequirement` then widens the array outward, taking the later of the two statuses at every distance already covered. The generation pyramid's declared requirements, with the parent requirement resolved in: | step | needs | may write | |---|---|---| | *STRUCTURE_STARTS* | *EMPTY* at 0 | — | | *STRUCTURE_REFERENCES* | *STRUCTURE_STARTS* from 0 out to 8 | — | | *BIOMES* | *STRUCTURE_REFERENCES* at 0, *STRUCTURE_STARTS* out to 8 | — | | *NOISE* | *BIOMES* within 1, *STRUCTURE_STARTS* out to 8 | radius 0 | | *SURFACE* | *NOISE* at 0, *BIOMES* at 1, *STRUCTURE_STARTS* out to 8 | radius 0 | | *CARVERS* | *SURFACE* at 0, *STRUCTURE_STARTS* out to 8 | radius 0 | | *FEATURES* | *CARVERS* within 1, *STRUCTURE_STARTS* out to 8 | radius 1 | | *LIGHT* | *INITIALIZE_LIGHT* within 1 | — | | *SPAWN* | *LIGHT* at 0, *BIOMES* at 1 | — | The rows that do the work are the radius-1 ones: they force a neighbour to run one step ahead of the chunk being built. Five requirements in the pyramid have radius 1, but only three of them widen the accumulated list, because `ChunkStep.Builder.getRadiusOfParent` counts a debt only when the step's own parent already sits a ring out. *NOISE* wanting *BIOMES*, *FEATURES* wanting *CARVERS* and *LIGHT* wanting *INITIALIZE_LIGHT* each add one; *SURFACE* and *SPAWN*, which also ask for *BIOMES* within 1, add nothing. Three ones on top of *STRUCTURE_STARTS* out to 8 is where the 11 comes from. `ChunkStatus.MAX_STRUCTURE_DISTANCE` is declared as 8 and the pyramid writes the literal each time — no reader of the constant survives the decompile. The same arithmetic sets the edge of the world. `ChunkPyramid.SAFETY_MARGIN_CHUNKS` is 32 plus the twelve accumulated entries plus one, doubled — 90 chunks — subtracted from the coordinate maximum to give `ChunkPyramid.MAX_CHUNK_COORDINATE_VALUE`, which `ChunkPos.isValid` enforces and the `GenerationChunkHolder` constructor throws on. It is a guard against arithmetic, not against players: at about 33.5 million blocks it sits three and a half million blocks *outside* `Level.MAX_LEVEL_SIZE`, the ±30 000 000 nobody can build past anyway. ## A ticket sets a ceiling, and a separate call names the target Two different numbers reach a holder from the ticket system ([tickets and loading](tickets-and-loading.md)). `DistanceManager.runAllUpdates` first gives every touched holder `GenerationChunkHolder.updateHighestAllowedStatus`, which is `ChunkLevel.generationStatus` of the new ticket level — 33 is *FULL*, 34 is *INITIALIZE_LIGHT*, 35 *CARVERS*, 36 *BIOMES*, 37 through 44 *STRUCTURE_STARTS*, and 45 is no status at all. That is a ceiling, not a goal: `GenerationChunkHolder.isStatusDisallowed` gates every request against it and hands back `GenerationChunkHolder.UNLOADED_CHUNK_FUTURE` for anything above. Then `ChunkHolder.updateFutures` crosses `FullChunkStatus.FULL`, calls `ChunkMap.prepareAccessibleChunk`, and *that* names the target: `ChunkMap.getChunkRangeFuture` over the 3×3, `ChunkStatus.FULL` on the centre and `ChunkLevel.getStatusAroundFullChunk` — *INITIALIZE_LIGHT* — on the eight around it, each through `GenerationChunkHolder.scheduleChunkGenerationTask`. If the ceiling later drops, `GenerationChunkHolder.updateHighestAllowedStatus` fails every pending future between the new ceiling and the old with `GenerationChunkHolder.UNLOADED_CHUNK` and reschedules the task at the highest status anyone is still waiting for. Nothing is interrupted; a worker mid-step finishes it and finds nobody listening. The other way in is synchronous. `ServerChunkCache.getChunk` from the server thread adds a `TicketType.UNKNOWN` ticket, runs the distance updates inline so the holder exists, and then `BlockableEventLoop.managedBlock`s on the future — with `ServerChunkCache.MainThreadExecutor.pollTask` overridden to drain chunk work while it waits, so the thread that is blocked on generation is also the thread finishing it. ## The task claims its 529 before it runs anything `GenerationChunkHolder.scheduleChunkGenerationTask` finds no task in flight, so `GenerationChunkHolder.rescheduleChunkTask` calls `ChunkMap.scheduleGenerationTask` and `ChunkGenerationTask.create` builds the holder set: a `StaticCache2D` whose radius is the *generation* pyramid's accumulated radius of `ChunkStatus.EMPTY` for the target — 11 for *FULL* — filled by `GeneratingChunkMap.acquireGeneration`, the five-method interface `ChunkMap` implements and the pipeline actually holds. That radius is taken from the generation pyramid unconditionally, before anything has looked at the disk, so even a chunk that turns out to be sitting complete in a region file claims all 529 holders first. The claim is a reference count. `GenerationChunkHolder.increaseGenerationRefCount` on the first claim arms `GenerationChunkHolder.generationSaveSyncFuture` and hangs it off the holder's save dependency, so nothing in the square can be saved or unloaded while the task lives ([chunk storage](chunk-storage.md)). The task itself waits in `ChunkMap.pendingGenerationTasks` until `ChunkMap.runGenerationTasks`, at the end of the same `ServerChunkCache.runDistanceManagerUpdates` that created it. ## Dispatch, and why the parallelism is smaller than the thread names `ChunkMap` builds two `ConsecutiveExecutor`s over the shared worker pool, named *worldgen* and *light*, each wrapped in a `ChunkTaskDispatcher`. A `ConsecutiveExecutor` runs **one task at a time**: `AbstractConsecutiveExecutor.run` pops a single item, runs it under the executor's name, and re-registers itself on the pool. The dispatcher in front of it is a `ChunkTaskPriorityQueue` of `ChunkTaskPriorityQueue.PRIORITY_LEVEL_COUNT` buckets — 46, `ChunkLevel.MAX_LEVEL` plus two — keyed by the holder's queue level, and `ChunkTaskDispatcher.scheduleForExecution` hands over one chunk's runnables at a time, polling again only when they have all completed. So all worldgen for a dimension is a single file, however many `Worker-Main-n` threads the pool has (`Util.maxAllowedExecutorThreads`: cores minus one, capped by the *max.bg.threads* property; there is no generation thread setting). **One** — worldgen runnables executing at a time per dimension (`ChunkMap.worldgenTaskDispatcher`, over a single `ConsecutiveExecutor`). Overlap comes from yielding, not from threads. `ChunkGenerationTask.runUntilWait` returns the moment a layer holds a future that is not yet done; `ChunkMap.runGenerationTask` chains a resubmit onto that future and the executor moves to another chunk's task at once. No worldgen thread ever blocks waiting for a neighbour, and a task parked on a biome fork costs nothing. Priority is live, not fixed at submission. `ChunkHolder.updateFutures` ends by telling both dispatchers through `ChunkTaskDispatcher.onLevelChange`, and `ChunkTaskPriorityQueue.resortChunkTasks` moves work already queued into its new bucket — at a higher priority inside the dispatcher's own four-slot queue than new submissions get, so "closer to a player runs first" stays true while the player is moving. `ThrottlingChunkTaskDispatcher` is a subclass of the same thing but is *not* worldgen: it caps how many player-view chunks the ticket tracker may have in flight, on the main thread. ## The EMPTY step asks the only question that changes the walk `ChunkGenerationTask.scheduleNextLayer` always begins with `ChunkStatus.EMPTY` at the *loading* pyramid's radius, which for a *FULL* target is 1. `ChunkMap.applyStep` special-cases that status: instead of a step body it runs `ChunkMap.scheduleChunkLoad` — the region read, `ChunkMap.upgradeChunkTag` on the pool under *upgradeChunk*, `SerializableChunkData.parse` on the pool under *parseChunk*, the POI file prefetched alongside through `SectionStorage.prefetch`, then `SerializableChunkData.read` **on the server thread**. What comes out is a `ProtoChunk` at whatever status the file recorded, an `ImposterProtoChunk` wrapping a real `LevelChunk` if the file was already at *FULL*, or `ChunkMap.createEmptyChunk` when there was no file. The futures are not done, so the task yields and is re-entered when they land ([chunk storage](chunk-storage.md)). Now `ChunkGenerationTask.canLoadWithoutGeneration` decides. It wants the centre persisted at or past the target, and every chunk in the loading pyramid's accumulated square — for *FULL*, the 3×3 — at or past what its distance requires there: *SPAWN* at the centre, *INITIALIZE_LIGHT* on the ring. If that holds, the walk stays narrow. `ChunkPyramid.LOADING_PYRAMID` passes seven of the twelve steps straight through and only four do anything — `ChunkStatusTasks.loadStructureStarts`, which just posts the saved starts to `StructureCheck`, the two light steps, and `ChunkStatusTasks.full`. **A loaded chunk still walks all twelve steps**, and it still needs its 3×3 neighbours at *INITIALIZE_LIGHT* before its own *LIGHT* step will run. If it does not hold, `ChunkGenerationTask.needsGeneration` goes true and *EMPTY* is scheduled a second time, now at radius 11 — reading only the chunks the first sweep did not touch. `GenerationChunkHolder.applyStep` runs `GenerationChunkHolder.acquireStatusBump`, a compare-and-set on `GenerationChunkHolder.startedWork` from a status's parent to the status itself, so exactly one caller ever runs a step for a holder and every other caller is handed the existing future. And the choice of pyramid is made again for **every chunk in every layer**, not once for the task. `ChunkGenerationTask.scheduleChunkInLayer` compares that chunk's persisted status with the layer being applied and takes the generation pyramid only if the chunk is genuinely behind, so a generating task's 23×23 square routinely mixes both — which is exactly what stops already-finished neighbours being generated a second time. ## Four steps may write, and only four `ChunkStep`'s default block-state write radius is **−1**, not 0 — so for eight of the twelve steps `WorldGenRegion.ensureCanWrite` fails even for the chunk's own column, and `WorldGenRegion.setBlock` logs and returns false rather than doing anything. Only *NOISE*, *SURFACE* and *CARVERS* (radius 0) and *FEATURES* (radius 1) can change a block at all. What rides on those steps — the density functions, the surface rules, the carvers, the features and the structures they place — is Part XII's subject ([terrain](../worldgen/terrain.md), [density functions](../worldgen/density-functions.md), [structure placement](../worldgen/structure-placement.md)). This page is the conveyor. *STRUCTURE_STARTS* runs `ChunkGenerator.createStructures` for every chunk in the radius-11 square that is not already past it — seed and placement state only, no terrain — and is skipped entirely when `WorldOptions.generateStructures` is off. Either way `ServerLevel.onStructureStartsAvailable` posts the chunk's starts to the server thread. *STRUCTURE_REFERENCES* then records, per chunk, which starts within eight chunks reach into it: the reason starts needed a radius of 8 around *it*. *BIOMES* forks — both `ChunkGenerator.createBiomes` and `NoiseBasedChunkGenerator`'s override put the work on the pool under *init_biomes* — so biomes always leave the worldgen executor. *NOISE* forks only for `NoiseBasedChunkGenerator`, under *wgen_fill_noise*, and applies the `BelowZeroRetrogen` bedrock fix-ups afterwards if the chunk is being deepened; `FlatLevelSource` and `DebugLevelSource` return a completed future and stay inline. *SURFACE* and *CARVERS* run inline at write radius 0. *FEATURES* primes the four final heightmaps with `Heightmap.primeHeightmaps`, decorates, and calls `Blender.generateBorderTicks` ([blending](../worldgen/blending.md)). *FEATURES* is the interesting one, because a tree at a chunk edge writes into a neighbour and nothing about that neighbour's status says it is safe. What makes it safe is the executor: the layer steps its chunks one at a time, and every worldgen task in the dimension is serialised behind the one `ConsecutiveExecutor`, so no two feature steps in a dimension are ever running at once. The ordinary cross-chunk write is the plain `ChunkAccess.setBlockState`, which takes and releases the section per write; only `OreFeature` holds a section open across many writes, through `BulkSectionAccess` ([chunk anatomy](chunk-anatomy.md)). ### A read too far crashes, a read too wide only warns Both bad accesses are caught, and they are caught differently. A read outside the step's `ChunkStep.directDependencies` — too far away, or at a status that distance does not guarantee — throws out of `WorldGenRegion.getChunk` as a crash report naming the step, the requested and actual statuses, the distance and the whole dependency list. A read that is merely outside the *write* zone is a log warning from `WorldGenRegion.warnIfReadOutsideWriteZone`, naming the feature through `WorldGenRegion.currentlyGenerating`. The first is a bug in the pyramid; the second is a bug in a feature, and the game keeps going. ## Light runs on a second executor, and the task waits for it `ChunkStatusTasks.initializeLight` calls `ChunkAccess.initializeLightSources` and `ProtoChunk.setLightEngine` — from that moment the proto chunk forwards block changes to the engine — and then hands the chunk to `ThreadedLevelLightEngine.initializeLight`. `ChunkStatusTasks.light` follows with `ThreadedLevelLightEngine.lightChunk`. Both queue through the *light* `ChunkTaskDispatcher` onto the *light* `ConsecutiveExecutor`, and the future each returns is completed by a later task on that same executor, so the generation task genuinely parks here ([lighting](lighting.md)). Both are passed a *lighted* flag from `ChunkStatusTasks.isLighted`: persisted status at or past *LIGHT* **and** `ChunkAccess.isLightCorrect`. When it is true, `ThreadedLevelLightEngine.lightChunk` skips propagation entirely and only marks the chunk correct again. Light saved on disk is re-enabled, never recomputed. ## FULL is assembled on the server thread `ChunkStatusTasks.full` is scheduled exactly like the other eleven steps, but its body is a *supplyAsync* on `WorldGenContext.mainThreadExecutor` — the `ServerChunkCache.MainThreadExecutor`, a `BlockableEventLoop` pinned to the server thread. There are two shapes it can take. If the chunk is already an `ImposterProtoChunk`, because the file held a finished chunk, it unwraps the `LevelChunk` inside and replaces nothing. Otherwise a `LevelChunk` is built from the `ProtoChunk`, sharing its sections, and `GenerationChunkHolder.replaceProtoChunk` rewrites slots 0 through 10 of the holder's future array to an `ImposterProtoChunk` over it with writes disallowed — every slot checked, and the whole step thrown out if any of them is not a `ProtoChunk` or was changed by another thread in the meantime. Then the chunk becomes part of the world, in order: `LevelChunk.setFullStatus` wired to the holder, `LevelChunk.runPostLoad` turning `ProtoChunk.getEntities` into real entities through `ServerLevel.addWorldGenChunkEntities`, `LevelChunk.setLoaded`, `LevelChunk.registerAllBlockEntitiesAfterLevelLoad`, `LevelChunk.registerTickContainerInLevel` and `LevelChunk.setUnsavedListener`. `GenerationChunkHolder.completeFuture` publishes it at *FULL*. Nothing here crosses the network. A chunk reaches a client only after the separate promotion to `FullChunkStatus.BLOCK_TICKING` ([tickets and loading](tickets-and-loading.md)). ## Release, and what the ring is left as `ChunkGenerationTask.runUntilWait` comes round, finds the scheduled status equal to the target, and calls `ChunkGenerationTask.releaseClaim`: `GenerationChunkHolder.removeTask` on the centre, then `ChunkMap.releaseGeneration` on all 529. Every holder whose count reaches zero completes its save-sync future, and the square is free to be saved or dropped as its own tickets dictate — which the outer rings, only ever raised to the status their distance demanded, mostly are. `ChunkHolder.scheduleFullChunkPromotion` was called back in `ChunkHolder.updateFutures`, long before any of this; what happens now is its confirmation landing on the server thread and `ChunkMap.onFullChunkStatusChange` tells the entity manager. `ChunkLoadCounter` watches this from outside — it counts holders that reach *FULL* for the spawn progress bar, and that count is what `MinecraftServer.prepareLevels` loops on until it is zero. ## The whole walk, once ```mermaid sequenceDiagram participant DM as DistanceManager participant CM as ChunkMap participant CTD as ChunkTaskDispatcher participant CGT as ChunkGenerationTask participant Worker as Worker participant TLE as ThreadedLevelLightEngine participant SL as ServerLevel Note over DM,SL: the Server thread, inside runDistanceManagerUpdates DM->>CM: the holder reaches level 33 — updateHighestAllowedStatus, then updateFutures CM->>CGT: prepareAccessibleChunk, getChunkRangeFuture, scheduleGenerationTask — create claims 529 holders CM->>CTD: runGenerationTasks submits runUntilWait at the holder's queue level Note over CTD,CGT: thread hop — the worldgen ConsecutiveExecutor, one task at a time per dimension CTD->>CGT: scheduleForExecution hands this chunk's batch over CGT->>CM: layer EMPTY at radius 1 — applyStep becomes scheduleChunkLoad CM->>Worker: region read, then upgradeChunk and parseChunk on the pool Worker->>SL: thread hop — SerializableChunkData.read builds the chunk object Note over CTD,CGT: the task yielded on the first unfinished future and was resubmitted CGT->>CGT: canLoadWithoutGeneration is false — EMPTY again, now to radius 11 CGT->>CM: STRUCTURE_STARTS to 11, then STRUCTURE_REFERENCES to 3 CM->>SL: onStructureStartsAvailable posts each chunk's starts to the server thread CGT->>Worker: thread hop — BIOMES to 3 as init_biomes, NOISE to 2 as wgen_fill_noise CGT->>CM: SURFACE and CARVERS to 2, FEATURES to 1 — inline, the steps that may write CGT->>TLE: thread hop — INITIALIZE_LIGHT at 1 and LIGHT at 0 on the light executor CGT->>SL: SPAWN inline, then FULL — thread hop, supplyAsync on the main-thread executor SL->>CM: LevelChunk built, replaceProtoChunk, setLoaded, tick containers registered CGT->>CM: releaseClaim — removeTask, then releaseGeneration on all 529 ``` ## Questions players ask **Why does adding cores not speed up world generation?** Because a dimension's worldgen is one `ConsecutiveExecutor` running one task at a time, and the dispatcher in front of it releases one chunk's work at a time. The pool is busy in parallel with plenty else — the *light* executor beside it, the disk read and its datafix, the POI prefetch, the biome and noise forks, the second dimension — but none of that is a second worldgen lane. There is no thread-count setting for generation. **Why does a chunk I have visited before still take work to load?** It walks all twelve steps. Seven of them pass through and cost nothing, but the disk read, the structure-start replay, both light steps and the *FULL* assembly are real work, and the light steps need the 3×3 neighbours read first. **Why does a chunk sometimes hang on the edge of the view forever?** Its ticket level puts the ceiling below *FULL*. `GenerationChunkHolder.isStatusDisallowed` refuses anything higher, so the chunk sits at *STRUCTURE_STARTS* or *BIOMES*, correct and unfinished, for as long as the level says so. **Why is there a limit on how far out I can build?** Not because of the pyramid. `ChunkPyramid.SAFETY_MARGIN_CHUNKS` does reserve 90 chunks at the coordinate maximum so that a chunk at the edge still has its radius-11 square to generate in, and `ChunkPos.isValid` refuses a holder outside it — but that edge is three and a half million blocks further out than `Level.MAX_LEVEL_SIZE`, which is the ±30 000 000 a player actually meets. ## Where to look `ChunkPyramid.GENERATION_PYRAMID` · `ChunkPyramid.LOADING_PYRAMID` · `ChunkStep.getAccumulatedRadiusOf` · `ChunkDependencies.getRadiusOf` · `ChunkLevel.RADIUS_AROUND_FULL_CHUNK` · `ChunkGenerationTask.create` · `ChunkGenerationTask.runUntilWait` · `ChunkGenerationTask.scheduleNextLayer` · `ChunkGenerationTask.canLoadWithoutGeneration` · `ChunkGenerationTask.scheduleChunkInLayer` · `GenerationChunkHolder.scheduleChunkGenerationTask` · `GenerationChunkHolder.applyStep` · `GenerationChunkHolder.acquireStatusBump` · `ChunkMap.applyStep` · `ChunkMap.scheduleChunkLoad` · `ChunkMap.runGenerationTask` · `ChunkTaskDispatcher.scheduleForExecution` · `ChunkTaskPriorityQueue.resortChunkTasks` · `AbstractConsecutiveExecutor.run` · `ChunkStatusTasks.full` · `WorldGenRegion.getChunk` · `WorldGenRegion.ensureCanWrite` · `StaticCache2D` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Lighting > Verified against **Minecraft 26.2** · Part IV · A torch is placed on a cave wall: the write queues a task, a worker floods the change, and the sections it touched are published as a copy and mailed to the client. A player right-clicks a torch onto stone. The block goes into its `LevelChunkSection` immediately, the heightmaps move, and then `LevelChunk.setBlockState` reaches the light engine at all only to call `LevelLightEngine.checkBlock`, which on the server computes nothing at all. It wraps the call in a runnable and puts it on a queue that nothing in the level tick will ever drain. There is no light thread and no light phase of the tick: the flood that turns the torch's 14 into a sphere of falling numbers runs because **the server thread finished early and went looking for something to do**. The kick is `ThreadedLevelLightEngine.tryScheduleUpdate`, whose only routine caller is `ServerChunkCache.MainThreadExecutor.pollTask` — the idle poll `MinecraftServer.pollTaskInternal` reaches only when the tick has budget left. Ask the server engine to light something synchronously and it refuses: `ThreadedLevelLightEngine.runLightUpdates` throws. ## The cast | class | what it decides | thread | |---|---|---| | `LevelLightEngine` | the facade both sides hold — one `LightEngine` per layer, the read side, and the padding above and below the world | whoever calls it | | `ThreadedLevelLightEngine` | the server's wrapper: every mutator becomes a queued task, and running updates on demand is an error | queued from Server, drained on the light executor | | `BlockLightEngine` · `SkyLightEngine` | the algorithm — what a changed block means, and how far the change travels | the light executor (Client: the render thread) | | `LayerLightSectionStorage` | the double buffer: which sections hold data, which changed, and when to publish | the same | | `DataLayer` | 2048 bytes of nibbles for one section, or no bytes at all | whoever holds it | | `ChunkSkyLightSources` | the chunk's 256-entry *lowest sky source Y* table — the one piece of light work that is not deferred | Server, inside the block write | | `ServerChunkCache` | the `LightChunkGetter`: which chunks the engine may read, and where the one callback lands | callback on the light executor, body on Server | | `ChunkHolder` | which sections each watching player is owed, as two `BitSet`s | Server | ## From a click to a lit wall ```mermaid sequenceDiagram participant LC as LevelChunk participant TLE as ThreadedLevelLightEngine participant SCC as ServerChunkCache participant BLE as BlockLightEngine participant LLSS as LayerLightSectionStorage participant CH as ChunkHolder participant CL as ClientLevel Note over LC,CH: one server tick, inside Level.setBlock LC->>LC: setBlockState sees different light properties, so ChunkSkyLightSources.update runs here and now LC->>TLE: checkBlock, wrapped as a PRE_UPDATE task and submitted at the chunk's queue level Note over LC,SCC: still the same tick, and nothing has been lit SCC->>TLE: pollTask found no distance-graph work, so tryScheduleUpdate Note over TLE,LLSS: the light executor, off the server thread TLE->>BLE: the window's PRE tasks, then LevelLightEngine.runLightUpdates BLE->>BLE: checkNode enqueues a pull-in decrease and an emission increase of 14 BLE->>LLSS: propagateDecreases to empty, then propagateIncreases writing 14, 13, 12 and down LLSS->>LLSS: markNewInconsistencies, then swapSectionMap publishes a copy LLSS->>SCC: onLightUpdate once per affected section Note over SCC,CH: back on the server thread, whenever the posted task is polled SCC->>CH: sectionLightChanged, which bails on a chunk not yet at INITIALIZE_LIGHT and otherwise marks it unsaved and sets a bit Note over SCC,CH: end of ServerChunkCache.tickChunks, normally the next tick SCC->>CH: broadcastChangedChunks reaches broadcastChanges CH->>CL: ClientboundLightUpdatePacket to players this chunk borders, put on lightUpdateQueue by ClientPacketListener Note over CL: the next frame, not the next tick CL->>CL: pollLightUpdates, applyLightData, then runLightUpdates on the client's own engine ``` Every section below walks one stretch of that diagram. Two classes are absent because they carry no decision: `ChunkTaskDispatcher`, which holds the queued task under the chunk's priority and hands it to the executor, and `ClientPacketListener`, which only wraps the packet in a closure for later. ## Two 4-bit fields, and the sections that may not exist Light is `LightLayer.SKY` and `LightLayer.BLOCK`, computed by separate machinery that shares only its shape. `LevelLightEngine` holds one `LightEngine` per layer — `LevelLightEngine.skyEngine` is null in a dimension whose `DimensionType.hasSkyLight` is false, and `LevelLightEngine.getLayerListener` hands out `LayerLightEventListener.DummyLightLayerEventListener` in its place. Readers rarely see the layers apart: `LevelLightEngine.getRawBrightness` is the maximum of block light and sky light minus a darkening term, which is what `LevelReader.getMaxLocalRawBrightness` passes `LevelReader.getSkyDarken` into, and `BlockAndLightGetter.canSeeSky` is just sky light at 15. Storage is per layer and per *section*, one section taller than the world at each end — `LevelLightEngine.LIGHT_SECTION_PADDING` is 1 and `LevelLightEngine.getLightSectionCount` is the world's sections plus two. A section's storage is a `DataLayer`: 16×16×16 nibbles in `DataLayer.SIZE` bytes, indexed *y* then *z* then *x*, and allocated lazily. `DataLayer.data` starts null and every read answers `DataLayer.defaultValue` until the first write forces the array into existence, and `DataLayer.fill` throws it away again. `DataLayer.isEmpty` therefore means *homogeneous zero*, not *all zeroes I checked*, and it is the test both the chunk saver and the packet builder use to charge nothing for a dark section. Which sections have a `DataLayer` at all is decided by `LayerLightSectionStorage.sectionStates`, one byte per section holding a has-data bit and a five-bit count of how many of its 26 neighbours have data (`LayerLightSectionStorage.SectionState`). A byte of zero means no storage, so a section of pure air beside a built-up one is allocated and a section in the middle of nothing is not. Above the sky column's top section there is no storage at all, and `SkyLightSectionStorage.getLightValue` answers 15 without looking; the upward walk it is known for happens *below* the top, when a section inside the column has no `DataLayer` and the search climbs for one. ## The write that queues nothing but a task `LevelChunk.setBlockState` asks two questions about light. If the section flipped between all-air and not — the torch is the first block into an empty section, or the last one out — `LevelLightEngine.updateSectionStatus` goes in. Then, if `LightEngine.hasDifferentLightProperties` says the old and new states differ in emission or in dampening, or if *either* of them uses a shape for light occlusion, two things happen. The first is `ChunkSkyLightSources.update` on the chunk's own table, and it runs **inline, on the server thread**, inside the block write: the sky column's *lowest source Y* for that one *(x, z)* is repaired immediately, because everything downstream reads it. The second is `LevelLightEngine.checkBlock`, and on the server that is where the work stops. `ProtoChunk.setBlockState` does the same pair, but only once the chunk's status has reached `ChunkStatus.INITIALIZE_LIGHT`; before that a generator writing blocks tells the light engine nothing. `ThreadedLevelLightEngine` overrides every mutator the same way. The call becomes a `Runnable` tagged `ThreadedLevelLightEngine.TaskType.PRE_UPDATE` or `ThreadedLevelLightEngine.TaskType.POST_UPDATE` and goes through `ChunkMap.lightTaskDispatcher`, at the chunk's own queue level for the five mutators that belong to one chunk — `LevelLightEngine.checkBlock`, `LightEventListener.propagateLightSources`, `LevelLightEngine.setLightEnabled`, `ThreadedLevelLightEngine.initializeLight` and `ThreadedLevelLightEngine.lightChunk` — and at a flat top priority for bookkeeping such as `LevelLightEngine.updateSectionStatus` and `LevelLightEngine.queueSectionData`. When the dispatcher releases the task it runs on the *light* executor, not on the server thread, and all it does there is append to `ThreadedLevelLightEngine.lightTasks` — a plain array list with no synchronisation, safe precisely because only that executor appends to it. ## What actually kicks it The light executor is a `ConsecutiveExecutor` named *light*, built in `ChunkMap`'s constructor over the shared background pool. It has no thread of its own: it takes one task from its queue, runs it on a borrowed pool thread, and re-registers itself. Two callers ever start a batch on it. `ServerChunkCache.MainThreadExecutor.pollTask` runs the distance-graph updates first and calls `ThreadedLevelLightEngine.tryScheduleUpdate` only if they had nothing to do — so light propagates in the gaps of a tick that finished early, after the ticket system is quiescent ([tickets](tickets-and-loading.md)). The other caller is `ChunkMap.scheduleUnload`, kicking the engine right after it has *queued* the nulling of an unloading chunk's data. `ThreadedLevelLightEngine.scheduled`, an `AtomicBoolean`, keeps exactly one batch in flight. If nobody kicks, the queue is still not unbounded: `ThreadedLevelLightEngine.addTask` runs a batch inline as soon as `ThreadedLevelLightEngine.lightTasks` has reached a thousand — the value `ThreadedLevelLightEngine.DEFAULT_BATCH_SIZE` names, though the test is written as a literal — still on the light executor, still never on the server thread. ## One batch, and what it publishes ```mermaid flowchart TD PRE["ThreadedLevelLightEngine.runUpdate takes a window of up to 1000 queued tasks and runs the PRE_UPDATE ones"] PRE --> NODES["every checkBlock in that window has now added a position to LightEngine.blockNodesToCheck"] NODES --> LAYER["LevelLightEngine.runLightUpdates runs the block engine to completion, then the sky engine, each running the stages below"] LAYER --> C["checkNode on every queued position, deciding what to enqueue, then the set is cleared"] C --> D["propagateDecreases drains decreaseQueue to empty, including the refills it discovers"] D --> I["propagateIncreases drains increaseQueue to empty, including those refills"] I --> M["markNewInconsistencies splices queuedSections in and drops removed sections"] M --> S["swapSectionMap publishes a fresh copy and fires LightChunkGetter.onLightUpdate once per affected section"] S --> POST["the POST_UPDATE tasks of that same window run, and the window is dropped"] UP["updatingSectionData, the engine's own map, cloned per section on its first write of the batch"] -.-> D UP -.-> I S -.-> VIS["visibleSectionData, volatile, and what every other thread reads"] ``` The two maps are why no reader of light ever waits on the light engine. `LayerLightSectionStorage.updatingSectionData` is the engine's scratch copy and no other thread reads it; `LayerLightSectionStorage.visibleSectionData` is volatile and is what every reader, saver and packet builder sees. `LayerLightSectionStorage.setStoredLevel` clones a section's `DataLayer` through `DataLayerStorageMap.copyDataLayer` the first time the batch touches that section, recording it in `LayerLightSectionStorage.changedSections`, and `LayerLightSectionStorage.swapSectionMap` copies the whole updating map into a new visible map at the end. A reader on another thread sees the state before the batch or the state after it, never a half-propagated flood. Inside a layer the order is fixed. Every position from `LightEngine.blockNodesToCheck` goes through `LightEngine.checkNode`, which only decides what to enqueue; then `LightEngine.propagateDecreases` runs the decrease queue to empty, then `LightEngine.propagateIncreases` runs the increase queue to empty. Decreases always finish first because a decrease discovers brighter neighbours and enqueues them as *increase back toward me* refills — running increases first would spread light that is about to be removed. Both queues hold pairs of longs whose second member is a `LightEngine.QueueEntry`: four bits of level, six of allowed directions, and the two flags `LightEngine.QueueEntry.FLAG_FROM_EMPTY_SHAPE` and `LightEngine.QueueEntry.FLAG_INCREASE_FROM_EMISSION`. The torch's own entries are `LightEngine.PULL_LIGHT_IN_ENTRY` — a level-1 decrease in all six directions, meaning *re-pull from my neighbours* — and an emission increase of 14, `Blocks.TORCH` being a `BlockBehaviour.Properties.lightLevel` of 14. `BlockLightEngine.propagateIncrease` then spreads level minus `LightEngine.getOpacity`, which is `BlockBehaviour.BlockStateBase.getLightDampening` floored at `LightEngine.MIN_OPACITY`, stopping where a neighbour is already brighter, where `LightEngine.shapeOccludes`, or when the next level would be 1. The batch's only exit is `LightChunkGetter.onLightUpdate`, fired from the map swap. The engine has no idea that `ChunkHolder` exists. ## The sky column is a table, not a flood Sky light does not use the heightmaps, and it does not usually propagate downward one block at a time. Each chunk owns a `ChunkSkyLightSources` (`ChunkAccess.skyLightSources`), a bit-packed 256-entry table of *the lowest Y at this (x, z) that still sees the sky*, filled by `ChunkSkyLightSources.fillFrom` and repaired per block by `ChunkSkyLightSources.update`. What ends a column is `ChunkSkyLightSources.isEdgeOccluded`: the lower block dampens light at all, or the two faces occlude each other. `SkyLightEngine.checkNode` reads that table and takes one of three paths. A block at or above the column's lowest source enqueues a remove-source decrease and an add-source increase — the expensive case. A block below it that held light has that light zeroed and decreased away. A block below it that was already dark, which is what a torch under a solid ceiling is, gets a pull-in that changes nothing: placing a torch in a cave does no sky work worth measuring. Before any of the three, `SkyLightEngine.updateSourcesInColumn` runs `SkyLightEngine.removeSourcesBelow` and `SkyLightEngine.addSourcesAbove` so the stored 15s agree with the table again. Two more pieces of the sky model exist because sections may have no storage. `SkyLightEngine.propagateFromEmptySections` handles light crossing a section edge sideways at the bottom row of a section: if the source column had no data for a run of sections below it, light had been continuing downward implicitly, so the same level is written straight down the destination column through that run. And `SkyLightSectionStorage.createDataLayer` seeds a newly needed section below existing data by repeating the bottom slice of the section above it (`SkyLightSectionStorage.repeatFirstLayer`) rather than starting dark. ## Lit before you ever see it Generation lights a chunk in two steps, and the first usually turns light *off*. `ChunkStatusTasks.initializeLight` builds the sky table with `ChunkAccess.initializeLightSources`, then calls `ThreadedLevelLightEngine.initializeLight`, whose PRE task marks every non-air section as having data and whose POST task sets the column's enabled flag to whatever `ChunkStatusTasks.isLighted` says — *false* for a freshly generated chunk, whose persisted status has not reached `ChunkStatus.LIGHT`. Enabling is the next step's doing: `ChunkStatusTasks.light` calls `ThreadedLevelLightEngine.lightChunk`, which for an unlit chunk runs `LevelLightEngine.propagateLightSources`, and both `BlockLightEngine.propagateLightSources` and `SkyLightEngine.propagateLightSources` open by enabling the column before seeding it — the block engine from `LightChunk.findBlockLightSources`, the sky engine from its own table and its four neighbours'. Only then does a POST task set `ChunkAccess.setLightCorrect`. A chunk read from disk with *isLightOn* set skips the propagation entirely, because `ThreadedLevelLightEngine.initializeLight` already enabled its column. Two consequences are worth naming. `ServerChunkCache.getChunkForLighting` hands the engine chunks at `ChunkStatus.FEATURES`, one status below `ChunkStatus.INITIALIZE_LIGHT` — the engine reads chunks the rest of the game is not allowed to see yet. And nothing waits for light before sending a chunk: what stops a half-lit chunk shipping is the pyramid, because `ChunkPyramid.GENERATION_PYRAMID` gives `ChunkStatus.LIGHT` a requirement of `ChunkStatus.INITIALIZE_LIGHT` at radius 1, so a chunk cannot climb to `ChunkStatus.FULL` until its neighbours have their sections marked ([the generation pipeline](chunk-generation-pipeline.md)). The one real send dependency, `ChunkMap.waitForLightBeforeSending` → `ThreadedLevelLightEngine.waitForPendingTasks` → `ChunkHolder.addSendDependency`, has exactly one caller: `EnderDragonFight`, grafting an exit portal's light onto chunks the client already holds. Unloading is the mirror: `ChunkMap.scheduleUnload` calls `ThreadedLevelLightEngine.updateChunkStatus`, which disables the column, drops the retain flag and nulls every section's data ([chunk storage](chunk-storage.md)). On load `SerializableChunkData.read` calls `LevelLightEngine.retainData` once and then `LevelLightEngine.queueSectionData` for each saved `DataLayer`. ## Off the server thread and onto the wire `ServerChunkCache.onLightUpdate` is the whole of the hop back. It is called on the light executor, and its body is a task posted to `ServerChunkCache.mainThreadProcessor` — no `ChunkHolder` is touched off-thread. When the server thread later runs that task it finds the holder in the visible map and calls `ChunkHolder.sectionLightChanged`, which marks the chunk unsaved (light is saved data), gives up if there is no ticking chunk to broadcast for, and otherwise sets one bit in `ChunkHolder.skyChangedLightSectionFilter` or `ChunkHolder.blockChangedLightSectionFilter` and puts the holder in `ServerChunkCache.chunkHoldersToBroadcast`. The packet goes out at the end of `ServerChunkCache.tickChunks` — so only on a tick where the level ticked chunks, and never at all in a debug world, whose `Level.isDebug` guard wraps that whole method. `ServerChunkCache.broadcastChangedChunks` calls `ChunkHolder.broadcastChanges`, which builds **one** `ClientboundLightUpdatePacket` per chunk from the two filters and sends it before any block-change packet — but not to everyone watching. It asks `ChunkHolder.PlayerProvider.getPlayers` with *borderOnly* true, so `ChunkMap.isChunkOnTrackedBorder` keeps only the players for whom this chunk has an untracked neighbour. A player in the middle of their own view distance is never sent light for a block change: they were sent the block, they have every neighbouring chunk, and their own engine will reach the same numbers. The packet exists for the players who cannot, because the flood may be arriving from a chunk they do not have. Its `ClientboundLightUpdatePacketData` carries four bitsets — a data mask and an empty mask per layer — and the 2048 bytes of each non-empty changed section, read through `LayerLightEventListener.getDataLayerData`, which answers the queued layer if there is one and the *visible* map otherwise, never the updating copy. A section whose `DataLayer` is empty costs one bit; one with no `DataLayer` at all appears in neither mask. The same `ClientboundLightUpdatePacketData` rides inside `ClientboundLevelChunkWithLightPacket` with both filters null — every section — when `PlayerChunkSender.sendChunk` first sends a chunk. **Up to 14** — sections one torch can dirty, and not because a write marks its neighbours. `LayerLightSectionStorage.setStoredLevel` marks only the sections within one block of the position written (`SectionPos.aroundAndAtBlockPos`): one for an interior block, up to eight for a block on a corner. The 14 comes from the flood, which writes as far as thirteen blocks away in taxicab distance. Grow that octahedron by the one-block halo and it reaches three sections deep on one axis or another, but never on all three at once — three per axis would be 27, and a corner section needs a diagonal the octahedron does not have. Fourteen sections, across as many as seven chunk columns. The real 3×3×3 marking, `LayerLightSectionStorage.markSectionAndNeighborsAsAffected`, fires only when a section is first given a `DataLayer`. ## The client lights per frame The client runs the same `LevelLightEngine` unwrapped: `ClientChunkCache.lightEngine` is a plain one, and its `LightChunkGetter.onLightUpdate` goes straight to `LevelExtractor.setSectionDirty`. It runs from `ClientLevel.update`, which `Minecraft.renderFrame` calls once a **frame**, not once a tick, so light converges at your framerate. `ClientPacketListener.handleLightUpdatePacket` applies nothing; it pushes a closure onto `ClientLevel.lightUpdateQueue`, and `ClientLevel.pollLightUpdates` runs a bounded number of them per frame — the larger of ten and a tenth of the backlog, or the whole backlog once it reaches a thousand — so a burst of chunk loads is spread over frames instead of stalling one. Each closure is `ClientPacketListener.applyLightData`: `ClientPacketListener.readSectionList` turns every masked section into a cloned or empty `DataLayer` through `LevelLightEngine.queueSectionData` and dirties it with its neighbours, and then the chunk's column is enabled with `LevelLightEngine.setLightEnabled`. Only after the polled closures does `ClientLevel.update` call `LevelLightEngine.runLightUpdates`, splicing the queued layers in and swapping the map exactly as the server does. Enabling a column is a separate thing from lighting it, and on the client it gates geometry. `SectionUpdateTracker.hasAllNeighbors` asks for each of the eight surrounding columns both that the chunk is there and that `LevelLightEngine.lightOnInColumn` is true for it, and `LevelExtractor` queues a never-yet-meshed section for rebuild only when they all answer yes — a light flag deciding whether a section may have a mesh at all ([section meshing](../rendering/section-meshing.md)). Meanwhile the client had already lit this torch itself: `MultiPlayerGameMode.startPrediction` runs the placement locally through the same `LevelChunk.setBlockState`, so the packet mostly confirms what the client computed a frame or two earlier. > **For a 1.21-era reader.** *getLightBlock* is now > `BlockBehaviour.BlockStateBase.getLightDampening`, and it is derived rather > than declared — 15 for a solid render, 0 for a state that > `BlockBehaviour.BlockStateBase.propagatesSkylightDown`, 1 otherwise. > *LightTexture* is `Lightmap` (Part XI). > `DynamicGraphMinFixedPoint`, `LeveledPriorityQueue` and `SpatialLongSet` > still live in `world/level/lighting`, but no light engine uses them any > more. `DynamicGraphMinFixedPoint` survives for `ChunkTracker` and > `SectionTracker` ([tickets](tickets-and-loading.md)) and takes > `LeveledPriorityQueue` with it; `SpatialLongSet` has no callers at all. ## Questions players ask **Why does the torch light up a tick after it lands?** The write only queues a task, the queue is drained in the server thread's idle time, the callback is posted back to the server thread, and the packet is built at the end of `ServerChunkCache.tickChunks`. Four hand-offs, none of them scheduled. **Why does breaking one block re-light half a room?** A block-light change propagates thirteen blocks, and every written position marks the sections within one block of it — up to fourteen sections across seven chunk columns, each of which the client must re-mesh. **Does an empty sky section cost anything?** No. A `DataLayer` with no array is homogeneous zero, `SerializableChunkData.copyOf` skips it on disk, and the packet spends one bit on it instead of 2048 bytes. Sections above the sky column's top have no `DataLayer` at all and answer 15 by walking upward. **Why is a newly loaded chunk sometimes a black wall?** Its column's light is not enabled yet, and enabling is a separate step from lighting: until `LevelLightEngine.lightOnInColumn` is true for a section's eight neighbouring columns, `LevelExtractor` will not build that section's first mesh at all. **Why do the F3 light numbers not match what I see?** They are three numbers, and none of them is the one you are looking at. `DebugEntryLight` prints a combined figure and then the two raw layers, and the combined figure is `LevelLightEngine.getRawBrightness` called with the time-of-day darkening argument set to **zero** — so even it is a noon answer. What renders is the two layers looked up in the `Lightmap` texture, which is where the hour, the dimension and the mob effects arrive ([the lightmap](../rendering/lightmap-fog-and-sky.md)). ## Where to look `LevelChunk.setBlockState` · `LightEngine.hasDifferentLightProperties` · `ChunkSkyLightSources.update` · `ThreadedLevelLightEngine.addTask` · `ThreadedLevelLightEngine.tryScheduleUpdate` · `ThreadedLevelLightEngine.runUpdate` · `LightEngine.runLightUpdates` · `LightEngine.QueueEntry` · `BlockLightEngine.checkNode` · `BlockLightEngine.propagateIncrease` · `SkyLightEngine.checkNode` · `SkyLightEngine.propagateFromEmptySections` · `LayerLightSectionStorage.setStoredLevel` · `LayerLightSectionStorage.swapSectionMap` · `SkyLightSectionStorage.getLightValue` · `DataLayer` · `ThreadedLevelLightEngine.initializeLight` · `ThreadedLevelLightEngine.lightChunk` · `ServerChunkCache.onLightUpdate` · `ChunkHolder.sectionLightChanged` · `ChunkHolder.broadcastChanges` · `ClientboundLightUpdatePacketData` · `ClientPacketListener.applyLightData` · `ClientLevel.pollLightUpdates` · `SectionUpdateTracker.hasAllNeighbors` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Chunk storage > Verified against **Minecraft 26.2** · Part IV · A chunk nobody needs any more is dropped from the world and written to disk, and the server thread never waits for it. You walk away from your base. Soon after, the chunk you were standing in is no longer reachable from any ticket, its loading level climbs past `ChunkLevel.MAX_LEVEL`, and a queued task takes a snapshot of it, hands that to a worker to turn into NBT, and hands *that* to a lane that compresses it and finds it somewhere to live in *r.X.Z.mca*. Nothing about that is surprising. What is surprising is that the chunk was almost certainly written several times before you left, and that neither of those writes was anybody's idea. A chunk you keep changing is written by a background sweep roughly every ten seconds — `ChunkMap.saveChunksEagerly`, at most `ChunkMap.CHUNK_SAVED_EAGERLY_PER_TICK` (20) chunks a tick, only while fewer than `ChunkMap.MAX_ACTIVE_CHUNK_WRITES` (128) writes are in flight, each chunk no sooner than `ChunkMap.EAGER_CHUNK_SAVE_COOLDOWN_IN_MILLIS` (10 000 ms) after its last — and the autosave everyone thinks of as *the* save is five minutes of wall clock whatever `/tick rate` says. **Almost every write of your world is one nobody asked for.** ## The cast | class | what it decides | thread | |---|---|---| | `ChunkMap` | which chunks are dirty, at which of four moments each is written, and whether a half-generated chunk may overwrite a finished one — it *is* the *region/* store, because it extends `SimpleRegionStorage` | Server | | `SerializableChunkData` | the chunk file as a record: what gets copied while the world is frozen and what gets encoded after | Server copies, a *Worker-Main-n* encodes | | `IOWorker` | one store's single lane, and the write-behind map that lets a read answer from a write that has not landed | any *IO-Worker-n*, one task at a time | | `RegionFileStorage` | which *r.X.Z.mca* files are open — an LRU of `RegionFileStorage.MAX_CACHE_SIZE` (256) | the IO lane | | `RegionFile` | the sector allocator and the two header tables of one 32×32-chunk file, and the order the bytes land in | the IO lane | | `EntityStorage` | the *entities/* store: what a chunk's mobs cost to write, and that they are rebuilt on the server thread | Server builds and parses, IO lane writes | | `SectionStorage` | the *poi/* store under `PoiManager`: which sections are dirty, and the one load that blocks | Server | | `MinecraftServer` | when the next autosave falls, and whether every region file is opened with DSYNC | Server | ## Copy on the server, encode on a worker, write on the IO lane ```mermaid flowchart LR S["Server thread — ChunkMap.save decides, SerializableChunkData.copyOf takes the snapshot"] --> W["Worker-Main-n — SerializableChunkData.write builds the CompoundTag"] W --> F["IOWorker.store, foreground priority — joins the encode and parks the tag in pendingWrites"] F --> B["IOWorker.storePendingChunk, background priority — runs only when the lane has no foreground work"] B --> R["RegionFileStorage.write — compress through RegionFileVersion, place the sectors with RegionFile.write"] R --> D["r.X.Z.mca"] ``` That figure is the page's answer to *why doesn't saving lag the server*. Apart from flushing the position's POI section, the server thread's whole share of a save is the middle of `ChunkMap.save`: `SerializableChunkData.copyOf`, which copies every `LevelChunkSection` with `LevelChunkSection.copy` and each non-empty block and sky `DataLayer` out of `LevelLightEngine.getLayerListener`, clones the heightmaps the chunk's persisted status calls for, pulls block-entity NBT through `ChunkAccess.getBlockEntityNbtForSaving`, packs the ticks through `ChunkAccess.getTicksForSerialization`, and packs the structure starts. Everything after that — the palette codecs, the deflate, the sector arithmetic, the syscall — runs somewhere else, and `ChunkMap.save` returns as soon as the copy is done. ## Three folders, and the one thing that is not in *region/* `LevelStorageSource.LevelStorageAccess.getDimensionPath` defers to `DimensionType.getStorageFolder`, which puts **every** dimension — the overworld included — under *dimensions/\/\/* in the world folder. Inside are *region/*, *entities/*, *poi/* and *data/*. The first three are region stores of the same shape — a folder of *r.X.Z.mca* files, a `RegionFileStorage`, an `IOWorker`, and a `RegionStorageInfo` naming the store (*chunk*, *entities* or *poi*) so that `MinecraftServer.reportChunkSaveFailure` can say which one broke. Only *chunk* belongs to `ChunkMap` itself; `EntityStorage` and `SectionStorage` each *hold* a `SimpleRegionStorage` rather than being one. *data/* is not a region store at all — it is `SavedDataStorage`, on [its own page](../../reference/level-data-and-rules.md). A `LevelChunk`'s entities are **not** in *region/*. The `SerializableChunkData.entities` list is written only when the chunk's persisted status is a `ChunkType.PROTOCHUNK` — worldgen's spawns, waiting for the column to become full — and `SerializableChunkData.carvingMask` goes the same way. A full chunk's entities live in *entities/*, one file per chunk, holding a *Position* and an *Entities* list. If an old save still has entities inside a full chunk's *region/* entry, `ServerLevel.addLegacyChunkEntities` adopts them on load. ## The four moments a chunk is written | the moment | what runs it | which chunks | what holds it back | |---|---|---|---| | **an unload** | the task `ChunkMap.scheduleUnload` queued, drained by `ChunkMap.processUnloads` | the one chunk being dropped, at whatever status it reached | nothing — no cooldown, and whatever the queue holds beyond 2000 tasks drains regardless of the tick budget | | **the eager sweep** | `ChunkMap.saveChunksEagerly`, the last statement of that same `ChunkMap.processUnloads` | everything in `ChunkMap.chunksToEagerlySave` | 20 a tick, fewer than 128 writes outstanding, the tick's time budget, and ten seconds per chunk | | **an autosave** | `MinecraftServer.autoSave` → `ServerLevel.save` → `ServerChunkCache.save` without flush | every holder in `ChunkMap.visibleChunkMap` | only the per-chunk gates: `ChunkMap.saveAllChunks` clears `ChunkMap.nextChunkSaveTime`, but `ChunkMap.saveChunkIfNeeded` still wants an accessible, ready, unsaved `LevelChunk` or `ImposterProtoChunk` | | **a flush save** | `/save-all flush`, `/stop`, `ServerChunkCache.close` | every accessible holder, over and over until a pass saves none | it blocks the server thread instead | The dirty set behind the second row is narrower than it looks. `ChunkMap.setChunkUnsaved` is installed as `WorldGenContext.unsavedListener` and handed to a chunk by `ChunkStatusTasks` at the moment it becomes full, and `LevelChunk.markUnsaved` fires that listener **only on the false→true edge**. So `ChunkMap.chunksToEagerlySave` holds full chunks that have changed since their last write, each added once, and a chunk still being generated is never in it. Turning saving off is not as total as it sounds. `ChunkMap.tick` ticks `PoiManager` first and *unconditionally*, and only then asks `ServerLevel.noSave` whether to run `ChunkMap.processUnloads` — so a no-save world still writes village data through `SectionStorage.tick`, and stops letting go of chunks until something forces the issue. The only other drain of `ChunkMap.unloadQueue` and `ChunkMap.toDrop` is inside `ChunkMap.saveAllChunks` with flush, which runs `ChunkMap.processUnloads` on an always-true budget — so *`/save-all flush`* and shutdown do unload them, the first because `MinecraftServer.saveAllChunks` suppresses `ServerLevel.noSave` when *force* is set and the second because `ServerChunkCache.close` never consults it at all. An explicit save is a different question again: `MinecraftServer.saveAllChunks` passes `ServerLevel.noSave` on to `ServerLevel.save` only when its *force* flag is clear, and `/save-all` sets that flag while `MinecraftServer.autoSave` does not. ## A chunk nobody needs any more ```mermaid sequenceDiagram participant DM as DistanceManager participant CM as ChunkMap participant CH as ChunkHolder participant SCD as SerializableChunkData participant IOW as IOWorker participant SL as ServerLevel participant PESM as PersistentEntitySectionManager DM->>CM: the level climbs past ChunkLevel.MAX_LEVEL, updateChunkScheduling adds the key to toDrop Note over CM: a later tick, in ServerChunkCache.tick's unload phase CM->>CM: processUnloads moves the holder from updatingChunkMap to pendingUnloads CM->>CH: scheduleUnload reads getSaveSyncFuture and hangs the unload task off it CH-->>CM: the future completes, so the task is appended to unloadQueue Note over CM: a later tick again, while the tick budget still says yes CM->>CH: is getSaveSyncFuture still the same future — if not, scheduleUnload rearms on the new one CM->>CM: pendingUnloads.remove of this exact holder — false if a ticket re-adopted it, and the task ends CM->>CM: setLoaded false, then save — PoiManager.flush, tryMarkSaved, the proto-over-full guard CM->>SCD: copyOf takes the snapshot, and a Worker-Main-n turns it into a CompoundTag CM->>IOW: ChunkMap.write hands that encode future to IOWorker.store on the chunk lane CM->>SL: ServerLevel.unload clears the block entities and the tick containers, then ThreadedLevelLightEngine drops the layers SL->>PESM: later in the same level tick, processUnloads, then EntityStorage.storeEntities on the entities lane IOW-->>CM: PendingStore.result completes, activeChunkWrites goes back down ``` Three things there are load-bearing. The first is that nothing happens until `ChunkHolder.saveSync` is done: every promotion future is chained into it by `ChunkHolder.addSaveDependency`, and so is `GenerationChunkHolder.generationSaveSyncFuture` for as long as a generation step holds a reference, so a chunk mid-promotion or mid-generation cannot be saved or unloaded at all. The second is the guard. `ChunkMap.pendingUnloads` is removed *by identity*: if a ticket re-adopted the position while the task waited, `ChunkMap.updateChunkScheduling` has already pulled the holder back out of that map, the removal fails, and the task quietly does nothing — nothing is lost and nothing is written twice. And if the sync future changed while waiting, the task rearms itself on the new one rather than proceeding. The third is that entities go by a different road and a later step. `PersistentEntitySectionManager.updateChunkStatus` saw the same level change and queued the position in `PersistentEntitySectionManager.chunksToUnload`. If the chunk's entity file is still being read, `PersistentEntitySectionManager.storeChunkSections` returns false and the whole thing is retried next tick, so a half-loaded set never clobbers the file. Otherwise each entity `EntityAccess.shouldBeSaved` accepts is serialised with `Entity.save` **on the server thread**, the tag goes to the *entities* lane, and those entities are removed with `Entity.RemovalReason.UNLOADED_TO_CHUNK`. The filter runs before the removal, not after it, so what it turns away — a `Player`, an `EnderDragonPart`, a passenger, a vehicle carrying exactly one player — is neither written nor removed. Two other things leave with the chunk, both after the snapshot is taken: `ServerLevel.unload` clears its block entities and unregisters its tick containers, and `ThreadedLevelLightEngine.updateChunkStatus` queues the light engine to forget its layers ([lighting](lighting.md)). ## Why the server thread never waits, and the three times it does `IOWorker` is not a thread. It holds a `PriorityConsecutiveExecutor` over `Util.ioPool` — a cached pool whose threads are named *IO-Worker-n* — and its guarantee is that one task at a time runs for that store, not that the same thread runs them. Its three priorities are strictly ordered: `IOWorker.Priority.FOREGROUND` for `IOWorker.store` and `IOWorker.loadAsync`, `IOWorker.Priority.BACKGROUND` for `IOWorker.storePendingChunk` — the task that actually touches the disk — and `IOWorker.Priority.SHUTDOWN` last. The lowest priority has exactly one user in the whole game: the barrier `IOWorker.waitForShutdown` parks behind everything else when the store closes. A flush is not one of them — `IOWorker.synchronize` submits it at foreground priority like a store — but it still lands behind the writes, because before it flushes it waits on every `IOWorker.PendingStore` future, and those complete only when the background tasks have run. `IOWorker.pendingWrites` is what that buys. It is a sequenced map from `ChunkPos` to `IOWorker.PendingStore`, and a second store for a position already in it overwrites that entry's data *in place* without moving it, so N saves of one chunk before the lane drains become **one** disk write and one shared future. `IOWorker.loadAsync` looks in the same map first and returns a *copy* of the pending tag, so a chunk unloaded and re-loaded a second later never touches the region file — read-your-writes by lane order rather than by any lock. `IOWorker.STORE_EMPTY` is the null supplier that means *delete*, and `IOWorker.scanChunk` is the streaming `ChunkScanAccess` that `StructureCheck` uses to peek into chunks nobody has loaded. Three places do make the server thread wait on a disk. `ChunkMap.isExistingChunkFull`, the guard that stops a `ProtoChunk` overwriting a finished chunk, answers from `ChunkMap.chunkTypeCache` when it can but joins the read future inline on a cold entry — the IO lane, then a datafix pass on the worker pool. And `SectionStorage.getOrLoad` joins too, for a POI section that `SectionStorage.prefetch` never fetched. The third is not a chunk-storage method at all: `StructureCheck.tryLoadFromStorage` joins `IOWorker.scanChunk` to peek at a chunk it will not load, which is what an eye of ender, a dolphin, an explorer map and `/locate` all end up doing on the server thread. None of the three is on the save path, which is why the save path costs a copy. ## Inside a region file ```mermaid flowchart TD A["IOWorker.storePendingChunk pops the oldest entry of pendingWrites"] --> B["RegionFileStorage.getRegionFile, an LRU of 256 open files"] B --> C["RegionFile.getChunkDataOutputStream wraps a ChunkBuffer in the selected compressor, NbtIo writes into it"] C --> D["closing the buffer back-patches the length and calls RegionFile.write"] D --> E{"how many sectors"} E -- "under 256" --> F1["RegionBitmap.allocate takes the first free run"] F1 --> F2["the compressed chunk is written to those new sectors"] F2 --> F3["offsets and timestamps updated, then RegionFile.writeHeader"] F3 --> F4["any stale sidecar for this chunk is deleted"] F4 --> Z["and only now are the old sectors freed"] E -- "256 or more" --> G1["one sector is allocated for a stub"] G1 --> G2["the payload goes to a temp file in the same folder, and a five-byte stub with EXTERNAL_STREAM_FLAG is written to that sector"] G2 --> G3["offsets and timestamps updated, then RegionFile.writeHeader"] G3 --> G4["the temp file is moved onto c.X.Z.mcc, over the previous copy"] G4 --> Z ``` A `RegionFile` is one *r.X.Z.mca*: two header sectors (`RegionFile.SECTOR_BYTES` is 4096) holding a 1024-entry offset table, `RegionFile.offsets`, packed as sector number ≪ 8 with the sector count in the low byte, and a 1024-entry `RegionFile.timestamps`. Free space is a `RegionBitmap`, with the header's two sectors forced used at construction and `RegionBitmap.allocate` handing out the first run big enough. Each stored chunk starts with `RegionFile.CHUNK_HEADER_SIZE` (5) bytes — a length and a compression id — and both `RegionFile.write` and `RegionFile.getChunkDataInputStream` are synchronised on the `RegionFile` itself rather than on the channel, though in practice only one lane ever drives a given folder. Read the two branches of the figure against each other and the page's best fact falls out. For an ordinary chunk the new bytes are on disk **before** the header points at them, and the old bytes are released **after** — so a crash at any point leaves either the old chunk or the new one, and a chunk never overwrites itself in place. For an oversized chunk the ordering is reversed. Anything needing `RegionFile.EXTERNAL_CHUNK_THRESHOLD` (256) sectors or more cannot be described by an eight-bit count field at all, so it goes to a *.mcc* sidecar and the region file keeps only a stub carrying `RegionFile.EXTERNAL_STREAM_FLAG`; and the sidecar is moved into place *after* `RegionFile.writeHeader` has already committed the pointer to it, destroying the previous copy at a fixed path. The in-file case is content-then-pointer. The sidecar case is pointer-then-content. Either way the ordering only buys anything if the writes reach the platter in that order, which is what DSYNC is for: `MinecraftServer.forceSynchronousWrites` returns true as the base default, and two of the three servers override it — `DedicatedServer` from `DedicatedServerProperties.syncChunkWrites` (*sync-chunk-writes*, default true) and `IntegratedServer` from `Options.syncWrites`, whose default is true only on Windows. `GameTestServer` keeps the base answer. The compression byte is per chunk, not per file. `RegionFileVersion.selected` — set once by `RegionFileVersion.configure` from `DedicatedServerProperties.regionFileComression` (Mojang's spelling; the property is *region-file-compression*) — decides only what *new* writes use, choosing between `RegionFileVersion.VERSION_DEFLATE` (the `RegionFileVersion.DEFAULT`), `RegionFileVersion.VERSION_NONE` and `RegionFileVersion.VERSION_LZ4`. Reads honour whatever byte each chunk carries, including `RegionFileVersion.VERSION_GZIP`, which has no option name and so can be read but never chosen, and `RegionFileVersion.VERSION_CUSTOM`, which exists so that `RegionFile.createChunkInputStream` can recognise it and refuse. ## The way back in Loading is the same road driven backwards, and it changes hands four times. `ChunkMap.scheduleChunkLoad` starts with `IOWorker.loadAsync` on the IO lane; `ChunkMap.readChunk` then hops to `Util.backgroundExecutor` under the name *upgradeChunk* for `SimpleRegionStorage.upgradeChunkTag`, which is where datafixing happens; `SerializableChunkData.parse` runs on the same pool under *parseChunk*; and `SerializableChunkData.read` runs on the server thread, where the sections are installed, the saved light is queued into the light engine, and `PoiManager.checkConsistencyWithBlocks` re-derives each section's points of interest from its blocks. Running beside all of it, `SectionStorage.prefetch` pulls the POI file in, and the two are joined before the server-thread step — which is exactly why that step's `SectionStorage.getOrLoad` calls do not block. From there the [generation pipeline](chunk-generation-pipeline.md) takes over. Entities come back the same shape but land differently: `EntityStorage` schedules both the datafix and `EntityType.loadEntitiesRecursive` on `EntityStorage.entityDeserializerQueue`, a `ConsecutiveExecutor` over the **server** main-thread executor, so only the NBT read is off-thread. ## Questions players ask **Does the game stall when it saves?** Only on a flush. `ChunkMap.saveAllChunks` with flush loops over the accessible holders, blocking the main-thread executor on each `ChunkHolder.isReadyForSaving` until a whole pass saves nothing, then flushes POIs with `SectionStorage.flushAll`, runs `ChunkMap.processUnloads` with an always-true budget, and finally joins `IOWorker.synchronize` with flush. That is the only place where waiting for the disk is the point rather than an accident, and it is what `/save-all flush` and `/stop` do. **Why does lowering the tick rate not push out my autosave?** Because the interval is wall clock. `MinecraftServer.computeNextAutosaveInterval` is the tick rate times 300 — or, while the server is sprinting, 300 times the rate its recent tick times imply — floored at `MinecraftServer.MIMINUM_AUTOSAVE_TICKS` (100 — the typo is Mojang's); the very first interval is `MinecraftServer.AUTOSAVE_INTERVAL` (6000 ticks). `MinecraftServer.onTickRateChanged` recomputes it on every `/tick rate`, but assigns the result only when it is **smaller** than the pending countdown, so changing the rate can bring the next autosave forward and can never push it back. [The server tick](../server/server-tick.md) has the rest of that loop. **Can a half-generated chunk overwrite my base?** The guard is best effort. `ChunkMap.save` refuses to write a non-full chunk over a full one on disk — but `ChunkMap.isExistingChunkFull` returns false, meaning *go ahead*, whenever the read throws or comes back empty, so an IO error licenses exactly the clobber the guard exists to prevent. Worse, `ChunkAccess.tryMarkSaved` clears the unsaved flag *before* the guards run, so a chunk the guard turns away has already been marked clean and will not be offered again. A proto chunk still at `ChunkStatus.EMPTY` with no valid structure start is dropped by the same block, and an `ImposterProtoChunk` never reaches any of it: `ImposterProtoChunk.tryMarkSaved` and `ImposterProtoChunk.canBeSerialized` both answer false — not because the wrapper defers to the `LevelChunk` it wraps, which only `ImposterProtoChunk.markUnsaved` does, but because it refuses to be serialised at all. **Why is my *entities/* folder full of files with nothing in them?** It is not — but emptying a chunk costs one write. `EntityStorage.storeEntities` with an empty set only writes when `EntityStorage.emptyChunks` did not already contain the position, and that write is `IOWorker.STORE_EMPTY`, which zeroes the region entry and deletes any sidecar. The first time a chunk goes empty costs a write. Every later save of it costs nothing. **Do the file timestamps mean anything?** Not to the game. `RegionFile.write` stamps each entry with epoch seconds from `RegionFile.getTimestamp`, which reads `Util.getEpochMillis`, and nothing ever reads the table back; the save cooldown in `ChunkMap.nextChunkSaveTime` is monotonic `Util.getMillis`. Two clocks, and neither of them is game time. ## Where to look `ChunkMap.tick` · `ChunkMap.processUnloads` · `ChunkMap.scheduleUnload` · `ChunkMap.save` · `ChunkMap.saveChunksEagerly` · `ChunkMap.saveChunkIfNeeded` · `ChunkMap.saveAllChunks` · `SerializableChunkData.copyOf` · `SerializableChunkData.write` · `IOWorker.store` · `IOWorker.storePendingChunk` · `IOWorker.loadAsync` · `RegionFileStorage.write` · `RegionFile.write` · `RegionBitmap.allocate` · `PersistentEntitySectionManager.storeChunkSections` · `EntityStorage.storeEntities` · `SectionStorage.writeChunk` · `SectionStorage.prefetch` · `ChunkMap.scheduleChunkLoad` · `SerializableChunkData.read` · `MinecraftServer.computeNextAutosaveInterval` · `DimensionType.getStorageFolder` Next door: [tickets and loading](tickets-and-loading.md) raises the level, [chunk anatomy](chunk-anatomy.md) owns what `LevelChunkSection.copy` copies, [lighting](lighting.md) owns the layers the unload throws away, [points of interest](points-of-interest.md) owns the *poi/* store, [the server tick](../server/server-tick.md) owns the budget every method here is handed, [how a server dies](../server/how-a-server-dies.md) is the save that does not happen, and [entity lifecycle](../entities/entity-lifecycle.md) is what `Entity.RemovalReason.UNLOADED_TO_CHUNK` means to a mob. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Scheduled ticks > Verified against **Minecraft 26.2** · 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` 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 sapling sprouting after bonemeal, a pressure plate releasing, a piece of amethyst budding. 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 — worldgen workers book 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 | worldgen workers | | `BlackholeTickAccess` | accept every booking and run nothing — the client's answer to the whole system, and also an `ImposterProtoChunk`'s | Render, and the server thread | 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 ```mermaid flowchart TD B["a block books: ScheduledTickAccess.scheduleTick"] --> C["LevelAccessor.createTick — game time plus delay, a TickPriority, the next sub-order"] C --> S["LevelTicks.schedule finds the chunk's container"] S -- "no container registered for that chunk" --> DROP["Util.logAndPauseIfInIde — logged and dropped, never deferred"] S --> D["LevelChunkTicks.schedule — queued only if ticksPerPosition did not hold this type and position"] D --> I["onTickAdded: if the new tick is now the head, nextTickForContainer learns the earlier time"] I --> W["waiting — one priority queue per chunk, in ScheduledTick.DRAIN_ORDER"] W --> SC["LevelTicks.sortContainersToTick walks the index for containers due this tick"] SC -- "chunk fails ServerLevel.isPositionTickingWithEntitiesLoaded" --> W SC --> DR["LevelTicks.drainContainers polls the best container, LevelChunkTicks.poll frees the dedup slot"] DR --> RL["rescheduleLeftoverContainers, which always runs and has work only when the budget MAX_SCHEDULED_TICKS_PER_TICK cut the drain short: the rest go back to the index, still due next tick"] DR --> RUN["LevelTicks.runCollectedTicks hands each position and type to ServerLevel.tickBlock or ServerLevel.tickFluid"] RUN -- "the block books again from inside its own run" --> D RUN --> CL["LevelTicks.cleanupAfterTick empties toRunThisTick, containersToTick, alreadyRunThisTick"] ``` Everything below is one stage of that figure. ## 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 parameters, two parallel worlds: 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 from the worker pool. **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.md). > **For a 1.21-era reader.** `BlockBehaviour.updateShape` no longer takes a > `LevelAccessor`. It takes a `LevelReader` and a separate > `ScheduledTickAccess` — a small interface whose whole job is booking: > `ScheduledTickAccess.createTick`, `ScheduledTickAccess.getBlockTicks`, > `ScheduledTickAccess.getFluidTicks` and four `ScheduledTickAccess.scheduleTick` > overloads that compose them. ## Where an appointment waits Every `LevelChunk` owns exactly two containers, `LevelChunk.blockTicks` and `LevelChunk.fluidTicks`, and they are the only place a pending tick ever lives ([chunk anatomy](chunk-anatomy.md)): 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 fires 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.md)). 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 ([tickets and loading](tickets-and-loading.md)). 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. A container that is overtaken, or that still has something due when the budget is spent, goes back into the container queue; one merely overdue goes back to the index; one drained empty goes to neither. And `LevelTicks.rescheduleLeftoverContainers` returns whatever the budget cut off 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`, forty lines below. **Clean up** is `LevelTicks.cleanupAfterTick`, emptying all four working collections including `LevelTicks.toRunThisTickSet`, which is built lazily and only if somebody actually asks `LevelTicks.willTickThisTick`. ### 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 participant LC as LevelChunk SL->>RB: neighborChanged — the wire behind went to 15 RB->>RB: DiodeBlock.checkTickOnNeighbor — not locked, POWERED false, shouldTurnOn true 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 — ticksPerPosition accepts, tickQueue takes it LCTs-->>LTs: onTickAdded — nextTickForContainer learns gameTime plus 2 Note over SL,RB: next tick, the wire drops to 0. checkTickOnNeighbor finds POWERED false and shouldTurnOn false, so it books nothing and cancels nothing Note over SL,LC: two ticks after the booking, ServerLevel.tick, tickPending, blockTicks 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 here, so BlockBehaviour.BlockStateBase.tick RB->>SL: setBlock POWERED true, update flags 2 SL->>LC: setBlockState, then DiodeBlock.onPlace RB->>SL: updateNeighborsInFront — the block it powers, and that block's other neighbours RB->>LTs: shouldTurnOn is false now, so book the turn-off at TickPriority.VERY_HIGH ``` **A repeater almost never books at `TickPriority.NORMAL`.** `DiodeBlock.checkTickOnNeighbor` picks `TickPriority.HIGH` to turn on, `TickPriority.VERY_HIGH` to turn off and `TickPriority.EXTREMELY_HIGH` when `DiodeBlock.shouldPrioritize` holds — when the block it powers is itself a diode that is not pointing straight back at it. So a diode's turn-off beats another's turn-on due on the same tick, and a diode feeding a diode beats both. The single `TickPriority.NORMAL` booking a repeater makes is `DiodeBlock.setPlacedBy`, delay 1, when you place it into a powered spot. **The pending appointment is immune to the input changing.** `DiodeBlock.checkTickOnNeighbor` books only when the *current* `DiodeBlock.POWERED` disagrees with the *current* input, and nothing anywhere removes a booked tick. A pulse shorter than the delay therefore does not cancel the repeater: `DiodeBlock.tick` finds `DiodeBlock.POWERED` false, turns it on anyway, and — because the input is already gone — books its own turn-off one delay later. Pulse extension is two entries in this queue. **Nothing here uses `Block.UPDATE_NEIGHBORS`.** `DiodeBlock.tick` writes with `Block.UPDATE_CLIENTS` alone, and the signal leaves through `DiodeBlock.onPlace` — which `LevelChunk.setBlockState` runs on the server for any write without `Block.UPDATE_SKIP_ON_PLACE` — calling `DiodeBlock.updateNeighborsInFront`. The rest is [diodes and the observer](../blocks/diodes-and-observers.md). ## The other kind of turn: random ticks The appointment book is one of two ways a block gets a turn, and the contrast 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. `ServerChunkCache.tickChunks` reads `GameRules.RANDOM_TICK_SPEED` — default 3, minimum 0 — **once per level tick**, then walks `ChunkMap.forEachBlockTickingChunk`, which despite its name is `DistanceManager.forEachEntityTickingChunk` filtered to chunks with a live `ChunkHolder.getTickingChunk`. The scheduled-tick gate, `ServerLevel.isPositionTickingWithEntitiesLoaded`, reads the wider block-ticking radius instead, so random ticks stop one ring sooner than scheduled ticks do. Inside such a chunk, `ServerLevel.tickChunk` skips every section where `LevelChunkSection.isRandomlyTicking` is false — a pair of counters, `LevelChunkSection.tickingBlockCount` and `LevelChunkSection.tickingFluidCount`, maintained on every block write, so solid stone is skipped without one position being generated. In each surviving section it picks that many positions and rolls `BlockBehaviour.BlockStateBase.randomTick` where `BlockBehaviour.BlockStateBase.isRandomlyTicking` — a flag baked into the state at `BlockBehaviour.BlockStateBase.initCache` from `BlockBehaviour.Properties.randomTicks` — and then, separately, `FluidState.randomTick` where `FluidState.isRandomlyTicking`. **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. The same number also drives the ice-and-snow pass, so zeroing the rule freezes crops, fire, leaf decay and precipitation together ([the level tick](../server/server-level-tick.md)). ## Appointments that survive a restart A `SavedTick` stores a **relative** delay rather than an absolute time, so a world closed and reopened a month later still fires its ticks on schedule: `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` ([chunk storage](chunk-storage.md)). 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 queue fills 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 **I rescheduled the tick for sooner and nothing changed. Why?** Because `LevelChunkTicks.schedule` dedups on type and position only, and the first booking wins. No block moves or cancels a pending tick — only `/clone` and the gametest framework do, in bulk, through `LevelTicks.copyAreaFrom` and `LevelTicks.clearArea`. So ask `TickAccess.hasScheduledTick` whether one is already booked, or `LevelTickAccess.willTickThisTick` whether one is about to run in this very level tick, which reads the already-collected list that `LevelTicks.hasScheduledTick` can no longer see. **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 they are all collected in one drain. If the chunk unloads first they are written to disk with it. The only appointment actually lost is one booked into a chunk with no registered container. **Why does lava set things alight faster than the number of random ticks suggests?** Because a chosen lava position runs `LavaFluid.randomTick` twice per selection, once as a block and once as a fluid. ## Where to look `ScheduledTick` · `ScheduledTick.UNIQUE_TICK_HASH` · `ScheduledTick.DRAIN_ORDER` · `TickPriority` · `ScheduledTickAccess.scheduleTick` · `LevelAccessor.createTick` · `LevelTicks.schedule` · `LevelChunkTicks.schedule` · `LevelChunkTicks.poll` · `LevelTicks.tick` · `LevelTicks.sortContainersToTick` · `LevelTicks.drainContainers` · `LevelTicks.runCollectedTicks` · `ServerLevel.tickBlock` · `ServerLevel.isPositionTickingWithEntitiesLoaded` · `LevelChunk.registerTickContainerInLevel` · `LevelChunkTicks.unpack` · `SavedTick` · `ProtoChunkTicks` · `WorldGenTickAccess` · `BlackholeTickAccess` · `ServerLevel.tickChunk` · `LevelChunkSection.isRandomlyTicking` · `DiodeBlock.checkTickOnNeighbor` · `DiodeBlock.tick` · `LevelTicks.copyAreaFrom` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Fluids > Verified against **Minecraft 26.2** · Part IV · A bucket of water is emptied on flat stone and spreads one step. A player holding a water bucket clicks the top of a stone block. Their own client places the source at once, on a prediction, and it will sit there doing nothing for the rest of the session. The server places the same block and then also does nothing — for five ticks. When the appointment falls due, the source looks down, finds stone, and hands the decision to four independent searches, one per horizontal direction, each walking out through the surrounding blocks looking for somewhere the water could fall. On flat stone all four fail in exactly the same way, so all four tie and the water goes out evenly. Break the symmetry and it does not, because **water turns toward a hole four steps past the block it is about to fill, every side running its own depth-first search — and a side the water is not allowed to replace still votes on where the rest of it goes.** This page is the fluid model: the two registry objects behind each fluid, the block that carries them, what one fluid tick decides and where it sends the result. The queue that appointment sits in — booked, deduped, drained, saved and reloaded — is [scheduled ticks](scheduled-ticks.md), which this page borrows whole and never explains. ## The cast | class | what it decides | thread | |---|---|---| | `Fluid` | the registry object: its `Fluid.stateDefinition`, and every per-fluid number as an overridable method | built at bootstrap, read anywhere | | `FluidState` | one interned combination of `FlowingFluid.FALLING` and `FlowingFluid.LEVEL` — what a block reports and what a tick names | immutable | | `FlowingFluid` | the whole algorithm: what a position should hold, whether to go down or sideways, and which sides win | Server | | `WaterFluid` | water's numbers, and whether two sources may make a third | Server | | `LavaFluid` | lava's numbers, and the one override that turns liquid into rock | Server | | `LiquidBlock` | the block form of a fluid, and where a fluid tick is booked from when the fluid is the block | Server | | `SimpleWaterloggedBlock` | that a stair can be full of water without being a water block | Server | | `BucketItem` | where a source comes from, and the one attribute that stops it arriving | Server, with a client prediction | ## Two registry objects, one substance `Fluid` is to `FluidState` what `Block` is to `BlockState`, and it is literally the same machinery: `StateHolder`, a `StateDefinition` built in the constructor, and one interned instance per combination of properties, so two `FluidState`s can be compared by identity. `FlowingFluid` puts `FlowingFluid.FALLING` on every state it defines, and the flowing subclasses — `WaterFluid.Flowing`, `LavaFluid.Flowing` — add `FlowingFluid.LEVEL` (`BlockStateProperties.LEVEL_FLOWING`, 1 to 8) on top of it. The static initialiser in `Fluids` then walks `BuiltInRegistries.FLUID` and pours every state of every fluid into `Fluid.FLUID_STATE_REGISTRY`, the one global id table. **Thirty-seven** — every fluid state in the game: one for `Fluids.EMPTY`, and eighteen each for water and lava (two from the source object, sixteen from its flowing twin). Each fluid being *two* registry objects is not bookkeeping. `Fluids.WATER` is a `WaterFluid.Source` whose `WaterFluid.Source.getAmount` returns 8 and whose `WaterFluid.Source.isSource` returns true without consulting any property; `Fluids.FLOWING_WATER` is a `WaterFluid.Flowing` whose `WaterFluid.Flowing.getAmount` reads `FlowingFluid.LEVEL` and whose `WaterFluid.Flowing.isSource` returns false without consulting anything either. `WaterFluid.isSame` answers true for either, which is how the algorithm treats water as one substance no matter which object it is holding. The scheduler does not: a scheduled tick is keyed on the fluid object, so `Fluids.WATER` and `Fluids.FLOWING_WATER` are two different appointments and one of each can be pending at a single position ([scheduled ticks](scheduled-ticks.md)). The amount is also the height. `FluidState.getOwnHeight` delegates to `FlowingFluid.getOwnHeight`, which divides the amount by *nine* — so a full source stands 8/9 of a block tall, and `FlowingFluid.getHeight` has to special-case a same-fluid block overhead and return a flat 1 so that a submerged column is not full of seams. ## The block underneath the water Nothing in the world stores a `FluidState`. A chunk section stores `BlockState`s ([chunk anatomy](chunk-anatomy.md)), and `BlockBehaviour.BlockStateBase.getFluidState` returns a field — `BlockBehaviour.BlockStateBase.fluidState`, filled once by `BlockBehaviour.BlockStateBase.initCache` at bootstrap by asking the block. Reading the fluid at a position is a block lookup and a field read, which is why the flow algorithm can afford to ask hundreds of times a tick. For water and lava the block is `LiquidBlock`, whose single property is `LiquidBlock.LEVEL` (`BlockStateProperties.LEVEL`, 0 to 15). Its constructor builds `LiquidBlock.stateCache`, nine `FluidState`s: index 0 is the source, 1 through 7 are flowing at amount 8 minus the index, and index 8 is flowing at amount 8 with `FlowingFluid.FALLING` set. `LiquidBlock.getFluidState` clamps the level to 8, so block levels 8 through 15 all read back as *falling and full*, and `FlowingFluid.getLegacyLevel`, the encoder going the other way, is correspondingly lossy for falling flows — which costs nothing, because `FlowingFluid.getNewLiquid` only ever produces a falling state at amount 8. Waterlogging runs the other way round: a block that implements `SimpleWaterloggedBlock` reports water of its own accord. `SimpleWaterloggedBlock.canPlaceLiquid` accepts `Fluids.WATER` and nothing else, and `SimpleWaterloggedBlock.placeLiquid` sets `BlockStateProperties.WATERLOGGED` and books the water tick itself rather than leaving it to `LiquidBlock.onPlace`. The state it reports is a *source*, which is why a waterlogged stair is never drained by a fluid tick: the first half of `FlowingFluid.tick` is skipped entirely for a source. Exactly one block in the game reports a source that is also falling — `WaterloggedTransparentBlock`, the copper grate — and `FlowingFluid.FALLING` is the flag that lets `FlowingFluid.getFlow` add a downward component to the current it pushes entities with. ## A bucket, five ticks, four neighbours ```mermaid sequenceDiagram participant BI as BucketItem participant SL as ServerLevel participant LC as LevelChunk participant LB as LiquidBlock participant LTs as LevelTicks participant FF as FlowingFluid participant CPL as ClientPacketListener BI->>SL: emptyContents, then setBlock of the source with the flag word 11 SL->>LC: setBlockState, the section write and the counters LC->>LB: onPlace, server side and without UPDATE_SKIP_ON_PLACE LB->>SL: scheduleTick for Fluids.WATER, five ticks out SL->>LTs: schedule into this chunk's fluid container SL->>CPL: one ClientboundBlockUpdatePacket at broadcast time Note over BI,CPL: five ticks later, inside ServerLevel.tick LTs->>SL: the drain hands the position back to tickFluid SL->>FF: FluidState.tick, and a source skips getNewLiquid FF->>FF: spread tries down first, stone below refuses FF->>FF: spreadToSides, then getSpread scores the four sides FF->>SL: spreadTo, setBlock of flowing water at amount 7, four times SL->>LB: onPlace on each new block LB->>LTs: each books its own tick, because spreadTo schedules nothing SL->>LB: the shape pass reaches the source, updateShape books it again SL->>CPL: four more block updates, and nothing else ``` `BucketItem.use` picks the position, then `BucketItem.emptyContents` asks two questions before it places anything. Is the block there a `LiquidBlockContainer` that will take water — that is the waterlogging path — and does `EnvironmentAttributes.WATER_EVAPORATES` hold at this position, read through `EnvironmentAttributeReader.getValue`? The Nether's dimension type sets that attribute, so there the bucket plays a hiss, throws eight smoke particles and returns success having placed nothing: this page's whole trace never happens in the Nether ([environment attributes](environment-attributes-and-timelines.md)). The first question only decides whether the position is a legal target; the evaporation branch returns before the waterlogging is actually done, so a bucket on a Nether stair hisses too. On overworld stone it destroys and drops whatever was there and calls `Level.setBlock` with the source's `FluidState.createLegacyBlock` and the flag word 11 — `Block.UPDATE_NEIGHBORS`, `Block.UPDATE_CLIENTS`, `Block.UPDATE_IMMEDIATE` ([blocks and states](../blocks/blocks-and-states.md), [items and stacks](../items/items-and-stacks.md)). The appointment is the *block's* doing, not the fluid's. `LevelChunk.setBlockState` writes the section and then, server-side and unless `Block.UPDATE_SKIP_ON_PLACE` is set, calls `LiquidBlock.onPlace`, which asks `LiquidBlock.shouldSpreadLiquid` (always true for water) and schedules a tick for `Fluids.WATER` at `WaterFluid.getTickDelay`, five ticks out. `LiquidBlock.neighborChanged` and `LiquidBlock.updateShape` book the same appointment when something next door moves, and so does `SimpleWaterloggedBlock.placeLiquid` — but the habit is not `LiquidBlock`'s alone. Sixty-three call sites across fifty-six classes schedule a fluid tick, because every waterloggable block books water's tick from its own override of `BlockBehaviour.updateShape`, `WaterloggedTransparentBlock` included. Nothing in `FlowingFluid` books its own future except the single line in `FlowingFluid.tick` that follows a state change. The client is told none of this, because there is nothing to tell. The placing player's client ran `BucketItem.use` itself inside `MultiPlayerGameMode.startPrediction`, so the source appears locally with no round trip, and the blocks the water later touches arrive the way any run of block changes does: one `ClientboundBlockUpdatePacket` when a section changed exactly one block that tick, and a single `ClientboundSectionBlocksUpdatePacket` for the rest — which, for a spreading flow, is the ordinary case. `LevelChunk.setBlockState` skips `LiquidBlock.onPlace` off the server, and `ClientLevel.getFluidTicks` hands out a `BlackholeTickAccess` that accepts every appointment and keeps none. No client ever runs the spread. What it does run is `FluidState.animateTick` from `ClientLevel.doAnimateTick` — ambient sound and particles, with `Fluid.getDripParticle` fetched separately just after — and `FluidState.getFlow`, which both the fluid mesher and shared entity physics need to know which way the surface leans. Flowing water on a client is a stream of block updates and a direction. Five ticks on, `LevelTicks` drains the fluid queue and hands the position back through `ServerLevel.tickFluid`, which re-reads the block and fires `FluidState.tick` only if the fluid still there is the one the tick named. It is, and because it is a source, `FlowingFluid.tick` goes straight to `FlowingFluid.spread`. ## What one fluid tick decides For anything that is *not* a source, the first half of `FlowingFluid.tick` answers a question with nothing to do with spreading: what should this position hold, given its surroundings? `FlowingFluid.getNewLiquid` answers it, and the order of its three branches is most of a fluid's character. ```mermaid flowchart TD T["FlowingFluid.tick on a state that is not a source"] --> SCAN["one pass over the four horizontal neighbours reachable through canPassThroughWall, keeping the highest same-fluid amount and counting the sources"] SCAN --> B1{"two or more sources, canConvertToSource allows it, and the block below is solid or a source of this fluid"} B1 -- yes --> SRC["a source, not falling"] B1 -- no --> B2{"the same fluid directly above, through the same wall test"} B2 -- yes --> FALL["flowing at amount 8, falling"] B2 -- no --> B3["the highest neighbour amount minus getDropOff"] B3 --> Z{"zero or less"} Z -- yes --> EMPTY["Fluids.EMPTY"] Z -- no --> FLOW["flowing at that amount, not falling"] SRC --> CMP{"the same state that is already here"} FALL --> CMP FLOW --> CMP EMPTY --> AIR["set plain air with flags 3 and schedule nothing, which is how a flow dies"] AIR --> SPREAD CMP -- yes --> KEEP["write nothing, schedule nothing"] CMP -- no --> SET["set the new state with flags 3 and book a tick getSpreadDelay out"] KEEP --> SPREAD["then FlowingFluid.spread, which returns at once on an empty state"] SET --> SPREAD ``` The scan that feeds the branches counts a neighbour only if `FlowingFluid.canPassThroughWall` says the face between the two positions is open, so a pane of glass between two source blocks is enough to stop them making a third. The first branch is source conversion, and it is where infinite water lives: two source neighbours, a yes from `WaterFluid.canConvertToSource` — which reads `GameRules.WATER_SOURCE_CONVERSION`, true by default ([game rules](../../reference/gamerules.md)) — and something solid (`BlockBehaviour.BlockStateBase.isSolid`) or another source of the same fluid *directly below the position being filled*. `LavaFluid.canConvertToSource` reads `GameRules.LAVA_SOURCE_CONVERSION`, false by default, so infinite lava is one rule away rather than a property of lava. The second branch is the same fluid overhead, which always produces a falling state at full amount, however little is actually falling past. The fallback is the highest same-fluid neighbour minus `FlowingFluid.getDropOff` — one for water, two for lava outside the Nether — and zero or less is empty. Two outcomes at the bottom of the figure matter more than the branches above them. **Empty means the block becomes plain air and nothing is rescheduled**: a fluid tick that decides a position should be dry books no follow-up, and the appointment book simply forgets it. And when the answer equals the state already there — the ordinary case for a settled flow — neither the write nor the booking happens, so a stable pool that is ticked one last time falls silently off the books. ## Down first, then sideways `FlowingFluid.spread` tries below before anything else. If the block there can hold the fluid, its existing `FluidState.canBeReplacedWith` allows the swap and the floor between them is open, the fluid falls: `FlowingFluid.spreadTo` fills the position below and `FlowingFluid.spread` returns — *unless* `FlowingFluid.sourceNeighborCount` finds three or more source neighbours around the position that is pouring, in which case it spreads sideways as well. That is the lip of a large pool draining into a hole: it pours down and keeps flooding outward in the same tick. Under our bucket the block below is stone, so nothing passes. Because the state is a source, `FlowingFluid.spreadToSides` runs anyway (the other way in is a position whose floor is not a `FlowingFluid.isWaterHole`). Its first act is a gate, not a value: the amount minus the drop-off, or a flat 7 for a falling state, and if that is zero or less nothing spreads at all. What each side actually *receives* is whatever `FlowingFluid.getNewLiquid` computes for that side from scratch. ### Four searches, and the losers still vote `FlowingFluid.getSpread` returns a map from direction to the state that direction would get, and building it is the most expensive thing a fluid does. A direction is a candidate if `FlowingFluid.canMaybePassThrough` — not already a source of this fluid, a block that can hold fluid at all, and not walled off — and if the state it would receive can be held there. Each candidate is then scored by its distance to the nearest *hole*, where a hole is `FlowingFluid.isWaterHole`: a position whose floor will let the fluid through and whose lower neighbour will take it. A candidate that is itself a hole scores 0 with no search at all. Otherwise `FlowingFluid.getSlopeDistance` searches outward from it: a depth-first walk that tries the three horizontal directions other than the one it arrived from, returns the pass number the moment it finds a hole, and recurses only while the pass is still below `WaterFluid.getSlopeFindDistance` — 4 for water, 2 for lava, 4 for lava in the Nether. A search that finds nothing returns 1000. So a side's score is 0 to 4, or 1000 for *nowhere to fall from here*, and a score of 4 means the hole is four steps past the block the water is about to fill. Three branches, four deep, is up to a hundred and twenty positions per side; `FlowingFluid.SpreadContext`, built once per `FlowingFluid.getSpread` call, caches block states and hole answers in two maps keyed by the candidate's x and z offset from the origin packed into a short, so the overlap between the four searches is paid for once. The winners are the sides holding the minimum score. A better score clears everything collected so far and a tie is kept, which is why on flat stone all four directions score 1000, all four survive, and the water goes out evenly. And here is the part nobody expects: **the running minimum is updated by every scored candidate, but only a candidate whose existing fluid passes `FluidState.canBeReplacedWith` is put in the map.** `WaterFluid.canBeReplacedWith` allows replacement only from directly above and only by something that is not water, so a neighbour that is *already* water refuses every sideways spread — and still clears the map, and still lowers the minimum. One unreplaceable near neighbour can therefore suppress every other direction. It is also why a pool that has finished spreading is quiet: the source's four neighbours all refuse, the map comes back empty, and `FlowingFluid.spreadToSides` places nothing. `FlowingFluid.spreadTo` does the placing and is deliberately dumb. A `LiquidBlockContainer` gets `LiquidBlockContainer.placeLiquid`; anything else that is not air — air, the usual target, is skipped — has `FlowingFluid.beforeDestroyingBlock` run over it — `WaterFluid.beforeDestroyingBlock` drops the block's items through `Block.dropResources`, `LavaFluid` plays a fizz — and then `LevelWriter.setBlock` with flags 3. It schedules nothing at all. Every new flowing block books its own tick from its own `LiquidBlock.onPlace`, and the shape-update pass at the tail of `Level.setBlock` reaches back to the source, whose `LiquidBlock.updateShape` books the source again. ## How a flow stops Two ways, and only one of them is dramatic. The quiet one is the gate. Water leaves a source at amount 7 and loses one per block, so the seventh block out holds amount 1, its `FlowingFluid.spreadToSides` gate computes zero, and the front simply stops. Behind it every block's tick keeps getting the same answer from `FlowingFluid.getNewLiquid`, writes nothing and books nothing, and the pool falls off the books until a `LiquidBlock.neighborChanged` or a `LiquidBlock.updateShape` wakes it. The loud one is what happens when the source is taken back. `LiquidBlock.pickupBlock` swaps it for air — level 0 only, which is why you cannot fill a bucket from flowing water — the flag word runs `Level.updateNeighborsAt`, each neighbouring `LiquidBlock.neighborChanged` books a fluid tick, and five ticks later those blocks compute `FlowingFluid.getNewLiquid` with no source in reach. Only the outermost block of the flow comes back empty and turns to air; every ring behind it comes back one level *lower* than the ring beyond, writes that, and books itself again. So the pool re-levels repeatedly on its way out, one ring per tick delay, and each ring's last act is to schedule nothing. ## Lava is water with worse numbers, and three exceptions | | water | lava | lava under `EnvironmentAttributes.FAST_LAVA` | |---|---|---|---| | `Fluid.getTickDelay` | 5 | 30 | 10 | | `FlowingFluid.getDropOff` | 1 | 2 | 1 | | `FlowingFluid.getSlopeFindDistance` | 4 | 2 | 4 | | how far a flow reaches | 7 blocks | 3 blocks | 7 blocks | | source conversion | `GameRules.WATER_SOURCE_CONVERSION`, true | `GameRules.LAVA_SOURCE_CONVERSION`, false | unchanged | | `Fluid.isRandomlyTicking` | no | yes | yes | `LavaFluid.isFastLava` reads `EnvironmentAttributes.FAST_LAVA` through `EnvironmentAttributeReader.getDimensionValue`. The attribute is declared `EnvironmentAttribute.Builder.notPositional`, so it is a property of the whole dimension rather than of a place in it, and the Nether's dimension type sets it — along with `EnvironmentAttributes.WATER_EVAPORATES`. Nether lava is not special-cased anywhere in `LavaFluid`; it is the same three methods reading one boolean. On top of the numbers, `LavaFluid.getSpreadDelay` multiplies the delay by four, three times in four, whenever a non-falling flow is about to get deeper. Lava does not creep — it creeps unevenly, and the unevenness is rolled fresh on each tick. The three exceptions are where lava stops behaving like a fluid. `LavaFluid.spreadTo` intercepts a downward spread onto water: the fizz plays and nothing spreads, whatever the water is in, and the target becomes `Blocks.STONE` when — and only when — it was a `LiquidBlock`. So a lavafall into a pool builds a plug rather than replacing the water, while a lavafall onto a waterlogged stair is merely stopped. The other two are in `LiquidBlock.shouldSpreadLiquid`, called from `LiquidBlock.onPlace` and `LiquidBlock.neighborChanged`: for lava it walks `LiquidBlock.POSSIBLE_FLOW_DIRECTIONS` and tests each direction's *opposite*, so the faces it inspects are the top and the four sides and never the bottom. Water at any of them turns *this* block into `Blocks.OBSIDIAN` if its own fluid is a source and `Blocks.COBBLESTONE` if it is not, and returns false so no tick is booked at all. The `Blocks.BASALT` case is the exception to the exception and the only place the block below is read: `Blocks.SOUL_SOIL` underneath and `Blocks.BLUE_ICE` at one of those five opposites. Lava's *random* tick is fire rather than flow, and a selected position gets it twice — once as a block and once as a fluid — for reasons that belong to [scheduled ticks](scheduled-ticks.md). ## Questions players ask **Why does a water block have a block tick at all?** `LiquidBlock.tick` spreads nothing. It calls `BubbleColumnBlock.updateColumn`, and only when the fluid there is a full source in `FluidTags.BUBBLE_COLUMN_CAN_OCCUPY`; the tick was booked twenty ticks out by `LiquidBlock.tryScheduleBubbleBlockColumn` because soul sand or magma is underneath. Flow is entirely a *fluid* tick, in the other queue with its own budget. **Why is my infinite pool not infinite?** Because `GameRules.WATER_SOURCE_CONVERSION` can be turned off, and because the first branch of `FlowingFluid.getNewLiquid` also demands something solid or another source directly below the position being filled. Two sources over a hole make nothing. **Why does water refuse to run the way that looks downhill?** Because a water block already sitting on one side scores in the slope vote and then refuses to be replaced, so it can drag the minimum down to its own distance and empty the winners' map on the way past. **Why is the wall test worth caching?** Because it runs constantly. `FlowingFluid.canPassThroughWall` short-circuits the easy cases — either side a full cube is a no, both sides empty is a yes — and otherwise merges the two collision shapes with `Shapes.mergedFaceOccludes` and memoises the answer in `FlowingFluid.OCCLUSION_CACHE`, a thread-local 200-entry map keyed by `FlowingFluid.BlockStatePairKey`, which hashes both states by identity and is skipped entirely when either block has a dynamic shape (`Block.hasDynamicShape`). ## Where to look `BucketItem.emptyContents` · `LiquidBlock.onPlace` · `LiquidBlock.shouldSpreadLiquid` · `ServerLevel.tickFluid` · `FlowingFluid.tick` · `FlowingFluid.getNewLiquid` · `FlowingFluid.spread` · `FlowingFluid.spreadToSides` · `FlowingFluid.getSpread` · `FlowingFluid.getSlopeDistance` · `FlowingFluid.SpreadContext` · `FlowingFluid.spreadTo` · `FlowingFluid.canPassThroughWall` · `LiquidBlock.stateCache` · `SimpleWaterloggedBlock.placeLiquid` · `WaterFluid` · `LavaFluid.spreadTo` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Game events and vibrations > Verified against **Minecraft 26.2** · Part IV · A footstep reaches a sculk sensor. You walk across a stone floor and, eight blocks away, a sculk sensor's tendrils flick up and it pushes redstone power out of its side. Nothing scanned for you: the step itself posted a `GameEvent.STEP` into `GameEventDispatcher.post`, which walked the loaded chunk sections around you and called every listener inside its own radius *inline*, before `Entity.move` had finished. What a player believes about sculk lives in the gates that footstep then has to pass, and their shape is the surprise. **The sensor always hears you at least one tick late by design, because `VibrationSelector.chosenCandidate` hands over a candidate only if it was recorded on an *earlier* tick. The wool box works only if all six rays of `VibrationSystem.Listener.isOccluded` hit wool, not just the one on the straight line. And standing on the sensor skips the whole cascade — `SculkSensorBlock.stepOn` calls `VibrationSystem.Listener.forceScheduleVibration` with no dispatcher, no occlusion test and no `VibrationSystem.User.isValidVibration`, so sneaking does not save you.** Only a warden gets out of that one. ## The cast | class | what it decides | thread | |---|---|---| | `GameEvent` | how far the news travels — one field, `GameEvent.notificationRadius` | — (a record) | | `GameEventDispatcher` | which sections are walked, and who is told inline rather than sorted | Server | | `EuclideanGameEventListenerRegistry` | which listeners in one section Y are close enough to be visited | Server | | `DynamicGameEventListener` | which section a *moving* listener is registered in | Server | | `VibrationSystem.Listener` | the gates between an event and a candidate | Server | | `VibrationSelector` | which of a tick's candidates survives, and that none is used before the next tick | Server | | `VibrationSystem.Ticker` | the travel countdown, the particle, and the moment of arrival | Server, from the host's own tick | | `SculkSensorBlockEntity.VibrationUser` | whether this sensor takes a vibration, and what it does with one | Server | ## A game event is one number `GameEvent` is a record of a single field, `GameEvent.notificationRadius`. That is the whole type. Sixty-one constants live in `BuiltInRegistries.GAME_EVENT`, fifteen of them `GameEvent.RESONATE_1` through `GameEvent.RESONATE_15`, one per vibration frequency, so a resonating block can re-emit the frequency it heard. All but three take `GameEvent.DEFAULT_NOTIFICATION_RADIUS`, 16 blocks: `GameEvent.JUKEBOX_PLAY` and `GameEvent.JUKEBOX_STOP_PLAY` are 10, `GameEvent.SHRIEK` is 32. The radius is the *dispatcher's* number, and each listener then applies its own. Everything else rides beside the event in a `GameEvent.Context` — the source entity and the affected block state, either of which may be absent. The registry is a `DefaultedMappedRegistry` on *step*, which is narrower than it sounds. `DefaultedMappedRegistry.getValue` and `DefaultedMappedRegistry.byId` substitute `GameEvent.STEP` for an unknown key, so a raw lookup that misses becomes a footstep — but `GameEvent.CODEC` is a `RegistryFixedCodec`, which errors on an unknown name. Bad data in a pack fails loudly; only the raw lookup silently becomes a step. ## Listeners live in the chunk, one registry per section A `GameEventListener` is four methods: a `PositionSource` (`BlockPositionSource` for a block, `EntityPositionSource` for a mob, the latter resolving a stored UUID against the level the first time it is asked), a `GameEventListener.getListenerRadius`, `GameEventListener.handleGameEvent` and a `GameEventListener.getDeliveryMode`. They are stored per chunk section in `LevelChunk.gameEventListenerRegistrySections` ([chunk anatomy](chunk-anatomy.md)), and `LevelChunk.getListenerRegistry` creates an `EuclideanGameEventListenerRegistry` for a section Y the first time anyone asks — including the dispatcher, merely walking past. One is dropped again through `LevelChunk.removeGameEventListenerRegistry`, but only when an `EuclideanGameEventListenerRegistry.unregister` empties it. `ChunkAccess.getListenerRegistry` answers `GameEventListenerRegistry.NOOP`: proto chunks and the client have none. Block entities join on the chunk's terms: `LevelChunk.addGameEventListener` runs from `LevelChunk.addAndRegisterBlockEntity` on placement and from `LevelChunk.registerAllBlockEntitiesAfterLevelLoad` when the chunk comes back, asking `EntityBlock.getListener` — whose default returns the listener of any block entity implementing `GameEventListener.Provider` ([block entities](../blocks/block-entities.md)). A sensor's section is fixed for the life of the block. Entities move, so they carry a `DynamicGameEventListener` instead: a listener plus the last `SectionPos` it was filed under. `Entity.updateDynamicGameEventListener` is empty on `Entity` and overridden by `Warden` and by `Allay`, which hands over two. `ServerLevel.EntityCallbacks` drives it — `DynamicGameEventListener.add`, `DynamicGameEventListener.remove`, and `DynamicGameEventListener.move` on every section change. Its two halves are guarded separately and its record of where it was advances either way, so a move whose *old* chunk is not loaded to `ChunkStatus.FULL` leaves a stale registration behind, and one whose *new* chunk is not takes the listener out of the world entirely. ## The dispatcher never queues `ServerLevel.gameEvent` is one line into `GameEventDispatcher.post`, and `ServerLevel.gameEventDispatcher` owns nothing between calls. `GameEventDispatcher.post` turns the radius into a range of sections — 3 by 3 by 3 for the default 16, 5 by 5 by 5 for a shriek — fetches each chunk column once with `ServerChunkCache.getChunkNow`, and visits each section's registry. `EuclideanGameEventListenerRegistry.visitInRangeListeners` resolves each listener's position, compares block-position distance *squared* against the listener's own radius squared — a sphere inside the dispatcher's cube — and calls `GameEventListener.handleGameEvent` there and then. No queue, no ordering, no next-tick delivery: the broadcast is a nested loop inside `Entity.move`, and the emitter's method has not returned yet. Two things follow. `ServerChunkCache.getChunkNow` returns null for a column that is not already loaded and `GameEventDispatcher.post` skips it, so an event at the edge of the loaded world is never delivered over the border. And because a listener can be told mid-walk, a registry sets `EuclideanGameEventListenerRegistry.processing` while it iterates and defers every `EuclideanGameEventListenerRegistry.register` and `EuclideanGameEventListenerRegistry.unregister` to the end of the visit. One listener does wait. `GameEventListener.getDeliveryMode` is `GameEventListener.DeliveryMode.UNSPECIFIED` everywhere except `SculkCatalystBlockEntity.CatalystListener`, which answers `GameEventListener.DeliveryMode.BY_DISTANCE`. Those are collected as `GameEvent.ListenerInfo`s, sorted by squared distance and delivered by `GameEventDispatcher.handleGameEventMessagesInQueue` after the walk, because a catalyst *consumes* the dead mob's experience (`LivingEntity.skipDropExperience`) and `LivingEntity.wasExperienceConsumed` means only the first told gets any. Sorting is how the nearest one wins. ## The gates between a footstep and a candidate ```mermaid flowchart TD A["Entity.move, moveDist has passed nextStep"] --> B{"on ground, climbing, crouching without vertical movement or on rails, not swimming, and MovementEmission emits events"} B -->|"no"| X1["no event is posted at all"] B -->|"yes"| C["ServerLevel.gameEvent, GameEvent.STEP at the entity, context is the entity plus the block walked on"] C --> D["GameEventDispatcher.post, radius 16 becomes 3 by 3 by 3 sections"] D --> E{"ServerChunkCache.getChunkNow, is the column loaded"} E -->|"no"| X2["skipped in silence, nothing is loaded and nothing is retried"] E -->|"yes"| F{"EuclideanGameEventListenerRegistry.visitInRangeListeners, inside the listener's own radius"} F -->|"outside"| X3["not visited"] F -->|"inside"| G{"GameEventListener.getDeliveryMode"} G -->|"BY_DISTANCE"| Q["collected, sorted by distance, delivered after the walk. The sculk catalyst alone"] G -->|"UNSPECIFIED"| H{"VibrationSystem.Listener.handleGameEvent, is a vibration already in flight"} H -->|"yes"| X4["dropped, this listener is busy"] H -->|"no"| I{"VibrationSystem.User.isValidVibration"} I -->|"outside GameEventTags.VIBRATIONS, a spectator, sneaking on an event in GameEventTags.IGNORE_VIBRATIONS_SNEAKING, Entity.dampensVibrations, or the walked block is in BlockTags.DAMPENS_VIBRATIONS"| X5["dropped"] I -->|"passes"| P{"does the listener's own PositionSource resolve"} P -->|"no"| X6["dropped"] P -->|"yes"| J{"SculkSensorBlockEntity.VibrationUser.canReceiveVibration"} J -->|"a break or place at the sensor's own position, frequency 0, or the sensor is not inactive"| X7["dropped"] J -->|"passes"| K{"VibrationSystem.Listener.isOccluded, six rays nudged off the source block centre"} K -->|"all six hit BlockTags.OCCLUDES_VIBRATION_SIGNALS"| X8["dropped"] K -->|"any one ray gets through"| L["VibrationSelector.addCandidate"] S["SculkSensorBlock.stepOn, from Entity.applyEffectsFromBlocks while standing on the block"] --> S2{"not a warden, SculkSensorBlock.canActivate, and canReceiveVibration"} S2 -->|"yes"| L ``` The order is not the one a player would guess. The busy check comes first, so a sensor already carrying a vibration ignores everything without evaluating a single tag, and the occlusion raycast — much the most expensive gate — comes last, after every cheap refusal has had its chance. Before any of it, `Entity.applyMovementEmissionAndPlaySound` decides whether there is an event to post, by accumulating `Entity.moveDist` and firing only when it passes `Entity.nextStep` — which is why walking emits a footstep per stride rather than per tick. Two gates ask the *user* rather than the system: `VibrationSystem.User.getListenableEvents` supplies the tag `VibrationSystem.User.isValidVibration` tests first, and `SculkSensorBlockEntity.VibrationUser.canReceiveVibration` refuses `GameEvent.BLOCK_DESTROY` and `GameEvent.BLOCK_PLACE` at the sensor's *own* position — which is why placing a sensor does not set it off — refuses a frequency of `VibrationSystem.NO_VIBRATION_FREQUENCY`, and otherwise defers to `SculkSensorBlock.canActivate`: inactive only. The occlusion test is worth reading slowly. `VibrationSystem.Listener.isOccluded` takes the source block's centre, nudges it a hundred-thousandth of a block along each of the six `Direction` values in turn, and runs `BlockGetter.isBlockInLine` with a `ClipBlockStateContext` looking for `BlockTags.OCCLUDES_VIBRATION_SIGNALS`. It reports *occluded* only if all six rays are stopped, and returns the moment one is not — so a single block of wool on the straight line is almost never enough, and a wool box is a box because a box is what makes all six fail. ## The trace: one footstep, several ticks ```mermaid sequenceDiagram participant Entity as Entity participant SL as ServerLevel participant GED as GameEventDispatcher participant VSL as VibrationSystem.Listener participant VSel as VibrationSelector participant VST as VibrationSystem.Ticker participant SSB as SculkSensorBlock Note over Entity,SSB: tick T, the entity ticks and moves Entity->>SL: gameEvent, GameEvent.STEP at the entity's feet SL->>GED: post, every loaded section within 16 blocks GED->>VSL: handleGameEvent, inline, before Entity.move returns VSL->>VSel: addCandidate, a VibrationInfo stamped with game time T Note over VST,SSB: still tick T, whenever the sensor's block entity ticks VST->>VSel: chosenCandidate VSel-->>VST: nothing, the candidate is not from an earlier tick Note over Entity,SSB: tick T plus 1 VST->>VSel: chosenCandidate VSel-->>VST: the VibrationInfo, then startOver clears the slot VST->>SL: sendParticles, one VibrationParticleOption with the destination and the tick count Note over VST,SSB: the countdown starts in this same tick, one block per tick VST->>SSB: onReceiveVibration on SculkSensorBlockEntity's VibrationSystem.User — the event, the entities and the arrival distance SSB->>SL: setBlock PHASE active with POWER, scheduleTick 30, gameEvent SCULK_SENSOR_TENDRILS_CLICKING Note over Entity,SSB: 30 ticks later deactivate, then 10 more before inactive ``` `VibrationSystem.Ticker.tick` is the whole of the wait, and runs from whoever hosts the listener: `SculkSensorBlock.getTicker` and `SculkShriekerBlock.getTicker` for the blocks — server-side only, and already gated by `Level.shouldTickBlocksAt` on their own chunk — and `Warden.tick` and `Allay.tick` for the mobs. One call does three things in order: select, if nothing is in flight; send or re-send the particle; then decrement the travel time and arrive if it has reached zero. That particle is the only thing the client is told. `VibrationParticleOption` carries the destination `PositionSource` and the remaining tick count, and the client animates the flight from that alone: `ClientLevel.gameEvent` is an empty method and there is no vibration packet. When the block entity was loaded from disk, `VibrationSystem.Data.shouldReloadVibrationParticle` is set and the ticker re-sends the particle from a point interpolated along the path covered. ## One tick, structurally `VibrationSelector` holds at most one candidate, stamped with the game time it arrived. `VibrationSelector.addCandidate` takes an empty slot unconditionally; against a candidate from the *same* tick it takes the closer one, breaking a distance tie in favour of the higher frequency; and against one held over from an *earlier* tick it does nothing at all, because that one is already waiting to be consumed. That is the whole of "the nearest event wins" — per listener, per tick, one slot. The latency falls out of the read side. `VibrationSelector.chosenCandidate` returns the candidate only if its stamp is strictly *less* than the current game time, so one added during tick T is invisible for the rest of tick T however the ordering falls, and the earliest it can be selected is the tick after. Travel is measured from there: `VibrationSystem.User.calculateTravelTimeInTicks` is the floor of the distance — one block per tick — and the countdown's first decrement happens inside the same call that selected the vibration, so a source *n* whole blocks away arrives *n* minus 1 ticks after selection, and anything closer than two blocks arrives on the selecting tick itself. Arrival can be refused. `VibrationSystem.User.requiresAdjacentChunksToBeTicking` is true for both sculk blocks, and `VibrationSystem.Ticker` will not deliver unless all nine columns of the 3 by 3 around the listener are loaded and pass `Level.shouldTickBlocksAt` — [tickets and loading](tickets-and-loading.md) is what "ticking" means. It does not drop the vibration: the travel time is floored at zero, so the ticker asks again every tick until the neighbourhood ticks. And the whole of `VibrationSystem.Data` is written to disk under *listener* through `VibrationSystem.Data.CODEC`, so a vibration survives a reload with its countdown intact. ## What arrival costs the block `VibrationSystem.VIBRATION_FREQUENCY_FOR_EVENT` maps game events to frequencies 1 to 15 and returns 0 for anything unmapped: a step, a swim and a flap are 1, a death or an explosion 15. That number is kept by `SculkSensorBlockEntity.setLastVibrationFrequency` and is what a comparator reads out of an *active* sensor — `SculkSensorBlock.getAnalogOutputSignal` answers 0 in any other phase. The redstone power is a different number: `VibrationSystem.getRedstoneStrengthForDistance` is the larger of 1 and 15 minus the floor of 15 times the distance over the listener's radius, using a distance recomputed from the two *block* positions at arrival, not the float stored when the candidate was made. `SculkSensorBlock.activate` sets `SculkSensorBlock.PHASE` to `SculkSensorPhase.ACTIVE` with that power, schedules a block tick `SculkSensorBlock.getActiveTicks` out — 30 for a plain sensor, 10 for a calibrated one, and never the constant `SculkSensorBlock.ACTIVE_TICKS`, which nothing reads — runs `SculkSensorBlock.tryResonateVibration` — which, for each of the six neighbours in `BlockTags.VIBRATION_RESONATORS`, posts the matching `GameEvent.RESONATE_1` … `GameEvent.RESONATE_15` at the *neighbour's* position, so an amethyst block beside a sensor rebroadcasts the frequency it heard — and then emits `GameEvent.SCULK_SENSOR_TENDRILS_CLICKING`, the single entry in `GameEventTags.SHRIEKER_CAN_LISTEN`. Shriekers do not hear you. They hear sensors hearing you. Coming down takes two scheduled ticks. At 30, `SculkSensorBlock.tick` calls `SculkSensorBlock.deactivate`, which drops the power to zero, moves the phase to `SculkSensorPhase.COOLDOWN` and schedules another tick `SculkSensorBlock.COOLDOWN_TICKS` (10) out; only *that* tick returns the block to `SculkSensorPhase.INACTIVE`. In between the sensor is dark and still refuses every vibration, because `SculkSensorBlock.canActivate` tests for inactive. ## The other listeners | listener | radius | what it listens to | what a vibration does | |---|---:|---|---| | `SculkSensorBlockEntity` | 8 | `GameEventTags.VIBRATIONS` | activates for 30 ticks, power by distance, frequency on the comparator | | `CalibratedSculkSensorBlockEntity` | 16 | `GameEventTags.VIBRATIONS` | the same, but active for 10 ticks rather than 30, and when the block behind `CalibratedSculkSensorBlock.FACING` gives a redstone signal, only that exact frequency is accepted | | `SculkShriekerBlockEntity` | 8 | `GameEventTags.SHRIEKER_CAN_LISTEN` | needs a player behind the event, then `SculkShriekerBlockEntity.tryShriek` — warning level, darkness, and a warden at level 4 | | `Warden` | 16 | `GameEventTags.WARDEN_CAN_LISTEN` | anger through `Warden.increaseAngerAt`, a 40-tick `MemoryModuleType.VIBRATION_COOLDOWN`, and a disturbance location for `WardenAi` | | `Allay` | 16 | `GameEventTags.ALLAY_CAN_LISTEN`, note blocks only | `AllayAi.hearNoteblock` stores `MemoryModuleType.LIKED_NOTEBLOCK_POSITION`, after which it accepts that block and no other | | `Allay.JukeboxListener` | 10 | not a vibration at all | a plain `GameEventListener` for `GameEvent.JUKEBOX_PLAY` and `GameEvent.JUKEBOX_STOP_PLAY` — it makes the allay dance | | `SculkCatalystBlockEntity.CatalystListener` | 8 | not a vibration at all | on `GameEvent.ENTITY_DIE`, takes the mob's experience as sculk cursors and blooms | The tags are where the personalities live, and they are data ([tags](../foundations/tags.md)): `GameEventTags.WARDEN_CAN_LISTEN` covers shrieks and tendril clicks that `GameEventTags.VIBRATIONS` leaves out, and leaves out the flap that sensors hear. The warden is also the one entity whose `Entity.dampensVibrations` is *unconditionally* true — a dropped item answers true too, but only while it holds something in `ItemTags.DAMPENS_VIBRATIONS` — so the warden is invisible to every other listener while being the most sensitive one on the list, and the one entity `SculkSensorBlock.stepOn` refuses by name. Its brain and the allay's are [Part VI](../entities/ai-goals-and-brains.md). ## Questions players ask **Does sneaking make me silent?** It makes six events silent. `GameEventTags.IGNORE_VIBRATIONS_SNEAKING` holds `GameEvent.STEP`, `GameEvent.SWIM`, `GameEvent.HIT_GROUND`, `GameEvent.PROJECTILE_SHOOT`, `GameEvent.ITEM_INTERACT_START` and `GameEvent.ITEM_INTERACT_FINISH`, and the test is `Entity.isSteppingCarefully` — whether the sneak key is down. Open a chest while crouched and the sensor hears it. Sneak past one as a player and `CriteriaTriggers.AVOID_VIBRATION` notes the advancement, because sensors and wardens answer `VibrationSystem.User.canTriggerAvoidVibration`. **Then why does the sensor I am crouching on still fire?** That path is not the dispatcher's. `SculkSensorBlock.stepOn` runs from `Entity.applyEffectsFromBlocks` every tick an entity stands on the block and calls `VibrationSystem.Listener.forceScheduleVibration` directly: no section walk, no radius test, no occlusion, and no `VibrationSystem.User.isValidVibration`, which is where the sneaking tag lives. It still asks `VibrationSystem.User.canReceiveVibration`, so an active sensor stays quiet, and it still refuses a warden. The tick of latency remains, since the shortcut ends at `VibrationSelector.addCandidate` like everything else. **Why did my wool floor not stop it?** Wool underfoot and wool in the way are different tags doing different jobs. `BlockTags.DAMPENS_VIBRATIONS` on the block being walked on kills the event at `VibrationSystem.User.isValidVibration`, and `ItemTags.DAMPENS_VIBRATIONS` does the same for a dropped item through `ItemEntity.dampensVibrations`. `BlockTags.OCCLUDES_VIBRATION_SIGNALS` is the six-ray test, and it has to stop all six. **Why does a sensor near the edge of the world miss?** Two reasons, both silent: `GameEventDispatcher.post` skips any column `ServerChunkCache.getChunkNow` does not already have, and a sensor whose 3 by 3 neighbourhood is not ticking holds a finished vibration until it is. The second is visible on the debug channel; the first is not, because the broadcast happens only where a listener was actually visited — `DebugSubscriptions.GAME_EVENTS` and `DebugSubscriptions.GAME_EVENT_LISTENERS`, broadcast through `ServerLevel.debugSynchronizers` ([debugging the running game](../client/debugging-the-running-game.md)). ## Where to look `GameEvent` · `GameEventDispatcher.post` · `EuclideanGameEventListenerRegistry.visitInRangeListeners` · `LevelChunk.getListenerRegistry` · `LevelChunk.addGameEventListener` · `DynamicGameEventListener.move` · `ServerLevel.EntityCallbacks` · `Entity.vibrationAndSoundEffectsFromBlock` · `VibrationSystem.Listener.handleGameEvent` · `VibrationSystem.User.isValidVibration` · `VibrationSystem.Listener.isOccluded` · `VibrationSelector.addCandidate` · `VibrationSelector.chosenCandidate` · `VibrationSystem.Ticker.tick` · `SculkSensorBlockEntity.VibrationUser` · `SculkSensorBlock.activate` · `SculkSensorBlock.stepOn` · `SculkShriekerBlockEntity.tryShriek` · `Warden.VibrationUser` · `SculkCatalystBlockEntity.CatalystListener` — then [entity anatomy](../entities/entity-anatomy.md) for `Entity.updateDynamicGameEventListener` and [registries](../../reference/registries.md) for `BuiltInRegistries.GAME_EVENT`. The other index the world keeps about itself — where things worth walking to are, rather than what just happened — is [points of interest](points-of-interest.md). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Points of interest > Verified against **Minecraft 26.2** · Part IV · A villager claims a bed. A villager standing in the middle of a field at noon decides which bed is his. He is not near it, he is not looking at it, and he will not walk to it for another six thousand ticks. `AcquirePoi` asked the index for the beds with a free ticket within 48 blocks, took the five nearest, asked `PathNavigation` for a path to all five at once, and the moment `Path.canReach` came back true it called `PoiManager.take` and decremented that bed's ticket. Night has nothing to do with it. Hours later `SleepInBed` will finally put him in the bed and set `BedBlock.OCCUPIED`, and because both occupied variants of a bed head are in `PoiTypes.BEDS` and map to the same `PoiTypes.HOME`, that block change does not touch the record at all: **the claim and the *occupied* flag speak in one direction only. Going to sleep tells the index nothing; the single behaviour that reads the flag back can only take a claim away.** Neither is what a player would call ownership. ## The cast | class | what it decides | thread | |---|---|---| | `PoiType` | how many tickets a kind of block hands out and how close is close enough — a record of `PoiType.matchingStates`, `PoiType.maxTickets`, `PoiType.validRange` | static, built at bootstrap | | `PoiTypes` | the catalogue, and the block-state → type map `PoiTypes.TYPE_BY_STATE` that every lookup goes through | static | | `PoiRecord` | one position's type and its `PoiRecord.freeTickets` counter | Server | | `PoiSection` | the records in one 16³ section, by section-relative position and by type, plus the *Valid* flag that decides whether the section is rebuilt on load | Server | | `PoiManager` | the index: a `SectionStorage` over the *poi/* region files, the whole query family, and the village distance graph | Server | | `ServerLevel` | that a block change happened at all — `ServerLevel.updatePOIOnBlockStateChange` is the door every ordinary write goes through | Server, or a worldgen worker | | `AcquirePoi` | which POI a mob claims, and when to stop asking about one it cannot reach | Server | | `ValidateNearbyPoi` · `SleepInBed` | whether a remembered POI is still real, and what to do on arrival | Server | ## A ticket is a claim nothing enforces A point of interest is a block state the game has decided is worth going to. There are twenty-one kinds, none data-pack-driven: `PoiTypes.bootstrap` registers them into `BuiltInRegistries.POINT_OF_INTEREST_TYPE` and files every one of their block states into `PoiTypes.TYPE_BY_STATE`, throwing at startup if two types claim one state. `PoiTypes.forState` and `PoiTypes.hasPoi` are the only questions anyone asks of that map. A ticket is the claim. A `PoiRecord` starts with `PoiType.maxTickets` free and hands them out one at a time: `PoiRecord.acquireTicket` decrements, `PoiRecord.releaseTicket` increments, both refuse to run past their end of the range, and both mark the section dirty. `PoiRecord.hasSpace` asks whether any are left, `PoiRecord.isOccupied` whether any are gone — the second question being the one the village graph reads. Neither says anything about *who*: a record does not know its holder, a holder is not told when its record disappears, and no state on the block corresponds to a claim. ```mermaid stateDiagram-v2 [*] --> Free : block placed, PoiManager.add, freeTickets = PoiType.maxTickets Free --> Held : PoiManager.take then PoiRecord.acquireTicket Held --> Free : ValidateNearbyPoi, the bed is OCCUPIED and no villager is asleep in it Held --> Free : Villager.releaseAllPois, on death or on witch conversion Held --> Free : SetWalkTargetFromBlockMemory, unreachable for 1200 ticks Held --> Gone : the block changes, PoiManager.remove drops the whole record Free --> Gone : the block changes, PoiManager.remove drops the whole record Gone --> [*] note right of Held : occupied, so its section is a village centre if the type is in PoiTypeTags.VILLAGE note right of Gone : nothing is released, and the claimant is not told ``` The asymmetry in that figure is deliberate on the release side and merely survivable on the removal side. `PoiManager.release` **throws** when the section is not there and `PoiSection.release` throws when the record is not, which is why three of the four releasers check `PoiManager.getType` or `PoiManager.exists` first — `Villager.releasePoi` checks the type and then tests it against `Villager.POI_MEMORIES` before it dares. The fourth, `VillagerMakeLove`, checks nothing, and gets away with it because the position it releases is one `PoiManager.take` handed back a moment earlier. `PoiSection.remove` on a missing record only logs an error, so the removal path is allowed to be wrong and the release path is not. Erasing a memory is not a release, which is what makes the job-site behaviours noisy and harmless: `PoiCompetitorScan` awards a contested site to whichever villager has the higher `Villager.getVillagerXp` and makes the loser erase `MemoryModuleType.JOB_SITE`, `YieldJobSite` hands a `MemoryModuleType.POTENTIAL_JOB_SITE` over to an unemployed neighbour, and `AssignProfessionFromJobSite` promotes the potential site to the real one. None of those three touches a ticket — though `GoToPotentialJobSite` does, `GoToPotentialJobSite.stop` giving back the ticket `AcquirePoi` took on the potential site. ## The catalogue | types | the block | `PoiType.maxTickets` | `PoiType.validRange` | |---|---|---:|---:| | `PoiTypes.ARMORER` `PoiTypes.BUTCHER` `PoiTypes.CARTOGRAPHER` `PoiTypes.CLERIC` `PoiTypes.FARMER` `PoiTypes.FISHERMAN` `PoiTypes.FLETCHER` `PoiTypes.LEATHERWORKER` `PoiTypes.LIBRARIAN` `PoiTypes.MASON` `PoiTypes.SHEPHERD` `PoiTypes.TOOLSMITH` `PoiTypes.WEAPONSMITH` | one work block each — blast furnace, smoker, cartography table, brewing stand, composter, barrel, fletching table, lectern, stonecutter, loom, smithing table, grindstone, and for the leatherworker all four cauldrons | 1 | 1 | | `PoiTypes.HOME` | the bed's **head** half only — `PoiTypes.BEDS` filters `BedBlock.PART` to `BedPart.HEAD` | 1 | 1 | | `PoiTypes.MEETING` | the bell | 32 | 6 | | `PoiTypes.BEEHIVE` `PoiTypes.BEE_NEST` | hive and nest | 0 | 1 | | `PoiTypes.NETHER_PORTAL` | the portal block | 0 | 1 | | `PoiTypes.LODESTONE` | the lodestone | 0 | 1 | | `PoiTypes.LIGHTNING_ROD` | the rod, every facing | 0 | 1 | | `PoiTypes.TEST_INSTANCE` | the test instance block | 0 | 1 | Six of the twenty-one types are locatable but unclaimable. `PoiRecord.hasSpace` is false for them forever, so nothing can `PoiManager.take` one, and `PoiRecord.isOccupied` — which asks whether the free count has moved off `PoiType.maxTickets` — is false forever too. Not that it would matter: none of the six is in `PoiTypeTags.VILLAGE`, so none was ever a candidate for a village centre. They are indexed purely so something can find the nearest one fast: the bee's hive search asks for `PoiTypeTags.BEE_HOME` within 20 blocks and then filters through `Bee.doesHiveHaveSpace`, which asks the `BeehiveBlockEntity` whether it is full. The index answers *where* and something else answers *whether*. Three tags cut across the catalogue ([tags](../foundations/tags.md)): `PoiTypeTags.ACQUIRABLE_JOB_SITE` for the thirteen professions, `PoiTypeTags.BEE_HOME` for the two hives, and `PoiTypeTags.VILLAGE` for those thirteen plus `PoiTypes.HOME` and `PoiTypes.MEETING` — fifteen types whose occupied records are what a village *is*. ## Where the index lives, and how it repairs itself `PoiManager` extends `SectionStorage` ([chunk storage](chunk-storage.md)), so the unit of storage is a chunk section and the unit of file a region: `ChunkMap` builds it on the dimension's *poi/* folder with `DataFixTypes.POI_CHUNK`, and `PoiSection.Packed` is the on-disk shape — a *Valid* boolean and a list of *Records*, each a position, a type and a *free_tickets* count. Tickets survive a restart, and so do the villagers' memories of them, and nothing on load reconciles the two. `ChunkMap.tick` runs `PoiManager.tick` under the profiler's *poi*, which writes dirty chunks for as long as the tick has time and then settles the village graph ([the level tick](../server/server-level-tick.md) owns the budget). Reads are the interesting half. The query family is a dozen shapes of the same walk — `PoiManager.getInSquare` over a chunk range, `PoiManager.getInRange` narrowing it to a sphere, and `PoiManager.findClosest`, `PoiManager.getRandom`, `PoiManager.getCountInRange`, `PoiManager.exists` and `PoiManager.getType` above them — and every one bottoms out in `SectionStorage.getOrLoad`, where a section that was never prefetched is read from disk **synchronously, on the Server thread, blocking**. That is why `ChunkMap.scheduleChunkLoad` fires `SectionStorage.prefetch` beside the chunk's own parse and joins the two before either is used. The repair runs on every chunk read. `SerializableChunkData.read` calls `PoiManager.checkConsistencyWithBlocks` once per section with block data. If the section is in storage, `PoiSection.refresh` rebuilds it — but only if its *Valid* flag is false, and the rebuild reuses the existing `PoiRecord` objects for positions that still have a POI, so **a repair does not reset anybody's tickets**. If it is not in storage, one is created and scanned. Both scans are short-circuited by `PoiManager.mayHavePoi`, which asks `LevelChunkSection.maybeHas` whether the palette holds any state `PoiTypes.hasPoi` recognises ([chunk anatomy](chunk-anatomy.md) has the palette), so a section of plain stone is dismissed without one block read. The *Valid* flag's codec defaults to **false**: anything not explicitly written as validated gets rescanned. ## A record appears when a block changes, sometimes a task late `Level.setBlock` calls `Level.updatePOIOnBlockStateChange` last, after the neighbour updates, and on a `ServerLevel` that override compares `PoiTypes.forState` of the old and the new state. Equal types mean nothing happens — exactly the bed case, since a bed head that gains `BedBlock.OCCUPIED` is still `PoiTypes.HOME`. Different types mean a `PoiManager.remove` for the old and a `PoiManager.add` for the new, each wrapped in a `BlockableEventLoop.execute` on the server. That wrapper exists because `WorldGenRegion.setBlock` calls the same hook from worldgen workers and the index is Server-thread-only. What it does *not* do is defer the ordinary case: `MinecraftServer.scheduleExecutables` is false when the caller is already the Server thread and not inside a task the loop is running (`ReentrantBlockableEventLoop.runningTask`), so a block placed by a player, a command or a mob during the tick body gets its record synchronously. Deferral is the worldgen and nested-task case — there the record appears a task later than the block it describes, and a read in between gets the old answer. ## The trace: a villager claims a bed ```mermaid sequenceDiagram participant SL as ServerLevel participant PM as PoiManager participant Brain as Brain participant AP as AcquirePoi participant PN as PathNavigation participant VNP as ValidateNearbyPoi participant SIB as SleepInBed Note over SL,PM: any tick, Server thread SL->>SL: setBlock puts the bed head down, forState of old and new differ SL->>PM: add, a PoiRecord with one free ticket, section marked dirty Note over Brain,SIB: Activity.CORE, any hour of the day, HOME absent Brain->>AP: priority 10, and this evaluation is due AP->>PM: findAllClosestFirstWithType HOME, HAS_SPACE, 48 blocks PM-->>AP: the nearest five past the retry cache, then validateBedPoi AP->>PN: createPath to all five at once, reach range 1 PN-->>AP: a Path whose canReach is true, getTarget is one bed AP->>PM: take at that position, acquireTicket, one free becomes zero AP->>Brain: MemoryModuleType.HOME set to a GlobalPos, entity event 14 Note over Brain,SIB: thousands of ticks later, tick 12000, Activity.REST Brain->>Brain: SetWalkTargetFromBlockMemory writes WALK_TARGET, MoveToTargetSink walks Brain->>VNP: within 16 blocks, is the record still HOME Brain->>SIB: within 2 blocks and the bed not OCCUPIED SIB->>SL: startSleeping, setBlock with BedBlock.OCCUPIED true SL->>SL: forState is HOME either way, so nothing is queued and the record is untouched Note over Brain,SIB: morning, REST leaves the brain, WakeUp clears the flag, the ticket stays ``` The scan is cheap and the path is not. `PoiManager.findAllClosestFirstWithType` turns a 48-block radius into a chunk radius of four, walks every section of those chunks, filters by `PoiManager.Occupancy.HAS_SPACE` and sorts by distance; `AcquirePoi` takes the first five and only then runs `VillagerGoalPackages.validateBedPoi`, which re-reads each block to confirm it is in `BlockTags.BEDS` and not already `BedBlock.OCCUPIED`. Then `AcquirePoi.findPathToPois` hands all five positions to `PathNavigation.createPath` as one target set, at the reach range from `PoiType.validRange` — one, for a bed. A villager's constructor raises `PathNavigation.setRequiredPathLength` to 48 so that this search can span the scan range ([goals and brains](../entities/ai-goals-and-brains.md) owns the pathfinder). Beds that failed before are held off by `AcquirePoi.JitteredLinearRetry`, whose delay is **cumulative**: each attempt adds another 40 to 79 ticks to that position's own counter, capped at `AcquirePoi.JitteredLinearRetry.MAX_RETRY_PATHFINDING_INTERVAL`, 400. A bed behind a wall is checked ever more rarely — but never as rarely as once every twenty seconds, because a marker untouched for 400 ticks is dropped on the very tick the cap would first apply, so the interval saws back to the beginning. A successful claim clears the whole cache. The claim itself is five statements. `PoiManager.take` — alone among the radius searches in having no `PoiManager.Occupancy` parameter, because it always means *HAS_SPACE* — is called with radius 1 around the path's target and a filter accepting only that exact position, and calls `PoiRecord.acquireTicket` on what it finds. Then `MemoryModuleType.HOME` is set to a `GlobalPos`, then `ServerLevel.broadcastEntityEvent` sends event 14 — the green particle burst, and the only thing an ordinary client learns of any of this. The last two statements clear the retry cache and tell the debug channel. The memory is set inside `PoiManager.getType`'s *ifPresent*, not inside `PoiManager.take`'s — the take's result is never consulted. ## After the claim: the night shift `Brain.updateActivityFromSchedule`, run from `UpdateActivityFromSchedule` at priority 99 and only when more than twenty ticks have passed since it last did anything, reads the villager's schedule *attribute* — `EnvironmentAttributes.VILLAGER_ACTIVITY` for an adult, `EnvironmentAttributes.BABY_VILLAGER_ACTIVITY` for a child — at the villager's own position, and `Timelines.VILLAGER_SCHEDULE` puts the `Activity.REST` keyframe at tick 12000 of a 24000-tick period ([environment attributes](environment-attributes-and-timelines.md) owns the mechanism; the old *Schedule* class is gone). Only then does the bed half of the brain exist at all: `VillagerGoalPackages.getRestPackage` is where `SetWalkTargetFromBlockMemory`, `ValidateNearbyPoi` for `PoiTypes.HOME` and `SleepInBed` live, while the core package validates the *job site* and not the bed. So between dawn and dusk a villager whose bed was mined keeps pointing at a position with no record, and nothing tells it otherwise. At night the three run in priority order. `SetWalkTargetFromBlockMemory` at priority 2 writes `MemoryModuleType.WALK_TARGET` whenever the bed is more than one block away in Manhattan distance — straight at it when it is nearer than 150, and at a random intermediate position when it is further — and gives up, releasing the ticket and erasing the memory, once `MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE` has stood for more than 1200 ticks. `ValidateNearbyPoi` at priority 3 does nothing at all unless the bed is within 16 blocks and in this dimension: then it erases the memory if `PoiManager.exists` no longer agrees on the type, and if the bed is `BedBlock.OCCUPIED` and this villager is not itself the sleeper, it erases the memory and releases the ticket — unless some villager is asleep in that block, in which case the memory goes and the ticket stays, the sleeper being presumed to hold it. `SleepInBed`, also priority 3, needs the villager within 2 blocks, the bed unoccupied, and `SleepInBed.COOLDOWN_AFTER_BEING_WOKEN` ticks since `MemoryModuleType.LAST_WOKEN`. It calls `LivingEntity.startSleeping`, which is what actually sets the flag, and then records `MemoryModuleType.LAST_SLEPT` and erases the walk target itself. Morning ends it twice over: `WakeUp`, at priority 0 in the core package, calls `LivingEntity.stopSleeping` the instant `Activity.REST` goes inactive, and `SleepInBed.stop` does the same when the behaviour ends. Either way the flag clears and the ticket does not. ## What makes a village `PoiManager.DistanceTracker` is a `SectionTracker` — the same `DynamicGraphMinFixedPoint` flood the ticket system's two graphs use ([tickets and loading](tickets-and-loading.md)), and the only one of the five outside `server/level` — over chunk sections instead of chunks. Its sources are the sections where `PoiManager.isVillageCenter` holds: at least one record whose type is in `PoiTypeTags.VILLAGE` and whose `PoiManager.Occupancy` is *IS_OCCUPIED*. They sit at level 0 and the flood runs out to `PoiManager.MAX_VILLAGE_DISTANCE`, six sections, past which the level is simply absent from the map. **An unclaimed bed makes no village.** A hundred empty beds are a hundred records and zero sources; one villager taking one ticket lights the section up. `PoiManager.setDirty` and `PoiManager.onSectionLoad` re-seed the source, `PoiManager.tick` settles the flood every tick, and `PoiManager.sectionsToVillage` settles it again before answering. `ServerLevel.isVillage` is that distance being one section or less; `ServerLevel.isCloseToVillage` takes the distance as an argument and refuses anything past six. That boolean is load-bearing far outside this system. `BadOmenMobEffect` starts a raid only where `ServerLevel.isVillage` is true, `VillageSiege` needs one to put zombies in, `PatrolSpawner` refuses to spawn a patrol within two sections of one and `CatSpawner` insists on being within two. `Raid` re-checks it as the raid runs, and `Raids` sets the raid's centre to the average position of the occupied `PoiTypeTags.VILLAGE` records within 64 blocks. ## Everyone else who reads the index | who | what it asks for | radius | `PoiManager.Occupancy` | |---|---|---|---| | `PortalForcer.findClosestPortalPosition` | `PoiTypes.NETHER_PORTAL`, after `PoiManager.ensureLoadedAndValid` drags the sections in | 16 going to the Nether, 128 coming back | *ANY* | | `Bee` | `PoiTypeTags.BEE_HOME`, then `Bee.doesHiveHaveSpace` for the real occupancy | 20 | *ANY* | | `ServerLevel.findLightningRod` | `PoiTypes.LIGHTNING_ROD` standing at the surface height | 128 | *ANY* | | `LodestoneTracker` | one position — is `PoiTypes.LODESTONE` still there | — | — | | `LocateCommand` | `PoiManager.findClosestWithType` for a type or a tag | 256 | *ANY* | | `Raids` | the records it averages into a raid's centre | 64 | *IS_OCCUPIED* | | `CatSpawner` | more than four claimed `PoiTypes.HOME` nearby | 48 | *IS_OCCUPIED* | | `WanderingTraderSpawner` | a `PoiTypes.MEETING` near a player, to arrive at | 48 | *ANY* | | `NearestBedSensor` | `PoiTypes.HOME` for `MemoryModuleType.NEAREST_BED`, babies only, no ticket taken | 48 | *ANY* | `PoiManager.ensureLoadedAndValid` in the first row is the only caller that forces loading rather than tolerating what is in memory: for any nearby section missing or invalid it pulls the chunk in at `ChunkStatus.EMPTY`, once per chunk per server run since `PoiManager.loadedChunks` never forgets. The index is on the debug channel too — `LevelDebugSynchronizers.registerPoi`, `LevelDebugSynchronizers.updatePoi` and `LevelDebugSynchronizers.dropPoi` feed `DebugSubscriptions.POIS` and `DebugSubscriptions.VILLAGE_SECTIONS`. ## Questions players ask **Why is a villager sleeping in the bed I built for someone else?** Because the claim is a number in a file and the bed is a block, and neither knows the other. `PoiManager.take` decremented a counter 48 blocks away hours before anyone walked anywhere, and `SleepInBed` only ever checks that the bed is *not currently occupied* — never who holds the ticket. **Why do villagers stop breeding when I take a bed away?** `VillagerMakeLove.tryToGiveBirth` calls `VillagerMakeLove.takeVacantBed` first — a `PoiManager.take` for `PoiTypes.HOME` within 48 blocks, filtered by reachability. No free ticket, no baby, and the pair get entity event 13 instead. If the birth then fails the ticket is released; if it succeeds, `VillagerMakeLove.giveBedToChild` writes the baby's `MemoryModuleType.HOME` directly, with no `AcquirePoi` involved. This is the raw `PoiManager.take`, which finds the first reachable match in section order rather than the nearest, so the baby's bed need not be the closest. **Why does the same bed get claimed twice after I break and replace it?** Breaking it runs `PoiManager.remove` and the record ceases to exist with its ticket count — no release, no notification. The villager keeps its `GlobalPos` until `ValidateNearbyPoi` next runs, which needs `Activity.REST` active *and* the villager within 16 blocks. Replace the bed first and there is a brand-new record with a full ticket, claimable by anyone — including the villager that thought it already had one. ## Where to look `PoiTypes.bootstrap` · `PoiRecord.acquireTicket` · `PoiSection.refresh` · `PoiManager.add` · `PoiManager.take` · `PoiManager.release` · `PoiManager.getInRange` · `PoiManager.checkConsistencyWithBlocks` · `ServerLevel.updatePOIOnBlockStateChange` · `AcquirePoi.create` · `VillagerGoalPackages.getCorePackage` · `VillagerGoalPackages.getRestPackage` · `ValidateNearbyPoi.create` · `SleepInBed.start` · `Villager.releasePoi` · `PoiManager.DistanceTracker` · `ServerLevel.isVillage` The other index in this corner of the tree — the fire-and-forget broadcast sculk sensors listen to — is [game events and vibrations](game-events-and-vibrations.md). The brain belongs to [goals and brains](../entities/ai-goals-and-brains.md), death and conversion to [the entity lifecycle](../entities/entity-lifecycle.md), the bed block to [blocks and states](../blocks/blocks-and-states.md). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # V · Blocks > Verified against **Minecraft 26.2** · Part V · Everything that happens at the moment one block state replaces another: the click that asks for it, the write that performs it, and the four kinds of block that answer back. Part IV built the container. This part is about what is *in* it, and it has one moment at its centre: a position in a chunk section stops holding one block state and starts holding another. Every page here is about choosing that state, about the write itself, about a block that answers a write near it, or — once — about the state a position cannot hold at all. A player recognises the part by how it *feels* — the block that appears under the crosshair before the server has heard about it, the door whose top half swings with the bottom, the redstone lamp that does not light until the server says so. The first of those is a prediction and Part X owns it. The other two come from one distinction, and it is this part's: **there are two entirely different ways a block hears that its neighbour changed, and the client runs only one of them.** ## The shape of the part Part V is a hub and six spokes. The hub is `blocks-and-states`, and what the spokes reach back into it for is not the state table — it is the tail of a write, drawn there once as a flowchart and linked to from everywhere else. ```mermaid flowchart TD BS["Blocks and states — the table, and what a write actually does"] BI["Block interaction — the right click"] BB["Block breaking — the left click"] BE["Block entities — when a state is not enough"] SD["Signal and dust — reading power, and the cascade"] PE["Pistons and block events — a change that waits for a phase"] DO["Diodes and the observer — a change that books a turn"] BS -- "the shape channel, which the client runs too" --> BI BS -- "what a flags-3 write does after the section is written" --> BB BS -- "where a block entity is created, kept, replaced, removed" --> BE BS -- "the neighbour channel, which is the server's alone" --> SD BS -- "the flag word, and which bits the placeholders leave out" --> PE BS -- "a flag-2 write, and the onPlace that runs inside it anyway" --> DO BI -- "one lecture in two halves, one prediction ledger" --> BB SD -- "what powers a piston, and how the wire connects to it" --> PE SD -- "what a diode reads, and what reads a diode" --> DO BE -- "the one int redstone keeps outside a block state" --> DO BE -- "the placeholder's entity carries a whole state" --> PE ``` ## Before you start [Chunk anatomy](../world/chunk-anatomy.md), because the first half of every write in this part is a section write with four heightmaps and a light check behind it, and [the level tick](../server/server-level-tick.md), because half of this part's surprises are really claims about which phase of a tick something ran in — with [the server tick](../server/server-tick.md) behind it, for the two claims this part makes about what happens *outside* the level tick: that a packet handler runs before the levels do, and that a connection is flushed after they have. Two more Part IV pages are load-bearing here rather than merely adjacent: [scheduled ticks](../world/scheduled-ticks.md) is how a block gets a turn *later*, which is the whole of the diode lecture, and [fluids](../world/fluids.md) owns the `FluidState` that shares a `StateHolder` with every block state, and the waterlogging this part keeps writing around. One dependency runs the other way. [Prediction and acknowledgement](../client/prediction-and-acks.md) is Part X, and the two click lectures here use three of its six windows between them — but its own scenario is a block placed against a wall, which needs this part's vocabulary. So watch Part V first: both click pages open with the same four-sentence statement of the contract, which is all either lecture needs, and the machinery keeps until Part X. ## Watch in this order 1. [Blocks and states](blocks-and-states.md) — a right-click on stone puts one of oak stairs' eighty states into the world. Every state the game will ever have was built before the world was, and the world stores an index into that table. The second half — what a write does after the section has been written — is the figure the other six lectures point back at. 2. [Block interaction](block-interaction.md) — the right click, in full: a door opened by hand. It fires no neighbour update at all, and the top half follows anyway. 3. [Block breaking](block-breaking.md) — the same lecture's other half: two clocks that agree without exchanging a packet. Let go too early and the block comes back, then vanishes again, and nothing short of the block itself going away stops it. 4. [Block entities](block-entities.md) — a furnace smelts while nobody is looking. It tells nobody anything: the fire is a block state, the arrow is four ints from a menu, and both are a tick late by construction. 5. [Signal and dust](signal-and-dust.md) — a lever, two dust, and the cascade. A line turning off is visited once for every intermediate value it passes through, none of which is ever sent to anybody, and the game ships a second implementation behind a flag that does not do it at all. 6. [Pistons and block events](pistons-and-block-events.md) — the part's deferral with no delay in it: the work waits for one named phase of the level tick rather than for a number of ticks, and usually gets it in the same tick. Also the one place the client is handed a re-simulation instead of a result: no block update is ever sent for the moving blocks. 7. [Diodes and the observer](diodes-and-observers.md) — the part's closer. Three blocks that read their neighbours three different ways, and the one whose entire job is noticing change turns out not to be listening on the channel that carries it. ## Reference this part uses [Block update flags](../../reference/block-update-flags.md) — the ten bits of `Level.setBlock`'s flag word and what reads each. [Registries](../../reference/registries.md) — `Registries.BLOCK` and `Registries.BLOCK_ENTITY_TYPE` are two of its rows. [Packets](../../reference/packets.md) — every block update, block event and acknowledgement in one table. [Data components](../../reference/components.md) — `DataComponents.TOOL`, which decides how fast a stack mines and whether the block drops. [Game rules](../../reference/gamerules.md). [Math and primitives](../../reference/math-and-primitives.md) — `BlockPos`, `Direction` and the packings every page here assumes. [Diagram lanes](../../reference/lanes.md). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Blocks and states > Verified against **Minecraft 26.2** · Part V · A player right-clicks the top of a stone block holding oak stairs, and one of the stair's eighty pre-built states goes into the world. You are standing on stone with a stack of oak stairs, and you right-click the top of the block. A moment later a stair is up there, facing away from you, sitting on the bottom half of its cube. Nothing was constructed to make that happen. Oak stairs have four properties — *facing*, *half*, *shape*, *waterlogged* — and all eighty combinations of them were built before any world existed, in the class initialiser of `Blocks`, and numbered into one flat table, `Block.BLOCK_STATE_REGISTRY`. What a chunk stores is an index into that table. Both of the surprises on this page fall out of that single decision. Choosing a property allocates nothing: `StateHolder.setValue` reads one cell out of a table of neighbours computed at startup and hands back a state that already existed. And the index is not always checked: `Block.getId` answers **0** for a state its table has never seen, and `Block.stateById` answers `Blocks.AIR`'s default state for a number it does not know — so wherever the game reaches the table through that pair, a state the two sides disagree about raises nothing at all. It quietly becomes air. ## The cast | class | what it decides | thread | |---|---|---| | `Block` | one kind of thing: which properties it has, what its default state is, and — through fifty-eight statics — the drops, the particles and the shape-update helpers the rest of the game calls | built at class-initialisation, read from every thread after | | `BlockBehaviour` | every hook a block may override, from `BlockBehaviour.onPlace` to `BlockBehaviour.updateShape`. `Block` extends it and adds registration | as above | | `BlockBehaviour.Properties` | hardness, sound, map colour, whether it ticks — and the `ResourceKey` without which no block can be built at all | kept by the `BlockBehaviour` constructor and read from thereafter; hardness and map colour are never copied out | | `StateDefinition` | the table: which properties this block has, in which order, and the full product of their values | built in the `Block` constructor | | `Property` | one axis — a name, a value type, and where a value sits in that axis | immutable, shared between blocks | | `StateHolder` | one state's property values, and the table that answers *what state am I if this property becomes that value* | filled once by `StateDefinition`, read-only after | | `BlockBehaviour.BlockStateBase` | everything a state can answer without going to the block, and the caches that make collision and occlusion cheap | half-built in its constructor, finished by `BlockBehaviour.BlockStateBase.initCache` | | `Block.BLOCK_STATE_REGISTRY` | the integer a state is on the wire and in a section's global palette — never on disk, where a state is its name and its properties | appended once per state, in the `Blocks` class initialiser | ## Twelve classes and one Cartesian product ```mermaid flowchart TB PROPS["BlockBehaviour.Properties: the builder. Useless until setId hands it a ResourceKey"] BB["BlockBehaviour: 1,357 lines of overridable hooks"] BLOCK["Block: 643 lines, mostly statics, plus one state table and one default state"] PROP["Property: a name, a value type, and getInternalIndex"] BOOL["BooleanProperty: exactly two values, true at index 0"] INT["IntegerProperty: min to max, min never below zero"] ENUM["EnumProperty: any StringRepresentable enum, ordinalToIndex for the lookup"] SD["StateDefinition: propertiesByName sorted by name, states the full Cartesian product"] SH["StateHolder: propertyKeys, propertyValues, and the neighbors table"] BSB["BlockBehaviour.BlockStateBase: every hook a state answers, and the caches"] BS["BlockState: twenty lines. A constructor, asState, and CODEC"] REG["Block.BLOCK_STATE_REGISTRY: an IdMapper over every state of every block"] PROPS -- "kept by the BlockBehaviour constructor and read from thereafter" --> BB BB -- "extended by" --> BLOCK PROP -- "extended by, and only by these three" --> BOOL PROP --> INT PROP --> ENUM PROP -- "collected by StateDefinition.Builder.add" --> SD BLOCK -- "builds exactly one, in its own constructor" --> SD SD -- "one object per cell of the product, built once and never again" --> BS SH -- "extended by" --> BSB BSB -- "extended by" --> BS SD -. "fillNeighborsForState fills each state's neighbors, property index by value index" .-> SH BS -- "added in registry order by the Blocks class initialiser, then initCache" --> REG ``` ### The kind, three classes deep A *block* is a kind of thing — oak stairs, stone, water. A *block state* is one exact configuration of that kind, and it is a block state, never a block, that a chunk section stores, that a packet carries, that a model is chosen for. The kind is spread over three classes. `BlockBehaviour` is the base and holds the hooks; `Block` extends it and adds the registry holder, the state table and the statics everything else in the game reaches for. Both are constructed from a `BlockBehaviour.Properties`, a builder that must first be given an identity: `BlockBehaviour.Properties.setId` supplies the `ResourceKey`, the loot table and the translation key are derived from it, and the `BlockBehaviour` constructor throws *Block id not set* without one. So a block cannot be built from `BlockBehaviour.Properties.of` outside `Blocks.register`, which takes the id from `BlockItemIds` or `BlockIds` and hands it to the builder on the way past. None of that is data: `BlockBehaviour.Properties.CODEC` is a unit codec, so `Block.CODEC` is a constructor dispatch and hardness, sound and map colour never serialise. ### The table, sorted by name Each `Block` constructor calls its own `Block.createBlockStateDefinition`, collecting properties through `StateDefinition.Builder.add` — which rejects a name outside lower-case, digits and underscore, a *value* name that breaks the same pattern, a property with fewer than two values, and a duplicate name — and then `StateDefinition.Builder.create` builds every state the block will ever have. Zero properties gives one singleton state, one property gives a row, and two or more gives the full Cartesian product of every property's values, each cell constructed through a `StateDefinition.Factory` which for blocks is the `BlockState` constructor. **Eighty** — the states of oak stairs: four facings, two halves, five shapes, two waterlogged values, every one of them a distinct object built before any world existed. `StateDefinition.propertiesByName` is a sorted map, so the axes are ordered by property *name*, not by the order the block added them — for stairs the two orders happen to coincide, at *facing, half, shape, waterlogged*. Two things follow. The order of the global state ids follows it, because the product is built by walking that map. And so does the field order of `BlockState.CODEC` and `StateDefinition.propertiesCodec` — which is not the same as saying a state is written alphabetically anywhere: NBT is a hash map on disk, and the one alphabetical form is the *command* text, which `BlockStateParser` builds without going near the codec. And `StateDefinition.any` is the first cell of the product, which the `Block` constructor installs as the default state unless the block calls `Block.registerDefaultState` itself. Since `BooleanProperty.VALUES` lists *true* before *false*, a block that does not override its default gets *true* for every boolean it has. That is why `StairBlock` sets `StairBlock.WATERLOGGED` to false explicitly: the alternative is stairs that are born full of water. There are exactly three concrete kinds of `Property` and no *DirectionProperty* — facing is an `EnumProperty` over `Direction`. `BlockStateProperties` is the shared pool of **124** of them, and several share a serialised name while being different objects: `BlockStateProperties.FACING`, `BlockStateProperties.FACING_HOPPER` and `BlockStateProperties.HORIZONTAL_FACING` are all *facing* on disk. ### The state, a twenty-line leaf `StateHolder` is the generic state, shared with `FluidState` ([fluids](../world/fluids.md)). It holds its owner, two parallel arrays of property keys and values, and `StateHolder.neighbors` — a two-dimensional table, property index by value index, answering *what state am I if this property becomes that value*. `StateHolder.setValue` walks the key array comparing references to find the row, asks `Property.getInternalIndex` for the column, and returns the object already sitting in that cell. It allocates nothing and it never constructs. The table is installed once by `StateHolder.initializeNeighbors`, and a second call throws. Because every state is built once, `StateHolder.equals` is final and identity-based: two states are the same only if they are the same object. `BlockBehaviour.BlockStateBase` extends it and is the state-to-block hop — `BlockBehaviour.BlockStateBase.getShape`, `BlockBehaviour.BlockStateBase.canSurvive` and the rest each forward to the owning block with the state as the first argument. It is also where the caches live, and they arrive in two waves. Its constructor copies the flat values out of the block's `BlockBehaviour.Properties`. Everything that has to ask a *virtual* question — the fluid state, whether it random-ticks, the occlusion shape and its six faces, sky-light propagation, light dampening, and the `BlockBehaviour.BlockStateBase.Cache` of collision shape and sturdy faces built for every block without a dynamic shape — is filled later, by `BlockBehaviour.BlockStateBase.initCache`, because those questions may look at other blocks and so cannot be answered until every block exists. `BlockState` itself is **twenty lines**: a constructor, a `BlockState.asState` that returns *this*, and `BlockState.CODEC`. It exists so the generic plumbing has a concrete type to name. `BlockBehaviour.BlockStateBase` is the class people mean when they say *block state*. The `Blocks` class initialiser is that second wave and the only caller of `BlockBehaviour.BlockStateBase.initCache`: it walks `BuiltInRegistries.BLOCK`, adds each state to `Block.BLOCK_STATE_REGISTRY` and finishes it. Note what makes the result safe to share between the server thread, the client thread, the chunk workers and the meshing pool — it is **not** immutability, because those cached fields are non-final and written long after the constructor. It is that the writes happen inside a class initialiser, and every thread that later reaches a `BlockState` reaches it through `Blocks`. ## Four decisions, four lookups `BlockItem.getPlacementState` asks the block for a state and refuses if it cannot have one. The default `Block.getStateForPlacement` returns the block's default state; `StairBlock` overrides it and makes four decisions, each of them one `StateHolder.setValue` into the table above. `StairBlock.FACING` is `UseOnContext.getHorizontalDirection`, which is `Entity.getDirection` — the way the player is *facing*, so the tall side ends up away from them. `StairBlock.HALF` is `Half.BOTTOM` when the clicked face is the top, `Half.TOP` when it is the bottom, and otherwise decided by whether the hit point is in the upper or lower half of the clicked block. `StairBlock.WATERLOGGED` is whether the fluid already at the target position is `Fluids.WATER`. Then `StairBlock.SHAPE` is computed by `StairBlock.getStairsShape` from the *partly built* state: it looks at the neighbour in the direction the stair faces, and a stair there of the same half with a perpendicular facing gives `StairsShape.OUTER_LEFT` or `StairsShape.OUTER_RIGHT`; failing that it looks at the neighbour in the opposite direction for `StairsShape.INNER_LEFT` or `StairsShape.INNER_RIGHT`; failing both, `StairsShape.STRAIGHT`. In each case `StairBlock.canTakeShape` vetoes the corner if the stair on the far side is already aligned with this one. The same routine runs again in `StairBlock.updateShape` every time a horizontal neighbour changes, which is how a straight stair turns into a corner when you build next to it. Everything in front of that — the click, the reach check, the block-then-item ordering, the packet and the ack — belongs to [block interaction](block-interaction.md) and [prediction and acks](../client/prediction-and-acks.md). One sentence of it matters here: the client runs the identical `BlockItem.place` under a prediction, so the write below happens twice, once on each side, from the same code. Almost everything that differs is inside the write; what `BlockItem.place` itself does differently afterwards is to skip the block-entity tag and the advancement trigger on the client, and the state that lands is not affected by either. `BlockItem.placeBlock` calls `Level.setBlock` with flags **11**, `Block.UPDATE_ALL_IMMEDIATE`. ## The two update channels This is the shape the rest of Part V refers back to. A write is two half-writes with a re-read between them: `LevelChunk.setBlockState` changes the world and runs the side effects that belong to the *position*, then `Level.setBlock`'s tail runs the side effects that belong to the *neighbourhood* — and only if the state it reads back is the one it asked for. ```mermaid flowchart TB IN["Level.setBlock. Refuses a position out of bounds, and refuses everything on the server side of a debug world"] IN --> SEC subgraph CHUNK["inside LevelChunk.setBlockState"] SEC["write the section"] NOOP{"was the section all air and the state air, or is that exact state already there"} HM["update the four live heightmaps: MOTION_BLOCKING, MOTION_BLOCKING_NO_LEAVES, OCEAN_FLOOR, WORLD_SURFACE. The two worldgen ones are not touched"] LIGHT["if the section's emptiness flipped, tell the light engine and the chunk source. If the light properties differ, update the sky-light sources and queue LevelLightEngine.checkBlock"] PRE["server only, flag 256 clear, and only when the block changed and the new state does not keep the old block entity: BlockEntity.preRemoveSideEffects. The removal itself runs on both sides"] AFT["server only, flag 1 set or flag 64 set, and only when the block changed or the new block is a rail: affectNeighborsAfterRemoval"] GUARD{"is the block at that position still the one just written"} ONP["server only, flag 512 clear: BlockBehaviour.BlockStateBase.onPlace"] BE["create, keep or replace the block entity, then ChunkAccess.markUnsaved"] NOTHING["return nothing"] SEC --> NOOP NOOP -- "yes" --> NOTHING NOOP -- "no" --> HM HM --> LIGHT --> PRE --> AFT --> GUARD GUARD -- "no" --> NOTHING GUARD -- "yes" --> ONP --> BE end NOTHING --> FALSE["Level.setBlock returns false"] BE --> READ{"re-read the position: is it the state we wrote"} READ -- "no" --> TRUE["Level.setBlock returns true, having skipped its entire tail"] READ -- "yes" --> DIRTY subgraph TAIL["back in Level.setBlock"] DIRTY["Level.setBlocksDirty. Empty on Level, on the client a re-mesh through LevelExtractor.setBlockDirty"] SEND["flag 2, plus flag 4 clear on the client, plus a chunk at FullChunkStatus.BLOCK_TICKING or better on the server: Level.sendBlockUpdated"] NB["flag 1: Level.updateNeighborsAt, and on the server also updateNeighbourForOutputSignal when the new state has an analog output"] SHAPE["flag 16 clear and updateLimit still positive, with flags 1 and 32 masked out of what it passes on: three shape passes, indirect for the old state, direct for the new, indirect for the new"] POI["Level.updatePOIOnBlockStateChange"] DIRTY --> SEND --> NB --> SHAPE --> POI end POI --> TRUE ``` ### Inside the chunk write The section write, the four heightmaps and the light checks are the same on both sides — that is [chunk anatomy](../world/chunk-anatomy.md)'s territory. Three things after them are not. `BlockEntity.preRemoveSideEffects` is the block entity's last word before it is unregistered — the chest scattering its contents, say. It needs the server, and it needs `Block.UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS` clear; the removal that follows it happens either way, on both sides ([block entities](block-entities.md)). `BlockBehaviour.BlockStateBase.affectNeighborsAfterRemoval` is how the outgoing block tells its neighbours it is gone — a piston head taking its base, a broken lever telling the block it powered. It is *not* how a door drops its other half: `DoorBlock` does not override it, and the top half goes down the shape channel instead ([block interaction](block-interaction.md)). It is easy to put in the wrong place: it runs **inside** the chunk write, before the new state is even confirmed, not in `Level.setBlock`'s tail with the other neighbour work. It needs the server, it needs `Block.UPDATE_NEIGHBORS` set *or* the moved-by-piston bit, and it also needs the *block* to have changed — a write that only changes a property does not fire it, unless the new block is a `BaseRailBlock`. Then the chunk re-reads its own section. If a side effect has already replaced what was just written, `LevelChunk.setBlockState` returns nothing at all and `Level.setBlock` reports false. Otherwise `BlockBehaviour.BlockStateBase.onPlace` runs — server-side, with `Block.UPDATE_SKIP_ON_PLACE` clear — and the block entity is created, kept or replaced. A block entity that disagrees with the new state is logged as *mismatched* and thrown away. ### Back in Level.setBlock's tail The first two steps of the tail are how the change becomes visible. `Level.setBlocksDirty` is empty on `Level` itself; on the client it reaches `LevelExtractor.setBlockDirty`, which re-meshes only if `ModelManager.requiresRender` says the two states look different. The broadcast that follows is gated on `Block.UPDATE_CLIENTS`, and then on opposite conditions per side: the client also needs `Block.UPDATE_INVISIBLE` clear, the server also needs the chunk to be at `FullChunkStatus.BLOCK_TICKING` or better, so a write into a chunk that is loaded but not yet simulating tells nobody. Worldgen is silent for a different reason again: it never reaches `Level.setBlock` at all, writing through `WorldGenRegion` instead. The last three are the two update channels proper, and the difference between them is the fact the rest of this part rests on: **Neighbour updates are server-only.** `Level.updateNeighborsAt` and `Level.neighborChanged` are empty methods on `Level`, overridden only by `ServerLevel`. Gated on `Block.UPDATE_NEIGHBORS`, the server hands the position to its `CollectingNeighborUpdater`, which visits the six neighbours in `NeighborUpdater.UPDATE_ORDER` — west, east, down, up, north, south — calling each one's `BlockBehaviour.neighborChanged`. Beside it, `Level.updateNeighbourForOutputSignal` reaches the comparators in the four horizontal directions, directly or through one redstone conductor ([signal and dust](signal-and-dust.md)). **Shape updates run on both sides.** `Level.neighborShapeChanged` is implemented on `Level`, and both a `ServerLevel` and a `ClientLevel` own a `CollectingNeighborUpdater`. Unless `Block.UPDATE_KNOWN_SHAPE` is set, `Level.setBlock` runs three passes with a decremented limit and with `Block.UPDATE_NEIGHBORS` and `Block.UPDATE_SUPPRESS_DROPS` masked out of the flags it propagates: `BlockBehaviour.BlockStateBase.updateIndirectNeighbourShapes` for the *old* state, then `BlockBehaviour.BlockStateBase.updateNeighbourShapes` for the new, then the indirect pass again for the new. The middle one is the familiar one: six neighbours in `BlockBehaviour.UPDATE_SHAPE_ORDER` — west, east, north, south, down, up, a *different* order from the neighbour channel — each asked for a new state through `BlockBehaviour.BlockStateBase.updateShape` and then handed to `Block.updateOrDestroy`. The indirect passes are the hook a block uses to reach past its six neighbours. `Block.UPDATE_LIMIT`, 512, is the budget that stops the cascade. And there is the catch. `Block.updateOrDestroy` writes the new state on either side — but when the new state is air its destroy branch is server-gated, going through `Level.destroyBlock`. So a shape update that turns a block into nothing deletes it on the server and does nothing at all on the client, which then waits to be told. ### The flag word The flag word is `Level.setBlock`'s third argument — its last, except on the four-argument overload that takes an update limit after it. It is a bit set, tagged in signatures by `Block.UpdateFlags`, an annotation that carries no values of its own. The flowchart above names its bits by number; the ten bits, what reads each one and the four named combinations are in [block update flags](../../reference/block-update-flags.md). Placement's **11** is `Block.UPDATE_ALL_IMMEDIATE`, and `Block.UPDATE_LIMIT` is also 512 without being a bit at all — it is the default recursion budget for the shape cascade. ## Questions players ask **Why did `Level.setBlock` say false when the block is right there?** It returns true whenever the chunk accepted the write, even if the state was changed again immediately afterwards and the whole tail was skipped. It returns false from three statements: a position out of bounds, the server side of a debug world, and the chunk write coming back with nothing. That last one has three causes of its own, and only the third is a real failure — writing air into a section that holds only air, writing the state that is already there (states being interned, that is an identity comparison), or a side effect inside the chunk write having replaced the block before it could be confirmed. The first two are the common ones. **Why does my property lookup throw when the property looks identical?** Because states match properties by *identity* and properties match each other by *value*. `StateHolder.setValue` compares `Property` references with `==`, while `Property.equals` compares the value class and the name — refined by `IntegerProperty` and `EnumProperty` to compare the value list too. So two separately constructed properties can be equal to one another and still make `StateHolder.setValue` throw *Cannot set property … as it does not exist*. Use the `BlockStateProperties` constant, not a look-alike. **Does an unknown block state really become air?** For `Block.getId` and `Block.stateById`, yes, and that is the pair behind block-break particles, the falling-block spawn packet and `EntityDataSerializers.OPTIONAL_BLOCK_STATE`. It is not universal. `ClientboundBlockUpdatePacket.STREAM_CODEC` reads the same table through `ByteBufCodecs.idMapper`, which uses `IdMap.byIdOrThrow` and fails the connection instead, and `ClientboundSectionBlocksUpdatePacket` decodes with `IdMapper.byId`, which answers null. The tolerant lookup is a property of the two static methods, not of the id. ## Where to look `Blocks.register` · `BlockBehaviour.Properties.setId` · `StateDefinition.Builder.create` · `StateDefinition.StateCollection.fillNeighborsForState` · `StateHolder.setValue` · `StateHolder.neighbors` · `BlockBehaviour.BlockStateBase.initCache` · `Block.BLOCK_STATE_REGISTRY` · `StairBlock.getStateForPlacement` · `StairBlock.getStairsShape` · `BlockItem.placeBlock` · `LevelChunk.setBlockState` · `Level.setBlock` · `Block.updateOrDestroy` · `NeighborUpdater.executeShapeUpdate` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Block interaction > Verified against **Minecraft 26.2** · Part V · A player right-clicks the bottom half of an oak door, and the top half opens without a single neighbour update. You are standing in front of a closed oak door, crosshair on its bottom half, and you press the use key. Before the tick is over the door is open on your screen, both halves of it, and a packet is on its way to a server that has not yet been asked. The obvious guess about how the top half found out is wrong. Opening a door fires **no neighbour updates at all** — `DoorBlock` writes with flags 10, and the neighbour bit is not among them — and the top half follows anyway, down the *shape* channel, which is the half of the update machinery the client also runs. That is why a door feels instant on a laggy server and a redstone lamp does not. > **The contract both halves run under.** The client acts at once and remembers the state it overwrote, under a sequence number it sends with the action. The server's `ClientboundBlockChangedAckPacket` is a receipt for that number and *not* a verdict — it is sent for actions the server refused exactly as for actions it allowed — and correctness comes from ordering instead: any correction the server means to send travels in the same tick and earlier in the stream than the receipt. A correction *replaces* what the client remembered rather than being weighed against it, so when the receipt arrives the client writes back whatever the entry now holds — and only where that differs from what is on screen. [Prediction and acknowledgement](../client/prediction-and-acks.md) owns that machinery; this page and [block breaking](block-breaking.md) are its two applications. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | that a use-key press becomes one `Minecraft.startUseItem`, and that the main hand is tried before the off hand | Render | | `MultiPlayerGameMode` | the client's copy of the whole decision, wrapped in one prediction | Render | | `InteractionResult` | whether the caller stops here, who swings, and what the hand ends up holding | a record, no thread | | `BlockBehaviour.BlockStateBase` | which of the two block hooks a state answers with, and when its six neighbours are asked to re-fit | either side | | `DoorBlock` | whether this door opens by hand, what it writes, and what the other half becomes | either side | | `CollectingNeighborUpdater` | the order queued updates run in, and where a runaway cascade is cut | whichever thread wrote the block | | `ServerGamePacketListenerImpl` | the gate list the packet must pass, and what each refusal answers with | Server | | `ServerPlayerGameMode` | the same inner order as the client, plus the advancement triggers | Server | ## The whole click, both sides ```mermaid sequenceDiagram participant MC as Minecraft participant MPGM as MultiPlayerGameMode participant CL as ClientLevel participant DB as DoorBlock participant CNU as CollectingNeighborUpdater participant SGPL as ServerGamePacketListenerImpl participant SL as ServerLevel Note over MC,CNU: one client tick, all of it before the packet leaves MC->>MPGM: startUseItem, main hand first, useItemOn with the BlockHitResult MPGM->>MPGM: startPrediction opens sequence n MPGM->>DB: useItemOn returns TRY_WITH_EMPTY_HAND, so useWithoutItem runs DB->>CL: canOpenByHand, cycle OPEN, setBlock on the lower half with flags 10 CL->>CNU: updateNeighbourShapes, six directions, limit 511 CNU->>DB: updateShape on the upper half, direction DOWN CNU->>CL: updateOrDestroy writes the upper half, flags 10, limit 511 DB->>CL: playSound with the clicker as except, so only they hear it MPGM->>SGPL: ServerboundUseItemOnPacket carrying hand, hit and n MC->>SGPL: ServerboundSwingPacket, because SUCCESS swings on the client Note over SGPL,SL: server tick, packets drained before the levels tick SGPL->>SGPL: ackBlockChangesUpTo n, then reach, hit box, height, spawn protection SGPL->>DB: ServerPlayerGameMode.useItemOn, the same inner order DB->>SL: setBlock lower half, then the shape pass writes the upper half DB->>SL: playSound to everyone but the clicker, gameEvent BLOCK OPEN SGPL-->>CL: ClientboundBlockUpdatePacket, clicked position and face neighbour Note over SGPL,SL: still this tick, levels tick, then connections tick SL-->>CL: ClientboundSectionBlocksUpdatePacket, both halves in one section SGPL-->>CL: ClientboundBlockChangedAckPacket for n CL->>CL: endPredictionsUpTo n, both halves already agree, nothing is written ``` ## One press, one hand at a time `Minecraft.handleKeybinds` runs from the client tick and only when no screen and no overlay is open. Every queued press of the use key becomes its own `Minecraft.startUseItem`, unthrottled; `Minecraft.rightClickDelay` gates the held-down auto-repeat alone, and `Minecraft.startUseItem` is what sets it, to four ticks, and only when `MultiPlayerGameMode.isDestroying` is false. A player already using an item (drawing a bow, eating) never reaches that branch at all: the queued presses are drained and discarded. Inside, after `LocalPlayer.isHandsBusy`, the hands are tried in the order `InteractionHand.MAIN_HAND` then `InteractionHand.OFF_HAND`. Each hand's stack must pass `ItemStack.isItemEnabled` — a disabled item aborts the whole loop, not just its own hand — and then, for a `BlockHitResult`, the hand goes to `MultiPlayerGameMode.useItemOn`. A `InteractionResult.Success` or an `InteractionResult.Fail` ends the loop; only an `InteractionResult.Pass` falls through to `MultiPlayerGameMode.useItem` (right-click air, `ServerboundUseItemPacket`) and then to the off hand. The door returns success on the main hand, so the off hand is never asked. `MultiPlayerGameMode.useItemOn` sends a `ServerboundSetCarriedItemPacket` first if the hotbar selection has moved (`MultiPlayerGameMode.ensureHasSentCarriedItem`), refuses outright if the target is outside the world border, and otherwise opens the prediction with `MultiPlayerGameMode.startPrediction`, which allocates sequence *n*, runs the whole client-side interaction inside it, and sends the `ServerboundUseItemOnPacket` the interaction returned. ## Block, then empty hand, then item `MultiPlayerGameMode.performUseItemOn` and `ServerPlayerGameMode.useItemOn` run the same three-step order. First the block is offered the item: `BlockBehaviour.BlockStateBase.useItemOn`, whose `BlockBehaviour.useItemOn` default answers `InteractionResult.TRY_WITH_EMPTY_HAND`. That sentinel — and **only when the hand is `InteractionHand.MAIN_HAND`** — routes to `BlockBehaviour.BlockStateBase.useWithoutItem`, whose `BlockBehaviour.useWithoutItem` default is `InteractionResult.PASS`. If the block consumed nothing, the item gets its turn through `ItemStack.useOn`, provided the stack is non-empty and not held back by `ItemCooldowns.isOnCooldown`. Sneaking skips the first two steps, but only when *some* hand holds something: the guard is `Player.isSecondaryUseActive` **and** a non-empty main or off hand, so an empty-handed sneak still opens the door. Three things differ between the two copies, and none of them is the inner order. The server tests the block's `BlockBehaviour.requiredFeatures` through `FeatureElement.isEnabled` as its very first statement, while the client tests the same thing through `ClientPacketListener.isFeatureEnabled` inside the not-sneaking branch. A spectator gets a flat `InteractionResult.CONSUME` on the client, but on the server is routed to `BlockBehaviour.BlockStateBase.getMenuProvider` and may end up with an open container. And the advancement triggers exist only on the server: `CriteriaTriggers.ITEM_USED_ON_BLOCK` when the item did it, `CriteriaTriggers.DEFAULT_BLOCK_USE` when the empty-hand hook did, and `CriteriaTriggers.ANY_BLOCK_USE` from the packet handler for anything that consumed. The result is the vocabulary the whole pipeline turns on. `InteractionResult` is a sealed interface of four records — `InteractionResult.Success`, `InteractionResult.Fail`, `InteractionResult.Pass` and `InteractionResult.TryEmptyHandInteraction` — and the swing is part of it, not a separate decision. `InteractionResult.SUCCESS`, `InteractionResult.SUCCESS_SERVER` and `InteractionResult.CONSUME` are all `InteractionResult.Success` values differing only in `InteractionResult.SwingSource`: the client animates and sends `ServerboundSwingPacket`, the server animates for the trackers, or nobody does. `InteractionResult.consumesAction` is what the server's branches test — the client's own loop matches on the record types instead — but the record carries two more answers besides — `InteractionResult.Success.wasItemInteraction`, which decides whether `Stats.ITEM_USED` is awarded, and `InteractionResult.Success.heldItemTransformedTo`, which both game modes use to swap the stack the hand ends up holding. ## The door writes ten `DoorBlock.useWithoutItem` asks `BlockSetType.canOpenByHand` and, if the answer is no, returns `InteractionResult.PASS` and lets the item try. For oak it is yes: `StateHolder.cycle` flips `DoorBlock.OPEN`, and `Level.setBlock` is called with flags **10** — `Block.UPDATE_CLIENTS` and `Block.UPDATE_IMMEDIATE`, with `Block.UPDATE_NEIGHBORS` **clear**. Then `DoorBlock.playSound`, `LevelAccessor.gameEvent` with `GameEvent.BLOCK_OPEN` or `GameEvent.BLOCK_CLOSE` (posting is [game events and vibrations](../world/game-events-and-vibrations.md)), and `InteractionResult.SUCCESS`. Exactly this code runs on both sides. Ten is the whole story of the page. Bit 2 broadcasts, and on the client `LevelExtractor.blockChanged` reads bit 8 not as *immediate* but as *a player did this*, which can buy the section a priority remesh — `LevelRenderer` acts on that mark only when the *Chunk Builder* option is set to prioritise nearby or player-affected sections, which the fancy graphics preset does and the default does not. Bit 1 is absent, so `Level.setBlock` never reaches its neighbour fan-out — and on the client that would be a no-op anyway. What the flags then feed, and the rest of what a write does, is the flowchart on [blocks and states](blocks-and-states.md#the-two-update-channels); everything below is the part of it the door actually walks. ## The shape channel, which both sides run With `Block.UPDATE_KNOWN_SHAPE` clear and the update limit still positive, the tail of `Level.setBlock` calls `BlockBehaviour.BlockStateBase.updateNeighbourShapes`, which walks all six of `BlockBehaviour.UPDATE_SHAPE_ORDER` — west, east, north, south, down, up — and asks each neighbour, one top-level cascade at a time, whether it still fits. Note which direction travels: for the block above, the level is handed `Direction.DOWN`, the direction pointing *from that neighbour back at the door*. Each hop costs one from the limit, so the upper half is written at 511 and asks its own neighbours at 510. The whole distinction rests on three method bodies. `Level.updateNeighborsAt` and `Level.neighborChanged` are **empty on `Level`** and overridden only by `ServerLevel`; `Level.neighborShapeChanged` is implemented on `Level` itself and therefore runs on both sides. Shape updates are predictable because the client genuinely runs them; neighbour updates are not because the client's copy does nothing. `DoorBlock.updateShape` answers four of its six callers with the state it was given: the whole method is behind a test for the vertical axis, so the four horizontals fall through to `BlockBehaviour.updateShape`, which returns the state unchanged. The other two directions are where the door lives, and there are three outcomes between them. Asked from the matching vertical direction — up for a lower half, down for an upper — it returns **the neighbour's own state with `DoorBlock.HALF` swapped to its own**, so open, facing, hinge and powered are copied wholesale, which is why the top half is already open by the time it is written. Asked from that same direction when the neighbour is *not* the other half, it returns `Blocks.AIR`. And a lower half asked from `Direction.DOWN` returns air when `DoorBlock.canSurvive` fails — the block beneath must be face-sturdy upward. `Block.updateOrDestroy` then compares: a different non-air state becomes a `Level.setBlock` at the inherited limit, and air becomes a `Level.destroyBlock`, **but only when the level is not the client's**. That server-gated destroy branch, which writes with flags 3 and posts `GameEvent.BLOCK_DESTROY`, is the whole of "break the bottom and the top pops". ## The updater underneath: a stack, drained depth-first Every `Level` builds one `Level.neighborUpdater`, a `CollectingNeighborUpdater`, in its constructor — on the client too. Requests arrive as four small implementations of `CollectingNeighborUpdater.NeighborUpdates`, three of them records: `CollectingNeighborUpdater.ShapeUpdate` for the door's case, `CollectingNeighborUpdater.SimpleNeighborUpdate` and `CollectingNeighborUpdater.FullNeighborUpdate` for a single neighbour, and `CollectingNeighborUpdater.MultiNeighborUpdate`, one request that walks up to six directions in `NeighborUpdater.UPDATE_ORDER` — a different order from the shape one, west, east, down, up, north, south. `CollectingNeighborUpdater.addAndRun` decides where a request goes by whether a cascade is already running. The first one is pushed on `CollectingNeighborUpdater.stack` and drained immediately by `CollectingNeighborUpdater.runUpdates`; anything requested from inside a running hook lands in `CollectingNeighborUpdater.addedThisLayer` and is pushed on top of the stack before the current record's remaining work, so the drain is depth-first — a cascade finishes its children before its siblings. `NeighborUpdater.executeShapeUpdate` and `NeighborUpdater.executeUpdate` do the actual calls and wrap any throw in a crash report. The chain limit is coarser than it looks and gentler than folklore says. `CollectingNeighborUpdater.count` increments once per **request**, not per depth level and not per block touched — a `CollectingNeighborUpdater.MultiNeighborUpdate` is one request that can expand to six calls — and past `CollectingNeighborUpdater.maxChainedNeighborUpdates` further requests are silently dropped after a single logged error, never a crash. The count is reset when the outermost cascade unwinds, so the budget is per top-level cascade. It is **not** a game rule: it is the *max-chained-neighbor-updates* line in *server.properties* (`DedicatedServerProperties.maxChainedNeighborUpdates`, default one million, read through `DedicatedServer.getMaxChainedNeighborUpdates`), with `MinecraftServer.getMaxChainedNeighborUpdates` hard-coding the same number for the integrated server and `ClientLevel` passing the literal. Keep it distinct from `Block.UPDATE_LIMIT`, the 512 that bounds nested writes: that one counts *recursion depth* and is what the door's 511 and 510 come from. ## The gate list, and what each refusal answers with `ServerGamePacketListenerImpl.handleUseItemOn` tests in this order, and the interesting column is the second one — the refusals do not answer alike, and one of them lies. | the gate | what the client gets when it fails | |---|---| | `ServerGamePacketListenerImpl.hasClientLoaded` | nothing, not even the receipt | | `ItemStack.isItemEnabled` on the held stack | nothing | | `Player.isWithinBlockInteractionRange`, with 1.0 of slack | nothing | | the hit location lying within one block of the clicked block's centre on every axis — a 2×2×2 box, not the block | nothing, plus a server-side log line naming the player | | above `LevelHeightAccessor.getMaxY` or below `LevelHeightAccessor.getMinY` | `ServerPlayer.sendBuildLimitMessage` — an action-bar line, and **no block update** | | `MinecraftServer.isUnderSpawnProtection` | `ServerPlayer.sendSpawnProtectionMessage`, plus both block updates | | a pending teleport, or `ServerLevel.mayInteract` refusing for the world border | `ServerPlayer.sendBuildLimitMessage` — you are told you are building too high, whatever the real reason | | everything passed | `ServerPlayerGameMode.useItemOn` runs, plus both block updates | The two block updates are a `ClientboundBlockUpdatePacket` for the clicked position and one for the block on its clicked face, sent whatever the interaction returned — and they sit inside the branch below the build-height test, which is why a click that was out of reach or off the block is answered with silence while a click into spawn protection is answered with the truth. The door's *other* half is in neither: it reaches the client with everyone else's copy, through `ChunkHolder.broadcastChanges`, which turns two changed positions in one section into a single `ClientboundSectionBlocksUpdatePacket` (and into two `ClientboundBlockUpdatePacket`s when the halves straddle a section boundary). `ServerGamePacketListenerImpl.ackBlockChangesUpTo` was called before any of the gates, and `ServerGamePacketListenerImpl.tick` emits the receipt when `MinecraftServer.tickChildren` reaches connections — after the levels have already broadcast ([the server tick](../server/server-tick.md)). ## Questions players ask **Why can't I open an iron door by hand?** Because `DoorBlock.useWithoutItem`'s first question is `BlockSetType.canOpenByHand`, false on `BlockSetType.IRON` and `BlockSetType.GOLD` and true on `BlockSetType.COPPER`. Nothing on this path reads `BlockTags.WOODEN_DOORS` — the copper door proves it, since it opens by hand and is not in that tag. The tag is for mining and fuel. Mobs that open doors ask somewhere else again: `InteractWithDoor` reads `BlockTags.MOB_INTERACTABLE_DOORS`, while the older goals read `DoorBlock.isWoodenDoor`, which is `BlockSetType.canOpenByHand` under another name. **Why does the door sound different to me than to everyone else?** `DoorBlock.playSound` passes the clicking player as the *except* entity, and the two sides read that word oppositely: `ClientLevel.playSeededSound` plays the sound **only** when the except entity is the local player, while `ServerLevel.playSeededSound` broadcasts a `ClientboundSoundPacket` to everyone in range **but** them. So you hear your own prediction and never the server's copy — and since each side draws its own pitch from its own `Level.getRandom` and its own seed from `Level.soundSeedGenerator`, your door is genuinely a different sound from the one your friend heard. **Why does breaking the bottom of a door remove the top on the server but not on my screen?** Because that removal is a *shape* update whose destroy half is server-only. Both sides run `DoorBlock.updateShape` on the upper half, both get `Blocks.AIR` back, and both hand it to `Block.updateOrDestroy` — where the air branch is wrapped in a not-client check. Your client leaves the top half standing until the section packet arrives; the server has already dropped it, with the flags-3 neighbour updates that `Level.destroyBlock` implies. **Why does opening a door lag a busy server when nothing is powered?** Because the write still reaches `ServerLevel.sendBlockUpdated`, which compares the old and new collision shapes and, when they differ, walks `ServerLevel.navigatingMobs` — every tracked mob in the level, in full — asking each whether the position is near enough to its remaining path to be worth `PathNavigation.recomputePath`. A door changes shape every time it moves. None of that exists on the client, which is one more reason your half of the click is the fast half. Left-click is the same contract with a different pipeline: `Minecraft.startAttack` opens its own prediction and sends a `ServerboundPlayerActionPacket` instead, and the block hook is `BlockBehaviour.BlockStateBase.attack`. [Block breaking](block-breaking.md) takes it from there. ## Where to look `Minecraft.handleKeybinds` · `Minecraft.startUseItem` · `MultiPlayerGameMode.useItemOn` · `MultiPlayerGameMode.performUseItemOn` · `InteractionResult` · `BlockBehaviour.BlockStateBase.useItemOn` · `BlockBehaviour.BlockStateBase.useWithoutItem` · `DoorBlock.useWithoutItem` · `Level.setBlock` · `BlockBehaviour.BlockStateBase.updateNeighbourShapes` · `Level.neighborShapeChanged` · `CollectingNeighborUpdater.addAndRun` · `CollectingNeighborUpdater.runUpdates` · `NeighborUpdater.executeShapeUpdate` · `DoorBlock.updateShape` · `Block.updateOrDestroy` · `ServerGamePacketListenerImpl.handleUseItemOn` · `ServerPlayerGameMode.useItemOn` · `ChunkHolder.broadcastChanges` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Block breaking > Verified against **Minecraft 26.2** · Part V · A survival player holds left-click on stone with an iron pickaxe for eight ticks: two clocks that agree without talking, one loot roll, one cobblestone. Hold the button on a stone block and two programs start counting. The client adds a fraction to `MultiPlayerGameMode.destroyProgress` every client tick and paints the crack; the server sets `ServerPlayerGameMode.destroyProgressStart` to the tick the dig began and recomputes, from scratch, how far along it ought to be. Between the first packet and the last, **neither clock is ever mentioned on the wire** — no progress reports, no heartbeat, nothing but the swing animation going up — and on the eighth tick the two answers are the same number. That agreement is what the whole design rests on, and it is also why the failure mode is so strange: **releasing the button does not cancel a break.** A client that stops too early gets a deferral, not a rejection. The receipt for the STOP goes out in the same tick, the client dutifully puts the stone back — and then watches it vanish a second time when the server's own clock finishes the job, with nothing the player can do in between. > **The contract both halves run under.** The client acts at once and remembers the state it overwrote, under a sequence number it sends with the action. The server's `ClientboundBlockChangedAckPacket` is a receipt for that number and *not* a verdict — it is sent for actions the server refused exactly as for actions it allowed — and correctness comes from ordering instead: any correction the server means to send travels in the same tick and earlier in the stream than the receipt. A correction *replaces* what the client remembered rather than being weighed against it, so when the receipt arrives the client writes back whatever the entry now holds — and only where that differs from what is on screen. [Prediction and acknowledgement](../client/prediction-and-acks.md) owns that machinery; [block interaction](block-interaction.md) and this page are its two applications. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | that the button is down and the crosshair is on a block, and whether this frame's tick starts a dig or continues one | Render | | `MultiPlayerGameMode` | the client's clock: accumulated progress, the five-tick pause after a break, when to predict the removal and send STOP | Render | | `ClientLevel` | the predicted air, and the crack overlays — every breaker's, including the local player's | Render | | `ServerGamePacketListenerImpl` | which of the eight `ServerboundPlayerActionPacket.Action`s this is, and when the receipt for its sequence is flushed | Server | | `ServerPlayerGameMode` | the server's clock, the reach and permission gates, the 0.7 verdict, and the deferral | Server | | `BlockBehaviour.BlockStateBase` | hardness, whether the block needs the right tool for drops, and the per-tick fraction | either | | `Tool` | how fast this stack mines this block, and whether it drops — two separate answers from one rule list | either | | `Block` | the removal, the particles and sound event, the stat, the exhaustion and the loot roll | Server | ## One dig, end to end ```mermaid sequenceDiagram participant MC as Minecraft participant MPGM as MultiPlayerGameMode participant CL as ClientLevel participant SGPL as ServerGamePacketListenerImpl participant SPGM as ServerPlayerGameMode participant SL as ServerLevel participant Block as Block Note over MC,Block: client tick 1, the button goes down MC->>MPGM: startAttack sees a non-air block, startDestroyBlock MPGM->>CL: startPrediction opens sequence N, attack runs, getDestroyProgress is 0.133 MPGM->>CL: destroyBlockProgress with stage -1, which clears my own crack MPGM->>SGPL: ServerboundPlayerActionPacket START, sequence N SGPL->>SPGM: handleBlockBreakAction, destroyProgressStart = gameTicks SPGM->>SL: destroyBlockProgress broadcasts the first crack stage to everyone else within 32 SGPL-->>CL: ClientboundBlockChangedAckPacket N, nothing to reconcile loop client ticks 1-7 beside server ticks, with a swing packet up and nothing about progress either way MC->>MPGM: continueAttack, continueDestroyBlock adds 0.133 MPGM->>CL: my own stage, plus a hit sound every fourth tick SPGM->>SPGM: tick, incrementDestroyProgress recomputes 0.133 x (elapsed + 1) SPGM->>SL: a ClientboundBlockDestructionPacket only when the tenth changes end Note over MC,Block: client tick 8, and the client is the only side that acts on 1.0 MPGM->>CL: prediction M, playerWillDestroy plays event 2001 locally, setBlock to air with flags 11 MPGM->>SGPL: ServerboundPlayerActionPacket STOP, sequence M Note over SGPL,Block: a server tick, STOP handled off the task queue SGPL->>SPGM: handleBlockBreakAction STOP, own progress 1.07 clears the 0.7 bar SPGM->>Block: destroyAndAck then destroyBlock, playerWillDestroy sends 2001 to all but the breaker SPGM->>SL: removeBlock writes the fluid-or-air state under flags 3 SPGM->>Block: mineBlock spends one durability point, then playerDestroy Block->>SL: the blocks/stone table rolls, popResource adds the ItemEntity Note over SGPL,Block: same tick, later: the levels broadcast, then the connections flush SL-->>CL: ClientboundBlockUpdatePacket air, absorbed by the ledger SGPL-->>CL: ClientboundBlockChangedAckPacket M, syncBlockState finds air already ``` ## Two clocks, and the plus one that makes them agree `BlockBehaviour.getDestroyProgress` is the shared formula, and both sides call it through `BlockBehaviour.BlockStateBase.getDestroyProgress` with the same arguments. It answers the fraction of the block broken *per tick*: the player's speed, divided by the block's hardness, divided by **30** when `Player.hasCorrectToolForDrops` says yes — which it does for every block that does not require a tool at all — and by **100** when it says no. Hardness −1 returns zero forever. Hardness *zero* is not special-cased, so an instabreak block divides by zero and returns infinity: that, and not a branch on hardness, is what sends the START handler down its insta-mine path on the first tick. `Player.getDestroySpeed` builds the numerator from `Inventory.getSelectedItem` in one pass, and the surprising parts are all constants rather than data. The stack's `ItemStack.getDestroySpeed` gives the base. If that is above 1.0 — only then — `Attributes.MINING_EFFICIENCY` is *added*, which is where `Enchantments.EFFICIENCY` lands, at level² + 1. Haste and conduit power are read together through `MobEffectUtil.hasDigSpeed` and `MobEffectUtil.getDigSpeedAmplification`, which returns the **greater** of the two amplifiers — a beacon and a conduit are interchangeable and do not stack — and multiply by 1 + 0.2 × (amplifier + 1). Mining fatigue is not an attribute at all but four literal factors switched on the amplifier — 0.3, 0.09, 0.0027, and 0.00081 for anything higher. Then `Attributes.BLOCK_BREAK_SPEED`, then `Attributes.SUBMERGED_MINING_SPEED` (0.2 by default) if the eyes are in `FluidTags.WATER`, and finally **divide by five if the player is not on the ground**. For stone at hardness 1.5 and an iron pickaxe at 6.0, that is 6 ÷ 1.5 ÷ 30 = 0.133 per tick, so the eighth tick is the one that passes 1.0. ### Why the two answers match The two clocks count differently and still land on the same number. The client accumulates: `MultiPlayerGameMode.continueDestroyBlock` adds one tick's fraction each time it runs. The server keeps no accumulator, and its number is in fact a tick *ahead* of the client's all the way down — it simply never acts on it, because the live branch of `ServerPlayerGameMode.tick` throws the value away and only the delayed branch compares it with 1.0. `ServerPlayerGameMode.incrementDestroyProgress` multiplies the per-tick fraction by *elapsed ticks plus one*, and that plus one is exactly the client's first `Minecraft.continueAttack`, which happens in the same client tick as the `Minecraft.startAttack` that opened the dig. Recomputing rather than accumulating has a second consequence: swap tools or lose haste mid-dig and the server rescales the *whole* dig retroactively, while the client keeps the progress it already banked. They agree without talking because every input is either static data both sides loaded — hardness, the block tags, the `Tool` component travelling with the stack — or a syncable attribute, or a synced effect. The inputs that could drift are the ones the client reports rather than shares, and the sharpest of them is which slot is selected: `MultiPlayerGameMode.ensureHasSentCarriedItem` runs at the top of every `MultiPlayerGameMode.continueDestroyBlock` to send a `ServerboundSetCarriedItemPacket` the moment it changes. ## The button is not the switch **Seventy per cent** — how much of the server's own clock a STOP must have run before the block breaks immediately (`ServerPlayerGameMode.handleBlockBreakAction`). For stone that is about two ticks of slack. Below the bar the STOP is not refused: the handler sets `ServerPlayerGameMode.hasDelayedDestroy`, copies the position and the *original* start tick into `ServerPlayerGameMode.delayedDestroyPos` and `ServerPlayerGameMode.delayedTickStart`, and lets its own clock run on. The sequence is acknowledged regardless — `ServerGamePacketListenerImpl.handlePlayerAction` calls `ServerGamePacketListenerImpl.ackBlockChangesUpTo` for all three break actions, unconditionally, after the game mode has run. So the receipt arrives with no correction in front of it, the client settles prediction M against the stone it recorded, and `ClientLevel.syncBlockState` puts the stone back. The block is visibly there again. A tick or two later the server's clock crosses 1.0, `ServerPlayerGameMode.destroyBlock` runs, and the air arrives as an ordinary block update. The prediction was not wrong — it was undone and then redone. Letting go changes nothing. The ABORT branch clears `ServerPlayerGameMode.isDestroyingBlock` and erases the crack, and it never touches `ServerPlayerGameMode.hasDelayedDestroy` — and `ServerPlayerGameMode.tick` tests the delayed dig **first**, before the live one. Starting a dig on a different block does not help either: the START is processed normally, but the delayed branch keeps winning the tick. The delayed path re-checks almost nothing on its way through — not reach, not `MinecraftServer.isUnderSpawnProtection`, not `ServerLevel.mayInteract`, not that the player is still in the same room. It calls `ServerPlayerGameMode.destroyBlock` directly rather than `ServerPlayerGameMode.destroyAndAck`, so a failure there is silent, with no corrective block update. Its only escape is the block turning to air: that is the one condition `ServerPlayerGameMode.tick` tests before recomputing progress. ### What a real refusal looks like The refusals that *are* refusals differ in what they send back, and the difference is observable. A failed `Player.isWithinBlockInteractionRange` check — which allows a full block of slack — sends **nothing at all**, and it guards ABORT as well as START, so an abort from too far away is dropped on the floor. Being above `LevelHeightAccessor.getMaxY`, failing `ServerLevel.mayInteract` or failing `Player.blockActionRestricted` each answer with a `ClientboundBlockUpdatePacket` carrying the true state. Spawn protection answers with an overlay message from `ServerPlayer.sendSpawnProtectionMessage` and no block update whatsoever. Every one of those exits is named in a string behind `SharedConstants.DEBUG_BLOCK_BREAK`, which is the best map of this state machine there is. ## Speed and drops are two scans of one list `DataComponents.TOOL` holds a `Tool`: a list of `Tool.Rule`, a `Tool.defaultMiningSpeed`, a `Tool.damagePerBlock` and a `Tool.canDestroyBlocksInCreative`. Each rule names a set of blocks and carries an *optional* speed and an *optional* drop verdict, so a rule can answer one question and stay silent on the other. `Tool.getMiningSpeed` and `Tool.isCorrectForDrops` are two independent walks of the same list, each taking the first rule that both matches the block *and* carries the field it came for. `ToolMaterial.applyToolProperties` builds every pickaxe, axe, shovel and hoe from exactly two rules, in this order: | the iron pickaxe's rules, in order | what `Tool.getMiningSpeed` does | what `Tool.isCorrectForDrops` does | |---|---|---| | deny drops on `BlockTags.INCORRECT_FOR_IRON_TOOL` — no speed field | skips it | obsidian matches, answers **no** | | mine and drop `BlockTags.MINEABLE_WITH_PICKAXE` at 6.0 | obsidian matches, answers **6.0** | never reached | | nothing matched | `Tool.defaultMiningSpeed`, 1.0 | false | That is why an iron pickaxe mines obsidian and drops nothing. The speed scan falls through the deny rule — it has no speed — and takes the full pickaxe 6.0; the drop scan stops at the deny. The block still takes forever, but for a different reason: `Player.hasCorrectToolForDrops` is false, so `BlockBehaviour.getDestroyProgress` divides by 100 instead of 30. No item in the game uses all three rule shapes: `Tool.Rule.deniesDrops` and `Tool.Rule.overrideSpeed` never appear in the same `Tool`, because the items that deny drops on a tag are exactly the ones that name their own speed on another. The sword is one of exactly three whose `Tool.canDestroyBlocksInCreative` is false — the other two are the mace and the trident. ## The cracks belong to everyone but you `ServerLevel.destroyBlockProgress` sends a `ClientboundBlockDestructionPacket` to every player in the level within 32 blocks whose entity id is not the breaker's. You are never sent your own cracks. What you see is `MultiPlayerGameMode` writing straight into `ClientLevel.destroyBlockProgress` each tick — the same method the packet handler calls, reached by a different road. The same asymmetry runs through the break itself: `Block.playerWillDestroy` posts level event 2001, which `ServerLevel.levelEvent` broadcasts within 64 blocks *excluding* the breaker, because the breaker already played it locally inside `MultiPlayerGameMode.destroyBlock`. `BlockDestructionProgress` is a plain holder — id, position, progress, a last touched tick — and `BlockDestructionProgress.setProgress` clamps only the top, at 10. The 0–9 window everybody quotes is enforced by the *caller*: `ClientLevel.destroyBlockProgress` stores a stage only for values in [0, 10), and reads anything else — including the −1 that `MultiPlayerGameMode.getDestroyStage` returns at zero progress — as an instruction to **remove** that breaker's entry. Entries are indexed twice, by breaker id and by position, the latter into a sorted set so the deepest crack at a position wins; `LevelExtractor` collects those within 32 blocks of the camera each frame. Entries untouched for 400 ticks are swept every twentieth tick, which is what eventually clears the cracks left by someone who disconnected mid-dig. ## Remove, damage, roll, drop `ServerPlayerGameMode.destroyBlock` runs a short gauntlet before it writes anything: `ItemStack.canDestroyBlock`, then `GameMasterBlock` against `Player.canUseGameMasterBlocks`, then `Player.blockActionRestricted`. It captures the `BlockEntity` first, because the write is about to destroy it. Then `Block.playerWillDestroy` — particles and sound to everyone else, piglins angered for `BlockTags.GUARDED_BY_PIGLINS`, and a `GameEvent.BLOCK_DESTROY` posted for sculk ([game events](../world/game-events-and-vibrations.md)). The write itself is `Level.removeBlock`, not `Level.destroyBlock`. It puts the *fluid* that was in the block back — water for a waterlogged block, air here — under flags 3, and everything that follows from those flags is the one flowchart on [blocks and states](blocks-and-states.md#the-two-update-channels). Drops come last and in a fixed order. If `Player.preventsBlockDrops` (creative) the method returns here. Otherwise the tool is copied, `Player.hasCorrectToolForDrops` is asked *once* and remembered, and `ItemStack.mineBlock` is called **unconditionally** — though what it does is not: `Item.mineBlock` awards `Stats.ITEM_USED` and spends `Tool.damagePerBlock` only on the server, only for a stack that carries a `DataComponents.TOOL` at all, only when that tool's damage per block is above zero, and only when the block's hardness is non-zero ([items and stacks](../items/items-and-stacks.md)). Only then, and only if the write succeeded and the remembered answer was yes, does `Block.playerDestroy` run: `Stats.BLOCK_MINED`, 0.005 of food exhaustion, and `Block.dropResources`. The loot side is thin. `Block.getDrops` supplies `LootContextParams.ORIGIN` at the block centre, `LootContextParams.TOOL` and `LootContextParams.THIS_ENTITY`, `BlockBehaviour.getDrops` adds `LootContextParams.BLOCK_STATE`, and the set is `LootContextParamSets.BLOCK`. The table key is not looked up by name at break time: `BlockBehaviour.Properties.effectiveDrops` resolves the block's id under *blocks/* once, when the block is constructed. What *blocks/stone* then does is two lines of JSON — a silk-touch alternative, else cobblestone if it survives an explosion — rolled from a seeded per-table sequence rather than the level random ([loot tables](../items/loot-tables.md)). Each surviving stack goes to `Block.popResource`, which respects `GameRules.BLOCK_DROPS`, jitters the position ±0.25 on all three axes around the block centre, gives the `ItemEntity` its small upward kick in the constructor and a ten-tick pickup delay. Ores add their experience afterwards, in `BlockBehaviour.BlockStateBase.spawnAfterBreak`. ## Questions players ask **Why did the block come back, and then break anyway?** You released a tick or two before the server's clock agreed you were done, so the STOP fell under 0.7 and became a deferral. The receipt for it carried no correction, so your client rolled the prediction back and restored the stone; the server finished the dig on its own a tick or two later. See *The button is not the switch*. **Why does my pickaxe lose durability on obsidian, which drops nothing, but not on short grass, which does?** Durability is spent by `ItemStack.mineBlock`, which runs before the drop verdict is consulted and does not care about it — what it cares about is that the block's hardness is non-zero and the tool has a damage-per-block above zero. Obsidian is hard and drops nothing: you pay. Short grass is `Blocks.SHORT_GRASS`, hardness zero: you never pay, whatever it drops. Shears are the exception at both ends — `ShearsItem` is the only override of `Item.mineBlock` in the game, and it tests neither hardness nor drops, only that the block is not in `BlockTags.FIRE`. Shearing grass costs a point. **Why can't I break blocks with a sword in creative?** Because `Tool.canDestroyBlocksInCreative` is false on the sword's component and `ItemStack.canDestroyBlock` checks it on both sides. It is a property of the item, not a special case in the game mode — which is why the client refuses first, and why the correcting block update the server sends back is a no-op: the client predicted nothing to correct. **Why do other players' cracks lag behind mine?** Yours are written locally every client tick from your own accumulator. Theirs arrive as packets, sent only when the server's tenth-of-progress changes, from the server's own clock, and only if you are within 32 blocks. ## Where to look `Minecraft.startAttack` · `Minecraft.continueAttack` · `MultiPlayerGameMode.startDestroyBlock` · `MultiPlayerGameMode.continueDestroyBlock` · `MultiPlayerGameMode.destroyBlock` · `ServerGamePacketListenerImpl.handlePlayerAction` · `ServerPlayerGameMode.handleBlockBreakAction` · `ServerPlayerGameMode.tick` · `ServerPlayerGameMode.incrementDestroyProgress` · `ServerPlayerGameMode.destroyBlock` · `BlockBehaviour.getDestroyProgress` · `Player.getDestroySpeed` · `Tool.getMiningSpeed` · `Tool.isCorrectForDrops` · `Block.playerWillDestroy` · `Block.playerDestroy` · `Block.popResource` · `ServerLevel.destroyBlockProgress` · `ClientLevel.destroyBlockProgress` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Block entities > Verified against **Minecraft 26.2** · Part V · A furnace smelts raw iron while nobody is watching, and the player who opens it learns the fire from a block state and the arrow from a menu — never from the block entity itself. You drop raw iron in the top slot of a furnace, coal in the bottom, and walk away. Two hundred ticks later there is an iron ingot in a box nobody is looking at. A block state is one of a fixed table, shared by every position that has it, so the moment a position needs something of its own — an inventory, a timer, a name — it gets a `BlockEntity`: a plain object owned by its chunk, keyed by its position, created by the block and destroyed with it. The surprise is how little that object says for itself. A furnace tells nobody anything: `BlockEntity.getUpdatePacket` returns null for it, `BlockEntity.setChanged` sends nothing at all, and the two things a player *does* see — the fire in the world and the arrow in the GUI — are a block state and four ints from a menu, both of which arrive on the tick **after** the smelting step that produced them, because block entities tick in the level's last content phase, after the broadcast has already gone out. ## The cast | class | what it decides | thread | |---|---|---| | `BlockEntity` | the position, the cached state, the components, and the four defaults every subclass inherits — two of which are *say nothing* | whichever thread owns the level | | `BlockEntityType` | which blocks the entity is legal on, and what constructs it | immutable once the registry is built | | `EntityBlock` | whether a block has an entity at all, which one, and which ticker *per level* | — | | `LevelChunk` | the position-to-entity map, the ticker wrapper per position, and create / keep / replace / remove on every write | the chunk's owning thread | | `Level` | the flat list of tickers and the two gates over it | server thread, or the client's main thread | | `AbstractFurnaceBlockEntity` | three slots, four ints, a cached recipe check, and when the block's *lit* state has to change | server thread only — its ticker is null on the client | | `ChunkHolder` | which positions changed since the last drain, and the single call to `BlockEntity.getUpdatePacket` | server thread, chunk-source phase | | `FurnaceMenu` | what an open screen is allowed to see of all that: three slots and four ints | server thread, mirrored on the client | ## A furnace tells nobody anything `BlockEntity` has four hooks a subclass is expected to fill in, and its own answers to all four are deliberately weak. `BlockEntity.saveAdditional` and `BlockEntity.loadAdditional` do nothing. `BlockEntity.getUpdateTag` returns an empty tag. `BlockEntity.getUpdatePacket` returns **null** — the base class declines to be synced, and a subclass that wants to be must say so. **Nineteen** classes say so, and every one of them answers with `ClientboundBlockEntityDataPacket.create` of itself: signs, banners, beacons, skulls, spawners and trial spawners, conduits, end gateways, structure and jigsaw blocks, campfires, decorated pots, vaults, shelves, brushable blocks, creaking hearts, copper golem statues and the two test blocks. Counting those declarations in the decompile and mapping each class onto the **49** registrations in `BlockEntityTypes` gives **twenty** synced types out of forty-nine, because `HangingSignBlockEntity` is a type of its own that inherits `SignBlockEntity`'s override and adds nothing. The overriders of the packet and the overriders of the tag are not the same list, and the two classes that differ are instructive. `PistonMovingBlockEntity` overrides `BlockEntity.getUpdateTag` but not the packet, so its state travels only in a chunk send ([pistons and block events](pistons-and-block-events.md)). `CopperGolemStatueBlockEntity` overrides the packet but not the tag, so what it broadcasts is the base class's empty tag. Everything else a client knows about a block entity it knows by consequence: the block state it can see, a menu it has been given, and a block event — the third channel, and the one that swings a chest lid without either side saying what is inside ([pistons and block events](pistons-and-block-events.md)). The trace below is what the first two cost. ## One save hook, four ways out Saving is a tree, not a chain, and the branch a caller picks decides how much metadata rides along. Only `BlockEntity.saveAdditional` belongs to the subclass; everything else is bookkeeping the base class adds. | what runs | what it writes | who calls it | |---|---|---| | `BlockEntity.saveAdditional` | the subclass's own fields, and nothing else | nobody directly | | `BlockEntity.saveCustomOnly` | that alone | thirteen of the nineteen `BlockEntity.getUpdateTag` overrides, and the pick-block path, which then strips the keys that are now components | | `BlockEntity.saveWithoutMetadata` | that plus *components* | the two below, plus the copy-NBT debug key, `BlockInput` and the falling block | | `BlockEntity.saveWithId` | that plus *id* | two callers that record their own position separately: `AdventureModePredicate` and `StructureTemplate` | | `BlockEntity.saveWithFullMetadata` | that plus *id*, *x*, *y* and *z* | `LevelChunk.getBlockEntityNbtForSaving`, the chunk-save form, and every command that reads a block's NBT | Reading back is `BlockEntity.loadWithComponents` (fields plus components) or `BlockEntity.loadCustomOnly` (fields only) over a `ValueInput` ([codecs, NBT and JSON](../foundations/codecs-nbt-json.md)) — but something has to decide *which class* to construct first, and that cannot come through a `ValueInput`, because no entity exists yet to own one. So `BlockEntity.loadStatic` reads *id* off the raw `CompoundTag` with `BlockEntity.TYPE_CODEC`, calls `BlockEntityType.create`, and only then wraps the same tag in a `ValueInput` and loads it. Any of those three steps failing logs and returns null, and the position ends up with no entity at all. The network joins that path at the end rather than reusing it whole: `ClientPacketListener.handleBlockEntityData` never reads *id* or constructs anything — it finds the existing entity by position *and* type and hands the tag to `BlockEntity.loadWithComponents`. There is no separate network deserialiser. Where the chunk's *block_entities* list is written and read is [chunk storage](../world/chunk-storage.md). ## Create, keep, replace, remove A block entity that appears because a *block* appeared is created and destroyed inside one method: `LevelChunk.setBlockState`, after the section write, the heightmaps and the light checks ([what a write does](blocks-and-states.md#the-two-update-channels)). That is the lifecycle path, and it makes two decisions, in this order. It is not the only way one comes into being: a chunk arriving from disk or from the network builds its entities from saved tags, and `LevelChunk.getBlockEntity` in its *immediate* mode constructs a missing one on a plain read — which is the mode every `Level.getBlockEntity` asks for. **Removal** happens only when the *block* changed, the old state had an entity, and the new state does not claim it through `BlockBehaviour.BlockStateBase.shouldChangedStateKeepBlockEntity` — which exactly two blocks in 26.2 override, `CopperChestBlock` and `CopperGolemStatueBlock`, both keeping the entity when the old state was another block of the same family, so oxidising or waxing a copper chest does not empty it. Removal is two halves with different gates. The side effects — `BlockEntity.preRemoveSideEffects`, which for anything implementing `Container` drops the contents through `Containers.dropContents` — run **only on the server** and only with `Block.UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS` clear. The bookkeeping, `LevelChunk.removeBlockEntity`, runs regardless — though only the last of its four steps is itself unconditional. The map entry going, the game-event listener being unregistered and the entity being flagged removed all sit behind *is this chunk in a level*, which a chunk still being generated is not; the rebind of the ticker to `LevelChunk.NULL_TICKER` happens either way. **Creation** happens after `BlockBehaviour.BlockStateBase.onPlace`, and only if the state actually written still has a block entity. The chunk looks for an existing one without creating it, and if what it finds does not pass `BlockEntity.isValidBlockState` for the new state it logs a *mismatched block entity* warning, removes it and builds a fresh one from `EntityBlock.newBlockEntity` — the block's own factory, not `BlockEntityType.create`. Only a surviving match is kept, with its cached state refreshed and `LevelChunk.updateBlockEntityTicker` re-asking the block for a ticker. That is why flipping a furnace's *lit* property costs almost nothing: same block, valid state, same object — a fresh `LevelChunk.BoundTickingBlockEntity` rebound into the wrapper is the whole of the expense. Chunk load and unload use the ends of the same machinery. `ChunkStatusTasks.full` runs `LevelChunk.runPostLoad` to turn the saved tags into entities, then `LevelChunk.setLoaded` and `LevelChunk.registerAllBlockEntitiesAfterLevelLoad`, which attaches listeners and tickers to entities built before the chunk belonged to a level. `ServerLevel.unload` calls `LevelChunk.clearAllBlockEntities`: every entity flagged removed, every ticker pointed at `LevelChunk.NULL_TICKER`. ## Two hundred ticks nobody watches ```mermaid sequenceDiagram participant SL as ServerLevel participant LC as LevelChunk participant AFBE as AbstractFurnaceBlockEntity participant CH as ChunkHolder participant SP as ServerPlayer participant FM as FurnaceMenu participant CPL as ClientPacketListener Note over SL,CPL: tick N, blockEntities phase, the level's last content phase SL->>LC: tickBlockEntities reaches the wrapper, isTicking and isValid pass LC->>AFBE: serverTick, quickCheck finds the smelting recipe AFBE->>AFBE: fuel consumed, lit fields set to 1600, cookingTimer 1 AFBE->>SL: setBlock LIT true with flags 3 SL->>LC: setBlockState, same block, entity kept and ticker rebound SL->>CH: blockChanged only queues the holder, the drain already ran Note over SL,CPL: tick N plus 1, chunkSource phase, the broadcast drain CH->>CPL: ClientboundBlockUpdatePacket, the fire appears CH-->>CH: broadcastBlockEntity asks getUpdatePacket and gets nothing Note over SL,CPL: tick N plus 1, entities phase, players tick SP->>FM: broadcastChanges compares four data slots against remoteDataSlots FM->>CPL: ClientboundContainerSetDataPacket per changed slot: 0, 1 and 2 on this tick, 0 and 2 from the next ``` The furnace's ticker is handed out by `AbstractFurnaceBlock.createFurnaceTicker` **only when the level is a `ServerLevel`** — on the client it is null, so no furnace anywhere ever ticks there. `AbstractFurnaceBlockEntity.serverTick` is therefore the whole of smelting: burn down `AbstractFurnaceBlockEntity.litTimeRemaining`, ask `AbstractFurnaceBlockEntity.quickCheck` (a `RecipeManager.CachedCheck`, which retries last tick's match before scanning the type) for a recipe on the input slot, check that the result slot can take the output, and, if the fire is out but fuel is present, light it: both lit fields take `FuelValues.burnDuration` for that item — 1600 for coal — and one fuel item is consumed. Then `AbstractFurnaceBlockEntity.cookingTimer` advances by one. Where the recipe comes from is [recipes](../items/recipes.md). Two writes leave the block entity, and neither leaves the server this tick. The first is the fire: lit-ness is a *block state*, so the ticker calls `Level.setBlock` on its own position with `AbstractFurnaceBlock.LIT` flipped. `ServerChunkCache.blockChanged` marks the holder dirty — and the drain that turns dirty holders into packets, `ServerChunkCache.broadcastChangedChunks`, lives in the chunk-source phase, which ran before entities and long before block entities. The second is progress: `BlockEntity.setChanged` marks the chunk unsaved and pokes comparators through `Level.updateNeighbourForOutputSignal`, and that is all it does. It sends nothing. So a viewer sees both a tick late, by two different routes. Next tick's drain sends the `ClientboundBlockUpdatePacket` and then — for every broadcast position whose state has a block entity, including each position inside a `ClientboundSectionBlocksUpdatePacket` — calls `BlockEntity.getUpdatePacket`, the only call site in the game, and gets null from the furnace. Next tick's entity phase runs `ServerPlayer.tick`, which runs `AbstractContainerMenu.broadcastChanges`, which compares the menu's four data slots against the values last sent and emits a `ClientboundContainerSetDataPacket` per difference. Those four ints are the furnace's entire GUI: the flame is data 0 over data 1 and the arrow data 2 over data 3, read by `AbstractFurnaceMenu.getLitProgress` and `AbstractFurnaceMenu.getBurnProgress`. While smelting, only 0 and 2 change, so it is two packets a tick per open screen and none at all with no viewer. How a menu is opened, synchronised and closed is [containers and menus](../items/containers-and-menus.md). At the end, `AbstractFurnaceBlockEntity.burn` moves the ingot into the result slot and `AbstractFurnaceBlockEntity.setRecipeUsed` adds one to a counter map — not to a recipe object, which is why `AbstractFurnaceBlockEntity.getRecipeUsed` returns null. The experience is paid out on collection: `FurnaceResultSlot.checkTakeAchievements`, reached from a take *or* a shift-click, calls `AbstractFurnaceBlockEntity.awardUsedRecipesAndPopExperience`, which pops the orbs at the player and unlocks the recipes. ## Loaded is not enough to tick `Level.tickBlockEntities` walks one flat list, `Level.blockEntityTickers`, under two gates the tickers themselves never see. `TickRateManager.runsNormally` is the first, so `/tick freeze` stops every block entity in the game. `Level.shouldTickBlocksAt` is the second, and it is where the interesting asymmetry lives: on `Level` it is always true, on `ServerLevel` it is `DistanceManager.inBlockTickingRange` — the **simulation** chunk tracker, not the loading one. A chunk your view distance keeps loaded and your simulation distance does not reach holds furnaces that do not smelt, and nothing about the block entity records this: it is simply never called. Below the gates, `LevelChunk.BoundTickingBlockEntity` adds its own — not removed, adopted by a level, inside the world border, and then, on a `ServerLevel` only, the chunk at `FullChunkStatus.BLOCK_TICKING` with its entities loaded — before re-reading the live state and ticking only while `BlockEntityType.isValid` still holds, logging once and skipping while it does not. The list is never searched. Removal rebinds the chunk's `LevelChunk.RebindableTickingBlockEntityWrapper` to `LevelChunk.NULL_TICKER`, whose `TickingBlockEntity.isRemoved` is permanently true, and the next pass of `Level.tickBlockEntities` drops it on the way past — no search, on a list that is walked every tick anyway. Additions made *during* the walk go to `Level.pendingBlockEntityTickers` and are folded in at the top of the next pass, so a block entity created by another block entity's tick starts ticking one tick later. The client runs the same method from `Minecraft.tick`, after its entity pass and before `ClientLevel.tick`, and only while unpaused. ## Questions players ask **Why does my furnace stop smelting when I walk away, even though the chunk is still loaded?** Because loading and simulating are two different distances, and `Level.shouldTickBlocksAt` asks about the second. The chunk is in memory, its entity is in the map and its ticker is in the list — and `Level.tickBlockEntities` walks past it every tick without calling it. **Why doesn't the client know what is in a chest until I open it?** `ChestBlockEntity` overrides neither sync hook, so a chunk send carries its type and position with no tag at all (an empty update tag is stored as null) and the client builds its chest from the *block state* the packet's sections already decoded, with an empty container inside. What ticks on the client is animation only: `ChestBlock.getTicker` hands out `ChestBlockEntity.lidAnimateTick` on the client and null on the server, the exact mirror of the furnace. **Why does a shulker box keep its contents when every other container drops them?** Because dropping is the *base class's* behaviour, not the block's: `BlockEntity.preRemoveSideEffects` drops the contents of anything that implements `Container`. Eight classes override that hook, and `ShulkerBoxBlockEntity` overrides it to do nothing whatever. The furnace overrides it too, to pop the experience owed for uncollected smelts at the block — awarding the recipes to nobody. **Why does the arrow only move when the screen is open?** Because the arrow is not a property of the furnace. It is data slot 2 of an `AbstractContainerMenu` that exists only while a player has that screen open, reconciled once per tick by the player who owns it. Close the screen and the menu is gone, and the furnace goes on smelting with no packets at all. ## Where to look `BlockEntity.getUpdatePacket` · `BlockEntity.setChanged` · `BlockEntity.loadStatic` · `BlockEntity.saveWithFullMetadata` · `BlockEntity.preRemoveSideEffects` · `BlockEntityType.isValid` · `EntityBlock.newBlockEntity` · `EntityBlock.getTicker` · `BaseEntityBlock.createTickerHelper` · `LevelChunk.setBlockState` · `LevelChunk.updateBlockEntityTicker` · `LevelChunk.BoundTickingBlockEntity` · `LevelChunk.NULL_TICKER` · `Level.tickBlockEntities` · `Level.shouldTickBlocksAt` · `ChunkHolder.broadcastBlockEntity` · `AbstractFurnaceBlockEntity.serverTick` · `AbstractFurnaceMenu.getBurnProgress` How a block entity is *drawn* — and why a chest's block model is empty — is [block-entity rendering](../rendering/block-entity-rendering.md), in Part XI. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Signal and dust > Verified against **Minecraft 26.2** · Part V · A lever on the floor is flipped, and two redstone dust to the east of it go to 15 and 14. You flip a lever, and the dust beside it turns bright. Flip it back and the line goes dark — in one tick, but not in one pass. Each wire recomputes its own strength from scratch, writes it, and then **hand-issues seven `Level.updateNeighborsAt` calls — its own position and all six neighbours — which is forty-two neighbour updates for one wire that changed**; and because the recursion stops on *value* rather than on distance, the wire at the far end is reached once for every intermediate value the near end passes through on the way down. That staircase is the whole cost of redstone, and **nobody has ever seen it**: the cascade finishes inside one packet handler, and the packet a client is sent is built later in the same tick from whatever the position holds by then. The game ships a second implementation of exactly this computation, behind a feature flag, which walks the whole connected network in two ordered phases and does not produce the staircase at all. **Forty-two** — neighbour updates issued by one wire whose power changed (`DefaultRedstoneWireEvaluator.updatePowerStrength`). ## The cast | class | what it decides | thread | |---|---|---| | `SignalGetter` | every question about power: what a position emits, what reaches it, and the direction order the answers are gathered in | a `Level` interface, either side | | `BlockBehaviour.BlockStateBase` | the three answers a state gives — is it a source, what is its weak signal per face, what is its strong signal | either side | | `LeverBlock` | the trace's source: 15 in every direction, and 15 *strongly* into one block only | server — the client's copy writes nothing | | `RedStoneWireBlock` | which sides a wire connects to, what it emits through them, and the mutable flag that stops it counting itself | server | | `RedstoneWireEvaluator` | *minus one per block*: what the neighbouring wires are worth to this one | server | | `DefaultRedstoneWireEvaluator` | one wire at a time, recursively, with the fan-out issued by hand | server | | `ExperimentalRedstoneWireEvaluator` | the whole network at once, off in two phases and on in one, allocated fresh per call | server | | `Orientation` | in the experimental mode only, where an update *came from*, so the fan-out can be ordered relative to it | server | ## What one neighbour update to a wire costs Redstone has no scheduler and no graph. It is what happens when blocks answer two questions about their neighbours — *how much signal do you give me* and *did something near you change* — through the same `BlockBehaviour.neighborChanged` fan-out every *neighbour* update uses. This is the whole of the default implementation, from one update arriving to the next batch leaving. ```mermaid flowchart TB IN["RedStoneWireBlock.neighborChanged arrives on the server"] CLIENT{"is this a ClientLevel"} SURV{"RedStoneWireBlock.canSurvive"} DROP["dropResources and Level.removeBlock"] NOTHING["nothing at all. Level.updateNeighborsAt and Level.neighborChanged are empty on Level"] BLK["RedStoneWireBlock.getBlockSignal: set shouldSignal false, ask SignalGetter.getBestNeighborSignal, set it back"] WIRE["RedstoneWireEvaluator.getIncomingWireSignal: the best of the four side wires, the wire above a conducting neighbour, the wire below a non-conducting one, minus one"] TARGET["DefaultRedstoneWireEvaluator.calculateTargetStrength: block signal if it is 15, else the larger of the two"] SAME{"is the target the POWER already stored"} STOP["return. No write, no fan-out, and the cascade ends here"] WRITE["Level.setBlock with flag 2 alone. Clients are told, no neighbour updates come from the write itself, shape updates still run"] FAN["seven Level.updateNeighborsAt calls: this position and its six neighbours, collected in a hash set"] OUT["forty-two neighbour updates, queued in CollectingNeighborUpdater.addedThisLayer and run before the caller's remaining directions"] IN --> CLIENT CLIENT -- "yes" --> NOTHING CLIENT -- "no, and not a wire-sourced update under the feature flag" --> SURV SURV -- "no" --> DROP SURV -- "yes" --> BLK BLK -- "block signal under 15" --> WIRE --> TARGET BLK -- "block signal 15, so the wires are never asked" --> TARGET TARGET --> SAME SAME -- "yes" --> STOP SAME -- "no" --> WRITE --> FAN --> OUT ``` The two facts that make the staircase are both in that figure. The write uses `Block.UPDATE_CLIENTS` **alone**, so the fan-out is not the one `Level.setBlock` would have done — it is issued afterwards, by hand, over seven positions rather than one. And the recursion terminates on *value*, not on distance: a wire whose recomputed strength equals what it already holds writes nothing and tells nobody. A line going dark therefore re-enters every wire once per step of the descent, and each visit is the last one only when the value has stopped moving. None of those intermediate writes is ever sent. `Level.sendBlockUpdated` only records the position in a set on the `ChunkHolder`, and `ChunkHolder.broadcastChanges` builds the packet once per tick by reading the level again — so a position written five times in a tick is broadcast once, with the value it ended on. ## What a block answers when it is asked for power Three questions, all on `BlockBehaviour.BlockStateBase`, all answered by the block. `BlockBehaviour.BlockStateBase.isSignalSource` is whether the block emits at all. `BlockBehaviour.BlockStateBase.getSignal` is the **weak** signal it offers to a given face. `BlockBehaviour.BlockStateBase.getDirectSignal` is the **strong** one, and the difference between them is entirely a matter of who is allowed to pass it on. Conduction is `BlockBehaviour.BlockStateBase.isRedstoneConductor`, from `BlockBehaviour.Properties.isRedstoneConductor`, which defaults to *is this state's collision shape a full block*. `SignalGetter` is where the two meet, and the join is easy to get backwards. `SignalGetter.getSignal` reads the block's own weak signal and then, **only if that block is a redstone conductor**, takes the larger of it and `SignalGetter.getDirectSignalTo` — the strongest signal being pushed into that position from any of its six neighbours. It is a maximum, not a choice between two modes: a powered conductor offers the greater of what it emits itself and what is being forced into it. That single line is what "strongly powered" means, and it is why a block with a lever on it powers the dust beside it. A block a wire merely points into is strongly powered too — that is what a piston beside a line reads — but no *other dust* can see it, for a reason that has nothing to do with this line and everything to do with `RedStoneWireBlock.shouldSignal`, below. The lever shows both halves at once. `LeverBlock.ownSignal` is 15 in every direction when powered — that is the weak signal, and it is what the dust next to the lever reads. `LeverBlock.getDirectSignal` is 15 only into the one block the lever is attached to. So the block behind a lever becomes a source in its own right, and everything touching *that* block sees 15 too. ### Three direction orders, and only one of them is about reading Three fixed direction orders run through this page and they are not interchangeable. Two decide who gets **told** something; the third decides what a block **reads**, and it is the one this page uses. | array | order | what it governs | |---|---|---| | `SignalGetter.DIRECTIONS` | down, up, north, south, west, east | what a block reads. Only `SignalGetter.getBestNeighborSignal` walks the array; `SignalGetter.getDirectSignalTo` and `SignalGetter.hasNeighborSignal` are written out by hand in the same order | | `NeighborUpdater.UPDATE_ORDER` | west, east, down, up, north, south | which neighbour is told first about a change, on the neighbour channel | | `BlockBehaviour.UPDATE_SHAPE_ORDER` | west, east, north, south, down, up | which neighbour is asked first to re-fit, on the shape channel | All three stop early, and not on the same thing: the two that return a number stop at a 15, while `SignalGetter.hasNeighborSignal` — which returns a boolean — stops at the first answer above zero. That is not a micro-optimisation with no consequences: a position saturated from one side never reads the others at all. `SignalGetter.DIRECTIONS` is plain `Direction` order, which is the only reason its first entry is *down*. ## Dust, and how far it reaches `RedStoneWireBlock.POWER` is the number, 0 to 15. Four `RedstoneSide` properties — one per horizontal — record how the wire is drawn and, more importantly, which sides it will actually talk through. What a wire is worth to its neighbours is `RedstoneWireEvaluator.getIncomingWireSignal`, and the *minus one per block* everyone knows lives in its last line: it takes the best of the wires it can see and subtracts one, floored at zero. The wires it can see are the four beside it, plus the wire on top of a conducting neighbour when nothing conducts above this position, plus the wire below a non-conducting neighbour — which is the *power* half of "dust climbs a block and falls down one". The drawing half is `RedStoneWireBlock.getConnectingSide` below, which asks `BlockBehaviour.BlockStateBase.isFaceSturdy` where this one asks about conduction. Two asymmetries follow from `RedStoneWireBlock.getSignal` and are worth stating plainly, because they are the two questions every redstone build eventually asks. A wire returns **zero** when the direction asked about is `Direction.DOWN` — so dust never powers the block above it. It returns its full power without any connection test when the direction is `Direction.UP` — so dust always powers the block below it. In every other direction it answers only if its connection on the opposite side is made. Connection itself is two rules and a completion pass. `RedStoneWireBlock.shouldConnectTo` is the real one: another wire always, a `Blocks.REPEATER` along its own axis, a `Blocks.OBSERVER` only from its facing side, and otherwise any block that says it is a signal source — that last clause only when a direction is supplied, which the vertical rules do not do, so up and down connect to wire and to nothing else. `RedStoneWireBlock.getConnectingSide` adds the vertical cases — up over a face-sturdy neighbour, down past a non-conducting one. And then `RedStoneWireBlock.getConnectionState` runs a completion pass that produces most of the confusion: **if a wire has no north or south connection, west and east are set anyway**, and the same the other way round. That is why a lone dust is drawn as a cross, and why a wire fed from the west appears to point firmly into whatever is on its east — a piston, say — which satisfies neither real rule. The piston is not a source and, by `Blocks.pistonProperties`, not a conductor. It gets powered anyway, because the wire's east side is *SIDE* by completion and `RedStoneWireBlock.getSignal` asks about the side, not about the neighbour. > **For a 1.21-era reader.** `BlockBehaviour.neighborChanged` now takes a > nullable `Orientation` rather than a source `BlockPos`, and > `BlockBehaviour.affectNeighborsAfterRemoval` — which replaced the old > removal hook — does not take one at all. `RedStoneWireBlock.shouldSignal` is the oddest thing on the page: a mutable boolean on the block singleton, flipped false for the duration of `RedStoneWireBlock.getBlockSignal` so that a wire does not count itself or its neighbouring wires as sources while it works out its *block* power. It works because the server thread is the only writer and never re-enters the method. ## The lever, two dust, and a powered piston ```mermaid sequenceDiagram participant LevB as LeverBlock participant SL as ServerLevel participant CNU as CollectingNeighborUpdater participant RSWB as RedStoneWireBlock participant DRWE as DefaultRedstoneWireEvaluator participant PBB as PistonBaseBlock Note over LevB,PBB: all of this is one call stack, inside one packet handler, before the level ticks LevB->>SL: setBlock POWERED with flags 3 SL->>CNU: the write's own fan-out at the lever, drained on the spot CNU->>RSWB: neighborChanged at the first dust RSWB->>DRWE: getBlockSignal sees the lever at 15, so the target is 15 DRWE->>SL: setBlock POWER 15 with flag 2, then seven updateNeighborsAt CNU->>RSWB: neighborChanged at the second dust, depth-first, ahead of the lever's other directions RSWB->>DRWE: block signal 0, incoming wire signal 15 minus 1 DRWE->>SL: setBlock POWER 14 with flag 2, then seven more CNU->>PBB: neighborChanged, and the piston asks only whether the wire's east side is above zero Note over CNU,PBB: the remaining dozens of updates run against blocks that do not care, and the count resets LevB->>SL: updateNeighbours by hand, two more fan-outs: the lever and the block it stands on ``` The lever is the place in this trace where the client does not even try. `LeverBlock.useWithoutItem` writes no state on a `ClientLevel` — it spawns a particle, and only when the lever is going *on* — so unlike a door, a lever is not predicted, and the client's dust changes colour only when the block updates arrive. The dust and the piston do nothing on the client either, but for the ordinary reason: nothing ever calls their `BlockBehaviour.neighborChanged` there. The sound follows the same split for a different reason: `LeverBlock.pull` is handed a null player, so nobody is excluded and the clicker hears the server's `ClientboundSoundPacket` like everyone else. Compare [block interaction](block-interaction.md), where the door passes the clicker as *except* and they hear their own prediction instead. Two details in the diagram are worth naming. The lever's `Level.setBlock` uses flags 3, so `Block.UPDATE_NEIGHBORS` fans out once *before* `LeverBlock.updateNeighbours` fans out twice more, at the lever's own position and at the block it stands on — and because nothing was running when the first one was queued, it drains the entire dust cascade before the other two are even issued. And the ordering of the seven positions a wire updates is fixed by no array: they come out of a hash set. The depth-first drain of `CollectingNeighborUpdater` is [block interaction](block-interaction.md)'s subject, and it is what puts the second dust's whole cascade ahead of the lever's remaining directions. Placing and breaking a wire take a wider path again: `RedStoneWireBlock.onPlace` and `RedStoneWireBlock.affectNeighborsAfterRemoval` both call `RedStoneWireBlock.updateNeighborsOfNeighboringWires`, which walks the four horizontals and then the diagonals — reaching over a conducting neighbour and under a non-conducting one — and calls `RedStoneWireBlock.checkCornerChangeAt` on each. That is another seven `Level.updateNeighborsAt` per wire found. Which write does what is [blocks and states](blocks-and-states.md#the-two-update-channels). ## The second implementation `FeatureFlags.REDSTONE_EXPERIMENTS` is a feature flag, turned on by a built-in data pack whose entire content is the line that enables it, and `RedStoneWireBlock.useExperimentalEvaluator` asks the level for it on **every call** — `RedStoneWireBlock.evaluator` is always the default one, and an `ExperimentalRedstoneWireEvaluator` is a fresh object per update, because it carries working state. What changes is not speed but semantics. `ExperimentalRedstoneWireEvaluator.calculateCurrentChanges` computes the whole connected network before writing anything. Phase one drains `ExperimentalRedstoneWireEvaluator.wiresToTurnOff`: a wire whose recomputed power is lower than its stored value goes to **zero** in the working map rather than to its new value, and is re-queued into the turn-on deque if it has block power of its own. Phase two drains `ExperimentalRedstoneWireEvaluator.wiresToTurnOn`, raising each to its true value. Both phases spread through `ExperimentalRedstoneWireEvaluator.propagateChangeToNeighbors` and `ExperimentalRedstoneWireEvaluator.enqueueNeighborWire`, and every wire they reach is recorded in `ExperimentalRedstoneWireEvaluator.updatedWires`, an insertion-ordered map of position to a packed orientation and power. Only then are the states written — the write pass drops any entry whose stored power already matches, so what survives it is the wires that really changed — with `Block.UPDATE_CLIENTS` and, for every wire but sometimes the first, `Block.UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE`, which `NeighborUpdater.executeShapeUpdate` honours by skipping any shape update whose **target** is dust, whatever the source. The fan-out is the other half of the difference. `ExperimentalRedstoneWireEvaluator.causeNeighborUpdates` issues one `Level.neighborChanged` per *connected* side per changed wire — connected meaning the four horizontals the wire's own state records, plus `Direction.DOWN` unconditionally and `Direction.UP` never — in `Orientation.getDirections` order — an order derived from where the update came from rather than from a fixed array — and, where that side is a redstone conductor, five more at that conductor's own sides. That is how the experimental evaluator carries strong power without the seven-position scattergun. And `RedStoneWireBlock.neighborChanged` ignores wire-sourced updates entirely in this mode, which is what closes the recursion; the default evaluator does not, which is what opens it. ## Questions players ask **Does a long line of dust really count down through every value when it turns off?** Inside the tick, yes: each wire recomputes independently and tells its neighbours only when its own number moved, so the far end is reached once for each value the near end passes through on the way down. On screen, no. `ChunkHolder.broadcastChanges` builds one packet per changed position per tick by reading the level, so a position written five times sends the last value once. The staircase costs neighbour updates, not frames — and the experimental evaluator exists to make it one ordered pass instead. **Why does dust point into a block that cannot be powered?** Because the drawing rule and the powering rule are different rules. `RedStoneWireBlock.getConnectionState` fills in the missing half of a line whenever the perpendicular axis is empty, and `RedStoneWireBlock.getSignal` then answers on the strength of that filled-in side. Whether the neighbour does anything with the signal is the neighbour's business. **Why does dust power the block underneath it but not the one above?** `RedStoneWireBlock.getSignal` returns zero for `Direction.DOWN` and returns full power for `Direction.UP` without checking any connection. Those are two lines in one method, and every "torch under the dust" contraption rests on them. **Does a redstone torch really burn out after a fixed number of flickers?** Yes, and every number in the mechanism is a literal. `RedstoneTorchBlock.RECENT_TOGGLES` is a weak map from level to a list of toggles; `RedstoneTorchBlock.tick` prunes anything older than 60 ticks off the front of it, and `RedstoneTorchBlock.isToggledTooFrequently` burns the torch out on the **eighth** surviving entry for that position. `RedstoneTorchBlock.MAX_RECENT_TOGGLES`, `RedstoneTorchBlock.RECENT_TOGGLE_TIMER` and `RedstoneTorchBlock.RESTART_DELAY` hold 8, 60 and 160, and nothing in the corpus reads any of the three. ## Where to look `SignalGetter.getSignal` · `SignalGetter.getDirectSignalTo` · `SignalGetter.getBestNeighborSignal` · `SignalGetter.getControlInputSignal` · `SignalGetter.DIRECTIONS` · `BlockBehaviour.BlockStateBase.isRedstoneConductor` · `LeverBlock.pull` · `LeverBlock.updateNeighbours` · `RedStoneWireBlock.neighborChanged` · `RedStoneWireBlock.getBlockSignal` · `RedStoneWireBlock.getSignal` · `RedStoneWireBlock.getConnectionState` · `RedStoneWireBlock.getConnectingSide` · `RedStoneWireBlock.shouldConnectTo` · `RedStoneWireBlock.updateNeighborsOfNeighboringWires` · `RedstoneWireEvaluator.getIncomingWireSignal` · `DefaultRedstoneWireEvaluator.updatePowerStrength` · `ExperimentalRedstoneWireEvaluator.calculateCurrentChanges` · `ExperimentalRedstoneWireEvaluator.causeNeighborUpdates` · `NeighborUpdater.executeShapeUpdate` · `RedstoneTorchBlock.isToggledTooFrequently` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Pistons and block events > Verified against **Minecraft 26.2** · Part V · A powered piston pushes one stone block, and the client is never told where the moving blocks are. A piston cannot act when it is asked. A repeater told about a change books a turn in the appointment book and a wire recomputes itself on the spot; `PistonBaseBlock.checkIfExtend` does neither. It appends a four-value record to a set on the level and returns, and the push happens later, in a phase of the level tick named for exactly this. What comes out the other side is stranger still: **no block update is ever sent for the moving blocks.** The placeholders the server writes carry `Block.UPDATE_CLIENTS` deliberately clear, so nothing incremental is generated for them, and the copy on your screen exists only because your client re-ran `PistonBaseBlock.moveBlocks` itself against its own world, off a single `ClientboundBlockEventPacket`. It is not a prediction that gets confirmed. Nothing checks that the two animations agree; if that one packet is lost you watch nothing move, and only the landing write puts the block where everyone else already sees it. ## The cast | class | what it decides | thread | |---|---|---| | `BlockEventData` | the record itself: a position, a block, and two ints | a record, no thread | | `ServerLevel` | that a block event is *queued* rather than run, and the one phase per tick in which the queue drains | Server | | `Level` | that on any other level — which in practice means `ClientLevel` — a block event runs **immediately** | Render | | `PistonBaseBlock` | whether to extend, which of three events to raise, and — at the drain, having re-checked — what actually moves | Server, then the client's copy | | `PistonStructureResolver` | the set of blocks that move and the set that is destroyed, or a flat refusal | either side, allocated per attempt | | `MovingPistonBlock` | the placeholder state that occupies a position during the motion | either side | | `PistonMovingBlockEntity` | the two ticks of motion, the entities shoved along, and what is left behind at the end | both sides, in the block-entity phase | | `PistonHeadBlock` | the arm once the motion is over, and forwarding neighbour updates back to the base | Server | ## The queue, and which tick it drains in `Level.blockEvent` runs `BlockBehaviour.BlockStateBase.triggerEvent` on the spot. `ServerLevel.blockEvent` overrides it to add a `BlockEventData` to `ServerLevel.blockEvents` and return. That single override is the whole mechanism, and it is the reason the two sides of a piston behave so differently: the server always defers, the client never does. The drain is `ServerLevel.runBlockEvents`, in the *blockEvents* section of `ServerLevel.tick`, after *tickPending* and *chunkSource* and before *entities* ([the level tick](../server/server-level-tick.md)). It is worth being exact about what that timing means, because "a block event is a tick late" is only sometimes true: - **Queued by a packet handler — the same tick.** `MinecraftServer.processPacketsAndTick` drains the queued packets and *then* calls `MinecraftServer.tickServer` in the same lap ([the server tick](../server/server-tick.md)), so a lever a player flipped is handled before the level ticks at all, and the event it raised is drained in that same level tick. - **Queued by a scheduled tick — the same tick.** *tickPending* runs before *blockEvents*, so a repeater firing into a piston is also drained immediately ([scheduled ticks](../world/scheduled-ticks.md)). - **Queued by another block event — the same tick.** `ServerLevel.runBlockEvents` drains until the set is empty, so an event raised while the drain is running is taken by the same drain. - **Queued by an entity or a block entity — the next tick.** Those phases run after *blockEvents*, so anything they raise waits a full lap. A landing `PistonMovingBlockEntity` is one step short of this group: it raises no event itself, but its `Level.neighborChanged` can reach a neighbouring piston, whose `PistonBaseBlock.checkIfExtend` then queues one for next tick. - **In a chunk that is not block-ticking — parked.** Such an event goes to `ServerLevel.blockEventsToReschedule` and is re-added *after* the loop, so it is retried next tick rather than dropped. `ServerLevel.blockEvents` is a linked hash **set**, so two identical events raised in one tick collapse into one. And `ServerLevel.doBlockEvent` re-reads the position and runs the event only if the block there is still the block the event named — the same promise a scheduled tick makes. When `BlockBehaviour.BlockStateBase.triggerEvent` returns true, and only then, a `ClientboundBlockEventPacket` goes to every player within 64 blocks. The piston is the mechanism's most demanding customer but not its only one. Three blocks raise events directly — `PistonBaseBlock`, `NoteBlock` and `PotentSulfurBlock` — and seven block entities raise their own, reaching themselves back through `BaseEntityBlock.triggerEvent`, which is how a chest lid, an ender chest, a shulker box, a bell, a decorated pot, a spawner and an end gateway all get animated on clients that own no copy of their state. `ComparatorBlock` is the odd one out and worth a moment: it overrides `BlockBehaviour.BlockStateBase.triggerEvent` to forward to its block entity, but `ComparatorBlockEntity` overrides nothing and nothing anywhere raises a comparator event, so the override is dead in both directions. ## One push, tick by tick ```mermaid sequenceDiagram participant SL as ServerLevel participant PBB as PistonBaseBlock participant PSR as PistonStructureResolver participant PMBE as PistonMovingBlockEntity participant CPL as ClientPacketListener participant CL as ClientLevel Note over SL,CL: tick N, a packet handler, before the level ticks SL->>PBB: neighborChanged, so checkIfExtend PBB->>PBB: getNeighborSignal finds the wire, and EXTENDED is false PBB->>PSR: resolve, as a dry run. Stone is pushable, air beyond it PBB->>SL: blockEvent TRIGGER EXTEND with the facing packed in. Nothing moves Note over SL,CL: tick N, blockEvents phase, still the same tick SL->>PBB: doBlockEvent re-reads the block, then triggerEvent PBB->>PBB: getNeighborSignal again. A pulse shorter than the gap dies here PBB->>PSR: resolve a second time, for real PBB->>SL: moveBlocks writes MOVING PISTON placeholders at flags 324, one for the stone and one for the arm PBB->>SL: then triggerEvent itself writes the extended base at flags 67 SL-->>CPL: ClientboundBlockEventPacket within 64 blocks, and a sound packet CPL->>CL: Level.blockEvent runs immediately on the client CL->>PBB: the same triggerEvent, the same moveBlocks, against the client's world Note over SL,CL: tick N, blockEntities phase, and tick N plus 1, both sides PMBE->>PMBE: progress 0 to 0.5 to 1, moveCollidedEntities under NOCLIP Note over SL,CL: tick N plus 2, blockEntities phase PMBE->>SL: the placeholder becomes the real stone at flags 67, and the arm a PISTON HEAD ``` ## How a piston decides, and the line that cannot fire `PistonBaseBlock.getNeighborSignal` is the whole of quasi-connectivity, and it is one short method. It asks `SignalGetter.hasSignal` at all six neighbours except the one it faces; then, if none of those answered, it repeats the same question for five of the neighbours of the position **directly above** the piston, skipping `Direction.DOWN` because that would only read the piston again. The piston is not the only block that reaches up like this — `DispenserBlock.neighborChanged` asks `SignalGetter.hasNeighborSignal` at its own position *or* at the one above it, and `DropperBlock` inherits that, and `DoorBlock.getStateForPlacement` does the same — but each of the three writes the reach out by hand, and no other block in the game has it. That is why quasi-connectivity is a short list of block-by-block quirks rather than a redstone rule. What signal means, and how the wire beside the piston comes to be connected to it at all, is [signal and dust](signal-and-dust.md). Between the two loops sits a third question — `SignalGetter.hasSignal` at the piston's *own* position, looking down — and it can never return true. `SignalGetter.getSignal` consults the strong power pushed into a position only when the block there is a redstone conductor, and `Blocks.pistonProperties` declares a piston never to be one; `PistonBaseBlock` overrides no signal method of its own, so its weak answer is zero. Strong power reaches a piston the ordinary way, through its conducting neighbours, in the first loop. The middle call is dead. `PistonBaseBlock.checkIfExtend` turns the answer into one of three events. Powered and not extended raises `PistonBaseBlock.TRIGGER_EXTEND` — but only if a dry-run `PistonStructureResolver.resolve` succeeds first, so a piston with an immovable wall in front of it queues nothing at all. Unpowered and extended raises `PistonBaseBlock.TRIGGER_CONTRACT`, or `PistonBaseBlock.TRIGGER_DROP` when the extension it would retract is still in flight: the block two ahead is still a `Blocks.MOVING_PISTON` facing the same way and extending, and either its progress is under half, or it was ticked this very game tick, or `ServerLevel.isHandlingTick` says the level is still inside the window that closes when the block-event drain ends. ## What moves, and what is simply gone `PistonStructureResolver` runs twice per push — once as `PistonBaseBlock.checkIfExtend`'s dry run, once for real inside `PistonBaseBlock.moveBlocks` — and produces two lists, `PistonStructureResolver.toPush` and `PistonStructureResolver.toDestroy`. `PistonStructureResolver.addBlockLine` walks forward from the piston until it runs out of blocks — and backwards along the same axis while the block behind is sticky — refusing the whole push, not merely stopping, when it meets something unpushable or runs past twelve. Twelve is a literal at each of the three tests; `PistonStructureResolver.MAX_PUSH_DEPTH` holds it and is read nowhere. `PistonStructureResolver.addBranchingBlocks` follows slime and honey sideways, with `PistonStructureResolver.canStickToEachOther` refusing the one pairing everybody tests first — slime against honey does not stick. `PistonBaseBlock.isPushable` is the per-block veto and it is a longer list than folklore suggests: outside the build height or the world border, obsidian and its three relatives, a block whose destroy speed is −1, a `PushReaction` of `PushReaction.BLOCK`, a `PushReaction.DESTROY` where the caller did not allow destruction, a `PushReaction.PUSH_ONLY` being moved the wrong way, an already-extended piston, and — the clause that explains the most — **anything with a block entity**. A push straight down at the bottom of the world or straight up at the top is refused too, and a piston itself skips the destroy-speed and push-reaction tests entirely. A chest cannot be pushed because it has a block entity; that `PistonMovingBlockEntity` has nowhere to keep one is the reading of the code that makes sense of the clause, not something the code says. ## The write nobody is told about A push writes five kinds of position, and their flag words are the page's hook made concrete. Four are `PistonBaseBlock.moveBlocks`'s; the fifth is written by `PistonBaseBlock.triggerEvent` after the other four. Which bit does what is [blocks and states](blocks-and-states.md#the-two-update-channels). | what | written by | flags | the bits that matter | |---|---|---:|---| | each moving block's destination, and the arm | `PistonBaseBlock.moveBlocks` | 324 | `Block.UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS`, `Block.UPDATE_MOVE_BY_PISTON`, `Block.UPDATE_INVISIBLE` — and **no** `Block.UPDATE_CLIENTS` | | an old arm cleared on a retraction | `PistonBaseBlock.moveBlocks` | 276 | the same three bits again, with `Block.UPDATE_KNOWN_SHAPE` in place of the piston bit — also **not** sent | | a vacated position, set to air | `PistonBaseBlock.moveBlocks` | 82 | `Block.UPDATE_MOVE_BY_PISTON`, `Block.UPDATE_KNOWN_SHAPE`, `Block.UPDATE_CLIENTS` — this one *is* sent | | a destroyed block, set to air | `PistonBaseBlock.moveBlocks` | 18 | `Block.UPDATE_KNOWN_SHAPE`, `Block.UPDATE_CLIENTS` | | the piston base, now extended | `PistonBaseBlock.triggerEvent` | 67 | `Block.UPDATE_MOVE_BY_PISTON`, `Block.UPDATE_CLIENTS`, `Block.UPDATE_NEIGHBORS` | The vacated row is rarer than it looks. `PistonBaseBlock.moveBlocks` starts with every pushed position marked for deletion and then unmarks each destination and, on an extension, the arm — so for a straight push down a line every origin is somebody's destination and the set empties. In this page's trace nothing is written at 82 at all: the client is told that the base is extended, and **nothing** about the two positions now holding placeholders. Each placeholder's `PistonMovingBlockEntity` is injected by hand with `Level.setBlockEntity` — `MovingPistonBlock.newBlockEntity` returns null, because a moving piston is never created by the ordinary block-entity path — and carries the real block as `PistonMovingBlockEntity.movedState`. `ClientPacketListener.handleBlockEvent` hands the packet to the base `Level.blockEvent`, which runs `PistonBaseBlock.triggerEvent` immediately against the `ClientLevel`: the same resolver, the same placeholders, the same injected block entities. What the client's copy does not do is the server's-side-only work — no drops for a crushed block, no game event, no `BlockBehaviour.BlockStateBase.affectNeighborsAfterRemoval` — and, most audibly, no sound. `PistonBaseBlock.triggerEvent` passes a null *except* entity, and `ClientLevel.playSeededSound` plays a sound only when *except* is the local player — so the piston you hear is the server's `ClientboundSoundPacket`, arriving beside the event. The particles of a crushed block are the mirror image: the level event that spawns them is raised inside `PistonBaseBlock.moveBlocks` on the **client** side only, and only for a block outside `BlockTags.FIRE`. ## Two ticks of motion, and two ways to end `PistonMovingBlockEntity.tick` runs in the block-entity phase on both sides and does one thing per tick: add 0.5 to `PistonMovingBlockEntity.progress`, after shoving whatever is in the swept slab with `PistonMovingBlockEntity.moveCollidedEntities` and dragging honey-stuck entities with `PistonMovingBlockEntity.moveStuckEntities`. The `PistonMovingBlockEntity.NOCLIP` thread-local is set around each entity's own move — and holds the push `Direction` rather than a flag — so that a pushed entity may pass through the very block pushing it. `PistonMovingBlockEntity.TICKS_TO_EXTEND` is declared as 2, and the 0.5 is written as a literal — no reader of the constant survives the decompile. The tick *after* progress reaches 1 is the landing. The entity is removed, and `Block.updateFromNeighbourShapes` re-fits the moved state to its new surroundings before it is written at flags 67, with a waterlogged property cleared if it survived the trip. The client holds five extra `PistonMovingBlockEntity.deathTicks` before doing the same, which is why the visual arrival lags the server's slightly — and why it does not matter, since the server's own write is broadcast anyway. `PistonMovingBlockEntity.finalTick` is a **different** operation, not an early-exit form of that one, and the difference is what makes an interrupted extension clean up after itself. It writes at flags 3 rather than 67, and for the entity carrying the arm — the one with `PistonMovingBlockEntity.isSourcePiston` — it writes **air** instead of the moved state, so a retraction that catches its own extension in flight leaves nothing behind. That flag is set both on the arm's placeholder and on the one a contracting piston writes at its own position. It is reached from `PistonMovingBlockEntity.preRemoveSideEffects`, and directly from `PistonBaseBlock.triggerEvent`'s contract branch. ## Questions players ask **Is a piston really a tick late?** The queue is not a fixed delay — it is a wait for one named phase. A lever flipped by a player and a repeater firing both land in the same tick the piston was told about, because packets and scheduled ticks are both handled before the *blockEvents* phase. What you see as the piston's delay is two ticks of motion and a third for the landing, and the first of the three is the very tick the piston was told about: the placeholders' tickers go straight into the level's list, which the *blockEntities* phase then walks later in the same tick. **Why did my one-tick pulse move nothing at all?** Because `PistonBaseBlock.triggerEvent` asks `PistonBaseBlock.getNeighborSignal` again at the drain, and a piston no longer powered simply returns false. The event is consumed, no blocks move, and no `ClientboundBlockEventPacket` is sent, so nobody sees anything happen either. **Why does a piston push a block that is powering it?** Because the two questions are asked of different positions. `PistonBaseBlock.getNeighborSignal` deliberately skips the direction the piston faces when looking for power, so the block in front never counts as a source — and then reaches up a block, which is the only place in the game anything does. **Can a piston push a chest?** No, and the reason is one clause in `PistonBaseBlock.isPushable`: anything with a block entity is refused. `PistonMovingBlockEntity` has a field for the moved *state* and none for a moved block entity, so there is nowhere to put a chest's contents for the two ticks of the journey. **Why did the blocks stay put on my screen when everyone else saw them move?** Because the placeholders are not synchronised. Your client built its copy by re-running the push from one `ClientboundBlockEventPacket`, and that packet is sent once, to players within 64 blocks, only if the server's own `BlockBehaviour.BlockStateBase.triggerEvent` returned true. Nothing checks afterwards that the two animations agree; what does converge is the destination, because the landing write carries `Block.UPDATE_CLIENTS` and is broadcast like any other. ## Where to look `Level.blockEvent` · `ServerLevel.blockEvent` · `ServerLevel.runBlockEvents` · `ServerLevel.doBlockEvent` · `BlockEventData` · `BlockBehaviour.BlockStateBase.triggerEvent` · `BaseEntityBlock.triggerEvent` · `PistonBaseBlock.checkIfExtend` · `PistonBaseBlock.getNeighborSignal` · `PistonBaseBlock.triggerEvent` · `PistonBaseBlock.moveBlocks` · `PistonBaseBlock.isPushable` · `PistonStructureResolver.resolve` · `PistonStructureResolver.addBlockLine` · `PistonStructureResolver.addBranchingBlocks` · `MovingPistonBlock.newMovingBlockEntity` · `PistonMovingBlockEntity.tick` · `PistonMovingBlockEntity.finalTick` · `PistonMovingBlockEntity.moveCollidedEntities` · `ClientPacketListener.handleBlockEvent` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Diodes and the observer > Verified against **Minecraft 26.2** · Part V · A repeater, a comparator and an observer in one circuit — three blocks that learn about the world three different ways, and one of them is not listening to redstone at all. Put a repeater, a comparator and an observer side by side and they look like variations on one idea: flat-looking blocks that take a signal in one side and push one out the other. Two of them genuinely are — `RepeaterBlock` and `ComparatorBlock` are both `DiodeBlock`s and share all of their output machinery but the number they emit. The observer is not a diode at all — it is a `DirectionalBlock`, a full cube, with a six-way facing — and the way it finds out that something changed is the page's hook: **`ObserverBlock` fires from `ObserverBlock.updateShape` — a *shape* update — so the one block whose entire job is noticing change is not on the channel that carries change notifications.** That is not a curiosity. It is why an observer sees a door opened by hand, an event that writes with `Block.UPDATE_NEIGHBORS` clear and so fires no neighbour update of its own. And the repeater quietly uses the same trick for its lock. ## The cast | class | what it decides | thread | |---|---|---| | `DiodeBlock` | everything the repeater and the comparator share: what counts as input, what counts as a side input, and how output leaves | Server | | `RepeaterBlock` | a delay in two-tick units, and whether it is locked | Server | | `ComparatorBlock` | one arithmetic operation, and how far in front of itself it can see | Server | | `ComparatorBlockEntity` | one integer — the comparator's whole reason for having a [block entity](block-entities.md) at all | Server | | `ObserverBlock` | that a neighbour's *state* changed, on a channel the other two do not use for input | Server | | `Level` | that any write of a state with an analog output pokes the comparators around it | Server | ## Three blocks, five rows Five rows are the whole of what a redstone circuit sees them do differently. | | `RepeaterBlock` | `ComparatorBlock` | `ObserverBlock` | |---|---|---|---| | **what it reads from the front** | `DiodeBlock.getInputSignal` — the signal at the block it faces, and if that is under 15, the raw `RedStoneWireBlock.POWER` of a wire there | the same, then overridden: an analog output if the block in front has one, else one block further through a conductor | nothing. It reads no signal at all | | **what it reads from the sides** | `DiodeBlock.getAlternateSignal`, restricted to other diodes (`DiodeBlock.sideInputDiodesOnly` is true), and used only to lock | the same, unrestricted, and used as the second operand | nothing | | **how it books its turn** | `DiodeBlock.checkTickOnNeighbor` unchanged: `RepeaterBlock.DELAY` doubled, at one of three priorities | overrides it entirely: always a delay of 2, at `TickPriority.HIGH` or `TickPriority.NORMAL` | `ObserverBlock.startSignal` from a shape update: delay 2, no priority, and only if one is not already booked | | **what it stores** | everything, in the block state | the same, plus one int in a `ComparatorBlockEntity` | everything, in the block state | | **how it outputs** | `DiodeBlock.updateNeighborsInFront` | the same | `ObserverBlock.updateNeighborsInFront`, an independent copy making the same two calls | ## A diode never writes into its target The output half is the least-known part of all three blocks, and it is shared. A diode declares itself a source unconditionally (`DiodeBlock.isSignalSource`), answers `DiodeBlock.ownSignal` with `DiodeBlock.getOutputSignal` when `DiodeBlock.POWERED` and zero otherwise, and restricts `DiodeBlock.getSignal` to the one direction it faces — so a repeater offers its 15 to precisely one neighbour, and `DiodeBlock.getDirectSignal` hands out the same value, which is what makes a diode able to strongly power the block in front of it. A diode's `HorizontalDirectionalBlock.FACING` points at the **input**. `DiodeBlock.getStateForPlacement` takes the player's horizontal direction and reverses it, so the output is at `Direction.getOpposite` of its *facing*, and that is the position `DiodeBlock.updateNeighborsInFront` acts on. It does two things there: a direct `Level.neighborChanged` on the output block, and a `Level.updateNeighborsAtExceptFromFacing` around that same block, skipping the direction that points back at the diode. **It never writes a state into the target.** It notifies, and lets the target read back through `SignalGetter.getSignal` — which is why a repeater feeding a repeater works without either of them knowing what the other is. The signal leaves by an unexpected door. `DiodeBlock.tick` writes `DiodeBlock.POWERED` with `Block.UPDATE_CLIENTS` alone — flags 2, no neighbour bit — so `Level.setBlock`'s neighbour fan-out never runs, though its three shape passes still do. What actually propagates the change is `DiodeBlock.onPlace`, which `LevelChunk.setBlockState` runs on the server for any write without `Block.UPDATE_SKIP_ON_PLACE`, and which calls `DiodeBlock.updateNeighborsInFront` ([blocks and states](blocks-and-states.md#the-two-update-channels)). ## What each one can see `DiodeBlock.getInputSignal` reads the block it faces and then, if that gave less than 15, takes the maximum with the raw `RedStoneWireBlock.POWER` of a wire sitting there — the special case that lets a diode read a wire whose connection state does not point at it. `DiodeBlock.getAlternateSignal` reads the two horizontals perpendicular to the facing through `SignalGetter.getControlInputSignal`, and `DiodeBlock.sideInputDiodesOnly` decides what counts. For a repeater it is true, so only another diode can reach a repeater's side, and the value is used solely by `RepeaterBlock.isLocked`. For a comparator it is false, so a redstone block reads as 15, a wire reads as its power, and any signal source reads as its strong output — and the value is the comparator's second operand. `ComparatorBlock.getInputSignal` is where comparators earn their reputation. If the block in front has an analog output, that value replaces the redstone reading outright. Otherwise, if the reading is under 15 and the block in front is a redstone conductor, the comparator looks **one block further** and takes the best of two things there: the analog output of whatever block is at that position, and the reading of an `ItemFrame` — but only if **exactly one** frame in that block's space faces the way the comparator's `HorizontalDirectionalBlock.FACING` points. The direction test comes first and the count second, so a frame pointing some other way is not counted at all; two frames facing the comparator's way, and the comparator reads neither. A container's analog output is `AbstractContainerMenu.getRedstoneSignalFromContainer`: every slot's count divided by the smaller of the container's own cap and *that stack's* maximum size, summed, divided by the number of slots, and mapped onto 0–15. For every container that reaches this formula the cap is 99 and the stack's own maximum wins, so a chest of shulker boxes and a chest of cobblestone at the same item count read very differently. ## Booking a turn, and why a repeater turns off first All three answer a change by booking a turn rather than acting on it; the queue itself, its dedup rule and the drain are [scheduled ticks](../world/scheduled-ticks.md), which traces a repeater in full. What belongs here is *which priority each one asks for*, because that is where the two diodes stop agreeing. `DiodeBlock.checkTickOnNeighbor` books only when the diode is not locked, only when the current `DiodeBlock.POWERED` disagrees with the current input, and only when `LevelTickAccess.willTickThisTick` says nothing is already about to run there. It picks `TickPriority.EXTREMELY_HIGH` when `DiodeBlock.shouldPrioritize` holds — when the block it outputs into is itself a diode whose own input is not on the far side of it, so a diode reading this one or standing sideways to it, but not one aimed the same way — `TickPriority.VERY_HIGH` when the diode is currently on, and `TickPriority.HIGH` otherwise. So a diode's turn-off beats another's turn-on due on the same tick, and a diode feeding a diode beats both. The only `TickPriority.NORMAL` booking a repeater makes is `DiodeBlock.setPlacedBy`, at delay 1, when you place one into an already-powered spot. `ComparatorBlock.checkTickOnNeighbor` throws all of that away. It has no lock to consult — `DiodeBlock.isLocked` is false unconditionally and the comparator does not override it — its delay is a flat 2 whatever the state says, and it chooses between `TickPriority.HIGH` and `TickPriority.NORMAL` alone; the two urgent priorities a repeater relies on are not available to it. It also books on a second condition the repeater does not have: not only when the powered flag disagrees with the input, but whenever the *computed output value* differs from the int currently in the block entity. `DiodeBlock.tick` is what pulse extension is made of. Finding itself off, it turns on regardless of whether the input is still there — and then, if the input has already gone, books its own turn-off one delay later at `TickPriority.VERY_HIGH`. A pulse shorter than the delay is not swallowed; it is stretched to the delay. ## The channel the observer listens on ```mermaid flowchart TB NC["the neighbour channel: Level.updateNeighborsAt and Level.neighborChanged, server only. A write enters it only with Block.UPDATE_NEIGHBORS set, and a block may call it directly"] SC["the shape channel: Level.neighborShapeChanged, run on both sides by every write without Block.UPDATE_KNOWN_SHAPE"] RB["RepeaterBlock and ComparatorBlock: DiodeBlock.neighborChanged, then checkTickOnNeighbor"] RL["RepeaterBlock.updateShape recomputes LOCKED, but only off-axis and only on the server"] OB["ObserverBlock.updateShape, but only from the direction it faces and only while unpowered"] SS["ObserverBlock.startSignal books a tick at delay 2, and only if this is not the client and none is booked"] BOOK["the appointment book"] TICK["the scheduled tick runs: write POWERED with flag 2. The observer then calls updateNeighborsInFront itself, a diode reaches it as onPlace inside the write"] NC --> RB --> BOOK SC --> RL SC --> OB --> SS --> BOOK BOOK --> TICK ``` An observer watches for `ObserverBlock.updateShape` arriving from the one direction it faces, while `ObserverBlock.POWERED` is false, and books a two-tick appointment. Two ticks later `ObserverBlock.tick` writes the powered state with flags 2, schedules its own turn-off two ticks after that, and pulses through `ObserverBlock.updateNeighborsInFront`. The shape channel is the right one for the job because it carries *your neighbour's state changed* regardless of whether the neighbour told anybody: a door opened by hand writes with flags 10 and issues no neighbour update at all, and the observer still sees it ([block interaction](block-interaction.md)), and dust the observer is watching carries the same news through [signal and dust](signal-and-dust.md)'s flag-2 writes. `RepeaterBlock.LOCKED` works the same way, and it is the only diode property computed from a redstone reading *outside* tick time — `DiodeBlock.POWERED` is the only one computed from a reading at all. `RepeaterBlock.updateShape` recomputes it whenever a neighbour **off the facing axis** changes, which is the two sides and, harmlessly, up and down; the value itself comes from the two sides alone. So locking follows a neighbouring repeater's state without either block scheduling anything. The tempting conclusion is that both blocks chose the shape channel because it is the half of the update machinery a client also runs. That is not what the code does: **both hooks refuse to act on the client.** `RepeaterBlock.updateShape` recomputes the lock only when the level is not client-side, and `ObserverBlock.startSignal` returns immediately on a `ClientLevel`. Nor would it help if they did — a client keeps no appointment book at all, so a scheduled tick could never fire there. Everything a client knows about any of these three blocks arrives as a block update. ## One int, and the fan-out that exists to deliver it A comparator has a block entity for one reason, and it is not a common one: `ComparatorBlock.calculateOutputSignal` can produce a number that the block state has nowhere to keep. `DiodeBlock.POWERED` is one bit, and a comparator's output is 0–15. Plenty of redstone blocks have block entities — the sculk sensor keeps its last vibration frequency in one and answers with it, and every container answers from its contents — but a `DaylightDetectorBlockEntity` stores nothing at all, so having one is no evidence of state that a block state could not hold. `ComparatorBlockEntity.getOutputSignal` is that number, written by `ComparatorBlock.refreshOutputState` — which also flips the powered bit when it needs to, and then calls `DiodeBlock.updateNeighborsInFront` whether or not anything changed, whenever the mode is `ComparatorMode.COMPARE`. The other half of making comparators work is `Level.updateNeighbourForOutputSignal`. It walks the four horizontals and notifies any `Blocks.COMPARATOR` it finds; failing that, where the neighbour is a redstone conductor, it reaches one further and notifies a comparator there instead, mirroring the comparator's own reach in the opposite direction. Twelve call sites reach it, and the two general ones are the pair that matters here. `Level.setBlock` calls it on the server for a write that carries `Block.UPDATE_NEIGHBORS` and whose new state has an analog output — but an item entering a chest is not a write at all. That path is `BlockEntity.setChanged`, which calls it unconditionally, and it is what makes a comparator notice a hopper filling a chest that nothing else in redstone would have reported. ## Questions players ask **Why does an observer fire when I open a door next to it, when doors do not power anything?** Because the observer is not looking for power. It watches the shape channel, which every ordinary write runs on both sides, and a door opening is an ordinary write — flags 10, no neighbour updates, three shape passes. **Why did my repeater stay on after the input dropped?** Because `DiodeBlock.tick` turns a repeater on whether or not the input is still there, and books the turn-off one delay later. That is the mechanism behind pulse extension, and it is two entries in the scheduled-tick queue rather than any kind of memory. **Why can I lock a repeater with another repeater but not with a lever?** `DiodeBlock.getAlternateSignal` goes through `SignalGetter.getControlInputSignal` with `DiodeBlock.sideInputDiodesOnly` true for a repeater, and that flag makes the call answer zero for anything that is not a `DiodeBlock`. **Why does my comparator ignore the item frame?** Because `ComparatorBlock.getInputSignal` gathers only the frames in that block's space that face the way the comparator's own facing points, and then insists on exactly one of them. A second frame pointing the same way makes the count two and the method returns nothing rather than choosing; a second frame pointing anywhere else is never in the count at all. ## Where to look `DiodeBlock.isSignalSource` · `DiodeBlock.ownSignal` · `DiodeBlock.getDirectSignal` · `DiodeBlock.getInputSignal` · `DiodeBlock.getAlternateSignal` · `DiodeBlock.sideInputDiodesOnly` · `DiodeBlock.checkTickOnNeighbor` · `DiodeBlock.shouldPrioritize` · `DiodeBlock.tick` · `DiodeBlock.updateNeighborsInFront` · `DiodeBlock.onPlace` · `RepeaterBlock.getDelay` · `RepeaterBlock.isLocked` · `RepeaterBlock.updateShape` · `ComparatorBlock.checkTickOnNeighbor` · `ComparatorBlock.calculateOutputSignal` · `ComparatorBlock.getInputSignal` · `ComparatorBlock.refreshOutputState` · `ComparatorBlockEntity.getOutputSignal` · `AbstractContainerMenu.getRedstoneSignalFromContainer` · `Level.updateNeighbourForOutputSignal` · `ObserverBlock.updateShape` · `ObserverBlock.startSignal` · `ObserverBlock.tick` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # VI · Entities > Verified against **Minecraft 26.2** · Part VI · Everything in the world that is not a block: what one is, who is allowed to move it, how it is described to the other side, and how it stops. Part V was about a position in a chunk section changing its mind. This part is about everything that is *not* in the grid — the zombie, the arrow, the boat, the dropped pickaxe, the invisible marker a data pack left as a bookmark. They share one base class that is deliberately thin *on behaviour*, one numbered array for telling clients about themselves, one bag of named numbers, one collision resolver and one abstract method for being hurt. A player recognises the part by the things that seem inconsistent about it: a mob glides where your own movement is crisp, a sheared sheep changes on every screen at once, a mob you named never despawns, and a hit that lands during the red flash does nothing at all. Every one of those is the same question in a different costume — **which of the two programs is allowed to decide this?** ## The shape of the part Part VI is a ladder. Each page needs the one below it and nothing above it, and the second rung is the one everything else leans on. ```mermaid flowchart BT A["Entity anatomy — what an entity is"] B["Authority — who is allowed to simulate it"] C["Entity lifecycle — how it enters a world and leaves one"] D["Synched entity data — the channel that describes it"] E["Attributes — the named numbers it carries"] F["Movement and collision — what it does"] G["Goals and brains — why it does it"] H["Pathfinding — how a decision becomes a direction"] I["Damage and death — how it stops"] A -- "one type, one factory, one live object" --> B B -- "and one side of each pair does the arithmetic" --> C C -- "now it is in a world, findable and ticking" --> D D -- "one of six channels that describe it" --> E E -- "gravity, step height, speed: the physics knobs are attributes" --> F F -- "something has to set xxa and zza" --> G G -- "a decision is only a position until something walks there" --> H H -- "and everything above can be ended by one abstract method" --> I ``` One dependency runs forward instead of back, and it is Part X's rather than this part's: [the client level](../client/the-client-level.md) opens by saying it is *not* an authority either, which only lands once [authority](authority.md) has said what one is. So this part is watched first, and nothing here waits on Part X. ## Before you start [The level tick](../server/server-level-tick.md), because half this part's surprises are claims about *which phase* something ran in — the entity phase runs *after* the phase that broadcasts entity changes, and that one ordering explains why an attribute change is a tick late while a sheep sheared out of the packet queue, before the tick proper, is not. [Tickets and loading](../world/tickets-and-loading.md), because whether an entity ticks at all is a property of the chunk it is standing in — for everything except a player, which is exempt — and [chunk anatomy](../world/chunk-anatomy.md) for the heightmaps that decide where a mob may spawn. [Points of interest](../world/points-of-interest.md), because a villager's whole day is claims on them and [goals and brains](ai-goals-and-brains.md) draws `PoiManager` as a lane rather than explaining it. [Blocks and states](../blocks/blocks-and-states.md) for the shapes that entities collide with, and Part IV's *scheduled ticks* is *not* needed — entities keep no appointment book. One more Part IV page, for one lecture: [environment attributes and timelines](../world/environment-attributes-and-timelines.md) before [goals and brains](ai-goals-and-brains.md). A villager's schedule is a data-pack `Timeline` looked up at a position, and this part asks that system a question rather than teaching it. ## Watch in this order 1. [Entity anatomy](entity-anatomy.md) — one `EntityType` from the registry, through a factory, to a live object the level ticks. The registry's default is a pig, and that default reaches the network and never reaches your save file. 2. [Authority](authority.md) — a zombie, a player and a boat each take one step on both sides. The client runs no physics at all for the mob chasing you, and the server runs your own player's physics every tick and then overwrites the answer with a number you sent it. 3. [Entity lifecycle](entity-lifecycle.md) — a zombie appears in the dark, is ticked for a while, and is either forgotten or written to disk. The spawner rolls one height per category per chunk per tick, so caves and the open field compete for the same slice. 4. [Synched entity data](synched-entity-data.md) — a sheep is sheared and every screen in range agrees within the tick. The slot the wool lives in is written nowhere in `Sheep`: it is 18 because eighteen slots were handed out above it, and the packet stops at 254 because 255 means stop. 5. [Attributes](attributes.md) — Strength II lands, and no packet is sent at all. Eight of the forty attributes never reach the client, and attack damage is one of them. 6. [Movement and collision](movement-and-collision.md) — one tick of a falling zombie. The mover answers *what did I walk through* afterwards, by replaying the tick's movement, which is why fire and water touched in one step always end in the extinguish. 7. [AI: goals and brains](ai-goals-and-brains.md) — a villager's day, and the same machinery under a zombie that has none of it. Schedules are gone: a villager goes to bed because it asked the world what time it is where it is standing. 8. [Pathfinding](pathfinding.md) — the other half of the same lecture. Giving up is machinery: the node being walked towards carries a timeout, and the mob you watch walk into a wall and then wander off is running a scheduled surrender. 9. [Damage and death](damage-and-death.md) — the part's closer. An arrow, a dozen owners of one number, and one abstract method. A hit that lands inside the red flash usually does nothing at all, and when it is stronger than the last, only its excess lands — silently. ## Reference this part uses Three of them were written for this part. [Attributes](../../reference/attributes.md) — all forty, with defaults, ranges and the syncable flag. [Entity data serializers](../../reference/entity-data-serializers.md) — all 43, in wire-id order. [Damage outside `LivingEntity`](../../reference/non-living-damage.md) — what each of the twenty-one non-living classes does when you hit it. Then [packets](../../reference/packets.md), [registries](../../reference/registries.md), [game rules](../../reference/gamerules.md), [math and primitives](../../reference/math-and-primitives.md) and [diagram lanes](../../reference/lanes.md). The part stops at `Avatar`, the class 26.2 inserted below `Player`: everything player-shaped is Part VIII, drawing an entity is Part XI, and the prediction ledger behind the blocks you place is Part X. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Entity anatomy > Verified against **Minecraft 26.2** · Part VI · What an entity *is*: one `EntityType` from the registry, through a factory, to a live object the level ticks. You type */summon pig*, and by the next tick there is a pig standing where you are. Between the command and the animal are three objects and one factory call: a `ResourceKey` in `EntityTypeIds`, the `EntityType` that `EntityTypes` built from it before any world existed, and the `Entity` that the type's `EntityType.EntityFactory` returns. This page is the vocabulary of that chain, and the rest of Part VI is built on it. The surprise is what happens when the name is *wrong*, because that depends entirely on which door the name came through. `Registries.ENTITY_TYPE` is one of the few **defaulted** registries and its default is *pig* — and `DefaultedMappedRegistry` overrides nine lookups to hand it back, `DefaultedMappedRegistry.byId` and `DefaultedMappedRegistry.getValue` among them. So an entity-type id the client has never heard of, arriving inside a `ClientboundAddEntityPacket`, is decoded by `ByteBufCodecs.registry` through `IdMap.byIdOrThrow` — which cannot throw here, because `DefaultedMappedRegistry.byId` never returns null — and a pig walks out of the packet. One lookup is overridden the other way: `DefaultedMappedRegistry.getOptional` calls the *superclass* method and so still answers empty. `EntityType.CODEC` is `Registry.byNameCodec`, which resolves through the plain `Registry.get`, untouched here. The same unknown name in a region file therefore yields nothing at all: `EntityType.create` logs *Skipping Entity with id …* and leaves a hole where the entity was. The default reaches the network and never reaches your save file. ## The cast | class | what it decides | thread | |---|---|---| | `EntityType` | one registered kind: its factory, category, frozen dimensions, feature flags, and the two numbers that decide how it reaches clients | built in a class initialiser, read from both game threads after | | `EntityTypes` | which 158 kinds exist and what every one of them is *sized* like — the most useful single table in the package | class initialiser, once | | `Entity` | position, box, network id, synched values, vehicle, removal reason. Deliberately thin on behaviour | the tick thread of whichever level owns it | | `EntityDimensions` | width, height, eye height, attachment points, and whether `Attributes.SCALE` may touch them | immutable record, shared by every entity of a type | | `SynchedEntityData` | which of the entity's fields the other side is told about ([synched entity data](synched-entity-data.md)) | written on the owning side, applied on the receiving one | | `EntityInLevelCallback` | whether the object is *in* a world or merely on the heap | installed by the level's entity manager | | `ServerEntity` | when a tracked entity's state becomes packets ([entity lifecycle](entity-lifecycle.md)) | server main thread | | `EntityReference` | how one entity remembers another across a chunk unload | wherever it is resolved | Only one packet is this page's own: `ClientboundAddEntityPacket`, the birth announcement. Everything after it — `ClientboundSetEntityDataPacket`, `ClientboundEntityPositionSyncPacket`, `ClientboundEntityEventPacket`, `ClientboundRemoveEntitiesPacket` — belongs to a sibling page. ## The type is frozen, the entity is not An entity is anything in the world that is not a block: mobs, players, items on the ground, arrows, boats, item frames, experience orbs, the invisible markers a data pack uses as bookmarks. The kind is an `EntityType`, built once and never changed. The individual is an `Entity`, and nearly all of its state is mutable by design. ```mermaid flowchart TB ET["EntityType: one object per registered kind, 158 of them"] DIM["EntityDimensions: width, height, eye height, EntityAttachments, and a fixed flag. Frozen by EntityType.Builder.build"] CAT["MobCategory: the spawn cap and despawn distance"] FAC["EntityType.EntityFactory: the constructor reference"] NET["clientTrackingRange in chunks (default 5), updateInterval in ticks (default 3)"] E["Entity: the live object"] SED["SynchedEntityData: eight accessors defined inline, then whatever the subclass chain adds"] POS["Entity.position at the feet, and Entity.bb, a stored box rather than a computation"] CB["Entity.levelCallback: EntityInLevelCallback.NULL until a level takes the object"] REM["Entity.removalReason: null is the whole of the am-I-alive bit"] RIDE["Entity.vehicle and Entity.passengers, an immutable list"] ET --> FAC ET --> DIM ET --> CAT ET --> NET FAC -- "creates" --> E DIM -- "copied into Entity.dimensions and Entity.eyeHeight, both caches" --> E E --> SED E --> POS E --> CB E --> REM E --> RIDE ``` `Entity` implements nine interfaces — `Nameable`, `EntityAccess`, `ScoreHolder`, `SyncedDataHolder`, `DataComponentGetter`, `ItemOwner`, `SlotProvider`, `DebugValueSource` and `TypedInstance` over `EntityType` — which is a fair measure of how many systems reach into it. Health, AI, damage and inventory are all further down the tree. In 26.2 the type constants are **not on `EntityType`**. They live in two parallel files: `EntityTypeIds`, 158 `ResourceKey`s with no reference to any entity class, and `EntityTypes`, the 158 matching objects that `EntityType.Builder` produced from them. `MobCategory` — `MobCategory.MONSTER`, `MobCategory.CREATURE`, `MobCategory.AMBIENT`, `MobCategory.AXOLOTLS`, `MobCategory.UNDERGROUND_WATER_CREATURE`, `MobCategory.WATER_CREATURE`, `MobCategory.WATER_AMBIENT`, `MobCategory.MISC` — carries the spawn cap and despawn distance that [entity lifecycle](entity-lifecycle.md) uses. None of this is data-driven: `Registries.ENTITY_TYPE` is code-registered, and what a data pack *can* reach is the tags in `EntityTypeTags`, the loot table at *entities/<id>*, `DataComponents.ENTITY_DATA` on a spawn egg, and the per-species variant registries (`Registries.WOLF_VARIANT`, `Registries.CAT_VARIANT`, `Registries.PAINTING_VARIANT` and the rest). ### Dimensions, attachments and pose `EntityDimensions` is a record — width, height, eye height, an `EntityAttachments` map and a *fixed* flag — and `EntityDimensions.makeBoundingBox` centres the box in X and Z on the position and grows it **upward**, because the position is the feet. `EntityDimensions.scale` returns the record unchanged when it is fixed, and also when both factors are 1. The flag is not the only way to escape `Attributes.SCALE`, either: plain `Entity.getDimensions` never scales at all, so every non-living entity ignores scale for free, and `LivingEntity.getDimensions` — which is final, the overridable hook being `LivingEntity.getDefaultDimensions` — short-circuits a sleeping entity to `LivingEntity.SLEEPING_DIMENSIONS` before any scaling happens. `EntityAttachments` answers where a passenger sits, where the name tag floats, where the lead attaches: `EntityAttachment.PASSENGER`, `EntityAttachment.VEHICLE`, `EntityAttachment.NAME_TAG` and `EntityAttachment.WARDEN_CHEST`, each with a fallback such as `EntityAttachment.Fallback.AT_HEIGHT` that `EntityAttachments.Builder.build` fills in from the width and height. `Pose` is eighteen constants with **explicit wire ids**, not ordinals — the ones that matter are `Pose.STANDING`, `Pose.CROUCHING`, `Pose.SWIMMING`, `Pose.FALL_FLYING`, `Pose.SLEEPING`, `Pose.SPIN_ATTACK` and `Pose.DYING`, and the rest are single-mob animation states such as `Pose.EMERGING` and `Pose.DIGGING`. `Pose.BY_ID` is built with `ByIdMap.OutOfBoundsStrategy.ZERO`, so a pose id outside the range decodes silently to `Pose.STANDING` rather than failing the connection. Pose is the synched value on the *base* class that changes physics — a dozen subclasses have their own, from a pufferfish's puff state to a slime's size — and the loop is worth stating because everything else on this page hangs off it: `Entity.setPose` writes the value, `SynchedEntityData.set` calls `Entity.onSyncedDataUpdated` **inside the setter**, before anything is marked dirty, and that sees `Entity.DATA_POSE` and calls `Entity.refreshDimensions`, which asks `Entity.getDimensions` for the new record, overwrites both caches, and calls `Entity.reapplyPosition` to rebuild the box. The side that set the pose resizes immediately, the other resizes when the value lands. If the box grew, `Entity.fudgePositionAfterSizeChange` nudges the entity out of whatever it now overlaps — but only on the server, only after the first tick, only with physics on, only when the entity is not a `Player`, and only when the new box is at most four blocks in both width and height. ## The tree, and the class that was inserted into it `Entity` has **18** direct subclasses and 191 descendants. `LivingEntity` and its 124 descendants are two thirds of that; the non-living branches are the other 66.
*(figure: tree-Entity.svg — a generated SVG, not reproduced here)*
The Entity tree to three levels, generated from the decompile. Click to enlarge.
The full drawing, with the block, item and screen trees beside it, is in [what extends what](../../maps/hierarchy.md). What matters here is the shape: a long spine and a scattering. `LivingEntity` holds 124 of the 191 and has exactly **three** direct subclasses — `Avatar`, `ArmorStand` and `Mob` — which is worth saying plainly, because it means **an armour stand is a living entity with no AI at all**: no `GoalSelector`, no `PathNavigation`, both of those being `Mob`'s. The `Brain` is not `Mob`'s, though. It is declared on `LivingEntity`, built in its constructor and written under a *Brain* tag by every living entity, so an armour stand carries an empty one. `PathfinderMob` is 86 lines that add walk-target valuation, not movement, which is why `Ghast` and `Phantom` navigate without ever being one. `AgeableMob` adds babies, `Animal` and `Monster` split by disposition, and `Monster` implements `Enemy`, a marker interface carrying nothing but XP-reward constants. **`Avatar` is new and it is the biggest structural change in the part.** It sits between `LivingEntity` and `Player`, and it is 57 lines: the player-shaped `Avatar.POSES` dimension map, `Avatar.DEFAULT_EYE_HEIGHT` of 1.62, the skin-part and handedness synched values (`Avatar.DATA_PLAYER_MAIN_HAND`, `Avatar.DATA_PLAYER_MODE_CUSTOMISATION`) and one abstract method, `Avatar.getProfile`. Its point is `Mannequin` — a posable, profile-skinned, player-looking entity in the decoration package that is *not* a `Player` and carries none of the inventory, abilities or hunger. Anything written against "`Player extends LivingEntity`" is now wrong by one level. On the client `AvatarRenderer` serves both `AbstractClientPlayer` and `ClientMannequin`, a client-only subclass that `Mannequin` accepts by holding a mutable `Mannequin.constructor` factory the client swaps at startup. Cutting across the tree are the capability interfaces, where most of the shared behaviour actually lives: `Leashable` is the fattest, with `Leashable.tickLeash` called from `Entity.baseTick`, and beside it `Bucketable`, `EquipmentUser`, `NeutralMob`, `Attackable`, `Targeting`, `TraceableEntity`, `OwnableEntity`, `Shearable`, `PlayerRideableJumping`, `ItemSteerable`, and `ContainerUser`, with exactly two implementors: `Player` and `CopperGolem`. `EntityReference` deserves a name of its own. It stores either a UUID or the live object, and `EntityReference.getEntity` resolves lazily *and decays back to the UUID* the moment the target reports itself removed. It is how "who last hurt me", "who owns this pet" and "who shot this arrow" survive a chunk unload. ### Where the 716 files are | subpackage | files | what | |---|---:|---| | `entity/ai` | 277 | goals, brains, navigation, attributes, sensors | | `entity/animal` | 130 | one subpackage per species now | | `entity/monster` | 84 | likewise | | `world/entity` itself | 75 | `Entity`, `LivingEntity`, `Mob`, `Avatar`, `EntityType`, `EntityTypes`, the capability interfaces | | `entity/projectile` | 37 | arrows, fireballs, thrown items | | `entity/boss` | 24 | dragon and wither | | `entity/vehicle` | 24 | boats and minecarts | | `entity/npc` | 15 | villagers and traders | | `entity/player` | 14 | `Player`, `Inventory`, `Abilities` | | `entity/decoration` | 12 | armour stands, frames, paintings, `Mannequin` | | `entity/variant` | 11 | the data-driven mob variants | | `entity/item`, `entity/raid`, `entity/ambient`, `entity/schedule` | 13 | items, raids, bats, villager day plans | ## From a registry entry to a live object `EntityTypes` runs `EntityType.Builder.build` for each of the 158 keys in `EntityTypeIds` and registers the result, and it is that call which freezes the `EntityDimensions` and its attachment points for the life of the type. Everything below happens per entity, long afterwards. ```mermaid sequenceDiagram participant SumC as SummonCommand participant ET as EntityType participant Entity as Entity participant SL as ServerLevel participant PESM as PersistentEntitySectionManager participant SE as ServerEntity participant CPL as ClientPacketListener SumC->>SumC: reject out of bounds, then reject on peaceful via isAllowedInPeaceful SumC->>ET: loadEntityRecursive(tag with an id string, level, EntitySpawnRequest) ET->>ET: by(ValueInput) reads id through EntityType.CODEC ET->>ET: create checks canSpawn, then calls the EntityFactory ET->>Entity: constructor takes the next id, invents a UUID, copies the type dimensions Entity->>Entity: eight base accessors, then defineSynchedData down the chain, then setPos ET->>Entity: load(ValueInput) then readAdditionalSaveData SumC->>Entity: snapTo, the postLoad processor, before any passenger exists ET->>Entity: each Passengers child loaded the same way, then startRiding SumC->>SL: tryAddFreshEntityWithPassengers, one addFreshEntity per body SL->>PESM: addNewEntity PESM->>Entity: setLevelCallback, and only now is it in a world Note over SL,Entity: the next server tick SL->>Entity: setOldPosAndRot, then the tick count rises, then tick Note over SE,CPL: later, when a player comes into range SE->>Entity: getAddEntityPacket SE->>CPL: ClientboundAddEntityPacket, bundled with data, attributes and equipment CPL->>CPL: createEntityFromPacket, then recreateFromPacket, then ClientLevel.addEntity ``` **Name to type.** `EntityType.by` reads the *id* field through `EntityType.CODEC`. An unknown id yields nothing, and the entity is dropped with the log line the opening quoted — this is the save-file half of the hook. **Type to object.** `EntityType.create` checks `EntityType.canSpawn`: the feature flags, plus a peaceful-difficulty test gated on the type's own `EntityType.isAllowedInPeaceful` flag, which is a declared property and not a synonym for *hostile*. `EntitySpawnRequest.ignoreChecks` skips both. Then the factory runs. The `Entity` constructor takes the next id from the level, invents a UUID with `Mth.createInsecureUUID`, copies the type's dimensions into its cache, and builds the synched-data container: eight accessors defined inline — the shared flags byte, air supply, custom name and its visibility, silence, no-gravity, pose and frozen ticks — and *then* the abstract `Entity.defineSynchedData`, which contributes nothing on the base class and exists only for the subclasses. It then calls `Entity.setPos` at the origin, so a fresh entity already has a full-size box rather than the zero-size `Entity.INITIAL_AABB` the field initialiser gave it. **Tag to state.** `Entity.load` reads position (clamped to ±3.0000512E7 horizontally), motion, rotation and UUID, then calls the abstract `Entity.readAdditionalSaveData`, then `Entity.reapplyPosition` again if `Entity.repositionEntityAfterLoad` says so — which everything says except `BlockAttachedEntity`, the corpus's one override, so paintings, item frames and leash knots are precisely the entities that *skip* it and keep the position their own load computed. The save twin is `Entity.addAdditionalSaveData`, and passengers save inside their vehicle: `Entity.save` returns false for anything currently riding, and the vehicle writes them into its *Passengers* list through `Entity.saveAsPassenger`, using `Entity.getEncodeId`, which is null for types that never serialise. **Object to level.** `ServerLevel.tryAddFreshEntityWithPassengers` refuses if any UUID in the stack is already loaded, then `ServerLevelAccessor.addFreshEntityWithPassengers` calls `ServerLevel.addFreshEntity` once per body. `PersistentEntitySectionManager.addNewEntity` files it into a section and replaces `EntityInLevelCallback.NULL` with a real callback. *That* is the moment it stops being an object on the heap; [entity lifecycle](entity-lifecycle.md) takes it from here. **Level to client.** `ServerEntity.addPairing` sends `ServerEntity.sendPairingData` as one bundle: the packet `Entity.getAddEntityPacket` returns — id, UUID, type, position, velocity, three rotation *bytes* and one varint of type-specific data — then the non-default synched values, then attributes and equipment. On the client, `ClientPacketListener.handleAddEntity` builds the object, calls `Entity.recreateFromPacket`, and only then adds it to the level. The spawn-egg path is a different pipeline that meets this one at the end. `EntityType.spawn` snaps the new entity out of collision with `EntityType.getYOffset`, aligns head and body rotation, runs `Mob.finalizeSpawn`, applies a `PostSpawnProcessor`, adds it through `ServerLevelAccessor.addFreshEntityWithPassengers` and plays an ambient sound. `SummonCommand` also calls `Mob.finalizeSpawn`, but only when the command asks for it, and it never touches the Y offset. ## The tick both sides share `ServerLevel.tickNonPassenger` calls `Entity.setOldPosAndRot`, increments `Entity.tickCount`, opens a profiler section named after the entity type and calls `Entity.tick` — through `Level.guardEntityTick`, which turns any exception into a crash report with the entity's details attached. `ClientLevel.tickEntities` does the same, through the same guard, having first skipped anything removed, riding or frozen by the tick-rate manager. No entity is ever *ticked* on a worker pool, and `Entity` is not thread-safe — though entities are constructed on one: `ChunkStatus.SPAWN` runs `NaturalSpawner.spawnMobsForChunkGeneration` on the worldgen executor, so a chunk's first animals are built and finalised off the main thread before the chunk ever becomes live. `Entity.tick` on the base class is one line: call `Entity.baseTick`. Everything readers remember happening "in tick" is in `Entity.baseTick` — the dead-vehicle check, the boarding cooldown, the portal handling, the fluid snapshot, swimming, fire ticking, the lava halving of fall distance, the below-world check, the leash — or in an override. `LivingEntity.tick` calls up and then runs `LivingEntity.aiStep`, and `Mob.tick` calls up and refreshes its goal-control flags through `Mob.updateControlFlags` every five ticks — and only on the server, which is the one line of this section the client does not run. What differs between the sides is not the tick but what the tick is allowed to *do*, and that is exactly the subject of the next page, [authority](authority.md). ## Three things about the id **Why do two entities from different worlds compare equal?** Because `Entity.equals` compares the network id and nothing else — not the UUID, not identity — and `Entity.hashCode` *is* the id. Ids are handed out by `ServerLevel.getNextEntityId` from a static `AtomicInteger` on `ServerLevel`, so they are process-global and the level is consulted only to avoid a collision. Never put entities from two levels in one set. **Why does a client-side entity throw before its packet arrives?** `Level.getNextEntityId` returns a literal zero and `ClientLevel` does not override it, while zero is the reserved invalid id and `Entity.getId` throws *Tried to access entity ID before ID assignment* on it. Between construction and `Entity.setId` — which `Entity.recreateFromPacket` performs — a client-side entity has no id, and therefore no equality and no hash. `ServerLevel.getNextEntityId` skips zero deliberately. **Why is my hitbox offset from where I think the entity is?** `Entity.position` is the bottom centre and the box grows up from the feet. **Why did the hitbox not change when I changed the size?** Because `Entity.dimensions`, `Entity.eyeHeight` and `Entity.bb` are caches, and only two things refresh them unasked for every entity: a pose change on the base class and `LivingEntity.onAttributeUpdated` on `Attributes.SCALE`. Twelve subclasses do the same for a value of their own — `AgeableMob` on the baby bit among them — and everything else has to call `Entity.refreshDimensions` itself. The two eye-height accessors disagree while a cache is stale: `Entity.getEyeHeight` with no argument reads the cache, the `Pose` overload recomputes. **Why is a player never created from its own entity type?** Because `EntityTypes.PLAYER` was built with `EntityType.Builder.createNothing`, so its factory returns null, and it is `EntityType.Builder.noSave` and `EntityType.Builder.noSummon` besides. `EntityType.create` for a player always yields null, which is why `ClientPacketListener.createEntityFromPacket` special-cases it and hand-builds a `RemotePlayer` from the player info it already has. **Why does that entity never move smoothly?** Possibly because it is on a list. `EntityType.trackDeltas` names ten types whose velocity is simply never sent — `EntityTypes.PLAYER`, `EntityTypes.WITHER`, `EntityTypes.BAT`, both item frames, `EntityTypes.PAINTING`, `EntityTypes.LEASH_KNOT`, `EntityTypes.LLAMA_SPIT`, `EntityTypes.END_CRYSTAL` and `EntityTypes.EVOKER_FANGS` — one of the few places in the codebase where behaviour is a literal list of types rather than a tag or a flag. Or because of the other two numbers: `EntityType.clientTrackingRange` is in **chunks** and `EntityType.updateInterval` in ticks, both fixed at registration. `EntityTypes.MARKER` has a tracking range of 0 and is never sent to anyone, and `EntityTypes.AREA_EFFECT_CLOUD` has an update interval of *Integer.MAX_VALUE*. **Why does entity render distance depend on my render distance?** Because `Entity.viewScale` is a **static** field on `Entity` — process-global state — and `LevelExtractor` writes it from the effective render distance *and* the entity-distance option together, not from the option alone. ## Where to look `EntityTypeIds` · `EntityTypes` · `EntityType.Builder.build` · `EntityType.CODEC` · `EntityType.by` · `EntityType.create` · `EntityType.canSpawn` · `EntityType.loadEntityRecursive` · `EntityType.spawn` · `MobCategory` · `EntityDimensions.makeBoundingBox` · `EntityAttachments` · `Pose.BY_ID` · `Entity` · `Entity.defineSynchedData` · `Entity.load` · `Entity.refreshDimensions` · `Entity.setRemoved` · `Entity.baseTick` · `Entity.getAddEntityPacket` · `PersistentEntitySectionManager.addNewEntity` · `LivingEntity` · `Avatar` · `Mob` · `PathfinderMob` · `EntityReference` · `ClientboundAddEntityPacket` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Authority: who is allowed to simulate > Verified against **Minecraft 26.2** · Part VI · A zombie, a player and a boat each take one step, on the server and on the client, and only one side of each pair does any arithmetic. A zombie walks towards you across a field. Both programs have a `Zombie` object; both tick it every twentieth of a second; both call the same `Entity.tick`. Only one of them works out where it goes. Standing next to the zombie is another player, and their copy is the other way up — and the boat you are sitting in is a third case again, authoritative on your machine and nowhere else. Five predicates on `Entity` — one of them final — decide all of it, and they invert the naive picture in **both** directions: **the client runs no physics at all for the mob chasing you, while the server runs your player's physics every tick and then overwrites the answer with a number your client sent.** This is the single most error-prone idea in the entity part, and four other pages depend on it — [movement and collision](movement-and-collision.md) here, [input to movement](../player/input-to-movement.md) in Part VIII, [what the client is told](../networking/what-the-client-is-told.md) in Part IX, and [the client level](../client/the-client-level.md) in Part X. It is stated in full once, here. ## The cast | class | what it decides | thread | |---|---|---| | `Entity` | the five predicates, and every gate inside `Entity.move` that reads them | both main threads | | `Player` | overrides four of the five, and is the reason the picture inverts | both | | `Mob` | narrows `Entity.isEffectiveAi` with `Mob.isNoAi` | both, but only the server's copy acts on the answer | | `LivingEntity` | `LivingEntity.aiStep`, which either simulates or coasts | both | | `ClientPacketListener` | whether an inbound position packet moves the entity or only updates a codec | client main | | `ServerGamePacketListenerImpl` | re-runs the client's numbers for the entities the client owns | server main | ## Five predicates, and the final one the other four hang off `Entity.isLocalInstanceAuthoritative` is the root and it is **final** — no class overrides it. It asks a different question on each side: on the client, *am I locally client-authoritative?*; on the server, *am I **not** client-authoritative?* Everything else hangs off those two. ```mermaid flowchart TD Q["Entity.isLocalInstanceAuthoritative — final"] Q -- "on the client" --> LCA["Entity.isLocalClientAuthoritative"] Q -- "on the server" --> NCA["not Entity.isClientAuthoritative"] LCA --> LCAD["base: my controlling passenger's answer, or false"] LCA --> LCAP["Player: am I the local player?"] NCA --> CAD["base: my controlling passenger's answer, or false"] NCA --> CAP["Player: always true"] Q --> SIM["Entity.canSimulateMovement — defaults to it"] Q --> AI["Entity.isEffectiveAi — defaults to it"] SIM --> SIMP["Player overrides: not a client, or I am the local player"] AI --> AIP["Player overrides: the same"] AI --> AIM["Mob narrows: and not Mob.isNoAi"] ``` Two things in that picture are easy to miss. The base implementations of both `Entity.isLocalClientAuthoritative` and `Entity.isClientAuthoritative` **delegate to the controlling passenger** — an entity with nobody steering it answers false to both, and an entity with a rider inherits the rider's answer. That single line is the whole vehicle model. And `Player` overrides `Entity.canSimulateMovement` and `Entity.isEffectiveAi` to something *different* from the root — *not a client, or I am the local player* — which is what lets the server simulate a player it is not authoritative for. Three classes narrow the AI predicate further and are worth naming because they are the exceptions people trip over: `Mob.isEffectiveAi` adds `Mob.isNoAi`, which is where the *NoAI* tag actually bites, and both `ArmorStand.isEffectiveAi` and `Mannequin.isEffectiveAi` add a physics or immovability flag of their own. ## Three cases, read on both sides The columns are three shapes an entity can have; the rows are what each predicate answers and what follows from it. | | a tracked mob | a player | a boat you are riding | |---|---|---|---| | `Entity.isClientAuthoritative` | false | **true**, unconditionally | true, inherited from you | | `Entity.isLocalInstanceAuthoritative`, **server** | **true** | false | false | | `Entity.isLocalInstanceAuthoritative`, **client** | false | true only on *your own* player | **true**, on your machine only | | `Entity.canSimulateMovement`, server | true | **true** — the override | false | | `LivingEntity.travel` runs on the server | yes | yes, and the result is overwritten | n/a | | `LivingEntity.travel` runs on the client | **never** | only for your own player | n/a — `AbstractBoat.floatBoat` and `AbstractBoat.controlBoat` do it instead | | `Entity.checkFallDamage` inside `Entity.move` | server only | **your own client only** — the server reaches it by the packet path | client only | | what the other side does instead | is interpolated, and stands still when the interpolation runs out | applies your movement packet | applies its own copy's packet | The row that surprises people is the last-but-one. `Entity.move` gates `Entity.checkFallDamage` on `Entity.isLocalInstanceAuthoritative`, which for a player is true on your own client and **false on the server** — so the copy that runs it every tick is the one that cannot hurt you. `LivingEntity.checkFallDamage` needs a `ServerLevel` before it computes any damage, so your client only accumulates the fall distance and lets `Block.fallOn` fire. The server reaches fall damage from `Entity.doCheckFallDamage` instead, driven by the movement packet, which is [Part VIII's subject](../player/input-to-movement.md). ### The mob: the client is not correcting, it is replaying A client-side zombie fails `Entity.isLocalInstanceAuthoritative` because nothing is riding it, so `Entity.canSimulateMovement` is false and `LivingEntity.aiStep` never reaches `LivingEntity.travel`. What it does instead is the branch `LivingEntity.aiStep` opens with: if an `InterpolationHandler` is running, step it; **otherwise scale the stored delta by 0.98** — and nothing then applies that delta, because the only thing that would is `Entity.move`, which on this side only `LivingEntity.travel` reaches. There is no collision, no gravity, no friction and no attempt at prediction. It is not simulating and being corrected — it is replaying what `ClientboundMoveEntityPacket` and `ClientboundEntityPositionSyncPacket` tell it, and standing perfectly still when the interpolation runs out. ### The player: simulated twice, believed once A `ServerPlayer` passes `Entity.canSimulateMovement` and `Entity.isEffectiveAi` — both true on the server by `Player`'s override — so the server's copy runs the whole of `LivingEntity.travel` during the entity phase of its tick. It also fails `Entity.isLocalInstanceAuthoritative`, so none of the consequences that gate on it fire. Then the next `ServerboundMovePlayerPacket` arrives, and `ServerGamePacketListenerImpl.handleMovePlayer` moves the player again with `MoverType.PLAYER` and the *client's* distance, and finishes with `Entity.absSnapTo` at the client's claimed position. The server's own simulation is a sanity check that produced a number nobody uses. One consequence reaches a block. `SweetBerryBushBlock.entityInside` needs to know how far the entity moved this tick, and it asks `Entity.isClientAuthoritative` to decide **how to find out**: `Entity.getKnownMovement` for a player, whose movement the server did not compute, and old-position-minus-current for everything else. Authority is not only about physics — it is about which of two numbers is real. ### The boat: authoritative on exactly one machine Sit in a boat and the base delegation makes it yours. On your client `Entity.isLocalClientAuthoritative` walks to the controlling passenger, finds you, and returns true, so your machine simulates the boat for real. On the server the same delegation makes `Entity.isClientAuthoritative` true, so the server's copy is *not* authoritative and does not simulate — it zeroes its own delta outright. Every other client's copy does the same, and is moved only by `AbstractBoat.interpolation`. ```mermaid sequenceDiagram participant CL as ClientLevel participant LP as LocalPlayer participant AB as AbstractBoat participant Wire as the network participant SGPL as ServerGamePacketListenerImpl participant SL as ServerLevel participant CPL as ClientPacketListener CL->>AB: tickNonPassenger, and isLocalInstanceAuthoritative is true AB->>AB: floatBoat, then controlBoat, then move for real LP->>Wire: ServerboundMoveVehiclePacket.fromEntity, once per client tick Wire->>SGPL: handleMoveVehicle SGPL->>AB: move with MoverType.PLAYER and my distance, then absSnapTo SGPL->>AB: setOnGroundWithMovement then doCheckFallDamage Note over SGPL,SL: the server never simulated it, so this is where the boat gets its physics consequences SGPL-->>Wire: nothing, when the move is accepted SGPL->>Wire: ClientboundMoveVehiclePacket, only when it is rejected Wire->>CPL: handleMoveVehicle CPL->>AB: absSnapTo the server's position, then echo a ServerboundMoveVehiclePacket back ``` The inbound half of that is the sharpest demonstration of what the predicate is for. `ClientboundMoveVehiclePacket` is not a routine update — the server sends it only when it has *rejected* your movement, and the client applies it only for a vehicle it is authoritative for, and then immediately echoes a `ServerboundMoveVehiclePacket` back to confirm. Meanwhile the ordinary per-entity position packets take the opposite branch: `ClientPacketListener.handleEntityPositionSync` and `ClientPacketListener.handleMoveEntity` both check `Entity.isLocalInstanceAuthoritative` and, when it holds, **do not move the entity** — `ClientPacketListener.handleMoveEntity` decodes the delta into the entity's position codec and stops there, and `ClientPacketListener.handleEntityPositionSync` records the absolute position in the codec on either branch. The server's opinion about where your boat is gets recorded and ignored. ## Where the gates actually sit Authority is not one flag consulted once. It is read at eight places in `Entity.move` and `LivingEntity.aiStep` alone, and three of those eight read the same member: - the vertical collision flags and `Entity.setOnGroundWithMovement` run if the entity moved vertically **or** is locally authoritative — the horizontal flags are always updated; - `Entity.checkFallDamage` runs only if it is locally authoritative; - `Entity.restituteMovementAfterCollisions` — the bounce — runs on `Entity.canSimulateMovement`; - the step sound and `GameEvent.STEP` run if this is not a client **or** the instance is locally authoritative; - in `LivingEntity.aiStep`, the 0.98 decay of the stored delta runs precisely when `Entity.canSimulateMovement` is **false** — it is the not-authoritative branch, not a fallback inside the authoritative one; - `Mob.serverAiStep` runs on `Entity.isEffectiveAi` **and** not client-side, and `LivingEntity.travel` on `Entity.canSimulateMovement` **and** `Entity.isEffectiveAi`; - `Entity.applyEffectsFromBlocks` follows the travel fork, on the same not-a-client-or-authoritative test as the step sound. The last one has a fork in front of it. If the controlling passenger is a `Player` and the mob is alive, `LivingEntity.travelRidden` runs instead — and it has its own `Entity.canSimulateMovement` test, zeroing the delta outright when it fails. That is the path every horse, pig and happy ghast takes. ## What the predicates explain **Why does a mob rubber-band and my own player does not?** Neither one is being corrected, and for opposite reasons. Your client does simulate your own player — and a boat or a horse you are riding, and a dropped item, which consult no predicate at all — but it never simulates a tracked mob, so there is nothing about the mob for the server to disagree with. What you see on a mob is the gap between position packets, walked by `InterpolationHandler`. **Why does a boat feel responsive and a horse feel heavy?** Both are ridden, and both are authoritative on your machine — but a horse is a `LivingEntity` going through `LivingEntity.travelRidden`, which asks the mob for its own speed and drag, while a boat runs its own physics directly. The authority answer is the same; the layer above it is not. **Does *NoAI* stop a mob moving?** Completely, and by more than the obvious route. It stops `Mob.serverAiStep`, because `Mob.isEffectiveAi` is what `LivingEntity.aiStep` gates that call on — and the same predicate is the second half of the gate on `LivingEntity.travel`, so nothing reaches `Entity.move` for that mob either. A *NoAI* mob does not even fall. ## Where to look `Entity.isLocalInstanceAuthoritative` · `Entity.isLocalClientAuthoritative` · `Entity.isClientAuthoritative` · `Entity.canSimulateMovement` · `Entity.isEffectiveAi` · `Player.isLocalPlayer` · `Mob.isNoAi` · `LivingEntity.aiStep` · `LivingEntity.travel` · `LivingEntity.travelRidden` · `Entity.move` · `Entity.checkFallDamage` · `Entity.doCheckFallDamage` · `ServerGamePacketListenerImpl.handleMovePlayer` · `ServerGamePacketListenerImpl.handleMoveVehicle` · `ClientPacketListener.handleEntityPositionSync` · `ClientPacketListener.handleMoveEntity` · `ClientPacketListener.handleMoveVehicle` · `InterpolationHandler` · `SweetBerryBushBlock.entityInside` · `AbstractBoat.floatBoat` · `AbstractBoat.controlBoat` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Entity lifecycle > Verified against **Minecraft 26.2** · Part VI · A zombie spawns in a dark chunk at night, is ticked for a while, and then either despawns or is written to disk when the chunk unloads. Night falls, you are standing in a field, and somewhere behind you a zombie appears. What the server actually did is smaller and stranger than it looks. Once a tick, for each chunk near a player and each mob category still under its cap, `NaturalSpawner.getRandomPosWithin` rolls a random x, a random z and **one** y — a single uniform draw between the world bottom and the surface height of that column. Three group attempts follow, and they jitter only x and z. So the whole of one category's chance in one chunk this tick lives on **one horizontal slice**: every eligible category gets its own slice, caves and the open field compete for the same rolls, and a world with more vertical space between bedrock and grass spreads those rolls thinner over the surface. Everything else on this page — the caps, the light test, the despawn radius, the write to disk — hangs off that one roll, or off the moment several rejections later where the mob is finally allowed to exist. ## The cast | class | what it decides | thread | |---|---|---| | `NaturalSpawner` | every test between a chunk and a mob, in one file of static methods | server main | | `NaturalSpawner.SpawnState` | the per-tick census, the global cap per `MobCategory`, and the biome energy budget through `PotentialCalculator` | server main, rebuilt each tick | | `LocalMobCapCalculator` | whether any player near *this* chunk is still under the per-player limit | server main, rebuilt each tick | | `SpawnPlacements` | the placement type, heightmap and predicate for each `EntityType` — code, not data | a static table, read on the server main thread | | `PersistentEntitySectionManager` | which entities exist, which are findable, which tick, which chunks are queued to unload | server main, with one concurrent load inbox | | `Visibility` | the three-state projection of `FullChunkStatus` that everything above reads | an enum, read on both sides | | `EntityTickList` | the set the tick walks, and the double buffer that makes mutating it mid-walk safe | server main, and the client's main thread for `ClientLevel` | | `EntityStorage` | the *entities/* region files, separate from the block ones | reads on the IO pool, deserialises and writes on the server main thread | ## A spawn attempt is a filter, not a conversation Almost every step of the spawner is a **rejection**. Drawing it as a conversation hides that, so here it is as the cascade it is — top to bottom in the order the code runs, with everything that can drop an attempt drawn as an arrow leaving the path. ```mermaid flowchart TD T["ServerChunkCache.tickChunks, once a tick"] --> ST["NaturalSpawner.createState walks every entity in the level, skipping MISC and persistent mobs"] ST --> CAT{"NaturalSpawner.getFilteredSpawningCategories"} CAT -->|"a monster category, and the monster game rules are off"| X1["this category is dropped from the tick's list"] CAT -->|"a persistent category, and game time is not a multiple of 400"| X1 CAT -->|"already at the global cap for the category"| X1 CAT -->|"eligible"| CH{"ChunkMap.collectSpawningChunks, then shuffled"} CH -->|"no ticking chunk there"| X2["this chunk is skipped"] CH -->|"ChunkMap.playerIsCloseEnoughForSpawning fails, meaning no non-spectator player within 128 blocks measured horizontally to the chunk centre"| X2 CH --> TICK{"ServerLevel.canSpawnEntitiesInChunk"} TICK -->|"not entity-ticking, or outside the world border"| X2 TICK --> LOC{"LocalMobCapCalculator.canSpawn, per category"} LOC -->|"every nearby player is at their own cap, or there is no nearby player at all"| X2 LOC --> POS["NaturalSpawner.getRandomPosWithin, a random x and z and ONE y between the world bottom and the surface"] POS -->|"the roll landed at the very bottom"| X2 POS --> RC{"is the block at that position a redstone conductor?"} RC -->|"yes, before any species is picked"| X2 RC -->|"no"| JIT["three group attempts, jittering only x and z, the y fixed"] JIT --> NP{"a non-spectator player anywhere in the level?"} NP -->|"no"| X3["this attempt is dropped"] NP --> D{"NaturalSpawner.isRightDistanceToPlayerAndSpawnPoint"} D -->|"within 24 blocks of the nearest player"| X3 D -->|"within 24 of the respawn point, and the respawn point is in this dimension"| X3 D -->|"jittered into a neighbouring chunk that cannot spawn"| X3 D --> PICK{"NaturalSpawner.getRandomSpawnMobAt, weighted, once per group"} PICK -->|"the list is empty, or a reduced-water-ambient biome, 98 per cent of the time"| X3 PICK --> TY{"NaturalSpawner.isValidSpawnPostitionForType, the typo is Mojang's"} TY -->|"the category is MISC"| X3 TY -->|"too far out for a type that cannot spawn far from a player"| X3 TY -->|"unsummonable, or the species is no longer in the list at this exact block"| X3 TY -->|"SpawnPlacements.isSpawnPositionOk fails on the placement type"| X3 TY -->|"SpawnPlacements.checkSpawnRules fails, and this is where the light test lives"| X3 TY -->|"the type's spawn box collides with the world"| X3 TY --> BUD{"NaturalSpawner.SpawnState.canSpawn, the biome energy budget"} BUD -->|"over budget"| X3 BUD --> MAKE["EntityType.create, and ONLY NOW does a Mob object exist"] MAKE -->|"feature-flagged off, or Peaceful and not allowed there"| X4["this category's attempt on this chunk returns"] MAKE --> OBS{"Mob.checkSpawnRules and Mob.checkSpawnObstruction, on the real object"} OBS -->|"either fails, or it would despawn instantly anyway"| X3 OBS --> FIN["Mob.finalizeSpawn, then addFreshEntityWithPassengers, then SpawnState.afterSpawn"] ``` The boundary that matters is the one marked **only now**. Everything above it is decided against the `EntityType` — the placement type, the heightmap ([chunk anatomy](../world/chunk-anatomy.md)), the light rule, the collision box — because constructing a mob to ask it costs more than answering from the type. Nothing above that line has an object to call a method on. `Monster.checkMonsterSpawnRules` is the light rule for a zombie, and it hands the light half to `Monster.isDarkEnoughToSpawn`, three tests in a row: sky light against a random draw from zero to 31, then the dimension's `DimensionType.monsterSpawnBlockLightLimit` if that limit is below 15, then the local brightness against a sample of `DimensionType.monsterSpawnLightTest`. The last of those is where storms come in: during thunder the brightness is computed with a fixed sky-darkening of 10 instead of the level's current one, which is what lets monsters spawn outdoors in the daytime. The light rule is per-dimension data, not a constant, and `EntitySpawnReason.ignoresLightRequirements` exempts exactly one reason, `EntitySpawnReason.TRIAL_SPAWNER`. Construction is itself a filter, and the harshest-tempered one: `EntityType.create` returns null when the type is feature-flagged off or the difficulty is Peaceful and the type is not `EntityType.isAllowedInPeaceful`, and `NaturalSpawner.spawnCategoryForPosition` answers a null by returning outright — not by trying the next position. On Peaceful the spawner does all the work up to construction and then abandons this category's attempt. ### The two caps, and where 289 comes from A mob must pass both caps, and they are counted differently. The **global** cap in `NaturalSpawner.SpawnState.canSpawnForCategoryGlobal` is `MobCategory.getMaxInstancesPerChunk` — 70 for `MobCategory.MONSTER`, 10 for `MobCategory.CREATURE` — times the number of spawnable chunks, divided by `NaturalSpawner.MAGIC_NUMBER`. That divisor is 17², and the 17 is not arbitrary: `DistanceManager` tracks spawn chunks out to eight chunks from each player over a neighbourhood that includes diagonals, so one player contributes a Chebyshev square of 17×17 chunks. The constant normalises the cap back into *seventy monsters per player's worth of area*, which is why it grows with player count and shrinks when players stand together. The **local** cap is `LocalMobCapCalculator.canSpawn`, and it is a veto rather than a budget: it walks the players near this chunk and answers yes the moment it finds one under the raw per-chunk number for the category. With no player near the chunk the walk finds nobody and the answer is **no**. (`SharedConstants.DEBUG_IGNORE_LOCAL_MOB_CAP` is the development switch that turns that half off.) The census both caps count from skips any mob that is `Mob.isPersistenceRequired` or `Mob.requiresCustomPersistence` — named, leashed or ridden — so a named zombie costs nothing against either cap. That is the same predicate pair that makes `Mob.checkDespawn` return early, which is why *name it and it stays* and *name it and it stops counting* are one fact and not two. ### The four constants that are not the numbers `NaturalSpawner` declares `NaturalSpawner.MIN_SPAWN_DISTANCE` 24, `NaturalSpawner.SPAWN_DISTANCE_CHUNK` 8 and `NaturalSpawner.SPAWN_DISTANCE_BLOCK` 128, and **not one of the three is read anywhere in the game** — the live values are the literals 576.0 and 16384.0 at their use sites, both already squared. The two that *are* read are `NaturalSpawner.MAGIC_NUMBER` and one more. That one, `NaturalSpawner.INSCRIBED_SQUARE_SPAWN_DISTANCE_CHUNK`, is neither 8 nor 24: it is the floor of 8 divided by the square root of two, so **5**, and `DistanceManager.hasPlayersNearby` uses it as the fast *yes* of a three-way answer — inside 5 chunks certainly near, beyond 8 certainly not, and in between fall through to the real per-player distance test. Reading a name and believing the number is how a page gets this wrong. ### What finalizeSpawn settles for the whole pack `Mob.finalizeSpawn` adds a triangular random bonus to `Attributes.FOLLOW_RANGE` under `Mob.RANDOM_SPAWN_BONUS_ID` and rolls a 5 % chance of left-handedness. `Zombie.finalizeSpawn` then rolls loot-pickup and door-breaking against local difficulty, equipment and its enchantments, and — the part players notice — returns a `Zombie.ZombieGroupData` that the loop feeds back into the *next* mob of the same group. Baby-or-adult is decided once, by the first zombie, and inherited by the rest: a spawn group is all-baby or all-adult, never mixed. A baby gets a 5 % roll at an existing unridden `Chicken` in a box five blocks wide and three tall, and *only if that roll fails* a second 5 % roll to create one. Two different limits end it. `Mob.isMaxGroupSizeReached` breaks the current group and lets the next of the three attempts start; `Mob.getMaxSpawnClusterSize` returns outright and kills all three. The base value is four, and seven species change it — horses to 6, fish and wolves to 8, and ghasts, happy ghasts and pillagers **down** to 1. ## The other ways in Natural spawning is one caller of `LevelWriter.addFreshEntity` among many. `BaseSpawner` drives the `SpawnerBlockEntity` and `TrialSpawner` the trial chambers ([block entities](../blocks/block-entities.md)); `SpawnEggItem` and `SummonCommand` are the deliberate ones; `AgeableMob.getBreedOffspring` makes babies; and five `CustomSpawner` implementations — `PhantomSpawner`, `PatrolSpawner`, `CatSpawner`, `WanderingTraderSpawner` and `VillageSiege` — are ticked as a list by `ServerLevel.tickCustomSpawners` after the chunks. Each stamps one of the nineteen `EntitySpawnReason` constants, though not a distinct one — phantoms and cats both count as *natural*, sieges and wandering traders both as *event* — and that reason never leaves the server: nothing about *why* something spawned crosses the wire. ## Entry: what addFreshEntity actually does `LevelWriter.addFreshEntity` is a default method that returns **false**. `Level` does not override it and neither does `ClientLevel`. Exactly two classes do. `ServerLevel.addFreshEntity` is the one this page is about. `WorldGenRegion.addFreshEntity` is the other, and it does something entirely different: it writes the entity straight into the `ChunkAccess`'s own list and never touches `PersistentEntitySectionManager` at all. That is the `EntitySpawnReason.CHUNK_GENERATION` path — worldgen mobs are parked in the proto-chunk as NBT and only enter the manager later, when the chunk is promoted and `PersistentEntitySectionManager.addWorldGenChunkEntities` is handed them ([the generation pipeline](../world/chunk-generation-pipeline.md)). On the client the only way in is `ClientLevel.addEntity`, called from the packet handler, and it begins by *removing* whatever already holds that network id. ```mermaid sequenceDiagram participant SL as ServerLevel participant PESM as PersistentEntitySectionManager participant CM as ChunkMap participant ETL as EntityTickList participant Mob as Mob participant ES as EntityStorage participant Wire as the network Note over SL: the tick it is created SL->>SL: addFreshEntityWithPassengers walks getSelfAndPassengers, vehicle first SL->>PESM: addNewEntity PESM->>PESM: claim the UUID, put it in its EntitySection, install the Callback PESM->>SL: LevelCallback.onCreated PESM->>CM: startTracking, through ServerChunkCache.addEntity CM->>Wire: ClientboundAddEntityPacket, bundled with data, attributes, equipment, passengers and leash PESM->>ETL: startTicking, EntityTickList.add Note over SL: every later tick SL->>Mob: checkDespawn, for every entry in the tick list SL->>Mob: tickNonPassenger, only when the chunk is in entity-ticking range Note over PESM: the tick the chunk drops to hidden PESM->>ETL: stopTicking, EntityTickList.remove PESM->>CM: stopTracking CM->>Wire: ClientboundRemoveEntitiesPacket Note over PESM: some later PersistentEntitySectionManager.tick PESM->>ES: storeEntities, and only then UNLOADED_TO_CHUNK ``` `ServerLevel.EntityCallbacks` is the class those callbacks land in — five of `LevelCallback`'s seven appear in the figure — and it is where a surprising amount of the level hangs: the scoreboard entry, the players list and the sleeping-player recount, waypoint tracking, the navigating-mob set the block-change notifier walks, the `EnderDragonPart` id registrations, and the dynamic `DynamicGameEventListener` registration ([game events](../world/game-events-and-vibrations.md)). ## Findable, ticking, or neither `Visibility` is the whole idea in three constants, and `Visibility.fromFullChunkStatus` is the projection: `FullChunkStatus.FULL` makes a chunk's entities findable, `FullChunkStatus.ENTITY_TICKING` makes them tick, anything less hides them ([tickets and loading](../world/tickets-and-loading.md)). ```mermaid stateDiagram-v2 state "Visibility.HIDDEN" as H state "Visibility.TRACKED" as T state "Visibility.TICKING" as K [*] --> H H --> T : chunk reaches FULL, startTracking adds it to EntityLookup and ChunkMap sends the ClientboundAddEntityPacket bundle T --> K : chunk reaches ENTITY_TICKING, startTicking adds it to EntityTickList K --> T : below ENTITY_TICKING, stopTicking removes it from EntityTickList T --> H : below FULL, stopTracking sends ClientboundRemoveEntitiesPacket and the chunk key joins chunksToUnload K --> H : straight down in one call, stopTicking first and stopTracking second H --> [*] : a later manager tick writes the section and marks UNLOADED_TO_CHUNK note right of H : hidden is not written yet. The client was told at the status change, the disk hears several ticks later. ``` The asymmetry is real and worth stating precisely. `PersistentEntitySectionManager.updateChunkStatus` runs its four tests in a fixed order — stop ticking, stop tracking, start tracking, start ticking — so on the way **up** an entity becomes trackable before it becomes tickable, and on the way **down** it stops ticking before it stops being tracked. That order holds only on the chunk-status path. The other transition path, an entity walking across a section boundary into a differently-statused section, runs through `PersistentEntitySectionManager.Callback` instead, which does tracking first in *both* directions and then ticking, and fires `LevelCallback.onSectionChange` at the end. And the always-ticking exemption, `Entity.isAlwaysTicking`, which lifts an entity clear of every one of those filters, is claimed by exactly one class in 26.2: `Player`. ## The tick it gets, and the despawn check it gets anyway The entity block of `ServerLevel.tick` is skipped in its entirety once the level has gone 300 ticks without an active ticket. Otherwise the tick walks `EntityTickList` and, for each entry that is neither removed nor frozen by `TickRateManager.isEntityFrozen`, calls `Entity.checkDespawn` — whose base implementation is *empty*, overridden only by `Mob`, `EnderDragon`, `WitherBoss` and `ShulkerBullet`, so for an item or an arrow it is a no-op call. Only **after** that does the range test run: a `ServerPlayer` is exempt outright, everything else needs `DistanceManager.inEntityTickingRange` for its own chunk, and an entity already riding a live vehicle returns here to be ticked by `ServerLevel.tickPassenger` instead. So despawn is checked for every member of the tick list while ticking is not. The two sets should agree, and mostly do — the tick list *is* the ticking set — but they read different sources: membership follows the chunk holder's promoted `FullChunkStatus`, while the per-tick gate reads the simulation tracker directly, and the two need not have converged in the same tick. `EntityTickList` is what makes that walk safe. It holds two id-keyed maps and a nullable reference to whichever one is being iterated. Adding or removing during a walk copies the live map into the spare and **swaps** them, so the in-flight iterator finishes over the original, untouched map and the mutation lands in the new one. A second concurrent walk is refused outright, by checking that reference rather than a boolean. ## Ending one: Mob.checkDespawn Left in a loaded chunk, the zombie ends through `Mob.checkDespawn`, whose first branch consults no player at all: on Peaceful, anything whose type is not `EntityType.isAllowedInPeaceful` is discarded on the spot, ahead of even the persistence check. Past that, a persistent mob has its `LivingEntity.noActionTime` pinned to zero and is done. Everything else is measured against the nearest non-spectator player — and if there is no player in the level at all, both remaining branches do nothing, so a mob alone in a world never despawns by distance. Beyond `MobCategory.getDespawnDistance` — 128 blocks for every category except `MobCategory.WATER_AMBIENT`, which is 64 — it is discarded instantly. Beyond `MobCategory.getNoDespawnDistance`, a flat 32 for every category, it is discarded on a 1-in-800 roll, but only once `LivingEntity.noActionTime` has passed 600; inside that 32 the same method resets that counter to zero, so standing near a mob keeps it alive. Both distance branches also require `Mob.removeWhenFarAway`, the per-species veto — and it is broader than people expect: `Animal` returns false for *every* animal, tamed or not, and `Villager` for every villager, so a wild cow on a hilltop never despawns by distance at all. That is why *128 blocks and it is gone* is a species-dependent rule and not a universal one. What both branches call is `Entity.discard`, which destroys and does not save. ## Ending two: the chunk goes away Walk far enough instead and the chunk falls out of entity-ticking: the zombie stops ticking but stays findable. Fall to `Visibility.HIDDEN` and two things happen, several ticks apart. At the status change, `PersistentEntitySectionManager.updateChunkStatus` stops ticking and stops tracking the section's entities, and stopping tracking is what reaches `ChunkMap` and sends `ClientboundRemoveEntitiesPacket` — the client is told *then*, not at the write. The chunk key goes into the manager's unload set, and some later `PersistentEntitySectionManager.tick` runs `PersistentEntitySectionManager.processUnloads` over it. That later step is not a formality, and it can refuse. A chunk whose entity data is still being read back off disk is deferred to a future tick. A chunk that has entities to save but has *never been read* is **loaded first**, so the two sets can be merged — the unload triggers a load. Only then does `EntityStorage.storeEntities` write the *Entities* list and a *Position* into the *entities/* region files ([chunk storage](../world/chunk-storage.md)), which are separate from the block *region/* files and which remember the chunks that came back empty so they are never re-read. Each saved entity and its passengers then take `Entity.RemovalReason.UNLOADED_TO_CHUNK` and drop their level callback. Two rules decide what is in that file. Passengers are written **inside** their vehicle, never beside it, so `Entity.shouldBeSaved` refuses any entity that is riding something; and a vehicle whose passengers are exactly one player is refused too, because it travels in that player's own data instead. The clause that is easy to miss is the first one in the method: an entity already carrying a non-saving removal reason is skipped, which is what keeps a discarded mob still sitting in a section out of the file. ## Five reasons, one label | reason | destroys | saves | what leaves it behind | |---|---|---|---| | `Entity.RemovalReason.KILLED` | yes | no | death, in every sense the game means it | | `Entity.RemovalReason.DISCARDED` | yes | no | `Entity.discard`, every despawn, the client replacing a network id | | `Entity.RemovalReason.UNLOADED_TO_CHUNK` | no | **yes** | the unload above — the only reason that saves | | `Entity.RemovalReason.UNLOADED_WITH_PLAYER` | no | no | a vehicle travelling inside a player's own save data | | `Entity.RemovalReason.CHANGED_DIMENSION` | no | no | a portal, where the entity is rebuilt on the far side | *Destroys* means `LevelCallback.onDestroyed` fires — the scoreboard entry and the waypoint go. An unloading zombie keeps all of it, because `Entity.RemovalReason.UNLOADED_TO_CHUNK` does not destroy. The five are not a state machine: `Entity.setRemoved` writes the reason **only if none is set**, so the first one wins and a second call cannot change it — though the rest of `Entity.setRemoved` still runs, dropping passengers and firing `EntityInLevelCallback.onRemove` with the *new* reason. It drops its passengers unconditionally, but dismounts the entity from its own vehicle only when the reason destroys. The one link that survives a removal by design is an `EntityReference` held by somebody else — it keeps a UUID, upgrades to the object on first resolution, and falls back to the UUID when the target goes, which is how *who last hurt me* survives a chunk unload. ## Where to look `NaturalSpawner.spawnCategoryForPosition` · `NaturalSpawner.SpawnState` · `LocalMobCapCalculator` · `ChunkMap.collectSpawningChunks` · `SpawnPlacements` · `EntitySpawnReason` · `Mob.finalizeSpawn` · `Mob.checkDespawn` · `MobCategory` · `ServerLevel.addFreshEntity` · `PersistentEntitySectionManager.updateChunkStatus` · `Visibility` · `EntityLookup` · `EntitySection` · `EntitySectionStorage` · `LevelCallback` · `EntityInLevelCallback` · `EntityTickList` · `ServerLevel.tickNonPassenger` · `Level.guardEntityTick` · `EntityStorage` · `Entity.RemovalReason` · `Entity.setRemoved` · `TransientEntitySectionManager` Before this page: [authority](authority.md), on which side is allowed to decide any of it. After it: [synched entity data](synched-entity-data.md) — what the `ClientboundAddEntityPacket` bundle above is carrying, and how it stays current. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Synched entity data > Verified against **Minecraft 26.2** · Part VI · A player shears a sheep: one byte flips on the server, and the wool disappears on every screen in tracking range. You right-click a sheep with shears. Somewhere on the server a single byte changes — bit four of one entry in a nineteen-slot array the sheep carries — and before that tick ends, every player watching has been told, by a packet that spends four bytes after the entity id. That array is `SynchedEntityData`, the channel the server uses to describe an entity to the clients that see it: the health bar over another player, a sneaking crouch, an armour stand's pose, an item frame's item, this sheep's wool. It is a numbered array, and the numbers are the surprising part. **The slot the wool lives in is not written anywhere in `Sheep`: it is 18 because eighteen slots were handed out above `Sheep` in its superclass chain, and one new field on `Entity` would renumber every entity in the game.** Ids are ordinals handed out down the class tree by a single shared `ClassTreeIdRegistry`, which walks only the *superclass* chain: `Entity` takes 0 to 7, `LivingEntity` 8 to 14, `Mob` 15, `AgeableMob` 16 and 17, and `Sheep`, last in the chain, gets 18. Nothing names these numbers, nothing writes them down, and they stop at 254 — because on the wire, 255 means *end of packet*. ## The cast | class | what it decides | thread | |---|---|---| | `SynchedEntityData` | one entity's numbered array, and whether anything in it has changed since the last flush | one container per side, confined to that side's main thread | | `SynchedEntityData.DataItem` | one slot: its value, the default it was built with, and its own dirty flag | with its container | | `EntityDataAccessor` | the key — an int id and a serializer, equal to another accessor **on the id alone** | immutable, shared by every instance of the class | | `ClassTreeIdRegistry` | which id a `SynchedEntityData.defineId` call gets, from the last id already taken by an ancestor class | whichever thread first loads the class | | `EntityDataSerializers` | the 43 registered serializers and the wire id of each, in registration order | a static block, once | | `ServerEntity` | whether this entity sends anything this tick, and what | the server main thread | | `ClientboundSetEntityDataPacket` | the wire form: an entity id, then id/serializer/value triples, then 255 | encoded on the Netty pipeline, built on the server thread | | `ClientPacketListener` | applying an incoming batch to the client's own container | the client main thread | ## Nineteen slots, and where the numbers come from `SynchedEntityData.defineId` is called from the static initialiser of an entity class and asks `ClassTreeIdRegistry.define` for a number. `ClassTreeIdRegistry` keeps one map from class to *last id issued*, and `ClassTreeIdRegistry.getLastIdFor` walks up the superclass chain until it finds an entry — so a subclass continues its parent's numbering rather than starting over. Java's guarantee that a superclass initialises before its subclass is the entire ordering mechanism. `ClassTreeIdRegistry.getCount` is the same walk plus one, and it is what sizes the array. A `Sheep` — `Entity` → `LivingEntity` → `Mob` → `PathfinderMob` → `AgeableMob` → `Animal` → `Sheep`, of which `PathfinderMob` and `Animal` define nothing — therefore has exactly nineteen slots: | id | field | serializer | default | |---:|---|---|---| | 0 | `Entity.DATA_SHARED_FLAGS_ID` | `EntityDataSerializers.BYTE` | 0 | | 1 | `Entity.DATA_AIR_SUPPLY_ID` | `EntityDataSerializers.INT` | `Entity.getMaxAirSupply` | | 2 | `Entity.DATA_CUSTOM_NAME` | `EntityDataSerializers.OPTIONAL_COMPONENT` | empty | | 3 | `Entity.DATA_CUSTOM_NAME_VISIBLE` | `EntityDataSerializers.BOOLEAN` | false | | 4 | `Entity.DATA_SILENT` | `EntityDataSerializers.BOOLEAN` | false | | 5 | `Entity.DATA_NO_GRAVITY` | `EntityDataSerializers.BOOLEAN` | false | | 6 | `Entity.DATA_POSE` | `EntityDataSerializers.POSE` | `Pose.STANDING` | | 7 | `Entity.DATA_TICKS_FROZEN` | `EntityDataSerializers.INT` | 0 | | 8 | `LivingEntity.DATA_LIVING_ENTITY_FLAGS` | `EntityDataSerializers.BYTE` | 0 | | 9 | `LivingEntity.DATA_HEALTH_ID` | `EntityDataSerializers.FLOAT` | 1.0 | | 10 | `LivingEntity.DATA_EFFECT_PARTICLES` | `EntityDataSerializers.PARTICLES` | empty | | 11 | `LivingEntity.DATA_EFFECT_AMBIENCE_ID` | `EntityDataSerializers.BOOLEAN` | false | | 12 | `LivingEntity.DATA_ARROW_COUNT_ID` | `EntityDataSerializers.INT` | 0 | | 13 | `LivingEntity.DATA_STINGER_COUNT_ID` | `EntityDataSerializers.INT` | 0 | | 14 | `LivingEntity.SLEEPING_POS_ID` | `EntityDataSerializers.OPTIONAL_BLOCK_POS` | empty | | 15 | `Mob.DATA_MOB_FLAGS_ID` | `EntityDataSerializers.BYTE` | 0 | | 16 | `AgeableMob.DATA_BABY_ID` | `EntityDataSerializers.BOOLEAN` | false | | 17 | `AgeableMob.AGE_LOCKED` | `EntityDataSerializers.BOOLEAN` | false | | 18 | `Sheep.DATA_WOOL_ID` | `EntityDataSerializers.BYTE` | 0 | The declaration order and the *definition* order are two different lists. Ids come from where `SynchedEntityData.defineId` sits in the class body; the values come from the `Entity` constructor, which defines its own eight and then calls the abstract `Entity.defineSynchedData` that every subclass overrides and chains up through. `LivingEntity` defines its seven in a different order from the one that numbered them, and it does not matter, because `SynchedEntityData.Builder.define` writes each item at `EntityDataAccessor.id`. `SynchedEntityData.Builder.build` then refuses to hand over a container with any slot still null, naming the id it is missing — which is why `SynchedEntityData.get` can index the array with no bounds check at all. Four of the nineteen are bitfields, and they are the dense part of the channel. Slot 0 is `Entity.FLAG_ONFIRE` 0, `Entity.FLAG_SHIFT_KEY_DOWN` 1, `Entity.FLAG_SPRINTING` 3, `Entity.FLAG_SWIMMING` 4, `Entity.FLAG_INVISIBLE` 5, `Entity.FLAG_GLOWING` 6 and `Entity.FLAG_FALL_FLYING` 7 — bit 2 is unnamed and unused — behind `Entity.getSharedFlag` and `Entity.setSharedFlag`, whose parameter carries a purpose-built `Entity.Flags` type-use annotation that every call site inside `Entity` ignores in favour of a bare integer. Slot 8 carries *using an item*, *off hand* and *spin attack* (`LivingEntity.LIVING_ENTITY_FLAG_IS_USING`, `LivingEntity.LIVING_ENTITY_FLAG_OFF_HAND`, `LivingEntity.LIVING_ENTITY_FLAG_SPIN_ATTACK`), slot 15 no-AI, left-handed and aggressive behind `Mob.setNoAi`, `Mob.setLeftHanded` and `Mob.setAggressive`, and slot 18 packs a `DyeColor` id into the low nibble and *sheared* into bit four — `Sheep.getColor`, `Sheep.setColor`, `Sheep.isSheared`, `Sheep.setSheared`, and the same storage read out as `DataComponents.SHEEP_COLOR` by `Sheep.get` and written back through `Sheep.applyImplicitComponent`. The numbering belongs to the class, not to the concept. `Avatar` — the class 26.2 inserts between `LivingEntity` and `Player` — owns `Avatar.DATA_PLAYER_MAIN_HAND` and `Avatar.DATA_PLAYER_MODE_CUSTOMISATION`, so the skin-part toggles belong to every avatar, while `Player.DATA_PLAYER_ABSORPTION_ID`, `Player.DATA_SCORE_ID` and the two shoulder parrots (`Player.DATA_SHOULDER_PARROT_LEFT`, `Player.DATA_SHOULDER_PARROT_RIGHT`, optional ints rather than NBT) sit one level further down. ## The serializer is the other half of the key An `EntityDataAccessor` is an id *and* an `EntityDataSerializer`, and the serializer is what turns the value into bytes. The interface is deliberately thin: a `StreamCodec` returned by `EntityDataSerializer.codec` — returned, not extended — paired with an `EntityDataSerializer.copy` that defends the container against a caller mutating a value it already handed over. `EntityDataSerializer.ForValueType` is the immutable case, where copying is identity, and `EntityDataSerializer.forValueType` builds one from a codec alone. `EntityDataSerializers` registers 43 of them into a `CrudeIncrementalIntIdentityHashBiMap`, from a single static block, so **registration order is the wire id** — `EntityDataSerializers.BYTE` is 0, `EntityDataSerializers.POSE` is 20, `EntityDataSerializers.HUMANOID_ARM` is 42 and last. Several of them carry `Holder`s of data-pack registries — the per-species variants, painting variants, resolvable profiles — which is why the buffer on both sides is a `RegistryFriendlyByteBuf` rather than a plain one ([codecs](../foundations/codecs-nbt-json.md)). The full list with wire ids and value types is [the serializer table](../../reference/entity-data-serializers.md). `EntityDataSerializers.registerSerializer` is public and is the only thing that fills the bimap: vanilla calls it 43 times from that one block, and nothing else in the tree ever calls it again. It is a mod extension point shipped with no caller outside its own file. ## The trace: a sheep is sheared ```mermaid sequenceDiagram participant MPGM as MultiPlayerGameMode participant SGPL as ServerGamePacketListenerImpl participant Sheep as Sheep participant SED as SynchedEntityData participant CM as ChunkMap participant SE as ServerEntity participant CPL as ClientPacketListener MPGM->>MPGM: predicts locally with Player.interactOn, unless spectator MPGM->>SGPL: ServerboundInteractPacket(entity id, hand, relative location, secondary) Note over SGPL: server tick, before MinecraftServer.tickServer runs SGPL->>SGPL: Entity.setShiftKeyDown from the packet flag, then range and border checks SGPL->>Sheep: Player.interactOn to Entity.interact to Mob.interact Sheep->>Sheep: checkAndHandleImportantInteractions, then Entity.interact, then Sheep.mobInteract Sheep->>Sheep: Sheep.shear — sound, then dropFromShearingLootTable, then setSheared Sheep->>SED: SynchedEntityData.set(Sheep.DATA_WOOL_ID, bit four set) SED->>Sheep: Entity.onSyncedDataUpdated, then DataItem.setDirty and isDirty Note over SGPL,SE: same tick, ServerLevel.tick chunkSource phase CM->>SE: ChunkMap.tick reaches this sheep, ServerEntity.sendChanges SE->>SED: isDirty opens the gate, then SynchedEntityData.packDirty SED-->>SE: one DataValue — id 18, serializer 0, one payload byte SE->>CPL: ClientboundSetEntityDataPacket, queued now and flushed at the end of the tick Note over SED: one container per side — the server's above, the client's below CPL->>SED: handleSetEntityData to assignValues, per item then the batch Note over CPL: next frame — SheepRenderer.extractRenderState reads Sheep.isSheared ``` **The click.** `MultiPlayerGameMode.interact` sends a `ServerboundInteractPacket` — a flat record of entity id, hand, an *entity-relative* hit location and the secondary-action flag, attacks having left for `ServerboundAttackPacket` — and *then*, on the next line, runs the interaction locally as a prediction, unless the local game mode is spectator. The packet goes first. **The server checks the geometry, not the outcome.** `ServerGamePacketListenerImpl.handleInteract` confirms the thread with `PacketUtils.ensureRunningOnSameThread` and that the client has loaded, resolves the entity with `ServerLevel.getEntityOrPart`, tests the world border and `Player.isWithinEntityInteractionRange`, and checks the held item against the level's feature flags. Before any of the geometry, though, it writes the packet's secondary-action flag straight into `Entity.setShiftKeyDown` — which is itself a synched-data write on the *player*, so every interaction packet is also a potential update on slot 0. **Dispatch down the hierarchy.** `Player.interactOn` calls `Entity.interact`, which dispatches virtually to the most derived override, `Mob.interact`. That runs three things in a fixed order: `Mob.checkAndHandleImportantInteractions` (name tags, spawn eggs), then the superclass hook `Entity.interact` with its leashing branch, and only if that passes, `Sheep.mobInteract` — the base hook runs *between* the two mob hooks, not before them. The shears test is item identity against `Items.SHEARS`, not a tag and not a component, plus `Sheep.readyForShearing` from the shared `Shearable` interface, whose other implementors are `MushroomCow`, `SnowGolem`, `Bogged`, `CopperGolem` and `SulfurCube`. A sheep that is *not* ready returns `InteractionResult.CONSUME` rather than falling through, which is why shears on an already-sheared sheep do nothing visible at all. **The effect, and one byte.** `Sheep.shear` plays `SoundEvents.SHEEP_SHEAR`, drops wool through `LivingEntity.dropFromShearingLootTable` with `BuiltInLootTables.SHEAR_SHEEP` — the tool is passed in, so the loot table can see it — and calls `Sheep.setSheared`, which ors bit four into slot 18. `SynchedEntityData.set` compares the new value against the current one, stores it, calls `Entity.onSyncedDataUpdated` for that accessor, and *then* marks the item and the container dirty. `Sheep` does not override the hook, and the base implementation reacts to exactly one accessor, `Entity.DATA_POSE`, by calling `Entity.refreshDimensions`. Back in `Sheep.mobInteract`: `Entity.gameEvent` with `GameEvent.SHEAR` for the sculk listeners ([game events](../world/game-events-and-vibrations.md)), `ItemStack.hurtAndBreak` on the shears, and `InteractionResult.SUCCESS_SERVER`. **The send, in the same tick.** `MinecraftServer.processPacketsAndTick` drains the queue with `PacketProcessor.processQueuedPackets` and only then calls `MinecraftServer.tickServer`, so the shear happened before the tick proper began. `ServerLevel.tick` reaches its *chunkSource* phase after block and fluid ticks and before block events and entity ticking, and that phase runs `ChunkMap.tick`, whose loop over `ChunkMap.TrackedEntity` is the only caller of `ServerEntity.sendChanges` in the tree. The dirty flag opens the gate, `ServerEntity.sendDirtyEntityData` calls `SynchedEntityData.packDirty`, and one `ClientboundSetEntityDataPacket` goes to every tracking player and to the entity itself. After the entity id, the wire carries an unsigned byte 18, a var-int 0 for `EntityDataSerializers.BYTE`, one payload byte and then the terminator 255 — and that terminator is the whole reason ids stop at 254. Both the pack and the unpack side write and test the literal, incidentally, so the public `ClientboundSetEntityDataPacket.EOF_MARKER` is referenced by nothing, exactly like the private `SynchedEntityData.MAX_ID_VALUE` beside it. The packet is not on the wire yet. `MinecraftServer.tickChildren` calls `ServerCommonPacketListenerImpl.suspendFlushing` on every player before it ticks any level and `ServerCommonPacketListenerImpl.resumeFlushing` at the end of the tick, so a whole tick's packets leave together. **The apply, and the frame.** `ClientPacketListener.handleSetEntityData` looks the entity up in `ClientLevel` and silently drops the entire packet if the id is unknown, then calls `SynchedEntityData.assignValues`, which checks that the incoming serializer is the one the accessor was defined with — a mismatch throws, loudly, on the client — stores each value, fires `Entity.onSyncedDataUpdated` per item and then the batch overload once. Nothing tells the renderer. Next frame `LevelExtractor` walks the visible entities, `EntityRenderDispatcher.extractEntity` builds a `SheepRenderState`, and `SheepRenderer.extractRenderState` copies `Sheep.isSheared` and `Sheep.getColor` into it, after which `SheepWoolLayer` draws nothing. The wool vanishing is a *layer skipped*, not a model swap — and only one layer, because `SheepWoolUndercoatLayer` tests colour, baby and invisibility but never the sheared flag, so a sheared coloured sheep still draws its undercoat. ## The gate that holds a packet back ```mermaid flowchart TD IN["ChunkMap.tick: section changed, or Entity.needsSync, or the chunk is in entity-ticking range"] --> SC["ServerEntity.sendChanges, opening with Entity.updateDataBeforeSync"] SC -->|"an ItemFrame, every tenth tick — the map bypass"| DATA["ServerEntity.sendDirtyEntityData"] SC --> GATE{"tickCount is a multiple of EntityType.updateInterval, or Entity.needsSync, or SynchedEntityData.isDirty"} GATE -->|"yes"| SEND["position, rotation and motion"] --> DATA GATE -->|"no"| HOLD["nothing goes out, and the dirty flags survive to the next tick"] ``` Two tests stand between a dirty byte and the wire. `ChunkMap.tick` decides whether `ServerEntity.sendChanges` is called at all; an entity that is tracked but outside entity-ticking range, and not moving between sections, simply is not asked, and keeps its dirty data until one of the three conditions becomes true. Inside `ServerEntity.sendChanges`, the interval gate covers the position block *and* the usual `ServerEntity.sendDirtyEntityData` call, which is why shearing a sheep also sends that sheep's position delta this tick: synched data is, incidentally, a latency channel for movement. The interval comes from `EntityType.updateInterval`, fixed when `ChunkMap.TrackedEntity` constructs the `ServerEntity`. `EntityTypes.PLAYER` sets 2 and `EntityType.Builder` defaults to 3. Thirty-seven types set the interval explicitly, most of them at 10 or 20 — and seven of those, item frames, paintings, leash knots and their kin, set *Integer.MAX_VALUE*. **Integer.MAX_VALUE** — the update interval of `EntityTypes.ITEM_FRAME`, which is to say its interval branch never fires again after tick zero. That is exactly why `ServerEntity.sendChanges` has an `ItemFrame` special case that calls `ServerEntity.sendDirtyEntityData` every tenth tick *before* the gate: it is the only path to the synched-data flush that skips the interval test, and without it a map in a frame would update only when something else set `Entity.needsSync`. (Two *sends* also sit outside the gate — the passengers packet and the `Entity.hurtMarked` motion packet — but neither touches the data channel.) `ServerEntity.handleMinecartPosRot` calls it too, but from inside the gate, not around it. `Entity.syncPosition` is the other lever: it realigns the tracker's own counter so the very next evaluation lands on a multiple of the interval, which works even when that interval is *Integer.MAX_VALUE*. `Entity.updateDataBeforeSync` opens `ServerEntity.sendChanges`, ahead of the gate, and it is the hook `LivingEntity` overrides to reconcile its effects: `LivingEntity.updateInvisibilityStatus` and the glowing status write slot 0, and the swirl list goes into slot 10. A mob effect that expired this tick can therefore dirty the container and open its own gate, in the same call that goes on to read the flag. ## Five more channels, all keyed by the same entity id Synched data is one of six clientbound descriptions of an entity, and knowing which one a fact travels on answers most *why does the client not know that* questions. There is no serverbound counterpart to any of them. The client cannot write to the channel directly — though one of its packets does move two slots by proxy: `ServerboundClientInformationPacket` reaches `ServerPlayer.updateOptions`, which sets the skin-customisation byte and the main hand. | channel | packets | note | |---|---|---| | synched data | `ClientboundSetEntityDataPacket` | to trackers **and self** — `ServerEntity.Synchronizer.sendToTrackingPlayersAndSelf` | | attributes | `ClientboundUpdateAttributesPacket` | flushed by the same `ServerEntity.sendDirtyEntityData` ([attributes](attributes.md)) | | equipment | `ClientboundSetEquipmentPacket` | incremental updates bypass `ServerEntity` entirely — `LivingEntity.handleEquipmentChanges` sends them, to trackers only, not the wearer | | mob effects | `ClientboundUpdateMobEffectPacket`, `ClientboundRemoveMobEffectPacket` | the authoritative list, unlike the swirl in `LivingEntity.DATA_EFFECT_PARTICLES` | | position and motion | `ClientboundMoveEntityPacket`, `ClientboundEntityPositionSyncPacket`, `ClientboundRotateHeadPacket`, `ClientboundSetEntityMotionPacket`, `ClientboundMoveMinecartPacket` | the block the synched-data gate shares; `ClientboundTeleportEntityPacket` is the exception, sent from `Entity` itself | | one-shot events | `ClientboundEntityEventPacket` | a single byte from `ServerLevel.broadcastEntityEvent`, dispatched by `Entity.handleEntityEvent` — `EntityEvent` declares 62 of them | Pairing a new viewer runs the same machinery once. `ServerEntity.addPairing` calls `ServerEntity.sendPairingData`, which bundles `ClientboundAddEntityPacket` with a `ClientboundSetEntityDataPacket`, attributes, equipment, passengers and a leash link into one `ClientboundBundlePacket`. The data packet is built not from a fresh pack but from `ServerEntity.trackedDataValues`, a snapshot taken in the `ServerEntity` constructor from `SynchedEntityData.getNonDefaultValues` and refreshed only when `SynchedEntityData.packDirty` later returns something. Two consequences: an entity still entirely at its defaults sends **no** data packet on pairing at all, and any change made while the tracker sat outside its send gate is already folded into that cache. ## Questions players ask **Why is a freshly loaded entity described by so little?** Because defaults never travel. `SynchedEntityData.getNonDefaultValues` skips any item still equal to its `SynchedEntityData.DataItem.initialValue`, so pairing describes only what has changed. Both sides construct their own defaults independently; if they ever disagreed, no packet would correct it. **If a value changes twice in a tick, do I get two packets?** No — and not always for the reason you would guess. `SynchedEntityData.packDirty` clears each flag as it packs, so one flush carries one value per slot. But the comparison in `SynchedEntityData.set` is against the *current* value, not the last one sent: setting A then B then A within a tick dirties the item twice and then sends A, a value the client already had. The only thing that never dirties is setting a slot to what it already holds — and even that can be overridden, because `SynchedEntityData.set` has a three-argument force-dirty form that skips the comparison entirely. `Display` uses it to restart an interpolation whose delay was re-set to the same number; `CopperGolem`'s two uses of it are redundant, because the value it writes is always the previous weather state and so always different. **Why does my client-side change never reach the server?** Nothing stops `SynchedEntityData.set` on the client — `LocalPlayer` prediction does it constantly — but `ServerEntity.sendDirtyEntityData` is the only caller of `SynchedEntityData.packDirty` in the whole tree. The client's dirty flag is read by no one, and the next server value overwrites the slot ([authority](authority.md)). The container is not useless to the client, though: `ClientPacketListener.handleRespawn` copies the old player's non-default values straight into the new one, the single client-to-client use of the channel. **Can two mods both add a field to `LivingEntity`?** Only by accident. Ids are ordinals, so inserting, removing or reordering a `SynchedEntityData.defineId` on a base class shifts every id below it in every subclass. Two mods claiming the same number either collide in `SynchedEntityData.Builder.define`, which rejects a duplicate, or land a value in the wrong slot and throw the serializer check on the client. `SynchedEntityData.Builder.define` also has an off-by-one in its bounds test — an id exactly equal to the array length slips past and dies on the array write instead. **Does any of this affect the physics the client simulates?** One value does. `Entity.DATA_POSE` travels on its own `EntityDataSerializers.POSE`, and `Entity.onSyncedDataUpdated` turns an incoming pose into `Entity.refreshDimensions` — a synched value resizing a hitbox on both sides. Everything else on the channel is cosmetic to the client, or read back by gameplay code that already knew. Two details that are only visible from the whole tree. The batch overload `SyncedDataHolder.onSyncedDataUpdated`, fired by `SynchedEntityData.assignValues` after every packet, is overridden by nothing in 7,055 classes — it is the only place a client could see a whole update atomically, and it is dead. And `Display` is the one class that treats accessor ids as *values*: `Display.RENDER_STATE_IDS` is an int set of the eight ids that force a render-state rebuild, tested against each incoming accessor, with `Display.TextDisplay` keeping a second set of its own. It is the sharpest demonstration in the codebase that these numbers really are ordinals. ## Where to look `SynchedEntityData` · `SynchedEntityData.Builder` · `SynchedEntityData.DataItem` · `SynchedEntityData.DataValue` · `EntityDataAccessor` · `EntityDataSerializer` · `EntityDataSerializers` · `ClassTreeIdRegistry` · `Entity.defineSynchedData` · `Entity.onSyncedDataUpdated` · `Entity.updateDataBeforeSync` · `ServerEntity.sendChanges` · `ServerEntity.sendDirtyEntityData` · `ServerEntity.sendPairingData` · `ChunkMap.tick` · `ChunkMap.TrackedEntity` · `ClientboundSetEntityDataPacket` · `ClientPacketListener.handleSetEntityData` · `Sheep.mobInteract` · `Shearable` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Attributes > Verified against **Minecraft 26.2** · Part VI · Strength II is applied to a player: one modifier lands on one attribute, nothing goes on the wire, and the swing three seconds later reads the new number. Strength II lands on you and thirty seconds later it wears off. In between, one `AttributeModifier` — an amount of +6, an operation of `AttributeModifier.Operation.ADD_VALUE`, an id of *effect.strength* — sits on one attribute of one entity, and every swing you make asks for that attribute and gets a bigger number back. The same mechanism is behind armour points, a horse's jump strength, the extra reach of a creative-mode player and the distance at which a mob notices you: *ask a question, get a number*, cheaply, with a defined order of operations. What is surprising is what does not happen. **Strength II sends no packet at all.** Eight of the forty registered attributes are not client-syncable and `Attributes.ATTACK_DAMAGE` is one of them, so for the whole thirty seconds your own client's copy of your attack damage sits at the base value it was born with — 1.0 — and nothing ever tells it otherwise. > **A different system with the same words.** `world/attribute` is > *environment* attributes — per-position world properties like sky darkness > ([environment attributes and > timelines](../world/environment-attributes-and-timelines.md)) — with its own > registries and its own class also named `AttributeModifier`. Nothing on this > page refers to it. ## The cast | class | what it decides | thread | |---|---|---| | `Attribute` | one named number's default, its description id, its `Attribute.Sentiment` (tooltip colour only) and the one boolean that decides whether the client is ever told | built in the `Attributes` class initialiser, read from every thread after | | `RangedAttribute` | the minimum and the maximum, and `RangedAttribute.sanitizeValue` — the clamp. The only subclass, and every registered attribute is one | as above | | `AttributeSupplier` | what attributes an `EntityType` has at all, and their base values. Frozen | built at class-init, inside `DefaultAttributes` | | `AttributeMap` | which of two dirty sets a change lands in, and therefore whether a packet is sent | server main thread for mutations, client main thread for the mirror | | `AttributeInstance` | the number: a base value, three modifier indices, a dirty flag and a cache | as above | | `AttributeModifier` | an `Identifier`, an amount and an operation. A record, and the identifier alone is its identity | immutable, shared | | `LivingEntity` | when the update set drains, and what reacts to a change | both sides, in `LivingEntity.tick` | | `ServerEntity` | when the sync set drains and what goes on the wire | server main thread, in the level tick's *chunkSource* phase | ## Five objects, two dirty sets, one filter ```mermaid flowchart TB ATTR["Attribute, always a RangedAttribute: a default, a minimum, a maximum, a sentiment, and one boolean called syncable"] SUP["AttributeSupplier: one frozen prototype map per EntityType, held in DefaultAttributes"] MAP["AttributeMap: one per LivingEntity, holding only the instances something has asked for"] INST["AttributeInstance: a base value, a cached value and a dirty flag"] BYOP["modifiersByOperation: three buckets. What calculateValue walks"] BYID["modifierById: the identity index, and the duplicate check"] PERM["permanentModifiers: the subset AttributeMap.pack writes to disk"] UPD["attributesToUpdate: every dirtied attribute"] SYNC["attributesToSync: only the syncable ones"] REACT["LivingEntity.refreshDirtyAttributes in the entities phase, calling onAttributeUpdated, then clear"] SEND["ServerEntity.sendDirtyEntityData in the chunkSource phase, then clear"] WIRE["ClientboundUpdateAttributesPacket to every tracking player and the entity itself"] PAIR["AttributeMap.getSyncableAttributes: NOT a dirty set. It filters the whole live map, for ServerEntity.sendPairingData"] ATTR -- "registered once, by the class initialiser of Attributes" --> SUP SUP -- "createInstance copies a prototype into a fresh instance" --> MAP MAP --> INST INST --> BYOP INST --> BYID INST --> PERM INST -- "setDirty calls AttributeMap.onAttributeModified, which always adds here" --> UPD INST -- "and additionally here, only if the attribute is syncable" --> SYNC UPD --> REACT SYNC --> SEND SEND --> WIRE MAP -. "a newly tracking player gets this instead" .-> PAIR PAIR -.-> WIRE ``` The two sets are **not a partition**: `AttributeMap.onAttributeModified` always adds to the update set and *additionally* to the sync set when the attribute is syncable, so a syncable attribute is in both and a non-syncable one is in the update set alone. ## Forty numbers, every one of them clamped The forty constants of `Attributes` register themselves in their own static field initialisers, into `BuiltInRegistries.ATTRIBUTE` under `Registries.ATTRIBUTE`. `Attributes.bootstrap` does nothing but return `Attributes.MAX_HEALTH`, and exists only as the class-loading trigger `BuiltInRegistries` needs — which is also why a data pack can *reference* an attribute but never add one. Every one of the forty is a `RangedAttribute`, and `RangedAttribute` is the only subclass of `Attribute`, so every attribute in the game has a minimum and a maximum and every computed value passes through one clamp. Several of those bounds are the reason a mechanic behaves the way it does: `Attributes.MAX_HEALTH` has a minimum of 1, so no entity's maximum health can reach zero, and `Attributes.KNOCKBACK_RESISTANCE` has a minimum of −2, so *amplified* knockback is a legal value rather than a bug. The full list — id, constant, default, minimum, maximum, syncable, sentiment — is [the attribute table](../../reference/attributes.md). What has to be said here is the syncable flag, because it explains most of what a client and a server disagree about. **Eight** of the forty never reach the client: `Attributes.ATTACK_DAMAGE`, `Attributes.ATTACK_KNOCKBACK`, `Attributes.KNOCKBACK_RESISTANCE`, `Attributes.FOLLOW_RANGE`, `Attributes.TEMPT_RANGE`, `Attributes.SPAWN_REINFORCEMENTS_CHANCE` — whose registry id is *spawn_reinforcements*, disagreeing with its own constant name — and the pair `Attributes.WAYPOINT_TRANSMIT_RANGE` and `Attributes.WAYPOINT_RECEIVE_RANGE`. An attribute is syncable only because its registration line called `Attribute.setSyncable`, and that setter is public and has no freeze behind it, on an object that lives in a registry. Nothing calls it after bootstrap. Nothing stops it either. ## The prototype, frozen at class-init `DefaultAttributes` holds one `AttributeSupplier` per `EntityType`, each built by chaining builders: `LivingEntity.createLivingAttributes` is the twenty-six-entry base, `Mob.createMobAttributes` adds `Attributes.FOLLOW_RANGE` at 16, `Monster.createMonsterAttributes` adds `Attributes.ATTACK_DAMAGE` and nothing else, and each species' own builder finishes the job. `AttributeSupplier.Builder.build` keeps the **last** entry for a repeated attribute, which is how `Zombie.createAttributes` overrides the attack damage the monster builder added, the follow range the mob builder added and the follow range the mob builder added — the movement speed it also declares has no earlier entry to beat. Anything outside `MobCategory.MISC` with no supplier at all is logged by `DefaultAttributes.validate`. A prototype is frozen in the sense that `AttributeSupplier.Builder.build` arms a callback that throws on any later write: reading one is fine, dirtying one throws. Reading an attribute the type does not have is also fatal — the fallback in `AttributeSupplier` raises rather than returning a default, which is why `LivingEntity.getAttribute` is nullable and `LivingEntity.getAttributeValue` is not. `Player.createAttributes` is on `Player`, not on `Avatar`. That intermediate class ([entity anatomy](entity-anatomy.md)) owns the player-shaped hitbox and the skin data but not the attribute set, so `Mannequin` — the other `Avatar` — is registered with `LivingEntity.createLivingAttributes` and gets the plain living set, including the registry's default movement speed of 0.7 rather than a player's 0.1. The default is not a dead value: the wandering trader, the phantom and the slime are registered with the bare `Mob` and `Monster` builders, neither of which sets a speed either. ## The map, and which set a change lands in `AttributeMap` starts empty. `AttributeMap.getValue` and `AttributeMap.getBaseValue` answer from the prototype when the entity has no instance of its own, and change nothing. `AttributeMap.getInstance` is different: it creates the instance on demand through `AttributeSupplier.createInstance`, which copies the frozen template with `AttributeInstance.replaceFrom` — which ends in `AttributeInstance.setDirty`. So **asking for an instance is a mutation**: the first call to `LivingEntity.getAttribute` for a syncable attribute enqueues it for broadcast before any modifier exists, and a share of the attribute packets a busy server sends are caused by something merely asking. The two sets drain in different phases of the same tick, and that is where the visible lag comes from. `ServerEntity.sendDirtyEntityData` is reached from `ChunkMap.tick`, which runs inside `ServerLevel.tick`'s *chunkSource* phase — **before** the *entities* phase ([the level tick](../server/server-level-tick.md)). An attribute dirtied during an entity's own tick (equipment, an effect, sprinting, powder snow, anything in `ServerPlayer.updatePlayerAttributes`) has therefore already missed this tick's send — and a dirty *attribute* set is not one of the three things that open `ServerEntity.sendChanges`'s gate, so it waits for the next tick whose count is a multiple of the entity's update interval: the tick after next for a player, the third for the default. Only a mutation made *before* the level tick — a command, an interaction handled out of the packet queue at the top of the server tick — reaches the wire in the tick that produced it. It is the same phase ordering that puts a block entity's writes a tick late ([block entities](../blocks/block-entities.md)). The update set drains in the entities phase, in `LivingEntity.refreshDirtyAttributes`, which calls `LivingEntity.onAttributeUpdated` once per dirtied attribute and then clears the set. That hook has exactly four branches: clamp health down to a reduced maximum health, clamp absorption, call `Entity.refreshDimensions` on a scale change, and register or unregister the transmitted waypoint with the `ServerWaypointManager`. Subclasses add two more — `ServerPlayer.onAttributeUpdated` takes the *receive* half of the waypoint pair, and `Mob.onAttributeUpdated` recomputes the pathfinder's node budget through `PathNavigation.updatePathfinderMaxVisitedNodes` on a change to `Attributes.FOLLOW_RANGE` **or** `Attributes.TEMPT_RANGE` ([pathfinding](pathfinding.md) owns that budget). `LivingEntity.refreshDirtyAttributes` is called from `LivingEntity.tick` with no side check, so the **client** runs `LivingEntity.onAttributeUpdated` too, clamping health and resizing an entity whose scale changed — which is why the waypoint branch inside it is the one that has to test for a `ServerLevel` explicitly. Two more things the map decides, and between them they answer *why did my `/attribute` change survive death?* `AttributeMap.pack` writes **every instantiated instance**, base value included, so a base value set by command persists with no modifier attached to carry it — `AttributeInstance.pack` writes only the permanent modifiers of each. And on respawn, `ServerPlayer.restoreFrom` always calls `AttributeMap.assignBaseValues` but calls `AttributeMap.assignPermanentModifiers` only on a *full* restore: returning from the End, not an ordinary death. Base values always come across, a command-added modifier only sometimes. ## The instance: three indices and one cached number An `AttributeInstance` keeps its modifiers three times over: bucketed by `AttributeModifier.Operation` for the arithmetic, indexed by `Identifier` for lookup and duplicate detection, and a second id-index of the *permanent* ones for saving. A modifier's identity is its `Identifier` alone — there is no UUID and no name — so two systems that pick the same identifier for the same attribute collide, and `AttributeInstance.addTransientModifier` and `AttributeInstance.addPermanentModifier` **throw** rather than silently overwrite. `AttributeInstance.addOrUpdateTransientModifier` and `AttributeInstance.addOrReplacePermanentModifier` are the safe forms. Most of vanilla removes by id before it adds; three mobs and `AttributeCommand` instead guard with `AttributeInstance.hasModifier` before adding. Transient versus permanent is *purely* about saving: both kinds sit in the same indices, both affect the value identically, both go on the wire, and only the permanent ones are packed. Mob-effect modifiers are added permanently, and that is the only reason they survive a reload — effects are restored from NBT straight into the active list without going through the apply path, so the hook that would add the modifier never runs on load. On the client, meanwhile, *every* modifier is transient, because `ClientPacketListener.handleUpdateAttributes` sets the base value, wipes the whole modifier list and re-adds the incoming ones with `AttributeInstance.addTransientModifier`. A client attribute map is never packed and never persisted. `AttributeInstance.getValue` recomputes through `AttributeInstance.calculateValue` only when the dirty flag is set, and the flag starts true, so the first read always computes — three passes and one clamp: ```mermaid flowchart TB B["base value: the prototype's, or one assigned by AttributeMap.assignBaseValues or by the attribute command"] P1["pass 1: add the amount of every ADD_VALUE modifier"] P2["pass 2: for each ADD_MULTIPLIED_BASE modifier, add the post-pass-1 base times its amount. Each reads the same base, so these do NOT compound"] P3["pass 3: for each ADD_MULTIPLIED_TOTAL modifier, multiply the running total by one plus its amount. Each reads the last one's output, so these DO compound"] C["RangedAttribute.sanitizeValue, once: NaN collapses to the minimum, anything else is clamped between the minimum and the maximum"] O["cachedValue, returned unchanged until the next setDirty"] B --> P1 --> P2 --> P3 --> C --> O ``` Operation order is therefore global, not insertion order, and intermediate values are never clamped. Within a bucket, iteration order is a hash map's — safe only because each bucket's arithmetic is commutative. ### …except in the other implementation, which is insertion-ordered `ItemAttributeModifiers.compute` is a second, disagreeing implementation of the same idea. It walks an item's `ItemAttributeModifiers.Entry` list in declaration order, applying each entry's operation to the running total as it goes, with no three-pass grouping at all. It is not a duplicate of `AttributeInstance.calculateValue` and it does not agree with it. Its one caller in the whole game is `Mob.getApproximateAttributeWith` — the "would this weapon be better than the one I am holding?" estimate a mob makes when deciding whether to pick an item up. ## Where the modifiers come from Equipment is the busiest source. `LivingEntity.detectEquipmentUpdates`, in the server-only half of `LivingEntity.tick`, only dispatches; `LivingEntity.collectEquipmentChanges` does the work, adding each incoming stack's modifiers as *transient* (removing by id first) through `ItemStack.forEachModifier`, which merges `DataComponents.ATTRIBUTE_MODIFIERS` with the enchantment modifiers from `EnchantmentHelper.forEachModifier`. Exactly eight vanilla enchantments carry `EnchantmentEffectComponents.ATTRIBUTES` — fire and blast protection, respiration, aqua affinity, depth strider, swift sneak, sweeping edge, efficiency — and `EnchantmentAttributeEffect` has a second, location-based path through `EnchantmentAttributeEffect.onChangedBlock` that exactly one uses: soul speed, registered under `EnchantmentEffectComponents.LOCATION_CHANGED` instead. The rest, in one breath: `MobEffect` declarations, permanently and scaled by amplifier; item components, whose `ItemAttributeModifiers.Display` decides whether *and how* a tooltip line appears — its default form adds the reader's own base value back in for `Item.BASE_ATTACK_DAMAGE_ID` and `Item.BASE_ATTACK_SPEED_ID`, so a weapon shows a total rather than a bonus; `SetAttributesFunction` in loot tables; `Zombie.handleAttributes` at spawn; `LivingEntity.setSprinting`; `ServerPlayer.updatePlayerAttributes`; and `AttributeCommand`, whose *modifier add* is **permanent**, and so saved. One packet, one direction: `ClientboundUpdateAttributesPacket`, server to every tracking player **and to the entity itself** — which is why your own client has a live attribute map at all. At most 128 attributes fit in one (checked on encode as well as decode), each snapshot carrying the attribute holder, the base value and the complete, uncapped modifier list. There is no serverbound attribute packet. ## The trace: Strength II ```mermaid sequenceDiagram participant EffC as EffectCommands participant LE as LivingEntity participant ME as MobEffect participant AttrM as AttributeMap participant AttrI as AttributeInstance participant SE as ServerEntity EffC->>LE: addEffect(Strength, amplifier 1) LE->>LE: onEffectAdded, guarded server-side LE->>ME: addAttributeModifiers(the map, amplifier 1) ME->>AttrM: getInstance(Attributes.ATTACK_DAMAGE) AttrM->>AttrI: createInstance copies the frozen prototype, replaceFrom ends in setDirty AttrI-->>AttrM: onAttributeModified adds it to attributesToUpdate ME->>AttrI: removeModifier(effect.strength), then addPermanentModifier(+6, ADD_VALUE) AttrI-->>AttrM: setDirty again. ATTACK_DAMAGE is not syncable, so attributesToSync stays empty Note over LE,SE: the next server tick: chunkSource phase, then entities phase SE-->>SE: sendDirtyEntityData finds an empty set and sends nothing LE->>LE: refreshDirtyAttributes drains the update set, onAttributeUpdated matches no branch Note over LE,AttrI: three seconds later, inside Player.attack LE->>AttrM: getAttributeValue(Attributes.ATTACK_DAMAGE) AttrM->>AttrI: getValue, dirty, so calculateValue AttrI-->>LE: 1.0 base plus the sword's base_attack_damage plus 6.0 ``` `MobEffects.STRENGTH` is declared with one attribute modifier: +3 on `Attributes.ATTACK_DAMAGE`, `AttributeModifier.Operation.ADD_VALUE`, under the id *effect.strength*, and the amount is multiplied by amplifier + 1 when the modifier is built — so Strength II is **+6**. (`MobEffects.WEAKNESS` is the same construction at −4.) `LivingEntity.addEffect` puts the instance into the active list and calls `LivingEntity.onEffectAdded`, which is guarded server-side and hands the entity's `AttributeMap` to `MobEffect.addAttributeModifiers`. If the effect was already running, the path is `LivingEntity.onEffectUpdated` instead, which removes and re-adds and, unlike the add path, refreshes the dirty attributes on the spot. `MobEffect.addAttributeModifiers` calls `AttributeMap.getInstance`, and that is where the instance for `Attributes.ATTACK_DAMAGE` is born, copying the frozen prototype's base value: 1.0 for a player, 3.0 for a zombie. Creation dirties it, before any modifier exists. Then the effect removes its own id and adds the +6 with `AttributeInstance.addPermanentModifier` — the remove is not optional, since the plain add throws on a duplicate id — and dirties it again. Both times the callback adds to the update set and consults the syncable flag before touching the sync set, so the sync set stays empty and **no packet is sent at all**. Next tick, `ServerEntity.sendDirtyEntityData` finds nothing to send and `LivingEntity.onAttributeUpdated` matches none of its four branches. Three seconds later `Player.attack` asks `LivingEntity.getAttributeValue` for the attack damage. The instance is dirty, so `AttributeInstance.calculateValue` runs: the addition bucket holds the sword's *base_attack_damage* modifier from `DataComponents.ATTRIBUTE_MODIFIERS` and the effect's +6, the two multiplication buckets are empty, the clamp passes, the result is cached. The rest of the swing — cooldown scale, enchantment bonuses, the crit — belongs to [the sword swing](../player/the-sword-swing.md). Expiry runs it backwards: `LivingEntity.onEffectsRemoved` calls `MobEffect.removeAttributeModifiers`, which removes by id from all three indices and dirties one last time. ### Swap Strength for Speed and the missing limb appears `MobEffects.SPEED` targets `Attributes.MOVEMENT_SPEED` with `AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL`, and movement speed *is* syncable. Everything above is identical until the callback, which now fills the sync set too. `ServerEntity.sendDirtyEntityData` drains it in the next chunkSource phase and emits a `ClientboundUpdateAttributesPacket` carrying the base value and **every** modifier on that attribute — sprinting, powder snow, the effect, all of them — and `ClientPacketListener.handleUpdateAttributes` rebuilds the client's instance from scratch. That difference, one boolean on the `Attribute`, is the whole design. ## Questions players ask **Why does a frozen mob flood the network?** `LivingEntity.aiStep` calls `LivingEntity.removeFrost` and `LivingEntity.tryAddFrost` back to back, server-side, with no test for whether anything changed. Each has a gate — the remove only dirties when the modifier is actually there, the add needs a non-air block underfoot *and* a non-zero frozen counter — but when both hold, the pair destroys and re-creates a modifier on `Attributes.MOVEMENT_SPEED`, dirtying a *syncable* attribute twenty times a second and re-sending that entity's whole movement-speed modifier list for as long as it stays frozen. Compare `ServerPlayer.updatePlayerAttributes`, which runs just as often but uses `AttributeInstance.addOrUpdateTransientModifier` with a constant modifier object, and so dirties nothing after the first tick. **Why does the client show the wrong number for a mob?** Because for eight attributes it was never told, and for the rest it was told a tick or more late. The client is authoritative about none of it: it reads its own `Attributes.MOVEMENT_SPEED` in `AbstractClientPlayer.getFieldOfViewModifier` and its own reach through `Player.blockInteractionRange` from whatever the last packet left in the map ([movement](movement-and-collision.md)). ## Where to look `Attributes` · `Attribute.setSyncable` · `RangedAttribute.sanitizeValue` · `DefaultAttributes` · `LivingEntity.createLivingAttributes` · `AttributeSupplier.Builder.build` · `AttributeSupplier.createInstance` · `AttributeMap.getInstance` · `AttributeMap.onAttributeModified` · `AttributeMap.getAttributesToSync` · `AttributeMap.getAttributesToUpdate` · `AttributeMap.getSyncableAttributes` · `AttributeMap.pack` · `AttributeInstance.replaceFrom` · `AttributeInstance.setDirty` · `AttributeInstance.calculateValue` · `ItemAttributeModifiers.compute` · `LivingEntity.collectEquipmentChanges` · `MobEffect.addAttributeModifiers` · `LivingEntity.refreshDirtyAttributes` · `LivingEntity.onAttributeUpdated` · `ServerEntity.sendDirtyEntityData` · `ServerEntity.sendPairingData` · `ClientboundUpdateAttributesPacket` · `ClientPacketListener.handleUpdateAttributes` · `AttributeCommand` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Movement and collision > Verified against **Minecraft 26.2** · Part VI · One tick of a falling zombie: 0.08 of gravity, one swept box against a stone floor, and the four booleans everything downstream reads. A zombie is two blocks above stone with nothing pushing it sideways. Its tick builds one delta vector, hands it to `Entity.move`, gets back the part of it the world allowed, and sets four booleans from the difference. Then it has to answer a harder question: *what did I just walk through?* It does not answer that by sampling the destination. Every segment of the tick's movement was recorded into a deque, `Entity.movementThisTick`, and `Entity.applyEffectsFromBlocks` replays those segments afterwards — in the same axis order the collision used, visiting every block the swept box actually crossed, `AABB.collidedAlongVector` rather than a static overlap. And the effects that replay finds are not applied where they are found: they are queued into an `InsideBlockEffectApplier.StepBasedCollector` and flushed in `InsideBlockEffectType` declaration order, so fire and water touched in the *same* step of the replay always end in the extinguish, whatever order the blocks came in. ## The cast | class | what it decides | thread | |---|---|---| | `Entity` | the geometry: clipping, stepping up, bouncing, whether you are on the ground and what you are standing on | server main, or client main for whoever is authoritative | | `LivingEntity` | the physics above it: gravity, drag, friction, swimming, gliding, climbing | same | | `CollisionGetter` | which blocks are candidates, through `BlockCollisions`, and which one is holding you up | same | | `Shapes` / `VoxelShape` | the clipping arithmetic, one axis at a time | same | | `Entity.Movement` | one recorded segment — from, to, and the pre-collision vector that fixes the replay's axis order | same | | `InsideBlockEffectApplier.StepBasedCollector` | when a block effect actually happens, and in what order | same | | `EntityFluidInteraction` | the once-per-tick snapshot of water and lava height, eye depth and current | same | | `ServerEntity` | whether this tick's new position costs a short delta or an absolute sync | server main, in the broadcast phase | ## Who is allowed to run this at all A tracked mob is simulated on the server and merely *carried* on the client — nothing in its own tick reaches `Entity.move`, though a piston or a shulker box can still shove it there from a block entity — while a player is the other way up, client-authoritative on both sides, so the client simulates for real and the server re-runs it as a check. The predicate that decides is `Entity.isLocalInstanceAuthoritative` rather than a bare "am I the client", and `Entity.canSimulateMovement` and `Entity.isEffectiveAi` default to it — though several of the gates below are written as *not a client **or** authoritative*, and `Player` overrides both predicates to exactly that. Which call site reads which, and why a mob and a player invert, is [authority](authority.md); this page notes each gate where the trace hits it. ## The tick ```mermaid sequenceDiagram participant SL as ServerLevel participant LE as LivingEntity participant Entity as Entity participant CG as CollisionGetter participant Shapes as Shapes participant Block as Block participant SE as ServerEntity SL->>Entity: tickNonPassenger — setOldPosAndRot, bump tickCount, tick Entity->>Entity: baseTick — updateFluidInteraction snapshots water and lava, lava halves fallDistance LE->>LE: aiStep — coast-or-interpolate, deadzone, applyInput, serverAiStep LE->>LE: travel picks travelInAir (not in fluid, not gliding) LE->>Entity: moveRelative at 0.02 of flying speed, then move(SELF, deltaMovement) Entity->>CG: collide — getEntityCollisions, the world border, getBlockCollisions CG->>Shapes: per candidate getCollisionShape, a full cube short-circuits, else joinIsNotEmpty Entity->>Shapes: collideWithShapes — axisStepOrder, Y first, then the larger horizontal Shapes-->>Entity: the allowed vector, y clipped by the stone floor Entity->>Entity: Movement recorded, setPos, then the collision booleans Entity->>CG: setOnGroundWithMovement — findSupportingBlock names the block Entity->>Block: checkFallDamage — fallOn, then GameEvent.HIT_GROUND Entity->>Entity: restitution, step sound, block speed factor LE->>LE: back in travelInAir — subtract 0.08 of gravity, then the drags LE->>Entity: applyEffectsFromBlocks — replay the deque, flush the collector LE->>LE: pushEntities — cramming check, then doPush Note over SL,SE: the next tick, in the chunkSource phase, before the entity loop runs again SL->>SE: sendChanges — a short Pos delta, or an absolute sync because onGround changed ``` ## Building the delta `Entity.baseTick` clears the cached block state, records whether the eyes were in water, and calls `Entity.updateFluidInteraction` — one sweep that fills the water and lava trackers of `EntityFluidInteraction` with their heights and accumulated current. Everything downstream — `Entity.isInWater`, `Entity.isInLava`, `Entity.getFluidHeight`, `Entity.isEyeInFluid` — reads that snapshot and never the live world. Fire ticks after it, lava *halves* `Entity.fallDistance` rather than clearing it, and `Entity.checkBelowWorld` discards anything 64 below the world floor — except a `LivingEntity`, which overrides the hook and takes four points of *fell out of the world* damage a tick instead. `LivingEntity.aiStep` is the order of every mob's tick and worth memorising: interpolate-or-coast, head turn, equipment, a deadzone that zeroes any delta component under 0.003 (a squared-horizontal test instead, for players), `LivingEntity.applyInput`, `Mob.serverAiStep` — the goal selector and the movement control, which set `LivingEntity.xxa` and `LivingEntity.zza` ([AI](ai-goals-and-brains.md), [pathfinding](pathfinding.md)) — the jump branch, gliding, the travel branch, `Entity.applyEffectsFromBlocks`, animation, freezing, `LivingEntity.pushEntities`. Our zombie's jump branch is skipped before `Entity.onGround` is ever consulted, because the branch is gated on `LivingEntity.jumping` and a falling zombie is not asking to jump. The travel branch is a fork, not a call. If the controlling passenger is a `Player` and the mob is alive it is `LivingEntity.travelRidden` — the path every horse, pig and happy ghast takes, and the reason a ridden mob's input comes from `LivingEntity.getRiddenInput` rather than its own AI. Otherwise, and only if both `Entity.canSimulateMovement` and `Entity.isEffectiveAi` hold, it is `LivingEntity.travel`, which picks one of three: `LivingEntity.travelInFluid` (splitting again into `LivingEntity.travelInWater` and `LivingEntity.travelInLava`), `LivingEntity.travelFallFlying` — the elytra model, lift from the square of the pitch cosine — or `LivingEntity.travelInAir`. `LivingEntity.shouldTravelInFluid` picks the first, and note what it reads: the *cached* in-water and in-lava flags, with the live `FluidState` at the block position used only for `LivingEntity.canStandOnFluid`, which is how a strider walks on lava. `LivingEntity.travelInAir` probes the block below through `Entity.getBlockPosBelowThatAffectsMyMovement` — 0.500001 down — for its friction: airborne, 1.0, and on stone `Block.getFriction`'s 0.6 through `LivingEntity.computeModifiedFriction`. For an airborne entity `LivingEntity.getFrictionInfluencedSpeed` then returns `LivingEntity.getFlyingSpeed`: **0.02** for a mob nobody is riding, which is why you have almost no air control — a literal, not `Attributes.FLYING_SPEED`, which is not even in the base living attribute set. `Entity.moveRelative` rotates the input by the yaw and adds it to `Entity.deltaMovement` through `Entity.setDeltaMovement`, which silently discards the whole vector if it is not finite, so NaN never enters the physics state. Every knob on the entity's side is a syncable attribute ([attributes](attributes.md)): `Attributes.GRAVITY` (0.08), `Attributes.STEP_HEIGHT` (0.6), `Attributes.MOVEMENT_SPEED` (0.7), `Attributes.JUMP_STRENGTH` (0.42), `Attributes.SAFE_FALL_DISTANCE` (3.0), `Attributes.FALL_DAMAGE_MULTIPLIER`, `Attributes.MOVEMENT_EFFICIENCY`, `Attributes.WATER_MOVEMENT_EFFICIENCY`, `Attributes.AIR_DRAG_MODIFIER`, `Attributes.FRICTION_MODIFIER`, `Attributes.BOUNCINESS`. The world's half is four block properties ([blocks and states](../blocks/blocks-and-states.md)): | property | default | who changes it | |---|---|---| | `Block.getFriction` | 0.6 | 0.98 on ice, packed ice and `Blocks.FROSTED_ICE`, 0.989 on blue ice, 0.8 on `Blocks.SLIME_BLOCK` | | `Block.getSpeedFactor` | 1.0 | 0.4 on soul sand and honey | | `Block.getJumpFactor` | 1.0 | 0.5 on honey | | `Block.getBounceRestitution` | 0.0 | 1.0 on `Blocks.SLIME_BLOCK`, 0.75 on beds | `MoverType` names who is moving you, in five constants. `MoverType.PISTON` is the one with real machinery — `Entity.limitPistonMovement` collapses the vector to a single axis, `Entity.applyPistonMovementRestriction` clamps it to ±0.51 per game tick, and that path alone is exempt from the *multiply* by `Entity.stuckSpeedMultiplier` — it still clears the field ([pistons](../blocks/pistons-and-block-events.md)). `MoverType.SHULKER_BOX` makes a `Shulker` teleport rather than move, and `MoverType.SELF` and `MoverType.PLAYER` are read together by `Player.maybeBackOffFromEdge` ([input to movement](../player/input-to-movement.md)). ## Resolving one axis at a time `Entity.move` opens with two things that are easy to miss. `Entity.stuckSpeedMultiplier` is applied to the delta and *cleared* in the same breath, zeroing `Entity.deltaMovement` with it — that pair is the whole cobweb, berry-bush and powder-snow model — and `Entity.noPhysics` is an escape hatch above even it: an entity with it set skips collision entirely and has all four booleans cleared. ```mermaid flowchart TD COLLIDE["Entity.collide"] GATHER["collect the colliders: every entity box, the world border if you are near it, then BlockCollisions over the swept box"] RESOLVE["Entity.collideWithShapes"] AXIS["Direction.axisStepOrder — Y first, always, then the larger horizontal axis, then the smaller. Each axis clips the box already displaced by the earlier ones"] TEST{"step height above zero, colliding horizontally, and on or hitting the ground?"} FLAT["return the flat result"] HEIGHTS["Entity.collectCandidateStepUpHeights — every Y face of every candidate shape inside maxUpStep, sorted ascending"] RETRY["retry the whole resolve at the next candidate height"] MORE{"any more horizontal distance than the flat attempt?"} WIN["return that one, minus the drop back to the old floor"] COLLIDE --> GATHER --> RESOLVE --> AXIS --> TEST TEST -- no --> FLAT TEST -- yes --> HEIGHTS --> RETRY --> MORE MORE -- "no, try the next candidate" --> RETRY MORE -- "no candidates left" --> FLAT MORE -- yes --> WIN ``` Two things in the gathering stage surprise people. The first is that **collision is against shapes, not blocks**: a candidate contributes whatever `BlockBehaviour.BlockStateBase.getCollisionShape` says, which for a fence is 1.5 blocks tall — to walk into *and* to stand on. The 1.0 you see outlined is `BlockBehaviour.BlockStateBase.getShape`, the selection box, and `CrossCollisionBlock` builds the two from different heights. The mover only ever asks for the first. The second is that most entities are not colliders at all: `EntityGetter.getEntityCollisions` wraps with `Shapes.create` the box of every entity that answers `Entity.canBeCollidedWith`, and the base class answers **false** — so the mob standing next to you contributes nothing, while boats, living shulkers and happy ghasts do. (Pushing is a different predicate, `Entity.isPushable`, and belongs to the crowding pass below.) `BlockCollisions` walks the box with a `Cursor3D`, reads chunks through `CollisionGetter.getChunkForCollisions` — a full chunk if it is already there, null otherwise, and a missing chunk is simply stepped past, so an entity at the edge of loaded space falls through empty space rather than blocking the tick. A full cube short-circuits to a box intersection; anything else goes through `Shapes.joinIsNotEmpty`. The step-up loop in the figure is the part worth slowing down for. It does not guess a height and it does not pick the best one. It harvests the Y coordinates of the candidate shapes that lie above the entity's feet and within `Entity.maxUpStep`, skipping the height the flat attempt already tried, sorts them ascending, and retries the *whole* resolve at each until one yields any more horizontal distance than the flat attempt — and returns that one. It is the lowest step that helps, which is also why an entity can step onto a shape's internal ledge and not only its top face. `Entity.maxUpStep` is zero on the base class and `LivingEntity.maxUpStep` reads `Attributes.STEP_HEIGHT`, raised to at least 1.0 when a `Player` is riding — which is how a ridden horse climbs a full block. ## What the move reports back Before committing, one clip: if `Entity.fallDistance` is non-zero and the allowed movement is at least a block long, `Entity.move` casts a ray up to eight blocks along it for `BlockTags.FALL_DAMAGE_RESETTING` and resets the fall distance on any hit. Then an `Entity.Movement` record — from, to, and the pre-collision delta — goes onto `Entity.movementThisTick`, and `Entity.setPos` moves the point and the bounding box together. The four booleans are then computed by comparing what was asked with what was allowed: `Mth.equal` on the two horizontals, but **exact** inequality on Y, and the whole vertical block only runs if the entity moved vertically at all or is authoritative. `Entity.onGround` is therefore a comparison and not a raycast — it is set from `Entity.verticalCollisionBelow`, meaning the vertical component was clipped and it was negative. The only geometric probe is `CollisionGetter.findSupportingBlock`, reached through `Entity.setOnGroundWithMovement` and `Entity.checkSupportingBlock`, and it answers *which* block is holding you (for sounds and the speed factor), not *whether* — probing a paper-thin slab under the box, retrying with the box shifted back along the movement if that finds nothing, and setting `Entity.onGroundNoBlocks` when it still does. `Entity.checkFallDamage` runs next, only when this instance is authoritative. It adds the downward movement to `Entity.fallDistance` and, on landing, calls `Block.fallOn`, posts `GameEvent.HIT_GROUND` and resets the distance. `Block.fallOn` is what calls `LivingEntity.causeFallDamage`, `LivingEntity.calculateFallPower` subtracts `Attributes.SAFE_FALL_DISTANCE` and `LivingEntity.calculateFallDamage` multiplies by `Attributes.FALL_DAMAGE_MULTIPLIER` and checks `EntityTypeTags.FALL_DAMAGE_IMMUNE` ([damage](damage-and-death.md)). Our two-block fall is 2 − 3 < 0, so the power is zero and **nothing at all** happens: the landing particles are gated on a positive power and the fall sound on positive damage, so only the game event and the reset fire. That reset is reached from more places than you would guess — landing, entering water in `Entity.updateFluidInteraction`, climbing in `LivingEntity.handleOnClimbable`, every `LivingEntity.rideTick`, under `MobEffects.SLOW_FALLING` or `MobEffects.LEVITATION` at the top of the travel branch, `Entity.makeStuckInBlock`, and the tag clip above. Lava halves it instead. Then `Entity.restituteMovementAfterCollisions`, gated on `Entity.canSimulateMovement`: a real restitution model, not slime-block code. It reflects the horizontal components, and for a downward hit combines `Attributes.BOUNCINESS` with `Block.getBounceRestitution`, damped to 80% for non-living entities, gated on the impact being at least one tick of gravity — which is why nothing jitters at rest on a slime block — and opted out of by `BlockTags.SUPPRESSES_BOUNCE` or by crouching. A bounce posts `GameEvent.BOUNCE` ([game events](../world/game-events-and-vibrations.md)) and sets `Entity.syncPosition`. `Entity.applyMovementEmissionAndPlaySound` follows, gated on *not client-side or authoritative*: it accumulates `Entity.moveDist` and fires `Entity.playStepSound` plus `GameEvent.STEP` when it passes `Entity.nextStep`. Last, the horizontal components are multiplied by `Entity.getBlockSpeedFactor` — soul sand's 0.4, lerped towards 1 by `Attributes.MOVEMENT_EFFICIENCY`. ## And then gravity Control returns to `LivingEntity.travelInAir`, *after* the move, and only now is gravity subtracted: `Entity.getEffectiveGravity`, 0.08, or capped at 0.01 while falling with `MobEffects.SLOW_FALLING`. `MobEffects.LEVITATION` replaces that step entirely rather than modifying it, and a client-side entity standing over an unloaded chunk gets a hard-coded −0.1. The horizontals are then multiplied by block friction times a 0.91 scaled by `Attributes.AIR_DRAG_MODIFIER`, the vertical by a 0.98 scaled by the same — `Attributes.FRICTION_MODIFIER` touches only the block-friction term, and block friction is 1.0 unless `Entity.onGround`. The whole drag step is skipped when `LivingEntity.shouldDiscardFriction` is set. Climbing lives inside this same step: `LivingEntity.handleOnClimbable` clamps the fall speed on a `BlockTags.CLIMBABLE` block, and a separate clamp in `LivingEntity.handleRelativeFrictionAndCalculateMovement` sets the vertical component to 0.2 when a climbing or powder-snow entity is either colliding horizontally or jumping — which is the whole of "you go up a ladder by pressing into it". So the delta `Entity.move` consumes carries the *previous* tick's gravity. That is one of two conventions in the codebase, and the other is `Entity.applyGravity`, which runs *before* the move. **An `ItemEntity` does it the other way, and the contrast is the clearest way to see both.** `ItemEntity.tick` applies gravity (a default of 0.04) before `Entity.move` and drag after it, **reverses** any downward velocity on landing at half strength — items bounce, they do not merely damp — and skips the move entirely when it is resting still on the ground and the tick count says it is not this item's turn, calling `Entity.applyEffectsFromBlocksForLastMovements` on the previous tick's segments instead. Its `Entity.getMovementEmission` is `Entity.MovementEmission.NONE`, so it makes no step sounds. Neither convention is wrong. The fluid snapshot has one exception, and it is a useful one: `LivingEntity.checkFallDamage` re-runs `Entity.updateFluidInteraction` from *inside* `Entity.move` whenever the entity is not already in water (and `ItemEntity.tick` re-runs it too), which is exactly why falling into water cancels the fall damage in the same tick that entered it. ## What did I pass through `Entity.applyEffectsFromBlocks` runs on the same gate as the step sound — not client-side, or authoritative. It drains `Entity.movementThisTick` into `Entity.finalMovementsThisTick` first — substituting a single old-position-to- position segment when the deque is empty, and appending a final segment when the entity ended somewhere the last recorded one did not — and only then runs the replay, which opens by calling `Block.stepOn` for the block underfoot, gated on `Entity.onGround`. Each segment is replayed in the *same axis order the collision used* — `Direction.axisStepOrder` again, over the segment's stored pre-collision vector — and `Entity.checkInsideBlocks` walks each leg with `BlockGetter.forEachBlockIntersectedBetween`, testing each block with `AABB.collidedAlongVector` (through `Entity.collidedWithShapeMovingFrom`) rather than a static overlap at the destination, and calling `BlockBehaviour.BlockStateBase.entityInside`, `Entity.onInsideBlock` and `FluidState.entityInside` on what it finds. `Entity.visitedBlocks` is the deduplicator: a block is visited at most once across the whole replay, however many segments cross it. Two budgets bound the work — sixteen sweep steps per segment, which is not sixteen blocks, because every block the box covers at one end of the sweep shares a single step index; and `Entity.movementThisTick` merges its two oldest entries once it reaches a hundred, buying bounded memory with a little precision. A segment that exhausts its steps gets one last zero-length visit at the destination, which covers every block the box ends up inside. Nothing found is applied inline. Each effect is queued into the `InsideBlockEffectApplier.StepBasedCollector`, which flushes a step's worth at a time in `InsideBlockEffectType` declaration order — `InsideBlockEffectType.FREEZE`, `InsideBlockEffectType.CLEAR_FREEZE`, `InsideBlockEffectType.FIRE_IGNITE`, `InsideBlockEffectType.LAVA_IGNITE`, `InsideBlockEffectType.EXTINGUISH` — so fire and water touched in the same step always end in the extinguish. The reordering is strictly per step: `InsideBlockEffectApplier.StepBasedCollector.advanceStep` flushes as the replay advances and `InsideBlockEffectApplier.StepBasedCollector.applyAndClear` runs the accumulated list at the end, so across steps the order stays chronological and fire in a *later* step than water still burns you. ## Off it goes `LivingEntity.pushEntities` closes the tick. It collects pushable neighbours through `Level.getPushableEntities` — a different predicate from the collision one, and on the client `ClientLevel.getPushableEntities` returns at most the local player, never the crowd. On a server it applies `GameRules.MAX_ENTITY_CRAMMING` (default 24, checked one tick in four, the damage 6) and then calls `LivingEntity.doPush` → `Entity.push`, a horizontal-only impulse scaled by 0.05 and ignored below a hundredth of a block. Nothing has crossed the network yet. `ServerEntity.sendChanges` runs in the chunk-source phase of `ServerLevel.tick`, which comes *before* the entity loop ([the level tick](../server/server-level-tick.md)) — so this tick's movement is broadcast at the start of the next one. It becomes a short delta, `ClientboundMoveEntityPacket.Pos`, only when it can: not too big for a short, no more than 400 ticks since the last teleport, not riding, the entity does not demand precision, **and `Entity.onGround` still matches what the last absolute sync recorded**. That last condition is the common case, and it is a real cost: every landing and every step off a ledge forces a full `ClientboundEntityPositionSyncPacket`. `Entity.syncPosition` forces the next send outright, and `ClientboundSetEntityMotionPacket` carries the delta separately. On the receiving side, `ClientPacketListener.handleEntityPositionSync` moves only an entity that is *not* locally authoritative, and snaps rather than interpolates past 64 blocks of correction — otherwise it feeds `InterpolationHandler`, three steps by default, which is the coast branch of `LivingEntity.aiStep` doing its real job. ## Where to look `LivingEntity.aiStep` · `LivingEntity.travel` · `LivingEntity.travelInAir` · `LivingEntity.handleRelativeFrictionAndCalculateMovement` · `LivingEntity.handleOnClimbable` · `Entity.move` · `Entity.collide` · `Entity.collideBoundingBox` · `Entity.collideWithShapes` · `Entity.collectCandidateStepUpHeights` · `CollisionGetter` · `BlockCollisions` · `Shapes.collide` · `Entity.setPos` · `Entity.setOnGroundWithMovement` · `Entity.checkSupportingBlock` · `Entity.checkFallDamage` · `Entity.restituteMovementAfterCollisions` · `Entity.applyMovementEmissionAndPlaySound` · `Entity.updateFluidInteraction` · `EntityFluidInteraction` · `Entity.applyEffectsFromBlocks` · `Entity.checkInsideBlocks` · `InsideBlockEffectApplier.StepBasedCollector` · `InsideBlockEffectType` · `LivingEntity.pushEntities` · `MoverType` · `InterpolationHandler` · `ServerEntity.sendChanges` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # AI: goals and brains > Verified against **Minecraft 26.2** · Part VI · A villager's day — wake, claim a job site, work, meet at the bell, walk home to bed — and the same tick under a zombie that has none of it. It is dawn in a village. One villager climbs out of bed, walks to its composter and works there until the bell rings. Ten blocks away a zombie catches fire, sees the villager, and comes for it. Both are `Mob`s, both are driven by the same `Mob.serverAiStep` on the server thread, and neither is running a script: each is being *re-asked*, every tick or every other tick, what it would like to be doing now. They are asked in two completely different ways, and the villager's is the surprising one. Its day looks like a timetable, and a reader who goes hunting for the class holding that timetable will not find one — *Schedule* does not exist in 26.2. A `Brain` holds an `EnvironmentAttribute` of `Activity`, a pointer into the *world* rather than a table on the mob, and `Brain.updateActivityFromSchedule` asks the `EnvironmentAttributeSystem` what that attribute's value is **at this position, at this time**. The villager goes to bed because it asked the world what hour it is *where it is standing*. The answer comes out of a data-pack `Timeline` — `Timelines.VILLAGER_SCHEDULE`, a 24000-tick loop whose adult track reads *10 idle, 2000 work, 9000 meet, 11000 idle, 12000 rest* beside a baby track that swaps *play* in — and because the lookup takes a position, the day can in principle differ by location. That system is [environment attributes and timelines](../world/environment-attributes-and-timelines.md); this page only asks it a question. ## The cast | class | what it decides | thread | |---|---|---| | `GoalSelector` | which goals run, by holding a four-entry table of `Goal.Flag` to the goal that owns it | server, from `Mob.serverAiStep` | | `Goal` | whether it wants to run, whether it may be interrupted, and which flags it needs | as above | | `WrappedGoal` | the arbitration — `WrappedGoal.canBeReplacedBy` — plus the priority and the running bit | as above | | `Brain` | which activity is active, what the memories hold, and which behaviours are asked at all | server, from `Mob.customServerAiStep` | | `MemoryModuleType` | the vocabulary a brain thinks in: 116 constants, of which the 53 with a codec are the mob's entire saved mind | declared, never ticked | | `Sensor` | when to look at the world, on its own scan rate, and which memories to write | server, from `Brain.tick` | | `ActivityData` | one activity's prioritised behaviour list, its memory requirements, and the memories erased when it stops | built per body by `Brain.ActivitySupplier` | | `Sensing` | whether this mob can see that entity, memoised for exactly one tick — and *both* systems go through it | server, cleared at the top of `Mob.serverAiStep` | ## Seven things they do differently | | the goal selector | the brain | |---|---|---| | **what holds the state** | `GoalSelector.availableGoals`, an insertion-ordered set of `WrappedGoal`, beside a lock table and a set of disabled flags | a memory map, a sensor map, and `Brain.availableBehaviorsByPriority` — priority to activity to behaviour set | | **what fills it** | `Mob.registerGoals`, once, from the constructor, and only when the level is a `ServerLevel` | `Brain.Provider.makeBrain`, from an activity list built *per body* — and built again whenever the body changes | | **what decides** | `Goal.canUse`, re-asked on every other tick | `Behavior.hasRequiredMemories` then `Behavior.checkExtraStartConditions`, asked once a tick | | **what arbitrates** | the flag table. Lower priority number wins a contested flag, and a non-interruptable incumbent wins outright | the active activity. A behaviour whose activity is not active is not asked at all | | **what persists across a save** | nothing. Not the running set, not the flags | 53 of the 116 memories, through `Brain.Packed` | | **what the world can push in** | `Mob.updateControlFlags` every five ticks, and the leash, both on one selector only | the schedule attribute, POI claims, hostiles seen by sensors, `Attributes.FOLLOW_RANGE` | | **which mobs use it** | every `Mob`. 58 goal classes and 10 targeting ones | 20 classes override `LivingEntity.makeBrain` — but only `Villager` sets a schedule | Every row below is one of those lines, taken in turn. ### Where both of them sit in one mob tick The profiler section names, because they are what a profile actually shows: ``` LivingEntity.tick LivingEntity.aiStep "ai" the guard: server side, and Mob.isEffectiveAi "newAi" Mob.serverAiStep "sensing" Sensing.tick — the line-of-sight memo is cleared "targetSelector" ┐ GoalSelector.tick on the full pass, "goalSelector" ┘ tickRunningGoals(false) on the off tick "navigation" PathNavigation.tick — see pathfinding "mob tick" Mob.customServerAiStep → "villagerBrain" → Brain.tick "controls" "move" / "look" / "jump" "jump" LivingEntity's own jump handling — outside the guard "travel" LivingEntity.travel — where the body actually moves "headTurn" Mob.tickHeadTurn — no side check, so both sides ``` Note the scope of that guard. `LivingEntity.aiStep` runs on the client too; what it wraps in *server side and effective AI* is the one call to `Mob.serverAiStep`, not the jump and travel sections beneath it. `Mob.isEffectiveAi` is the more interesting half of the condition, because `Mob` narrows it with `Mob.isNoAi` — which is where the *NoAI* tag takes effect. On the client neither selector nor brain is ticked at all — only the jump, travel and head-turn sections beneath the gate, and the debug renderers. ## What holds the state `GoalSelector` holds three things and none of them is a plan: a set of `WrappedGoal`, a map from `Goal.Flag` to the goal currently holding it, and a set of flags that have been switched off. There is no state machine and no sequence. The only persistent state a goal system has is *which goals are running* and *which flags are held*, and a `Mob` keeps two independent copies of it, `Mob.goalSelector` and `Mob.targetSelector`. A `Brain` holds a great deal more, and the piece to keep hold of is `Brain.availableBehaviorsByPriority`: a sorted map from priority, to activity, to a set of `BehaviorControl`. Priority is the *outer* key, so the whole brain is walked in priority order regardless of which activity a behaviour belongs to. Beside it sit the memory map, the sensor map, the per-activity requirements, the per-activity erase lists, the core activities, the active set and a default of `Activity.IDLE`. Two things about ownership surprise people. `Brain` is declared on `LivingEntity`, not on `Mob` — *every* living entity has one, the player included, and the base implementation hands back an empty one that reports itself `Brain.isBrainDead`. And the activity list is not static: `Brain.ActivitySupplier` is asked for it **per body**, which is how a villager's profession selects its work package. ## What fills it Goals go in exactly once. The `Mob` constructor calls `Mob.registerGoals` only when the level it is being built into is a `ServerLevel`, so a client-side mob's selectors are empty for its whole life. After that the set is fixed, but for the few mobs that add or remove a goal on a state change. Memories are filled continuously, by sensors and by behaviours alike. A `Sensor` looks at the world and writes what it saw: the default scan rate is 20 ticks (`Sensor.DEFAULT_SCAN_RATE`), `GolemSensor` uses 200 and `SecondaryPoiSensor` 40, and `Sensor.randomlyDelayStart` offsets each one at construction so a village does not scan in lockstep. What *registers* a memory slot is declaring it — every memory a sensor lists in `Sensor.requires` and every memory a behaviour names in its entry condition is registered when the brain is built. Reading one that was never registered throws *Unregistered memory fetched*: a behaviour has been installed on a mob that has no idea what it is talking about. The two systems share one piece of machinery here, and it is easy to file under the wrong heading. `Sensing` is the per-mob line-of-sight memo, cleared once per tick at the top of `Mob.serverAiStep`, and it is not the goal system's alone: `TargetingConditions.test` routes every line-of-sight check through `Mob.getSensing`, and the shared conditions the brain's sensors use have that check on by default. Those shared conditions deserve a second look. `Sensor` holds **six** static `TargetingConditions` objects, used by every brain mob in the world, and re-ranges all six from *this* body's `Attributes.FOLLOW_RANGE` immediately before every scan. That is correct only because AI is strictly single-threaded — there is not a future, an executor or a thread anywhere in the AI packages — and it is about as clear a demonstration of the fact as the codebase offers. ## What decides A goal is asked `Goal.canUse` **every other tick**, staggered across mobs by `tickCount + id`, with an exception for a mob's first two ticks, where the full pass runs whatever the parity. On the off tick `GoalSelector.tickRunningGoals` is called with *false*, so only goals that answer `Goal.requiresUpdateEveryTick` are ticked at all — and, more importantly, **no goal is stopped**, because the `Goal.canContinueToUse` sweep lives in the full pass. A goal that lost its reason to run on an off tick keeps running until the next even one. The full pass, `GoalSelector.tick`, is three phases, and they are the next level down in a profile. *goalCleanup* stops every running goal that either holds a now-disabled flag or fails `Goal.canContinueToUse`, then drops every lock whose holder is no longer running. *goalUpdate* walks the set again and starts anything that is not running, holds no disabled flag, can take all its flags, and answers `Goal.canUse`. *goalTick* — reached through `GoalSelector.tickRunningGoals` with *true* — ticks the survivors. A behaviour is asked once per tick, and the first question is not about the behaviour at all. `Brain.tick` runs four fixed phases: ```mermaid flowchart TB A["Brain.tick"] B["1. forgetOutdatedMemories — every MemorySlot counts down, and an expired one clears itself"] C["2. tickSensors — every Sensor, each counting its own scan rate down to zero before it looks at anything"] D["3. startEachNonRunningBehavior — walk availableBehaviorsByPriority, lowest number first"] E{"is this activity in activeActivities?"} F["skipped whole. A behaviour of an inactive activity is never even asked"] G["tryStart: hasRequiredMemories, then checkExtraStartConditions"] H["RUNNING, with an end timestamp rolled between minDuration and maxDuration"] I["4. tickEachRunningBehavior — tickOrStop on everything now RUNNING"] J{"timed out, or canStillUse false?"} K["doStop. canStillUse defaults to false, so most behaviours stop inside the same Brain.tick that started them, and Behavior.tick is never called at all"] L["Behavior.tick"] A --> B --> C --> D --> E E -- "no" --> F --> I E -- "yes, and the behaviour is STOPPED" --> G --> H --> I I --> J J -- "yes" --> K J -- "no" --> L ``` The branch marked *skipped whole* is what this page turns on. **An activity is a filter, not a mode.** The brain's active set is always the core activities plus exactly one other, so `Activity.CORE` behaviours run at every hour of the day and switching activity only swaps the second half. (*Core activities* is plural in the API and singular in practice: nothing in 26.2 calls `Brain.setCoreActivities` with anything but `Activity.CORE` alone.) The *canStillUse* branch is sharper than "a behaviour runs for one tick". `Behavior.canStillUse` defaults to false, and phases 3 and 4 are both inside the *same* `Brain.tick` — so for a behaviour that does not override it, `Behavior.tick` is not called once. Everything it does, it does in `Behavior.start`. The duration rolled at start between the behaviour's minimum and maximum (`Behavior.DEFAULT_DURATION` is 60) matters only for the ones that do override it, which is why the same behaviour class configured with different bounds behaves differently in two packages. ## What arbitrates On the goal side, **the flag table is the arbiter, not the priority list**. `GoalSelector.availableGoals` is insertion-ordered and never sorted; priority only settles a contested flag. Two goals with no flag in common run together whatever their numbers, and two that share one never do. `WrappedGoal.canBeReplacedBy` is the whole rule: the incumbent must answer `Goal.isInterruptable`, and the challenger's number must be strictly lower. There is a small piece of craft in how that is arranged. `GoalSelector` never puts a placeholder in its lock table; it reads the table with a *default* — a sentinel `WrappedGoal` of maximum priority that reports itself not running — so *this flag is free* and *this flag is held by someone worse than me* are the same `WrappedGoal.canBeReplacedBy` call on an entry that may not exist. On the brain side the arbiter is the active set, and the fallback is silent. `Brain.setActiveActivityIfPossible` checks the target activity's memory requirements and, if they do not hold, calls `Brain.useDefaultActivity` instead. A jobless villager at tick 2000 is not "off schedule": the switch to `Activity.WORK` fails its `MemoryModuleType.JOB_SITE` requirement and the villager is idle by construction. Switching is also not free — the brain first erases, for every activity leaving the set, the memories that activity's `ActivityData` names as *memoriesToEraseWhenStopped*. It is one of the few places the brain mutates state rather than reading it. ## What persists across a save A goal system saves nothing. Which goals were running, which flags were held, how far through an attack a mob was — all of it is rebuilt from scratch when the chunk reloads and the constructor calls `Mob.registerGoals` again. A brain saves `Brain.Packed`, and `Brain.pack` walks the memories keeping only those whose `MemoryModuleType` can serialise: 53 of 116, the remaining 63 transient by construction. Time-to-live travels with them, so a memory can expire across a reload as easily as within a tick — `MemorySlot` counts down in phase 1 of every `Brain.tick` and clears itself at zero. The reason `Brain.Packed` exists as a first-class shape is that a brain is built more than once in a mob's life. `Villager.refreshBrain` stops every running behaviour, packs the current memories, and runs the provider again from the packed state with a fresh activity list. Changing profession does that; so does growing up, which is how a baby swaps `EnvironmentAttributes.BABY_VILLAGER_ACTIVITY` for `EnvironmentAttributes.VILLAGER_ACTIVITY`. ## What the world can push in Into a goal selector, two things. The first is `Mob.updateControlFlags`, called from `Mob.tick` on the server every five ticks. It sets `Goal.Flag.MOVE` and `Goal.Flag.LOOK` from one question — *is a `Mob` steering me* — and `Goal.Flag.JUMP` from that **and** *am I in an `AbstractBoat`*. So a mob a mob is riding loses all three, and a mob sitting in a boat by itself loses only the jump. The second is the leash: `Mob.leashTooFarBehaviour` disables `Goal.Flag.MOVE` outright and `PathfinderMob.closeRangeLeashBehaviour` puts it back. Both touch **`Mob.goalSelector` only**; `Mob.targetSelector` is never disabled. `GoalSelector.tick` then stops any running goal holding a disabled flag and refuses to start another. Into a brain, rather more: the schedule attribute, whose value comes from the world; hostiles, players, items, beds and golems, all written by sensors that query the level; and POI claims, which go through the shared `PoiManager` ([points of interest](../world/points-of-interest.md)). Beyond that, a change to `Attributes.FOLLOW_RANGE` or `Attributes.TEMPT_RANGE` reaches `Mob.onAttributeUpdated`, which recomputes the pathfinder's node budget ([attributes](attributes.md), [pathfinding](pathfinding.md)). **Neither of them crosses the network.** There is no AI packet. What a client sees are consequences — head rotations, motion, position deltas, pose changes, the occasional entity-event byte — plus a debug channel that costs nothing until someone subscribes: `Mob.registerDebugValues` registers `DebugSubscriptions.ENTITY_PATHS` and `DebugSubscriptions.GOAL_SELECTORS` for every mob, and `DebugSubscriptions.BRAINS` only for one that is not brain-dead. Nor is either of them data-driven. The villager's day is data (`Timelines.VILLAGER_SCHEDULE` in `Registries.TIMELINE`) — but `VillagerProfession` and `PoiType` are not: both are `BuiltInRegistries` bootstrapped from code, with no directory under the built-in data pack. And **behaviours and goals are code** too, plain Java lists in `VillagerGoalPackages` and the `*Ai` classes, not registered and not addressable from a data pack. ## Which mobs use which Every `Mob` has both fields, and almost every mob uses exactly one. Twenty classes override `LivingEntity.makeBrain` — `Villager`, `Piglin`, `Warden`, `Hoglin`, `Frog`, `Allay`, `Axolotl`, `Goat` and twelve more — and each keeps its behaviour lists in a class named for the mob: `PiglinAi`, `WardenAi`, `FrogAi`. There are eighteen such classes for twenty mobs, and the two exceptions are worth naming. `Zoglin` keeps its lists inline, in the mob itself. And `Villager`'s live in `VillagerGoalPackages` — genuinely the 26.2 name, and not a typo: it is the last survivor of the old convention, on a mob that has no goals at all. Nor do most of the other nineteen: a brain mob typically registers none. **One** — brain mobs with a schedule. `Brain.setSchedule` has exactly two call sites and both are in `Villager`, picking the adult attribute or the baby one. The other nineteen never consult a clock: they call `Brain.setActiveActivityToFirstValid`, which walks a priority list and takes the first activity whose memory requirements hold. That is how `PiglinAi` picks *fight* over *idle* and `FrogAi` picks *tongue* over *swim*, with no time of day involved anywhere. ## The brain's trace: a villager's day ```mermaid sequenceDiagram participant Brain as Brain participant MTS as MoveToTargetSink participant AP as AcquirePoi participant PM as PoiManager participant UAFS as UpdateActivityFromSchedule participant EAS as EnvironmentAttributeSystem participant SIB as SleepInBed Note over Brain: one Brain.tick, behaviours tried in ascending priority Brain->>MTS: priority 1, core — WALK_TARGET present and PATH absent Note over MTS: a wanted position leaves here for the pathfinder Brain->>AP: priority 6, core — runs at every hour of the day AP->>PM: findAllClosestFirstWithType(acquirable job sites, 48, HAS_SPACE) PM-->>AP: the five best, closest first AP->>AP: one path to all five at once, claimed only if Path.canReach AP->>PM: take(pos), then set POTENTIAL_JOB_SITE Brain->>UAFS: priority 99 — the last behaviour in the package UAFS->>Brain: updateActivityFromSchedule, refused if under 21 ticks old Brain->>EAS: getValue(VILLAGER_ACTIVITY, this position) EAS-->>Brain: Timelines.VILLAGER_SCHEDULE says WORK from tick 2000 Brain->>Brain: requirements met, or fall back to the default silently Note over Brain: the next Brain.tick is the first to run the work package Note over Brain: tick 12000, REST, which has no requirement and always takes Brain->>SIB: rest package, priority 3 SIB->>SIB: startSleeping, record LAST_SLEPT, clear the walk target ``` Read the priorities in that diagram as the ordering claims they are. The schedule behaviour sits at 99, the last slot in every package that has one, so the activity a villager switches to is never the one the rest of *this* tick runs: the switch lands and the next tick acts on it. And it is only consulted when a behaviour asks — `Brain.updateActivityFromSchedule` refuses if fewer than 21 ticks have passed since the last one (the test is a strict *greater than* 20). Five of the ten packages carry no such behaviour: core, panic and hide have nothing at 99, pre-raid and raid have `ResetRaidStatus` there instead. The omission is how they pin the villager, and each of the three carries its own way out rather than leaving it to the clock: `VillagerCalmDown` sits at priority 0 in the panic package and calls `Brain.updateActivityFromSchedule` itself the moment the fear memories clear, `SetHiddenState` does the same for hide, and `ResetRaidStatus` for the two raid packages. Nothing is asking the clock on a schedule; the escape hatch asks once, on its own terms. The rest of the day hangs off that. **Claiming a job site** is `AcquirePoi` from the core package: it asks `PoiManager.findAllClosestFirstWithType` for free points of interest matching the profession within 48 blocks, takes the best five, and runs a single pathfind with all five as targets at once — and it claims only one the villager can actually *reach*, testing `Path.canReach` before `PoiManager.take` ([pathfinding](pathfinding.md)). A bell across a ravine is invisible to a villager. What it writes is `MemoryModuleType.POTENTIAL_JOB_SITE`, not the job site itself; `AssignProfessionFromJobSite` waits until the villager is within two blocks of that position, then erases the memory, writes `MemoryModuleType.JOB_SITE` and sets the profession — which is why walking to the workstation is a required step and not decoration. **Work** is a weighted `RunOne` over six: `WorkAtPoi` (or `WorkAtComposter`), `StrollAroundPoi`, `StrollToPoi`, `StrollToPoiList`, `HarvestFarmland` and `UseBonemeal`; `WorkAtPoi` wants 300 ticks since the last check and 1.73 blocks or less to the workstation. **Walking anywhere** is `MoveToTargetSink`, entered on *walk target present, path absent*: it turns a `MemoryModuleType.WALK_TARGET` into a path, hands it to the navigation, and records a failure as `MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE`. Below that hand-off is [pathfinding](pathfinding.md) and then [movement and collision](movement-and-collision.md). **Bed** is `SleepInBed`: a bed within two blocks, unoccupied, in the right dimension, at least 100 ticks since it was last woken. It never times out — it ends because `Brain.isActive` for `Activity.REST` goes false at dawn and the core `WakeUp` behaviour calls `LivingEntity.stopSleeping`. ## The goal selector's trace: the zombie A `Zombie` has a `Brain`, because every `LivingEntity` does, but it is the base one — no memories, no sensors, no behaviours, `Brain.isBrainDead` true. Everything it will ever do comes out of `Mob.registerGoals`, run once in the constructor: **seven** goals in `Mob.goalSelector` — a turtle-egg attack goal, a `SpearUseGoal`, a `ZombieAttackGoal`, a `MoveThroughVillageGoal`, a `WaterAvoidingRandomStrollGoal`, a `LookAtPlayerGoal` and a `RandomLookAroundGoal` — and **five** in `Mob.targetSelector`, one `HurtByTargetGoal` and four `NearestAttackableTargetGoal`s. Every other tick each of the twelve is re-asked, and the flag table settles it. The target goals hold `Goal.Flag.TARGET` and write `Mob.setTarget`. The attack goals hold `Goal.Flag.MOVE` and `Goal.Flag.LOOK` and drive the navigation, with `SpearUseGoal` at priority 2 sitting above `ZombieAttackGoal` at 3, so it is usually the one holding them. `LookAtPlayerGoal` wants `Goal.Flag.LOOK` alone and `RandomLookAroundGoal` wants `Goal.Flag.MOVE` as well, and both lose to whoever already has them. No activity, no schedule, nothing persisted, nothing the world can push in: the zombie's entire mind is a handful of running bits and one target field. Even that field is not read directly — `Mob.getTarget` filters through `Mob.asValidTarget` on every call, so a target that turned creative or spectator is gone the moment it is asked for, and brain mobs source theirs from `Mob.getTargetFromBrain` instead. ## Questions players ask **Why does a ridden mob stop moving on its own but still glare at me?** Because `Mob.updateControlFlags` disables `Goal.Flag.MOVE`, `Goal.Flag.JUMP` and `Goal.Flag.LOOK` on `Mob.goalSelector` and never touches `Mob.targetSelector`. Target selection is a separate `GoalSelector` with its own lock table, and nothing in the game switches it off. A boat alone does less than people expect: it costs the mob only `Goal.Flag.JUMP`. **Why did the villager ignore a perfectly good workstation?** Either it could not reach it — `AcquirePoi` pathfinds before it claims, and an unreachable site is skipped — or it claimed it and has not walked there yet, in which case the memory still says *potential* job site and the profession has not changed. `PoiCompetitorScan` will also hand a contested claim to the more experienced villager, and `ValidateNearbyPoi` erases it if the block is gone. **Why does a spooked villager stay spooked past bedtime?** Because the schedule does not push, it is pulled — and the panic package has nothing at priority 99 to pull it. `VillagerCalmDown` is what lets the clock back in. ## Where to look `GoalSelector.tick` · `WrappedGoal.canBeReplacedBy` · `Goal.Flag` · `Mob.registerGoals` · `Mob.serverAiStep` · `Mob.updateControlFlags` · `Sensing` · `TargetingConditions.test` · `Brain.tick` · `Brain.updateActivityFromSchedule` · `Brain.setActiveActivityIfPossible` · `Brain.setActiveActivityToFirstValid` · `Brain.Provider` · `Brain.Packed` · `ActivityData` · `MemoryModuleType` · `MemorySlot` · `Sensor` · `Behavior.tryStart` · `Behavior.canStillUse` · `BehaviorBuilder` · `GateBehavior` · `RunOne` · `VillagerGoalPackages` · `Villager.refreshBrain` · `AcquirePoi` · `MoveToTargetSink` · `SleepInBed` · `Timelines.VILLAGER_SCHEDULE` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Pathfinding > Verified against **Minecraft 26.2** · Part VI · A villager decides to walk to its bed, and a hundred ticks later it is standing still against a fence, having formally given up. A behaviour has produced a position and stopped caring. Everything between that position and a mob actually leaning into a direction is this page: one A\* search over a snapshot of already-loaded chunks, a `Path` of nodes, and a control that has to be told again every single tick. The part of it people recognise is the failure. **Giving up is machinery, not an absence of it** — every node the mob is walking towards carries a timeout computed from its distance and the mob's current speed, three times over that budget abandons the path outright, and a separate check every hundred ticks asks whether the mob has covered a quarter of the ground its speed says it should have. The mob you watch walk into a wall and then wander off is running a scheduled surrender. Above the waterline is [goals and brains](ai-goals-and-brains.md), which decides *where*; this page is *how*, and it is the same machinery whichever decision system asked. ## The cast | class | what it decides | thread | |---|---|---| | `PathNavigation` | when to search, what budget to search with, when to give up | server main | | `PathNavigationRegion` | which blocks the search is allowed to see — a snapshot, never a load | server main | | `NodeEvaluator` | what a block *is* to this mob, as a `PathType` | server main | | `PathTypeCache` | the 4,096-entry memo that makes that affordable | server main, owned by `ServerLevel` | | `PathFinder` | the A\* itself, bounded by a node budget | server main | | `Path` | the node list, and whether it actually reaches the target | server main, with a stream codec for the debug channel only | | `MoveControl` | where a wanted position becomes a yaw and a speed, and forgets it again the same tick | server main | Nothing here is asynchronous. There is not a future, an executor or a thread anywhere in the pathfinder, and every entry point takes a `ServerLevel` or a `Mob` on one. Nothing crosses the network either, except when a debug client has subscribed. ## The pipeline ```mermaid flowchart TB WANT["a goal or a behaviour calls PathNavigation.createPath or moveTo"] GATE["four early exits: no targets, mob below the world floor, canUpdatePath false, or a live path to the same target"] REGION["PathNavigationRegion: a cube of chunks fetched with ChunkSource.getChunkNow"] EVAL["NodeEvaluator turns each candidate block into a PathType, through PathTypeCache"] SEARCH["PathFinder: A* over a BinaryHeap, bounded by maxVisitedNodes"] PATH["a Path — reached, or the best node it found"] FOLLOW["PathNavigation.tick advances the node index"] CTRL["MoveControl.setWantedPosition, re-issued every tick"] GIVEUP["stuck check every 100 ticks, node timeout at three times the budget"] WANT --> GATE GATE -- "returns null, or the existing path unchanged" --> WANT GATE --> REGION REGION --> EVAL EVAL --> SEARCH SEARCH --> PATH PATH --> FOLLOW FOLLOW --> CTRL FOLLOW --> GIVEUP GIVEUP -- "stop, and the behaviour is told the path is done" --> WANT ``` ## Asking: the four ways a search does not happen `PathNavigation.createPath` refuses before it does anything expensive. It returns null on an empty target set, on a mob below `Level.getMinY`, and on `PathNavigation.canUpdatePath` — which for `GroundPathNavigation` means *on the ground, in liquid, or riding something*, so an airborne mob simply cannot ask. The fourth exit is the interesting one: if there is already a path that is not done and the requested target is among its targets, **the existing path is returned unchanged**. Re-asking for a destination you are already walking to costs nothing and changes nothing. `PathNavigation.recomputePath` is the other entrance, and it is rate-limited rather than refused. More often than every twenty game ticks, or with `PathNavigation.canUpdatePath` false, it sets a flag instead and the next `PathNavigation.tick` tries again. That deferral is what keeps a mob standing in a doorway from re-searching twenty times a second. ## The budget, which is also the map One number governs both how hard the search may work and how much world it may look at. `PathNavigation` builds its `PathFinder` with `Attributes.FOLLOW_RANGE`'s **base** value times sixteen, and `PathNavigation.updatePathfinderMaxVisitedNodes` later recomputes it as sixteen times the larger of the *modified* follow range and `PathNavigation.setRequiredPathLength` — 16 by default, and raised by seven classes: 48 for `Villager`, `Allay`, `Bee`, `CopperGolem` and `HappyGhast`, 40 for `Llama`, 32 for `Fox`. `PathNavigation.setMaxVisitedNodesMultiplier` scales the result, and keeps scaling it until `PathNavigation.resetMaxVisitedNodesMultiplier` puts it back: `Bee` is the only class that touches either. The same maximum path length becomes the **radius of the `PathNavigationRegion`**, plus an offset of 8 or 16 depending on which `PathNavigation.createPath` overload was used — and inside the search it appears twice more, as a test on the current node's distance from the start and on each neighbour's walked distance. A villager can find a bed 48 blocks away because its *required path length* is 48 — it never touches `Attributes.FOLLOW_RANGE` at all, so its follow range is `Mob`'s default 16 and the larger of the two is the number it set itself; it cannot find one 60 blocks away no matter how open the ground is. That region is built by asking `ChunkSource.getChunkNow` for every chunk in the cube. **A path search never loads a chunk and never blocks** — an absent chunk is a null entry that reads as air. This is the same discipline `Entity.move` uses for collisions, and it is why AI cannot stall a tick. ## What a block is `NodeEvaluator` answers *what is this position, to this mob* with a `PathType`: 27 constants, each carrying a default cost, and **a negative cost means impassable, not expensive**. Nine of the twenty-seven are −1 — `PathType.BLOCKED`, `PathType.LAVA`, `PathType.FENCE`, `PathType.LEAVES`, `PathType.POWDER_SNOW`, `PathType.DAMAGING`, `PathType.UNPASSABLE_RAIL` and the two closed doors; `PathType.WATER` is 8, `PathType.FIRE` 16, `PathType.OPEN` and `PathType.WALKABLE` 0. A mob overrides any of them for itself with `Mob.setPathfindingMalus`, read back through `Mob.getPathfindingMalus` — which is how the same lava is free ground to a `Strider`, merely expensive to a `ZombifiedPiglin`, and a wall to the ordinary `Piglin` that never overrides it. Four evaluators in the pathfinder package cover the movement modes: `WalkNodeEvaluator`, and the three that specialise it or replace it — `FlyNodeEvaluator` and `AmphibiousNodeEvaluator` extend the walker, `SwimNodeEvaluator` extends `NodeEvaluator` directly. Two mobs subclass one further for themselves, `Frog` and `Creaking`. Each `PathNavigation` subclass chooses one: `GroundPathNavigation`, `FlyingPathNavigation`, `WaterBoundPathNavigation`, `AmphibiousPathNavigation`, and `WallClimberNavigation` on top of the ground one. Classifying a block is expensive enough to memo. `PathTypeCache` is a fixed 4,096-entry table owned by `ServerLevel`, consulted through `PathfindingContext` — which attaches it **only** on a `ServerLevel` and falls back to computing the type from scratch otherwise. It is invalidated one position at a time from `ServerLevel.sendBlockUpdated`, which is the subject of the last section. ## The search `PathFinder.findPath` is plain A\* over a `BinaryHeap`, and three of its details decide what mobs feel like. **It is bounded twice: by a node count and by a distance.** The loop breaks as soon as its visit counter reaches the budget — `PathFinder.setMaxVisitedNodes`, scaled by the multiplier — and inside the loop the maximum path length is the second bound, gating which nodes are expanded and which neighbours are admitted at all. Under the node budget alone a search through open ground would reach much further than the same budget spends in a maze; the distance bound is what stops it. **The heuristic is inflated.** Each neighbour's *h* is the best straight-line estimate multiplied by 1.5, which makes the search greedy: it finds a route sooner and the route it finds is not guaranteed to be the shortest. When several targets were reached, the winner is simply the path with the fewest nodes; when none was, it is the path that ends closest to a target, with fewest nodes as the tie-break. **A failed search still returns a path.** If no target came within the *reach range* — measured as a Manhattan distance from the popped node — the finder reconstructs a path to the closest node it managed to reach and marks it *not reached*. That is what `Path.canReach` reports, and it is the number that matters rather than "was a path found": `AcquirePoi` tests it before claiming a point of interest, and `MoveToTargetSink` turns a false into a *cannot reach* memory. A null from `PathNavigation.createPath` means one of the four early exits rather than a search that came back empty-handed — the finder always has a best node to reconstruct towards. One more thing the search does not do unless asked: it accumulates its closed set, and attaches it to the `Path`, only while something is subscribed to `DebugSubscriptions.ENTITY_PATHS`. `PathNavigation` installs that predicate in its constructor, off the server's `ServerDebugSubscribers`. `Path` has a stream codec for exactly this and no gameplay reason. ```mermaid sequenceDiagram participant MTS as MoveToTargetSink participant PN as PathNavigation participant PNR as PathNavigationRegion participant NE as NodeEvaluator participant PF as PathFinder participant MoveC as MoveControl MTS->>PN: createPath to the walk target, then moveTo with a speed modifier PN->>PN: four early exits, then push the pathfind profiler section PN->>PNR: build a cube of maxPathLength plus the offset, with getChunkNow PN->>PF: findPath — region, mob, targets, maxPathLength, reachRange, multiplier PF->>NE: getStart, then getNeighbors per popped node NE->>PNR: getPathTypeFromState, through PathTypeCache on a server level PF-->>PN: a Path, reached or best-effort, with canReach set accordingly PN->>PN: trimPath, record the stuck-check position, keep the node index Note over PN,MoveC: every tick from here PN->>MoveC: setWantedPosition for the next node, at speedModifier MoveC->>MoveC: tick — reset the operation to WAIT first, then set the yaw and the speed ``` ## Following it, one tick at a time `PathNavigation.tick` advances the node index and calls `MoveControl.setWantedPosition` with the next node and the speed modifier. It has to do that **every** tick, because `MoveControl.tick` sets its own `MoveControl.Operation` back to `MoveControl.Operation.WAIT` as it handles the move — the control is a one-shot instruction, not a destination it remembers. `MoveControl.setWantedPosition` is the main way a decision, goal or brain, becomes movement, but it is neither the only method nor a single call site. `MoveControl.strafe` is a second entrance, used by `RangedBowAttackGoal` and the brain behaviour `BackUpIfTooClose`; `MoveControl.setWait` is a third. `MoveControl.setWantedPosition` itself has twelve callers outside the navigations, in eight classes: the shared goals `TemptGoal.ForNonPathfinders` and `TryFindWaterGoal`, the per-mob goals of `Bee`, `Blaze`, `Ghast` and `Vex`, `Rabbit` from the mob itself rather than from a goal, and `Fox` to pin a sleeping fox where it lies. The first of those is why a happy ghast drifts towards you in a straight line through terrain a path search would have routed around — an ordinary tempted cow runs the base `TemptGoal`, which calls the navigation like anything else. Three more controls sit beside it and are the rest of what turns a decision into a pose. `LookControl` aims the head, `JumpControl` fires a jump the mover then executes, and `BodyRotationControl` swings the body to follow the head the moment the head is more than fifteen degrees off — it is the *reverse* move, easing the head back towards the front, that waits for ten stable ticks. `BodyRotationControl.clientTick` is a leftover name: `LivingEntity.tick` calls `Mob.tickHeadTurn` with no side check at all, so it runs on both sides every tick. The mover itself is Part VI's [movement and collision](movement-and-collision.md) — the control sets the yaw and calls `Mob.setSpeed`, which writes `LivingEntity.zza` with it, and `LivingEntity.travel` does the rest. `LivingEntity.xxa` is written only by the strafe branch, which pathfinding never takes. ## Giving up Two independent timers, and they answer different questions. `PathNavigation.doStuckDetection` runs its first half **every hundred ticks**: it compares where the mob is with where it was at the last check, against a threshold of the mob's effective speed times 100 times 0.25 — a quarter of the ground the speed claims. Below that, `PathNavigation.isStuck` is set and the path is stopped. The effective speed is the speed itself at 1.0 or above and the *square* of it below, which quietly makes the threshold far more forgiving for slow mobs. The second half is per node. When the next node changes, the navigation computes a time budget for it — the distance to that node divided by the mob's speed, times twenty — and accumulates real ticks against it. Past **three times** that budget, `PathNavigation.timeoutPath` resets the counters and stops. This is the one that catches a mob whose path is fine and whose route is blocked by something the search could not see. Both endings are the same ending from outside: `PathNavigation.isDone` becomes true, and whichever behaviour or goal was waiting on the path finds out on its next evaluation. ## The one place the world pushes back Everything above is AI asking the world questions. `ServerLevel.sendBlockUpdated` is the single call in the other direction. It invalidates the changed position in the path-type cache **unconditionally**, and then — only if `Shapes.joinIsNotEmpty` says the collision shape actually changed — walks `ServerLevel.navigatingMobs`, asks each navigation `PathNavigation.shouldRecomputePath` about the position, and calls `PathNavigation.recomputePath` on the ones that say yes. That last loop — and only that loop — is wrapped in a re-entrancy flag, because a recompute can itself change blocks; a call that arrives while the flag is set logs and, in a development environment, pauses. That is why closing a door in front of a mob re-routes it and repainting a block does not. ## Why mobs look stupid **Why do mobs take silly routes?** The heuristic is multiplied by 1.5, so the search is deliberately greedy — it stops at the first route that reaches, not the best one. Cost is a per-block malus, not a distance, so a mob will happily walk three blocks further to avoid a `PathType.WATER` node worth 8. **Why does a mob stop dead at the edge of my render distance?** It did not. The search only sees chunks already loaded on the server, and a target outside them is simply not reachable; the path comes back with `Path.canReach` false and the behaviour that asked gives up. **Why does a villager find a bed across the village but not one behind a wall?** Because 48 is `Villager`'s required path length, so distance is rarely the limit — and because `AcquirePoi` runs a real path search before it claims anything, so unreachable is invisible rather than merely far. ## Where to look `PathNavigation` · `PathNavigation.createPath` · `PathNavigation.moveTo` · `PathNavigation.tick` · `PathNavigation.recomputePath` · `PathNavigation.canUpdatePath` · `PathNavigation.doStuckDetection` · `PathNavigation.updatePathfinderMaxVisitedNodes` · `PathNavigation.setRequiredPathLength` · `GroundPathNavigation` · `FlyingPathNavigation` · `WaterBoundPathNavigation` · `AmphibiousPathNavigation` · `WallClimberNavigation` · `PathNavigationRegion` · `PathFinder.findPath` · `BinaryHeap` · `Node` · `Target` · `Path.canReach` · `NodeEvaluator` · `WalkNodeEvaluator` · `SwimNodeEvaluator` · `FlyNodeEvaluator` · `AmphibiousNodeEvaluator` · `PathType` · `PathTypeCache` · `PathfindingContext` · `Mob.getPathfindingMalus` · `MoveControl.setWantedPosition` · `LookControl` · `JumpControl` · `BodyRotationControl` · `ServerLevel.sendBlockUpdated` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Damage and death > Verified against **Minecraft 26.2** · Part VI · An arrow hits a player in full iron with Protection II: six damage becomes two, and if it kills, a message, a loot drop and a death screen. The arrow lands, the screen goes red, the hearts drop by one, and a notification sound plays somewhere behind you. Six damage left the bow and about two reached your health, which is the part everybody knows. The part nobody sees is what happens when a second arrow arrives four ticks later, inside the flash. If more than ten ticks of invulnerability remain, that hit is worth only its *excess* over `LivingEntity.lastHurt`: a weaker one returns immediately, having done nothing at all, and a stronger one takes a partial branch that clears an internal *took full damage* flag. That one flag gates the damage-event broadcast, the knockback, the hurt sound and the red flash alike. Health goes down and nothing else happens — which means neither `ClientboundDamageEventPacket` nor the red flash is a reliable "was hit" signal. It is all in `LivingEntity.hurtServer`. ## The cast | class | what it decides | thread | |---|---|---| | `DamageSource` | *what* hit and *who*: a direct entity, a causing entity, and — rarely — a position instead | built wherever the hit starts, server main | | `DamageType` | the message id, the difficulty scaling, the food cost, the hurt sound and the kind of death message | a dynamic registry entry, loaded from a data pack and synced to clients | | `DamageTypeTags` | almost every behavioural branch on the path below | read on the server main thread | | `LivingEntity` | the whole reduction pipeline, the i-frames, the flash, the two attribution references and death | server main thread | | `CombatRules` | the two pieces of arithmetic — armour and enchantment protection | stateless statics | | `CombatTracker` | what killed you, and the sentence that says so | server main, cleared on its own timer | | `ServerPlayer` | PvP, the death screen packet, the death message, the inventory drop | server main thread | | `Entity` | that `Entity.hurtServer` is **abstract** — there is no default behaviour to inherit | — | Everything above is server-side, and `LivingEntity` declares no `Entity.hurtClient` at all: no damage is ever *calculated* on a client. A client does set health — from `LivingEntity.DATA_HEALTH_ID`, from `ClientboundSetHealthPacket`, and to zero on the death event — but it never decides a number. See [authority](authority.md) for why `Entity.hurtServer` takes a `ServerLevel`, so the compiler enforces that and not a convention. ## The number the arrow decides `AbstractArrow.onHitEntity` builds the *source* first, because the number needs it: `EnchantmentHelper.modifyDamage` takes the `DamageSource` as an argument. It starts from `AbstractArrow.baseDamage`, 2.0 for an ordinary arrow, runs `EnchantmentHelper.modifyDamage` over the bow that fired it so Power raises the *base*, then multiplies by the arrow's current speed and rounds up with `Mth.ceil` — so the damage that leaves the bow is an integer, and a slowing arrow does less. `AbstractArrow.isCritArrow` adds a random bonus of up to half the damage plus one. Six is a fresh arrow at full draw. `DamageSources.arrow` then names two entities: the arrow as the *direct* entity, the shooter as the *causing* one — or, when the arrow has no owner left, the arrow itself in both roles. `DamageSource.getWeaponItem` reaches through the direct entity, which is how Breach on the attacker's weapon reaches the victim's armour calculation. A source can carry a position instead, though far less often than you would guess: ordinary explosions do not, and fall back to the direct entity in `DamageSource.getSourcePosition`. Exactly three sources are genuinely positional, and only they put a position on the wire through `DamageSource.sourcePositionRaw` — `DamageSources.badRespawnPointExplosion`, `/damage … at` (`DamageCommand`), and `ExplodeEffect` unattributed. The `DamageType` behind the source is five fields — message id, `DamageScaling`, food exhaustion, `DamageEffects` (which picks the hurt *sound*, and only for a `Player` — `Player.getHurtSound` is its one caller) and `DeathMessageType` — and it lives in a data pack. There are 51 keys in `DamageTypes` and 35 tags in `DamageTypeTags`, and **almost every behavioural branch below is tag-driven rather than type-driven**. The exceptions are worth listing because they are the whole list: thorns picks its own secondary sound in `LivingEntity.playSecondaryHurtSound`, wind charge is excluded from mob-anger attribution against an `EntityTypeTags.NO_ANGER_FROM_WIND_CHARGE` entity, and a `ServerPlayer` mid-dimension-change is invulnerable to all but `DamageTypes.ENDER_PEARL`. ## Three gates before anything is computed `LivingEntity.hurtServer` opens with three early returns: already invulnerable, already dying, or a `DamageTypeTags.IS_FIRE` source against `MobEffects.FIRE_RESISTANCE` — a mob-effect immunity sitting *outside* the reduction pipeline entirely. Invulnerability itself is `Entity.isInvulnerableToBase` (removed, the invulnerable flag, fire immunity, fall immunity) **or** an enchantment-granted one through `EnchantmentHelper.isImmuneToDamage`, which is how Frost Walker makes its wearer immune to magma blocks. Two subclasses get there first. `ServerPlayer.hurtServer` adds the PvP and team check — `ServerPlayer.canHarmPlayer` refuses when the level forbids PvP or the two share a team that disallows friendly fire — testing both a `Player` causing entity directly and an `AbstractArrow` causing entity by asking it for its owner. `ServerPlayer.isInvulnerableTo` adds the two conditions that have no tag: mid-dimension-change, and a client that has not finished loading. Then `Player.hurtServer` refuses a creative player unless the source is `DamageTypeTags.BYPASSES_INVULNERABILITY`, and applies **difficulty scaling** — halved and offset on easy, one and a half times on hard, zero on peaceful — but only when `DamageSource.scalesWithDifficulty` says so. That reads `DamageScaling` off the type, and `DamageTypes.ARROW` is *when_caused_by_living_non_player*: a skeleton's arrow scales and a player's does not. `Player.isInvulnerableTo` is also where `GameRules.DROWNING_DAMAGE`, `GameRules.FALL_DAMAGE`, `GameRules.FIRE_DAMAGE` and `GameRules.FREEZE_DAMAGE` live — switching one off makes a player *immune* rather than making the damage smaller. ## One number, a dozen owners Past the gates the number goes down a chain in which every link owns one arithmetic step — five multiplications and three subtractions — and knows about none of the others. ```mermaid flowchart TB N0["6.0 leaves the bow — Mth.ceil(speed times baseDamage), Power already folded into the base"] N1["LivingEntity.applyItemBlocking — subtract what BLOCKS_ATTACKS resolves for this angle and this damage type"] N2["freezing times 5 on a FREEZE_HURTS_EXTRA_TYPES entity, helmet times 0.75 on a DAMAGES_HELMET source"] N3["the i-frame window — over ten ticks left, only the excess over lastHurt survives"] N4["CombatRules.getDamageAfterAbsorb — armour, floored at a fifth and capped at 20"] N5["Resistance — five points of twenty-five per level, total immunity at amplifier four"] N6["CombatRules.getDamageAfterMagicAbsorb — protection points, capped at 20 as well"] N7["absorption hearts, spent before health is, then CombatTracker.recordDamage and LivingEntity.setHealth"] N0 -- "6.0 — Player.hurtServer scales by difficulty, but not a player's arrow" --> N1 N1 -- "6.0 — nothing raised" --> N2 N2 -- "6.0 — neither applies" --> N3 N3 -- "6.0 — the window was clear" --> N4 N4 -- "3.12 — 15 armour, no toughness, 48 per cent off" --> N5 N5 -- "3.12 — no Resistance" --> N6 N6 -- "2.12 — 8 protection points, 32 per cent off" --> N7 ``` The first link is blocking, and **shields are no longer a mechanism, only a vocabulary**. `LivingEntity.applyItemBlocking` asks the item being used — through `LivingEntity.getItemBlockingWith`, which enforces `BlocksAttacks.blockDelayTicks` — for a `DataComponents.BLOCKS_ATTACKS` component, checks the damage type against `BlocksAttacks.bypassedBy`, computes the angle between the source position and the victim's head rotation, and lets `BlocksAttacks.resolveBlockedDamage` pick a reduction from the component's own list. `BlocksAttacks.hurtBlockingItem` then charges durability — for a blocking `Player` only; a mob's item never wears — and a non-projectile block sends the *blocker* reeling through `LivingEntity.blockedByItem`, which is how a `Hoglin` throws whoever blocked it and a `Ravager` stuns itself while shoving them. An arrow with any piercing level skips all of it before the angle is computed. `ShieldItem` still exists, the statistic is still `Stats.DAMAGE_BLOCKED_BY_SHIELD`, and the axe-disables-shield rule survives as `LivingEntity.getSecondsToDisableBlocking` — which `Warden` overrides — feeding `Player.blockUsingItem` and `BlocksAttacks.disable`. None of it is shield-specific code any more. The two multipliers after it almost never fire and are both pure tag lookups: `DamageTypeTags.IS_FREEZING` against an entity in `EntityTypeTags.FREEZE_HURTS_EXTRA_TYPES` multiplies by five, and `DamageTypeTags.DAMAGES_HELMET` against a helmeted victim calls `LivingEntity.hurtHelmet` and multiplies by 0.75. ## Ten ticks in which nothing shows `Entity.invulnerableTime` is set to 20 by a hit that lands in full and counted down once a tick — from `LivingEntity.baseTick` for everything except a `ServerPlayer`, and from `ServerPlayer.tick` for players, earlier in the tick. `LivingEntity.hurtDuration` and `LivingEntity.hurtTime` are set to 10, so the red flash is only half the window. The other half is the silent one. Inside it, unless the type carries `DamageTypeTags.BYPASSES_COOLDOWN`, the incoming damage is compared against `LivingEntity.lastHurt` — what the last hit was worth — and only the excess is applied. A hit that is not bigger returns before any sound, packet, knockback or combat entry. A hit that *is* bigger applies the difference and clears a local *took full damage* flag, and everything downstream sits inside a test of that flag: no `ServerLevel.broadcastDamageEvent`, no `Entity.markHurt`, no `LivingEntity.dealDefaultKnockback`, no hurt sound, no reset of the flash. So the strongest hit in each window is the only one anyone can see, and the rest are free damage that leaves no trace on the wire. `LivingEntity.resolveMobResponsibleForDamage` and `LivingEntity.resolvePlayerResponsibleForDamage` sit outside that test and run on the silent *stronger* partial hit as well as the full one — only the weaker hit, which returns before them, leaves no trace. They write `LivingEntity.lastHurtByMob` and `LivingEntity.lastHurtByPlayer` (with its hundred-tick `LivingEntity.lastHurtByPlayerMemoryTime` countdown), crediting a tamed `Wolf`'s work to its owner. `LivingEntity.lastDamageSource` and `LivingEntity.lastDamageStamp` are set beside them, but only when the hit counted for something. ## Armour, and why big hits punch through it `LivingEntity.actuallyHurt` is where the number meets the victim's gear. `LivingEntity.getDamageAfterArmorAbsorb` first calls `LivingEntity.hurtArmor` — which is **empty on `LivingEntity`** and overridden only by `Player`, `Horse` and `Wolf`. A skeleton in full iron never wears its armour out. Where it is implemented it routes to `LivingEntity.doHurtEquipment`: one durability point per four damage, minimum one, per piece, and each piece must separately be `Equippable.damageOnHurt`, damageable, and pass `ItemStack.canBeHurtBy`. Then `CombatRules.getDamageAfterAbsorb` does the arithmetic. Effective armour is the armour points *minus the incoming damage divided by two plus a quarter of toughness*, clamped between `CombatRules.MIN_ARMOR_RATIO` of nominal and `CombatRules.MAX_ARMOR`, and the reduction is that over `CombatRules.ARMOR_PROTECTION_DIVIDER`. **Big hits punch through armour by design**: the subtraction is what makes a 40-damage hit see less armour than a 6-damage one, and `Attributes.ARMOR_TOUGHNESS` is exactly the term that slows it down. Full iron is 15 points and no toughness, so a 6-damage hit sees 12 effective armour, 48 per cent off, 3.12 left. Breach moves the resulting fraction through `EnchantmentHelper.modifyArmorEffectiveness` first. `LivingEntity.getDamageAfterMagicAbsorb` then applies Resistance and sums `EnchantmentHelper.getDamageProtection` across every equipment slot. Protection is not a class: it is `EnchantmentEffectComponents.DAMAGE_PROTECTION` holding a conditional value effect, confined to armour by its own *slots* declaration in the JSON rather than by the helper, worth one point per level per piece — so Protection II on four pieces is 8. `CombatRules.getDamageAfterMagicAbsorb` caps that sum at 20 and takes *sum over 25* off. 3.12 becomes about 2.12. **Armour is capped twice and floored once.** Effective armour never drops below a fifth of nominal and never counts above 20, and protection points cap at 20 as well — so armour alone tops out at 80 per cent reduction, protection at 80 per cent of what is left, 96 per cent combined — so armour alone can never take a hit to nothing. An effect can: Resistance at amplifier four multiplies by zero. What survives comes off absorption first and health second, at which point `CombatTracker.recordDamage` files a `CombatEntry`. `Player.actuallyHurt` overrides the whole method to add food exhaustion (`DamageType.exhaustion`, 0.1 for an arrow) and the damage statistics. ## Telling everyone, and what a block replaces ```mermaid sequenceDiagram participant AA as AbstractArrow participant SP as ServerPlayer participant LE as LivingEntity participant CT as CombatTracker participant SL as ServerLevel participant CPL as ClientPacketListener AA->>SP: hurtOrSimulate(arrow source, 6.0) SP->>SP: hurtServer — PvP and teams, then Player's creative gate and difficulty scaling SP->>LE: three gates, blocking, the two odd multipliers LE->>LE: i-frames — a partial hit clears the took-full-damage flag here LE->>CT: actuallyHurt — armour, protection, absorption, then recordDamage and setHealth LE->>LE: resolveMobResponsibleForDamage, resolvePlayerResponsibleForDamage LE->>SL: broadcastDamageEvent — full hits only, and only when nothing was blocked SL-->>CPL: ClientboundDamageEventPacket — every tracker, and the victim LE->>LE: markHurt, then dealDefaultKnockback LE->>SP: indicateDamage — skipped entirely if anything was blocked SP-->>CPL: ClientboundHurtAnimationPacket — that one player, nobody else LE->>LE: dead? checkTotemDeathProtection, else the death sound, then die SP->>SP: die — message, loot, byte 3, and no call up to LivingEntity SP-->>CPL: ClientboundPlayerCombatKillPacket — the death screen opens Note over LE,CPL: twenty ticks later, for a mob — tickDeath broadcasts byte 60 and removes it ``` `ServerLevel.broadcastDamageEvent` sends the type, the three entity ids and an optional position to every tracking player *and the victim*, and it runs **before** the knockback, not after. A successful block replaces it entirely: if the blocking component absorbed anything, `BlocksAttacks.onBlocked` plays the block sound and **no damage event is broadcast at all**, so a blocked hit puts no flash on anyone's screen. Only then does `Entity.markHurt` queue the velocity packet, and `LivingEntity.dealDefaultKnockback` compute a direction (from the projectile for a projectile, from the source position otherwise) for `LivingEntity.knockback`, which scales it by one minus `Attributes.KNOCKBACK_RESISTANCE`. `ServerPlayer.indicateDamage` ends that call, and is skipped when anything was blocked. The hurt sound comes after all of it — after the death check, not with the flash. Six packets carry the hit itself. `ClientboundDamageEventPacket` (type holder, causing and direct entity ids, optional position) and `ClientboundSetEntityMotionPacket` go to every tracker and the victim, `ClientboundHurtAnimationPacket` and `ClientboundSetHealthPacket` only ever to one player about themselves, `ClientboundSetEntityDataPacket` carries `LivingEntity.DATA_HEALTH_ID` for a mob, and `ClientboundEntityEventPacket` carries one byte — 3 for death, 60 for the poof, 35 for a totem. Inbound, only the respawn command. **The damage amount never crosses the wire.** The client picks a sound and a flash from the type and infers magnitude from health — and only for your own player, in `LocalPlayer.hurtTo`, the one place a hit is deduced from a health *drop*. `LivingEntity.handleDamageEvent` sets `Entity.invulnerableTime` to 20 and the flash to 10 and plays the sound, touching health not at all. ## Death, or not If health has reached zero, `LivingEntity.checkTotemDeathProtection` looks for `DataComponents.DEATH_PROTECTION` in either hand — unless the source is `DamageTypeTags.BYPASSES_INVULNERABILITY`, which is why `/kill` cannot be totemed — and on a hit consumes one, sets health to one, applies `DeathProtection`'s effects and broadcasts byte 35. Otherwise `LivingEntity.die` runs, and its order matters. Kill credit is read from the attribution references written a few lines earlier, `LivingEntity.handleKillingBlow` sets `LivingEntity.dead`, and `CombatTracker.recheckStatus` runs. Then — with a causing entity, **only if `Entity.killedEntity` on it agrees**; with none, unconditionally — the death game event fires, `LivingEntity.dropAllDeathLoot` runs and the wither rose is planted. The entity-event byte sits *outside* that veto and goes out to every watcher **before** `Pose.DYING` is set — the byte first, the pose after. Loot needs a *recent* player. `LivingEntity.dropAllDeathLoot` reads *killed by a player* off `LivingEntity.lastHurtByPlayerMemoryTime` still being above zero, so an attribution older than a hundred ticks costs the loot context its `LootContextParams.LAST_DAMAGE_PLAYER` and its luck, and costs the experience drop entirely unless the mob is an always-dropper. `LivingEntity.shouldDropLoot` gates loot on `GameRules.MOB_DROPS` and on not being a baby — but `Monster.shouldDropLoot` drops the baby condition, which is why baby zombies and piglins do drop. `LivingEntity.shouldDropExperience` mirrors it. Only `LivingEntity.dropEquipment` is outside both. ## The death screen, and what the client does alone **`ServerPlayer.die` never calls up.** It reimplements the sequence, so `LivingEntity.handleKillingBlow` never runs and `LivingEntity.dead` stays **false** on a dead player for the whole death screen. It reads the message from the combat log at the top and calls `CombatTracker.recheckStatus` at the bottom, so the log survives long enough to be read. `GameRules.SHOW_DEATH_MESSAGES` gates more than the chat line: with the rule off the message is never assembled and `ClientboundPlayerCombatKillPacket` carries an **empty** component, so the death screen still opens and says nothing. `ServerPlayer.die` also forgives neutral mobs under `GameRules.FORGIVE_DEAD_PLAYERS`, drops the inventory through `Player.dropEquipment` unless `GameRules.KEEP_INVENTORY`, and calls `ServerGamePacketListenerImpl.markClientUnloadedAfterDeath`. `DeathScreen` opens unless `GameRules.IMMEDIATE_RESPAWN`, and [player anatomy](../player/player-anatomy.md) owns the object that comes back. Two things then happen without a packet. **The client kills mobs on its own, from one byte**: `LivingEntity.handleEntityEvent` for byte 3 sets a non-player entity's health to zero and runs `LivingEntity.die` locally, so the twenty tick animation in `LivingEntity.tickDeath` is client-driven, not a consequence of a health update. And **`CombatTracker` clears itself** — after `CombatTracker.RESET_DAMAGE_STATUS_TIME` out of combat or `CombatTracker.RESET_COMBAT_STATUS_TIME` in it — from four places: a twenty tick timer in `LivingEntity.tick`, the top of `CombatTracker.recordDamage`, and both of `LivingEntity.die` and `ServerPlayer.die`, so a hit after a long lull discards the old log before filing its entry. ## Everything that calls it `Entity.hurt` and `Entity.hurtOrSimulate` are deprecated final wrappers over the abstract method: the second picks a side and returns `Entity.hurtClient` off a client level, the first does nothing there. `Entity.hurtClient` has nine declarations counting the base, and every one only ever answers *did this connect* — including `RemotePlayer`, which returns true unconditionally so a client-side arrow can play its own effects without knowing any numbers. Environmental damage is ticked from more places than *base tick* suggests. Fire (once per twenty fire ticks) is in `Entity.baseTick`, lava (four at a time) in `Entity.lavaHurt`, and suffocation, world-border and drowning damage in `LivingEntity.baseTick`. **Freezing and cramming are not**: freezing is in `LivingEntity.aiStep` every forty ticks, and cramming inside `LivingEntity.pushEntities`, called at the end of the same method under `GameRules.MAX_ENTITY_CRAMMING`. ## The five families of non-living damage `Entity.hurtServer` is abstract: there is no default behaviour at all, so every branch of the tree answers for itself, and the answers have nothing to do with anything above. **Fifty-four** files override it — a fifty-fifth, `Entity` itself, only declares it — thirty-three of them `LivingEntity` descendants — `ArmorStand` among them, a `LivingEntity` despite having no AI — and **twenty-one** not. Those twenty-one never touch armour, i-frames, absorption or the combat tracker, and fall into five families: - **Nothing happens.** `Entity.hurtServer` returns false, no side effect: `AreaEffectCloud`, `Display`, `Interaction`, `LightningBolt`, `Marker`, `OminousItemSpawner`, `PrimedTnt`, `EvokerFangs`, `EyeOfEnder` and `AbstractHurtingProjectile`. - **A flinch and nothing else.** `FallingBlockEntity` and `Projectile` call `Entity.markHurt` so the client sees the shove, then still return false. - **An int of health, no armour, no i-frames.** `ItemEntity` and `ExperienceOrb` keep a plain integer and subtract the damage from it. `ItemEntity` adds two rules: a `Mob` source is refused under `GameRules.MOB_GRIEFING`, and the stack itself is asked `ItemStack.canBeHurtBy`. - **One hit destroys.** `BlockAttachedEntity` kills itself and drops its item (mob-griefing gated too), `ShulkerBullet` is destroyed with particles without checking invulnerability at all, `EndCrystal` explodes and is **immune to the `EnderDragon` that eats it**, and `EnderDragonPart` forwards the whole call to `EnderDragon.hurt` on its parent. `ItemFrame` takes two hits — the first pops the item, the second falls through to `BlockAttachedEntity` and breaks the frame — while a *fixed* frame answers only to `DamageTypeTags.BYPASSES_INVULNERABILITY` or a creative player. - **An accumulator.** `VehicleEntity` adds *damage × 10* to a field through `VehicleEntity.setDamage` and destroys itself past 40. A creative player gets the hurt direction, the hurt timer and the accumulator all the same; the flag only redirects the destruction to `Entity.discard`, which drops nothing. `MinecartTNT` adds one rule on top: a *burning* `AbstractArrow` explodes it before the accumulator is even reached. The per-class detail is in [the non-living damage table](../../reference/non-living-damage.md). ## Where to look `DamageSource` · `DamageType` · `DamageTypes` · `DamageSources` · `DamageTypeTags` · `Entity.hurtServer` · `Entity.isInvulnerableToBase` · `ServerPlayer.hurtServer` · `Player.hurtServer` · `LivingEntity.hurtServer` · `LivingEntity.applyItemBlocking` · `BlocksAttacks` · `LivingEntity.actuallyHurt` · `LivingEntity.getDamageAfterArmorAbsorb` · `LivingEntity.getDamageAfterMagicAbsorb` · `CombatRules` · `LivingEntity.dealDefaultKnockback` · `ServerLevel.broadcastDamageEvent` · `LivingEntity.checkTotemDeathProtection` · `LivingEntity.die` · `ServerPlayer.die` · `LivingEntity.dropAllDeathLoot` · `Entity.killedEntity` · `LivingEntity.tickDeath` · `CombatTracker` · `CombatEntry` · `FallLocation` · `ClientboundDamageEventPacket` · `ClientboundPlayerCombatKillPacket` · `VehicleEntity` · `ItemFrame` — and [attributes](attributes.md) for armour, toughness and knockback resistance as attribute values. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # VII · Items and inventories > Verified against **Minecraft 26.2** · Part VII · The things you carry: what a stack is, what happens when you use one, how two machines agree about a chestful of them, and the three engines that make them out of data. A block is a position in a grid and an entity is a thing in the world. An item is neither: it is a *stack*, and a stack only exists inside something else — a hand, a slot, a chest, a recipe grid, a dropped `ItemEntity`, a packet. That makes this part the one where the two programs disagree most often and most cheaply, because almost everything a player does with items is predicted locally and confirmed afterwards. A player recognises the part by the small lies: the sword that swings before the server has heard about it, the chest whose contents appear a tick late, the bow that fires when you let go rather than when it finished drawing, and the dungeon chest that is empty until the moment somebody opens it. ## The shape of the part Part VII is **two tiers**, not a chain. The first three pages are the vocabulary — what a stack is, what using one does, and how a set of them is kept in agreement across the wire. The last five are three independent data-driven engines that produce or decorate stacks. Recipes, enchanting and loot tables lean on the vocabulary and hand each other nothing; *contexts and predicates* is the outlier, because its subject is not a stack at all — it is the question engine the other engines happen to run on, and it can be watched first. ```mermaid flowchart TD IS["Items and stacks — what a stack is"] UI["Using an item — what holding the button does"] CM["Containers and menus — how two machines agree about a set of them"] RE["Recipes — an arrangement of stacks becomes another stack"] EN["Enchantments — a named modifier other systems ask about"] EC["Enchanting — how one lands on an item"] CP["Contexts and predicates — the engine that answers questions about the world"] LO["Loot tables — its worked example"] IS -- "a stack is a diff over a prototype" --> UI UI -- "and a slot is where one lives" --> CM CM -- "an arrangement in a grid" --> RE IS -- "a modifier on the stack" --> EN EN --> EC CP -- "which needs no stack at all" --> LO ``` ## Before you start [Data components](../foundations/data-components.md) is the hard prerequisite: a stack *is* an item plus a component patch, and this part never re-teaches the component system. [Codecs, NBT and JSON](../foundations/codecs-nbt-json.md) for the four ways one stack is serialised, and [identifiers and registries](../foundations/identifiers-and-registries.md) and [the resource system](../foundations/resource-system.md) for where recipes, enchantments and loot tables come from and when. They come from three different places, and the difference bites: recipes are a reload listener and loot tables a reloadable registry layer, so `/reload` rebuilds both — while enchantments are a world-load dynamic registry that `/reload` never re-reads at all. Three ordering facts matter more than they look. [The server tick](../server/server-tick.md) drains the packet queue before any level ticks, which is why a click and its correction land in the same tick; [the level tick](../server/server-level-tick.md) decides *when* a menu's changes are broadcast, which is what makes a hopper's delivery visibly late; and [block interaction](../blocks/block-interaction.md) is how a chest gets opened in the first place, which is where two of these pages start. ## Watch in this order The first three in order, then the engines in any order you like. 1. [Items and stacks](items-and-stacks.md) — an `Item` holds almost no data, and an `ItemStack` holds a *diff*. The prototype it is a diff against does not exist until the first data-pack load. 2. [Using an item](using-an-item.md) — a meal and a bow, which are one machine read two ways. The client's countdown never stops at zero: the meal ends when a single byte arrives, and the bow ends when you let go. 3. [Containers and menus](containers-and-menus.md) — a shift-click out of a chest. One packet goes up, nothing comes back, and agreement is silence, because the server adopted the client's *claim* as its new baseline. 4. [Recipes](recipes.md) — eight planks and an empty middle. No recipe ever crosses the wire, and yet the client holds the whole contents of every recipe it has unlocked. 5. [Enchantments](enchantments.md) — there are no enchantment subclasses. Fire Aspect is a data-pack record whose "melee only" rule is one loot condition, and the burn that follows belongs to something else entirely. 6. [Enchanting](enchanting.md) — the five paths that change what an item is enchanted with, one of which runs backwards. The seed is per player, saved, and sent to the client, which is why the Standard Galactic gibberish is stable and why an anvil never changes what the table is offering. 7. [Contexts and predicates](contexts-and-predicates.md) — the engine that answers *is this true here*. Twelve of its twenty-six parameter sets never roll a loot table at all: `/execute if predicate`, entity selectors, advancement triggers and villager trades all run on it. 8. [Loot tables](loot-tables.md) — the worked example, and the part's closer. A dungeon chest is genuinely empty on disk, and the first thing to touch it — a hopper will do — commits the roll with no luck at all. Watched as lectures, five and six are the pair to keep together: *what an enchantment is* and *how you get one*. Seven and eight are the other pair, and seven is the one Part XIII comes back for. ## Reference this part uses Two were written for it. [Enchantment hooks](../../reference/enchantment-hooks.md) — every `EnchantmentHelper` entry point with the classes that call it, which is the enchantment system's real interface. [Loot context parameter sets](../../reference/loot-context-params.md) — all twenty-six, with the keys each one requires and allows. Then [data components](../../reference/components.md), [packets](../../reference/packets.md), [registries](../../reference/registries.md) and [diagram lanes](../../reference/lanes.md). The part stops at the slot. What a player's own inventory is, and how the hand relates to the equipment slots, is [player anatomy](../player/player-anatomy.md) in Part VIII; how a held stack picks the model you actually see is Part XI's, in [models and atlases](../rendering/models-and-atlases.md#how-an-item-picks-its-model). The container click is *not* on the prediction ledger — it carries no sequence number and opens no window — and [prediction and acknowledgement](../client/prediction-and-acks.md) in Part X is where that distinction is drawn. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Items and stacks > Verified against **Minecraft 26.2** · Part VII · A diamond pickaxe sits in a hotbar slot, is compared against its neighbours, is sent to a client, and finally loses its last point of durability. A diamond pickaxe is in your hotbar. The `Item` behind it, `Items.DIAMOND_PICKAXE`, is a single object shared by every diamond pickaxe that has ever existed on this server, and it holds four fields: a description id, a crafting remainder, a feature-flag set, and its own registry holder. Not the stack size. Not the mining speed. Not the durability. All of that is data components ([data components](../foundations/data-components.md)) — and they do not live on the `Item` either. They live on the item's `Holder.Reference` in `BuiltInRegistries.ITEM`, and the *stack* borrows that map as a read-only prototype and stores only the ways it differs from it. Two things follow, and they run through the whole page. **That prototype does not exist until the first data-pack load**: `Item.components` throws until a reload binds it, and `Item.CODEC_WITH_BOUND_COMPONENTS` exists purely to refuse an item whose components are not bound yet. And **a stack whose components equal its item's defaults carries an empty patch** — every item's prototype contains `DataComponents.ENCHANTMENTS` set to `ItemEnchantments.EMPTY`, so *enchanted with nothing* and *never enchanted* are not merely equal, they are the same object state, indistinguishable on disk and on the wire. ## The cast | class | what it decides | thread | |---|---|---| | `Item` | the behaviour hooks, and four fields that are not components | both main threads | | `Item.Properties` | the builder — which produces an *initializer*, never a component map | class-init, wherever the bootstrap runs | | `Holder.Reference` | where an item's default components actually live, and whether they exist yet | written on a main thread at reload | | `DataComponentInitializers` | the pile of pending default maps, one entry per registered item | built on the background executor | | `ItemStack` | a holder, a count, a pop time and a patched map — the mutable thing in a slot | both main threads | | `PatchedDataComponentMap` | prototype plus patch, and the copy-on-write flag that makes copying a stack free | wherever its stack is | | `ItemStackTemplate` | the immutable stack: what a stack looks like inside a component, a particle or a recipe | both | | `ItemEntity` | a stack that is an entity, with a five-minute clock and a merge rule | server main, mirrored on the client | ## Four fields, and only one of them is really data `ItemStack` is a final class holding exactly four things: two ints, a registry holder, and the interesting one. ```mermaid flowchart LR subgraph S["ItemStack — the object in the slot"] CNT["count"] POP["popTime"] HOL["item, a Holder of Item"] MAP["components"] end MAP --> PDM["PatchedDataComponentMap"] PDM --> PATCH["the patch: only what differs, plus a tombstone per removal"] PDM -. "prototype, borrowed and never written" .-> DEF HOL --> HR["Holder.Reference in BuiltInRegistries.ITEM"] HR --> IT["Item — descriptionId, craftingRemainingItem, requiredFeatures, its own holder"] HR --> DEF["DataComponentMap — the item's defaults, bound at reload"] ``` The dotted arrow is the shape of the whole system. A stack does not own its defaults and cannot change them: it points at a holder, and the holder owns one `DataComponentMap` shared by every stack of that item in both programs. `ItemStack.typeHolder` answers `Items.AIR`'s holder rather than null for an empty stack, which is why `ItemStack.getItem` never returns null either. The pop time is the odd one out: it is the five-tick squeeze the hotbar icon does when something lands in it, set to 5 by `Inventory` when a stack grows and by `ClientPacketListener.handleContainerSetSlot` when a slot update makes a hotbar stack larger, counted down by `ItemStack.inventoryTick` on **both** sides, and read by `Hud.extractSlot`, which scales the icon while it is above zero and hands the drawing to `GuiGraphicsExtractor` either way. It is ordinary shared state that only the client has any use for — and `ItemStack.copy` carries it across, so the animation survives being copied into a menu slot. `Item.Properties` is the builder used at class-init, and its output is not a component map. `Item.Properties.component` and every convenience over it — `Item.Properties.stacksTo`, `Item.Properties.durability`, `Item.Properties.food`, `Item.Properties.tool`, `Item.Properties.spear`, `Item.Properties.equippable`, `Item.Properties.useCooldown` — fold one more step onto a `DataComponentInitializers.Initializer`, a function that will be run against a `DataComponentMap.Builder` later, with a `HolderLookup.Provider` in hand. ## The map that does not exist yet Registration and definition happen at completely different times, on different threads, in different phases of the program. ```mermaid sequenceDiagram participant Boot as Bootstrap participant Items as Items participant Item as Item participant BIR as BuiltInRegistries participant Worker as Worker participant MS as MinecraftServer Boot->>BIR: bootStrap, then createContents touches Items.AIR BIR->>Items: the class loads, running 1177 static initialisers Items->>Item: one constructor per field, each given an Item.Properties Item->>BIR: DATA_COMPONENT_INITIALIZERS.add, one Initializer per item Note over Item: every item now exists and NO item has components Note over MS: much later — a world load, or a reload command MS->>Worker: ReloadableServerResources.loadResources Worker->>BIR: DataComponentInitializers.build against the reloaded registries and tags Note over Worker: DataComponentMap.Builder.build runs each item validator here Worker-->>MS: a PendingComponents per registry Note over MS: back on the main thread MS->>Item: PendingComponents.apply, then bindComponents on every holder ``` Between those two halves an `Item` is a live object whose `Item.components` call ends in a null check reading *Components not bound yet*; `Holder.Reference.areComponentsBound` is the polite way to ask, and `Item.CODEC_WITH_BOUND_COMPONENTS` refuses to name an unbound item at all. The client goes through the same gate on its own registries, in `RegistryDataCollector` while `ClientConfigurationPacketListenerImpl` finishes configuration — so a client on the title screen has componentless items too. Almost none of that map actually varies with the data pack. Every initializer starts by copying `DataComponents.COMMON_ITEM_COMPONENTS` — ten entries every item in the game gets, including the stack size of 64 and the empty enchantment list the hook rests on — and `Item.Properties.component` bakes literal Java values on top. Only `Item.Properties.delayedComponent` and `Item.Properties.delayedHolderComponent` read the context, and there are **twenty** call sites between them in the entire game, all in `Item` and `Items`: fire resistance resolving `DamageTypeTags.IS_FIRE`, the banner patterns, the goat horn, the jukebox songs, the egg variants, the spear's damage type. `Item.Properties.repairable` is the near miss — it takes a tag and is still eager, because it takes a lookup from `BuiltInRegistries.acquireBootstrapRegistrationLookup` at class-init and stores an unresolved `HolderSet` rather than waiting. ## A patch with tombstones, and a copy that copies nothing `PatchedDataComponentMap` holds three things: the prototype, a patch map, and a copy-on-write flag. Reads consult the patch and fall through to the prototype. Writes are where the design shows. `PatchedDataComponentMap.set` compares the new value against the prototype's and, if they are equal, **removes** the entry rather than storing it — the patch never contains a value the item already had. `PatchedDataComponentMap.remove` does the opposite trick: when the prototype has the component, it cannot simply drop the key, so it writes an empty optional as a tombstone meaning *this one is deliberately gone*. A patch is therefore a diff in both directions, which is exactly what `DataComponentPatch` serialises: additions, then removals. `ItemStack.copy` and `PatchedDataComponentMap.asPatch` both hand out the *same* patch map and set the copy-on-write flag on it, and every mutating method calls `PatchedDataComponentMap.ensureMapOwnership` first, which forks the map on the first write and clears the flag. Copying a stack — which menus, recipes, hover text and `ServerPlayerGameMode.destroyBlock` all do constantly — allocates one small object and copies no component data at all. The wire form falls straight out of it. `ItemStack.OPTIONAL_STREAM_CODEC` writes the count, then the item holder, then `PatchedDataComponentMap.asPatch` — **the patch only, never the prototype**, because the receiver already has the same prototype bound to the same holder. A count of zero is the whole encoding of an empty stack. ## When two stacks are the same stack Five static methods on `ItemStack` answer five different versions of that question, and menus, recipes and the renderer each want a different one. | method | compares | used for | |---|---|---| | `ItemStack.isSameItem` | the item holder only | *is this the same kind of thing* | | `ItemStack.isSameItemSameComponents` | the item, then the whole `PatchedDataComponentMap` | stacking, and every *are these interchangeable* test | | `ItemStack.matches` | the count as well | container synchronisation ([containers and menus](containers-and-menus.md)) | | `ItemStack.matchesIgnoringComponents` | everything except the component types a predicate excuses | the held-item swap animation | | `ItemStack.hashItemAndComponents` | the item's hash and the effective component map's | keying stacks in maps | The second row is where the hook pays off. `PatchedDataComponentMap` compares by prototype **and** patch, so for two stacks of the same item it amounts to comparing the patches — and since a patch can never hold a value equal to the prototype's, two pickaxes with the same damage are equal whether one reached that state by being set explicitly or by never being touched. The fourth row exists for one component. `DataComponents.DAMAGE` is the only component type in the game declared with `DataComponentType.Builder.ignoreSwapAnimation`, and `ItemInHandRenderer.shouldInstantlyReplaceVisibleItem` passes exactly that flag as the predicate — which is why a pickaxe losing a point of durability does not re-play the lower-and-raise animation. And a trap sits under all five rows: `ItemStack.EMPTY` is a singleton but is not identified by reference, because `ItemStack.isEmpty` also answers true for `Items.AIR` and for any count at or below zero. ## Two validators, one rule, two spellings Durability and stackability are mutually exclusive, and the game says so twice — in different places, at different times, against different components. | | the reload validator | `ItemStack.validateStrict` | |---|---|---| | installed by | `Item.Properties.finalizeInitializer` | — | | rejects | `DataComponents.DAMAGE` on a stackable item | `DataComponents.MAX_DAMAGE` on a stackable item, and a count over the maximum | | runs inside | `DataComponentMap.Builder.build` | `ItemInput`, `ItemStackTemplate.create`, `ItemStack.applyComponentsAndValidate` | | when | at reload, on the background executor | when a command, a template or a component patch builds a stack | | on failure | throws, failing the reload | depends on the caller: `ItemInput` throws a command syntax error, the other two log and yield `ItemStack.EMPTY` or restore the previous patch | Neither is reached from a network decode, and the client's stacks are proved a third way instead. Exactly **one** serverbound packet in the protocol carries an `ItemStack`: `ServerboundSetCreativeModeSlotPacket`, whose `ItemStack.OPTIONAL_UNTRUSTED_STREAM_CODEC` is wrapped in `ItemStack.validatedStreamCodec` — which runs no validator at all, but re-encodes the decoded stack through `ItemStack.CODEC` and throws if that fails. The contents of a container stack are checked one level down, but on the other path: `ItemStack.validateContainedItemSizes` runs inside `ItemStack.validateStrict`, over `DataComponents.CONTAINER`, `DataComponents.BUNDLE_CONTENTS` and `DataComponents.CHARGED_PROJECTILES` — so a shulker box full of impossible stacks is caught by a command, not at the creative slot's door. ## An item, a count, some components — said three ways `ItemInstance` is the read-only contract those validators are written against: `ItemInstance.count`, `ItemInstance.getMaxStackSize`, and — through `TypedInstance` and `DataComponentGetter` — five `TypedInstance.is` overloads for tags, holder sets, raw items, holders and resource keys, to which `ItemStack.is` adds a sixth taking a predicate. Its default `ItemInstance.getMaxStackSize` answers **1** when `DataComponents.MAX_STACK_SIZE` is missing — which for a bound item never happens, because the common set puts 64 there. Two classes implement it. `ItemStack` is the mutable one that lives in slots. `ItemStackTemplate` is an immutable record of a `Holder`, a count and a raw `DataComponentPatch` — what a stack becomes when it is stored *inside* something else: `ItemContainerContents` (so a shulker box's contents are templates, not stacks), `BundleContents`, `ChargedProjectiles`, `ItemParticleOption`, `HoverEvent`, `UseRemainder`, the recipe classes, and `Item`'s own crafting remainder. Its constructor refuses a count of zero or `Items.AIR`, so there is no empty template; but `ItemStackTemplate.create` and `ItemStackTemplate.apply`, which materialise a real stack, run `ItemStack.validateStrict` on the result and answer `ItemStack.EMPTY` with a log line rather than throwing. ## A pickaxe's last point of durability Durability is not one component: `ItemStack.isDamageableItem` demands `DataComponents.MAX_DAMAGE` present, `DataComponents.UNBREAKABLE` absent, and `DataComponents.DAMAGE` present. Take a diamond pickaxe one block short of breaking, and mine that block. `ServerPlayerGameMode.destroyBlock` copies the held stack *before* touching it — that copy is what `Block.playerDestroy` later hands the loot table, so the drops are decided by the tool as it was — then calls `ItemStack.mineBlock`, which delegates to `Item.mineBlock` and awards `Stats.ITEM_USED` if the item claims the block. The base `Item.mineBlock` is pure component work: it reads `DataComponents.TOOL`, does nothing without one, and damages the stack by `Tool.damagePerBlock` only on a server level, only when that number is above zero, and only when the block's destroy speed is not zero — so instant-break plants cost a tool nothing, and neither does a tool that declares no per-block damage. `ItemStack.hurtAndBreak` is the way in for almost everything, and the overload that does the work demands a `ServerLevel` outright. The overloads taking a `LivingEntity` pattern-match on the entity's level and **silently do nothing** on the client, which is why a client never predicts durability. The amount then goes through `EnchantmentHelper.processDurabilityChange` ([enchantments](enchantments.md)), which is how Unbreaking turns a point of damage into no damage at all, and a player with `Player.hasInfiniteMaterials` short-circuits to zero before even that. If anything survives, the stack fires `CriteriaTriggers.ITEM_DURABILITY_CHANGED` for a real player, writes the new `DataComponents.DAMAGE`, and — this being the last point — finds `ItemStack.isBroken` true, **shrinks itself by one**, and calls the break hook it was handed. For equipment that hook is `LivingEntity.onEquippedItemBroken`, which strips the item's attribute modifiers and broadcasts an entity event (47 for the main hand); `ServerPlayer.onEquippedItemBroken` adds `Stats.ITEM_BROKEN` on top. The client is told in one byte. `LivingEntity.handleEntityEvent` turns event 47 into `LivingEntity.breakItem`, which plays `DataComponents.BREAK_SOUND` from the stack still in that slot and spawns five item particles; the empty slot itself arrives separately, as a container update. Two siblings round the family out: `ItemStack.hurtWithoutBreaking` clamps one short of the maximum, and `ItemStack.hurtAndConvertOnBreak` transmutes rather than vanishing. What the player actually watched was three methods on `Item`. `Item.isBarVisible` is *is this stack damaged*, `Item.getBarWidth` scales the damage over thirteen pixels — the width `Item.MAX_BAR_WIDTH` names, though `Item.getBarWidth` spells the number out and no reader of the constant survives the decompile — and `Item.getBarColor` sweeps a hue from green to red. `GuiGraphicsExtractor` draws the two-pixel bar under the icon from those three answers and nothing else. ## The tick a stack gets, and the stack that is an entity `ItemStack.inventoryTick` runs on both sides and does exactly one thing there: decrement the pop time. It forwards to `Item.inventoryTick` only for a `ServerLevel` — the hook's parameter is declared as one, so it cannot be otherwise — and exactly two items override that hook, `CompassItem` and `MapItem`. It has two callers: `Inventory.tick`, from `Player.aiStep`, walks the thirty-six ordinary slots and tells the selected one it is the main hand; `EntityEquipment.tick`, from `LivingEntity.aiStep`, walks the worn and held slots of every living entity. A stack that leaves an inventory altogether becomes an `ItemEntity`, which keeps it in a synched data entry ([synched entity data](../entities/synched-entity-data.md)) rather than a plain field, counts up to a 6000-tick lifetime, and folds itself into neighbours through `ItemEntity.mergeWithNeighbours` — a merge that keeps the *smaller* of the two ages, so a fresh drop rejuvenates an old one. Blocks reach it through `Block.popResource` ([block breaking](../blocks/block-breaking.md)). ## What this page hands off Everything a stack *does* when a player holds down the use key — the prediction, the countdown, the consumable and cooldown components, the completion packet — is the next lecture: [using an item](using-an-item.md). How two machines agree about a set of stacks in a screen is [containers and menus](containers-and-menus.md); the component system itself is [data components](../foundations/data-components.md), catalogued in the [components reference](../../reference/components.md). And how an item picks the model, texture and tint you see in the slot is **not** this part's subject at all: it is Part XI's, in [models and atlases](../rendering/models-and-atlases.md#how-an-item-picks-its-model). ## Where to look `Item` · `Item.Properties` · `Items` · `BuiltInRegistries.ITEM` · `DataComponentInitializers` · `Holder.Reference` · `ReloadableServerResources.loadResources` · `ItemStack` · `PatchedDataComponentMap` · `DataComponentPatch` · `ItemInstance` · `TypedInstance` · `ItemStackTemplate` · `ItemContainerContents` · `ServerPlayerGameMode.destroyBlock` · `ItemEntity` · `Inventory.tick` · `EntityEquipment.tick` · `ServerboundSetCreativeModeSlotPacket` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Using an item > Verified against **Minecraft 26.2** · Part VII · A player holds the use key on a piece of cooked beef, then holds it on a bow — one countdown, two endings. You hold the use button on cooked beef and thirty-two ticks later you have eaten it. You hold the same button on a bow and nothing happens at all until you let go. These are the same machine: `Item.use` starts both, `LivingEntity.useItemRemaining` counts down on the client *and* the server for both, `ItemStack.onUseTick` runs every tick on both sides, and `LivingEntity.stopUsingItem` ends both. What differs is whether the count is allowed to mean anything — and the consequence is stranger than the difference. **The client's countdown does not stop at zero.** The meal ends because one byte arrives from the server, a `ClientboundEntityEventPacket` carrying `EntityEvent.USE_ITEM_COMPLETE`. The bow's countdown starts at 72000 and would take an hour to expire, so the shot is fired by a `ServerboundPlayerActionPacket` instead — the same packet, carrying the same action, that the meal would read as *the player changed their mind*. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | which key edge starts a use, and which one ends it | client main | | `MultiPlayerGameMode` | the client's own copy of the use, and the packet that reports it | client main | | `ServerGamePacketListenerImpl` | which handler each packet reaches, and whether the rotation is snapped first | server main | | `LivingEntity` | the countdown, the synched flags, and both endings | both main | | `ItemStack` | the dispatch into the item, and the after-use side effects | both | | `Item` | `Item.use`, `Item.getUseDuration`, `Item.releaseUsing`, `Item.useOnRelease` | both | | `Consumable` | everything the meal does, on both sides | both | | `ProjectileWeaponItem` | ammo, spread and the arrow — the counting on both sides, the spawning only on the server | both, but only the server's counts | The block-target branch of a use — right-clicking a chest rather than the air — leaves at `MultiPlayerGameMode.useItemOn` and belongs to [block interaction](../blocks/block-interaction.md); the stack itself is [items and stacks](items-and-stacks.md). ## The two paths, side by side | | the meal | the bow | |---|---|---| | what `Item.use` does | the default body finds `DataComponents.CONSUMABLE` and calls `Consumable.startConsuming` | `BowItem.use` overrides it, checks `Player.getProjectile`, calls `LivingEntity.startUsingItem` | | the refusal | `Consumable.canConsume` asks `Player.canEat`, for food only | no arrow anywhere and no infinite materials — `InteractionResult.FAIL` | | `Item.getUseDuration` | `Consumable.consumeTicks`, **32** for `Consumables.DEFAULT_FOOD` | **72000**, `Item.APPROXIMATELY_INFINITE_USE_DURATION` in all but name | | what `ItemStack.onUseTick` does | particles and the chew sound, on both sides | nothing — `BowItem` does not override `Item.onUseTick` | | how it ends | the count reaches zero on the server | the use key comes up on the client | | the packet that ends it | `ClientboundEntityEventPacket`, downward | `ServerboundPlayerActionPacket`, upward | | is the ending acknowledged | yes — that *is* the acknowledgement | **no**, nothing answers a release | | `Item.releaseUsing` | not overridden, returns false, so letting go simply abandons the meal | `BowItem.releaseUsing` is where the whole shot lives | | `ItemStack.useOnRelease` | false | **also false** | | what the client predicts | the entire meal, twice over | the animation, and nothing else | The last-but-one row is the one that looks wrong. `ItemStack.useOnRelease` is the third term of the completion guard and it is the obvious name for "this item is finished by letting go" — but **`CrossbowItem.useOnRelease` is its only override in the tree**. The bow and the trident return `Item.useOnRelease`'s default false. They are release-ended not because a predicate says so, but because their `Item.getUseDuration` is an hour long and their `Item.releaseUsing` does the work. The spyglass returns the default too and is not release-ended at all: at 1200 ticks its countdown really can run out. ## Starting: the client finishes before it speaks `Minecraft.handleKeybinds` sees the press and calls `Minecraft.startUseItem`, which sets the four-tick `Minecraft.rightClickDelay` itself, refuses while `LocalPlayer.isHandsBusy`, and walks both hands for a target. With nothing under the crosshair it reaches `MultiPlayerGameMode.useItem`, which opens a prediction window first (`MultiPlayerGameMode.startPrediction`, [prediction and acks](../client/prediction-and-acks.md)) and does everything else inside it: it builds the `ServerboundUseItemPacket` — hand, sequence number and both rotations — then consults the client's own `ItemCooldowns`, and runs `ItemStack.use` **locally first** only if the item is off cooldown. The packet goes up either way; the cooldown suppresses the prediction, not the report of it. The prediction is complete before a byte leaves the client, for the meal and the bow alike. Both answer `InteractionResult.CONSUME`, whose `InteractionResult.SwingSource.NONE` is why neither swings the arm, although `ItemInHandRenderer.itemUsed` still runs — the small dip the item makes as the use begins. An *instant* use, one whose `ItemStack.getUseDuration` is zero, returns its outcome the other way instead, through `InteractionResult.Success.heldItemTransformedTo`, which both game modes unwrap and write back into the hand. On the server, `ServerGamePacketListenerImpl.handleUseItem` acknowledges the sequence number, **snaps the player's rotation to the one in the packet**, and calls `ServerPlayerGameMode.useItem`. That snap is what makes the release strange later, because the release packet does no such thing. And `ServerPlayerGameMode.useItem` ends on a deliberate omission: it normally re-sends the player's inventory with `AbstractContainerMenu.sendAllDataToRemote` ([containers and menus](containers-and-menus.md)), but not when the use it just ran started a multi-tick one. **While you are eating or drawing, the server declines to correct your inventory.** ## While it runs: the flag on the wire and the flag the client believes `LivingEntity.startUsingItem` writes two bits of `LivingEntity.DATA_LIVING_ENTITY_FLAGS` ([synched entity data](../entities/synched-entity-data.md)) — bit one for *using*, bit two *assigned* the hand, so a main-hand use clears it — and only on the server. `LivingEntity.isUsingItem` and `LivingEntity.getUsedItemHand` read those bits, which is how every other client knows your arm is up. Your own client does not read them. `LocalPlayer.isUsingItem` overrides the base and returns a private local flag set by `LocalPlayer.startUsingItem`; `LocalPlayer.getUsedItemHand` likewise answers from a local field. Reconciliation happens afterwards, in **both** directions: `LocalPlayer.onSyncedDataUpdated` compares the arriving bits with the local flag and will start a use the client never predicted, or stop one it did. The base `LivingEntity.onSyncedDataUpdated` makes the matching repair on every *other* entity's copy — it adopts the held stack and re-derives `LivingEntity.useItemRemaining` from `ItemStack.getUseDuration`, which is why a remote player drawing a bow animates correctly although your client never saw the press. One abandonment rule is shared, and it lives a level above the countdown. `LivingEntity.tick` calls the private `LivingEntity.updatingUsingItem`, which compares the hand's current stack with the remembered one using `ItemStack.isSameItem` and calls `LivingEntity.stopUsingItem` if they differ; only on a match does it call `LivingEntity.updateUsingItem`, the countdown proper. The comparison is item identity, not components, so **swapping a bowl for a stew aborts the meal while a durability tick on the bow does not abort the draw.** ## Every tick, and what the bow does instead `LivingEntity.updateUsingItem` offers `ItemStack.onUseTick` the count *before* it decrements, so a thirty-two-tick meal is offered 32 down to 1 and never 0. For the meal that one call is the whole visible experience: `Consumable.shouldEmitParticlesAndSounds` is true once more than `Consumable.CONSUME_EFFECTS_START_FRACTION` of the duration has elapsed and the remaining count is a multiple of `Consumable.CONSUME_EFFECTS_INTERVAL`, and `Consumable.emitParticlesAndSounds` then spawns five item particles through `LivingEntity.spawnItemParticles` — behind `Consumable.hasConsumeParticles`, which drinks turn off — and plays the chew sound, which they do not. `Level.addParticle` does nothing on the server, so the crumbs are pure client simulation. For the bow the call does nothing whatever: `BowItem` does not override `Item.onUseTick`, and the base body is empty. Everything you see while drawing is the renderer reading the same counter the logic is decrementing. `ItemInHandRenderer` computes the draw curve for `ItemUseAnimation.BOW` from `LivingEntity.getUseItemRemainingTicks`, and the three-stage bow texture is not code at all: *items/bow.json* is a *condition* on *using_item* wrapping a *range_dispatch* on the *use_duration* property (`UseDuration`), scaled so its thresholds are fractions of `BowItem.MAX_DRAW_DURATION`. The crossbow is the exception that makes the rule legible. It *does* override `Item.onUseTick`, and that body is entirely server-side: it plays the three `CrossbowItem.ChargingSounds` at fixed fractions of `CrossbowItem.getChargeDuration` and, on reaching one, writes `DataComponents.CHARGED_PROJECTILES` onto the stack. Its client half is a render-thread computation — `CrossbowPull` and `ItemInHandRenderer` both call `CrossbowItem.getChargeDuration`, which calls `EnchantmentHelper.modifyCrossbowChargingTime` ([enchantments](enchantments.md)). **An enchantment hook, evaluated on the render thread, once per frame, to pick one of three textures.** > **For a 1.21-era reader.** There is no bow-pull item property class left > to hunt for. The old *pulling* / *pull* pair is now the shared > `UseDuration` range-select property plus a *using_item* condition, both > declared in the item's JSON; the crossbow keeps a bespoke one, > `CrossbowPull`, only because its denominator is enchantable. ## Moving while you use Neither path slows you through movement code — both read one component. `LocalPlayer.modifyInput` scales the movement input by `LocalPlayer.itemUseSpeedMultiplier`, which reads `UseEffects.speedMultiplier` off the stack, unless the player is riding, and `LocalPlayer.isSlowDueToUsingItem` blocks sprinting because `UseEffects.canSprint` is false. The famous twenty per cent is the default in `UseEffects.DEFAULT`, which sits in `DataComponents.COMMON_ITEM_COMPONENTS`, so every item has one — and neither cooked beef nor the bow overrides it, which is why **drawing a bow slows you by exactly as much as eating does, through exactly the same field.** `Item.Properties.spear` is the definition that overrides it outright, with a `UseEffects` that permits sprinting, suppresses vibrations and multiplies speed by one; the attack that ends *that* use is a different packet again ([the sword swing](../player/the-sword-swing.md)). ## The ending, in one picture ```mermaid flowchart TD T["LivingEntity.updateUsingItem, both sides, every tick"] T --> A["ItemStack.onUseTick with the count before the decrement"] A --> B["decrement LivingEntity.useItemRemaining"] B --> C{"reached zero"} C -- no --> T C -- yes --> D{"on the server"} D -- "no, this is the client" --> W["keep counting into the negatives and wait"] D -- yes --> E{"ItemStack.useOnRelease"} E -- "false, everything but a crossbow" --> F["LivingEntity.completeUsingItem"] E -- "true, a crossbow" --> T F --> G["ServerPlayer.completeUsingItem sends ClientboundEntityEventPacket 9 first"] G --> H["ItemStack.finishUsingItem, then LivingEntity.stopUsingItem"] R["the use key comes up, Minecraft.handleKeybinds"] --> S["MultiPlayerGameMode.releaseUsingItem sends RELEASE_USE_ITEM, then releases locally"] S --> P["ServerGamePacketListenerImpl.handlePlayerAction, no ack, no sequence"] P --> Q["LivingEntity.releaseUsingItem, each side on its own copy"] Q --> K["ItemStack.releaseUsing, then LivingEntity.stopUsingItem either way"] K -- "Item.releaseUsing returned true" --> L["the shot, then the after-use side effects"] K -- "returned false" --> M["the meal is simply abandoned"] ``` The **client's branch has no exit**. Nothing on the client ever reaches `LivingEntity.completeUsingItem` from the countdown — it is called from `Player.handleEntityEvent` when event 9 arrives, and nowhere else on that side. The counter meanwhile keeps falling past zero and only the renderer notices: `ItemInHandRenderer` draws a use pose solely while `LivingEntity.getUseItemRemainingTicks` is positive, so the arm drops at tick 32 whether or not the packet has landed. And **`ItemStack.useOnRelease` does not mean "ends on release"**. It means *do not let the countdown finish this, and give it one more tick when the key comes up*: `LivingEntity.releaseUsingItem` calls `LivingEntity.updatingUsingItem` again when it is true, so a crossbow gets a final `CrossbowItem.onUseTick` in which it can still latch the charge. No other item asks for that. Release is also not only a key-up — `LivingEntity.releaseUsingItem` has five other call sites: `LivingEntity.completeUsingItem` itself, when the stack turned out not to match the hand; `CrossbowAttack` and `RangedCrossbowAttackGoal`, which is how a pillager fires ([AI goals and brains](../entities/ai-goals-and-brains.md)); and `BrushItem.onUseTick` twice, ending its own use from inside the tick. ## The meal, tick by tick ```mermaid sequenceDiagram participant MC as Minecraft participant LP as LocalPlayer participant MPGM as MultiPlayerGameMode participant Wire as the network participant SGPL as ServerGamePacketListenerImpl participant SP as ServerPlayer participant Cons as Consumable Note over MC,Cons: tick 0, the press MC->>MPGM: startUseItem, nothing under the crosshair MPGM->>Cons: ItemStack.use, the default Item.use finds DataComponents.CONSUMABLE Cons->>LP: startConsuming, canConsume asks Player.canEat, then startUsingItem MPGM->>Wire: ServerboundUseItemPacket, hand and sequence and both rotations Wire->>SGPL: handleUseItem acks the sequence and snaps the rotation SGPL->>SP: the same Item.use, remaining = 32, the two flag bits are written Note over MC,Cons: ticks 1 to 31, both sides LP->>LP: ItemStack.onUseTick, five particles every fourth tick and the chew sound SP->>SP: the same call, particles discarded, sound broadcast to everyone else Note over MC,Cons: tick 32, the count reaching zero on the server alone SP->>Wire: ClientboundEntityEventPacket, EntityEvent.USE_ITEM_COMPLETE SP->>Cons: ItemStack.finishUsingItem, Consumable.onConsume, FoodData.eat Wire->>LP: Player.handleEntityEvent replays completeUsingItem locally SP->>Wire: ClientboundSetHealthPacket, same tick, overwrites the prediction Note over MC,Cons: a later tick SP->>Wire: broadcastChanges corrects the stack count ``` The replay is the interesting half. `Consumable.onConsume` runs on **both** sides and three parts of it do not: the `Stats.ITEM_USED` award and `CriteriaTriggers.CONSUME_ITEM` need a `ServerPlayer`, the `ConsumeEffect`s in `Consumable.onConsumeEffects` sit behind a server-side guard, and `GameEvent.EAT` is a no-op because `ClientLevel.gameEvent` has an empty body. Particles, sound, the `ConsumableListener` walk that finds `FoodProperties`, and the `ItemStack.consume` shrink all run on both — and every one of those client mutations is then overwritten. That is why a chorus fruit's teleport is never predicted while the hunger bar's jump is ([hunger and experience](../player/hunger-and-experience.md)). One meal, two exactly-once sound strategies. The chew sound goes through `Player.playSound`, which names the eater as the entity to *exclude*, so the server broadcasts it to everyone else and the eater's client plays it locally. The `FoodProperties` eat and burp sounds pass no exclusion at all — and `ClientLevel.playSeededSound` plays a sound only when the excluded entity *is* the local player — so those reach the eater as the server's broadcast alone. ## The bow, tick by tick ```mermaid sequenceDiagram participant MC as Minecraft participant LP as LocalPlayer participant MPGM as MultiPlayerGameMode participant Wire as the network participant SGPL as ServerGamePacketListenerImpl participant SP as ServerPlayer participant BowI as BowItem Note over MC,BowI: tick 0, the press MC->>MPGM: startUseItem, nothing under the crosshair MPGM->>BowI: ItemStack.use, BowItem.use asks Player.getProjectile BowI->>LP: startUsingItem, remaining = 72000 MPGM->>Wire: ServerboundUseItemPacket, hand and sequence and both rotations Wire->>SGPL: handleUseItem acks the sequence and snaps the rotation SGPL->>SP: the same BowItem.use, remaining = 72000, the two flag bits are written Note over MC,BowI: every tick after that, both sides LP->>LP: the count falls, onUseTick is empty, the model reads the use duration SP->>SP: the count falls, and nothing else happens at all Note over MC,BowI: the tick the key comes up MC->>MPGM: releaseUsingItem MPGM->>Wire: ServerboundPlayerActionPacket, RELEASE_USE_ITEM, sequence zero MPGM->>BowI: LivingEntity.releaseUsingItem, BowItem.releaseUsing on the client BowI->>LP: no ServerLevel, so no ammo and no arrow, then stopUsingItem Wire->>SGPL: handlePlayerAction, the rotation is whatever the server last heard SGPL->>SP: LivingEntity.releaseUsingItem SP->>BowI: BowItem.releaseUsing, ProjectileWeaponItem.draw then shoot BowI->>Wire: Projectile.spawnProjectile, then ClientboundAddEntityPacket SGPL-->>MPGM: nothing acknowledges the release itself Note over MC,BowI: a later tick SP->>Wire: the container sync corrects the arrow count and the bow's damage ``` `BowItem.releaseUsing` runs on both sides and gets nowhere on one of them. It measures the draw as `BowItem.getUseDuration` minus the remaining count, puts it through `BowItem.getPowerForTime` — a curve on the draw time in seconds, clamped at one after `BowItem.MAX_DRAW_DURATION` ticks — and returns false below a tenth of full power, which is why a tap of the button neither shoots nor costs durability. Above it, `ProjectileWeaponItem.draw` decides how many arrows leave the string (`EnchantmentHelper.processProjectileCount`) and `ProjectileWeaponItem.useAmmo` decides whether an arrow is actually spent (`EnchantmentHelper.processAmmoUse`). Both consult a `ServerLevel` and fall back to one and zero otherwise, and `ProjectileWeaponItem.shoot` is itself inside a `ServerLevel` test — so **on the client the draw produces a single phantom arrow marked `DataComponents.INTANGIBLE_PROJECTILE`, spends nothing and shoots nothing.** On the server it is five enchantment hooks and one ordering worth remembering. `EnchantmentHelper.processProjectileSpread` fans a multishot volley, and each arrow is aimed by `BowItem.shootProjectile` through `Projectile.shootFromRotation` using the **server's** rotation — which the release packet never updated, so the shot goes where the last movement packet said you were looking. `Projectile.spawnProjectile` aims, adds the entity to the level, and only *afterwards* calls `Projectile.applyOnProjectileSpawned`, which runs `EnchantmentHelper.onProjectileSpawned` **twice** when ammo and weapon are different items: once for the arrow's stack and once for the bow's. `ItemStack.hurtAndBreak` takes the durability after each arrow, and the volley breaks off if the bow dies mid-flight. Only when `Item.releaseUsing` returns true does `ItemStack.releaseUsing` run `ItemStack.applyAfterUseComponentSideEffects` — the same private step `ItemStack.finishUsingItem` runs for the meal, converting `DataComponents.USE_REMAINDER` into the empty bowl and starting `DataComponents.USE_COOLDOWN`. (For an instant use it runs from `ItemStack.use` instead, and only for a zero-duration success.) Cooldowns are grouped rather than per-item: `ItemCooldowns.getCooldownGroup` returns `UseCooldown.cooldownGroup` when the component names one and the item's `Identifier` otherwise, both sides own an `ItemCooldowns` — the client's is a real prediction, consulted before it will even attempt a use — and `ServerItemCooldowns.onCooldownStarted` mirrors the server's as one `ClientboundCooldownPacket` naming a *group*. ## What the ending never carries The release is the least-answered packet in the pipeline. `ServerGamePacketListenerImpl.handlePlayerAction` treats `ServerboundPlayerActionPacket.Action.RELEASE_USE_ITEM` in one line — call `LivingEntity.releaseUsingItem`, return — with no ack, no sequence number consumed, not even a spectator check. Everything the client learns about its own shot arrives as ordinary world traffic. The arrow is the quick one: adding it to the level starts its tracking inside the same call, so the spawn packet leaves on that tick. The spent ammo and the bow's damage wait for a container slot update ([containers and menus](containers-and-menus.md)) and the cleared using-flag for entity data it has already acted on, both a tick or more later. The shoot sound is broadcast with no exclusion, so — by the same rule as the burp — the shooter hears the server's copy and never their own. The completion is barely richer. There is no *you ate this* packet: event 9 tells the client to re-derive the outcome from components it already holds, and `ClientboundSetHealthPacket` corrects whatever it got wrong. The single override of `Item.finishUsingItem` in the whole tree is `SpyglassItem.finishUsingItem`, which plays a sound — the spyglass being the one item whose completion is worth a sound of its own — reached either at its 1200-tick duration or, like any use, the moment you let go. ## Where to look `Minecraft.handleKeybinds` · `Minecraft.startUseItem` · `MultiPlayerGameMode.useItem` · `MultiPlayerGameMode.releaseUsingItem` · `ServerGamePacketListenerImpl.handleUseItem` · `ServerGamePacketListenerImpl.handlePlayerAction` · `ServerPlayerGameMode.useItem` · `ItemStack.use` · `Item.use` · `LivingEntity.startUsingItem` · `LivingEntity.updatingUsingItem` · `LivingEntity.updateUsingItem` · `LivingEntity.completeUsingItem` · `LivingEntity.releaseUsingItem` · `ItemStack.releaseUsing` · `ItemStack.useOnRelease` · `Consumable` · `UseEffects` · `UseCooldown` · `BowItem` · `CrossbowItem` · `TridentItem` · `ProjectileWeaponItem` · `SpyglassItem` · `ItemInHandRenderer` · `UseDuration` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Containers and menus > Verified against **Minecraft 26.2** · Part VII · A player shift-clicks a stack out of a chest: one packet goes up, the server re-runs the same code against the real container, and nothing comes back down. You are standing at a chest with a stack of cobblestone in the top-left slot, and you shift-click it. Your machine moves the stack into your hotbar immediately, and one `ServerboundContainerClickPacket` leaves for the server. The server runs the *same* method against the real `ChestBlockEntity`, gets its own answer, compares the two — and if they agree, sends nothing at all. That is the steady state: **one packet up, zero packets down.** There is no transaction acknowledgement in this protocol; agreement is silence. What makes silence safe is the part nobody expects. The click packet does not carry the stacks the client thinks it produced, it carries a CRC32C *hash* per changed slot, and before comparing anything the server **installs that claim as its own belief about the client**. It adopts the client's belief object, never the client's data. ## The cast | class | what it decides | thread | |---|---|---| | `Container` | storage, and nothing about who is looking at it — `Container.getItem`, `Container.setItem`, `Container.stillValid` | wherever its owner runs | | `AbstractContainerMenu` | the slot list, the cursor, the state id, the click state machine, and the two baselines (one for listeners, one for the wire) | both main threads | | `Slot` | GUI policy — `Slot.mayPlace`, `Slot.mayPickup`, `Slot.getMaxStackSize` — and the guarded mutations a click goes through | both main threads | | `Inventory` | the player's own storage, present as slots in nearly every menu that opens — the lectern's one book slot is the exception | both main threads | | `MenuType` / `MenuProvider` | the registry entry with the *screen-side* constructor, and the server-side factory a block hands to `ServerPlayer.openMenu` | client / server main | | `ContainerSynchronizer` | the diffing channel to the connection, and every menu's writer but one — one per `ServerPlayer`, shared by every menu that player opens | server main | | `RemoteSlot` | what the server believes the client is holding in one slot, as either a stack or a hash | server main (`RemoteSlot.PLACEHOLDER` on the client) | | `HashedStack` | the client's claim about one slot after its own click | created on the client, matched on the server | ## The chest you see is not the chest The server's `ChestMenu` is built from a `MenuProvider` that `ChestBlock.getMenuProvider` produces and `ChestBlock.useWithoutItem` hands to `ServerPlayer.openMenu` ([block interaction](../blocks/block-interaction.md)), and its `Container` is the real `ChestBlockEntity` — or a `CompoundContainer` over both halves, for a double chest, from a separate anonymous provider. The client's `ChestMenu` is built from `MenuType`'s screen-side constructor by `MenuScreens`, and that constructor makes a **fresh `SimpleContainer`**. The client's chest contents have no connection to the block entity beyond the packet stream. Its `AbstractContainerMenu.stillValid` would be a lie for the same reason — `SimpleContainer.stillValid` returns true unconditionally — but the question is never put to it: every call site of `AbstractContainerMenu.stillValid` in the game is on the server. A client never closes its own menu because it walked away. The other half of that asymmetry is the synchronizer. Only `ServerPlayer.initMenu` ever calls `AbstractContainerMenu.setSynchronizer`, so a client menu has none, and every one of its `RemoteSlot`s stays `RemoteSlot.PLACEHOLDER`, whose `RemoteSlot.matches` always answers true, so the *wire* half of `AbstractContainerMenu.broadcastChanges` is inert on the client by construction. The listener half is not: `BeaconScreen`, `ItemCombinerScreen`, `LecternScreen` and the creative inventory all register a real `ContainerListener`, which is how the anvil's rename field repopulates itself after a slot changes. Three smaller facts about the model that a reader will otherwise trip over. A menu with a null `MenuType` cannot be opened over the network at all — `AbstractContainerMenu.getType` throws — which is why the player's own `InventoryMenu` is pinned to `InventoryMenu.CONTAINER_ID`, zero, and built independently on both sides, and why the mount menus need a `ClientboundMountScreenOpenPacket` of their own instead of `ClientboundOpenScreenPacket`. Every other menu takes an id from `ServerPlayer.nextContainerCounter`, which cycles 1 to 100 and never reaches zero. And `Slot.mayPlace` and `Container.canPlaceItem` are unrelated questions: the first is GUI policy and defaults to true without consulting the container at all, the second is what hopper automation reads. A player and a hopper can have different rights over the same slot. So can a player and an item: `Container.getMaxStackSize` defaults to **99**, and the familiar 64 comes only from the per-stack overload, which takes the minimum with the item's own maximum. ## One shift-click, end to end A three-row `ChestMenu` numbers slots 0–26 for the chest, 27–53 for the player's three main inventory rows and 54–62 for the hotbar, because `AbstractContainerMenu.addStandardInventorySlots` adds the main rows before the hotbar. Watch what the wire carries, and what it does not. ```mermaid sequenceDiagram participant MPGM as MultiPlayerGameMode participant ChestM as ChestMenu participant Wire as the network participant SGPL as ServerGamePacketListenerImpl participant ACM as AbstractContainerMenu participant RemS as RemoteSlot participant CSync as ContainerSynchronizer MPGM->>MPGM: copy every slot's stack, before touching anything MPGM->>ChestM: clicked with QUICK_MOVE, predicted on the twin ChestM->>ChestM: quickMoveStack, then moveItemStackTo backwards MPGM->>Wire: ServerboundContainerClickPacket, state id plus changed slots as hashes Wire->>SGPL: handleContainerClick, at the top of the server tick SGPL->>ACM: suppressRemoteUpdates, then the same clicked on the real chest SGPL->>ACM: setRemoteSlotUnsafe per claimed hash, then setRemoteCarried ACM->>RemS: receive, which throws away any concrete stack it held SGPL->>ACM: resumeRemoteUpdates, then broadcastChanges ACM->>RemS: matches? RemS->>RemS: the hash agrees, so adopt the server's stack as the copy RemS-->>CSync: nothing, sendSlotChange is never reached CSync-->>Wire: nothing goes down ``` **The press.** `AbstractContainerScreen.mouseClicked` resolves the hovered slot, sees an empty `AbstractContainerMenu.getCarried` and a held shift, and chooses `ContainerInput.QUICK_MOVE` on the press. Two near neighbours are release-path instead, from `AbstractContainerScreen.mouseReleased`: a shift-click with a *non-empty* cursor, and the shift-double-click sweep, which issues a separate `ContainerInput.QUICK_MOVE` — and a separate packet — for every matching slot in the same container. **The snapshot, then the prediction.** `MultiPlayerGameMode.handleContainerInput` copies every slot's stack before touching anything, because that copy is what the packet's diff is built against. Then it runs `AbstractContainerMenu.clicked` on the client's own menu. The `ContainerInput.QUICK_MOVE` branch loops `AbstractContainerMenu.quickMoveStack` while the clicked slot keeps yielding the same item, and it runs only for button 0 or 1, a non-negative slot index and a slot that `Slot.mayPickup` allows. **The move, and its two passes.** `ChestMenu.quickMoveStack` sees the index is inside the chest and calls `AbstractContainerMenu.moveItemStackTo` over the player's range with the backwards flag, which scans from the last slot down — **the hotbar first, from the right**. That method makes two passes. The first runs only if the stack is stackable at all, and merges into every existing compatible stack across the whole range (`ItemStack.isSameItemSameComponents`, topped up to `Slot.getMaxStackSize`). The second runs only if something is left, finds the first empty slot that `Slot.mayPlace` accepts, places what that slot's cap allows and **breaks**. One empty slot per call — which is why the caller loops. Note which pass consults policy: **the merge pass never asks `Slot.mayPlace`**, so a slot that would refuse the item on placement can still be topped up by a shift-click when it already holds a matching stack. Note also what `ChestMenu.quickMoveStack` never calls: `Slot.onTake`. `InventoryMenu.quickMoveStack` does. Shift-clicking out of a chest and out of the crafting result are structurally different operations. **The packet.** The client diffs the post-click slots against its snapshot, hashes each changed stack with `HashedStack.create`, and sends `ServerboundContainerClickPacket` with the container id, the client's *last-known* state id, the slot, the button, the `ContainerInput`, up to **128** changed slots as hashes, and the cursor as one more. **The real move.** The server takes the ladder below, then — with `AbstractContainerMenu.suppressRemoteUpdates` held — runs the identical `AbstractContainerMenu.clicked` path against the real `ChestBlockEntity` and the real `Inventory`. `Slot.setChanged` reaches `BlockEntity.setChanged`, which calls `Level.blockEntityChanged` to mark the chunk and `Level.updateNeighbourForOutputSignal` to re-derive the comparator output ([block entities](../blocks/block-entities.md)). The whole click is wrapped in a try/catch that builds a crash report category naming the menu class, the slot and the button. **Installing the claim, then comparing.** `AbstractContainerMenu.setRemoteSlotUnsafe` writes each hash the client sent into the matching `RemoteSlot`; `RemoteSlot.Synchronized` **discards any concrete stack it was holding** and keeps the hash alone. An out-of-range index is logged at debug and ignored rather than rejected. Then `AbstractContainerMenu.setRemoteCarried`, then `AbstractContainerMenu.resumeRemoteUpdates`, then `AbstractContainerMenu.broadcastChanges`, which makes one pass over the slots offering each to `AbstractContainerMenu.triggerSlotListeners` for advancements and to `AbstractContainerMenu.synchronizeSlotToRemote` for the wire, then the cursor, then the data slots. Where the hash agrees, `RemoteSlot.Synchronized` **promotes it to a concrete copy of the server's own stack** and nothing is sent. The advancement channel sees one state per click, not one per slot touched: `AbstractContainerMenu.triggerSlotListeners` runs only from `AbstractContainerMenu.broadcastChanges` and `AbstractContainerMenu.broadcastFullState` — and for a chest nothing calls back into the menu mid-click to reach either. That is a fact about the chest, not about menus: `CrafterSlot`, the anvil's and the smithing table's `ItemCombinerMenu` slots and the crafting grid all call `AbstractContainerMenu.slotsChanged` from `Container.setChanged`, and the base `AbstractContainerMenu.slotsChanged` is a bare `AbstractContainerMenu.broadcastChanges`. The click's own broadcast runs *after* `AbstractContainerMenu.resumeRemoteUpdates`, so suppression is not in force by then, and `ServerPlayer`'s `ContainerListener` filters to slots that are not a `ResultSlot` and whose container is the player's own `Inventory` before firing `CriteriaTriggers.INVENTORY_CHANGED` anyway. ## The ladder the server climbs before it believes you `ServerGamePacketListenerImpl.handleContainerClick` is four tests and a fork, and the interesting thing about it is how much of it ends in *nothing sent* rather than a correction. ```mermaid flowchart TD P["ServerboundContainerClickPacket, on the server main thread via PacketUtils.ensureRunningOnSameThread"] --> ID{"does containerId match the open menu?"} ID -->|"no"| D1["dropped in total silence, nothing logged, nothing sent"] ID -->|"yes"| SPEC{"spectator, or dead or dying?"} SPEC -->|"yes"| D2["sendAllDataToRemote, a full resync, and the click never runs"] SPEC -->|"no"| SV{"AbstractContainerMenu.stillValid"} SV -->|"fails"| D3["logged at debug, nothing sent, the menu is not closed here"] SV -->|"passes"| IX{"AbstractContainerMenu.isValidSlotIndex"} IX -->|"fails"| D4["logged at debug, nothing sent, nothing corrected"] IX -->|"passes"| ST["compare the packet's state id with the menu's, BEFORE anything is applied"] ST --> AP["suppressRemoteUpdates, run clicked, install the claimed hashes, resumeRemoteUpdates"] AP --> Q{"was that state id stale?"} Q -->|"stale"| FULL["broadcastFullState, ending in sendAllDataToRemote, one ClientboundContainerSetContentPacket with a fresh state id"] Q -->|"current"| BC["broadcastChanges, every slot against its RemoteSlot"] BC --> AG{"RemoteSlot.matches"} AG -->|"agrees"| SIL["the hash becomes a concrete copy of the server's stack, and nothing is sent"] AG -->|"disagrees"| ONE["one ClientboundContainerSetSlotPacket for that slot, with a fresh state id"] ``` The order of the last two boxes is the load-bearing part: **the state id is compared before the click is applied and acted on after**, so a click that quotes a stale id still runs, and still runs first. Its result is simply published wholesale instead of diffed. `AbstractContainerMenu.isValidSlotIndex` deserves suspicion. It accepts −1, accepts `AbstractContainerMenu.SLOT_CLICKED_OUTSIDE`, and otherwise only asks whether the index is below the slot count — so **every negative index passes it**. The branches that need a floor test for it themselves; the two that do not, `ContainerInput.SWAP` and the painting phase of `ContainerInput.QUICK_CRAFT`, index the list directly, and the click's own try/catch turns the failure into a `ReportedException` rather than swallowing it: what swallows it is `PacketProcessor`, which logs a game-listener error and carries on. An out-of-range click is therefore neither corrected nor fatal, but it is loudly logged, and it closes nothing: `ServerGamePacketListenerImpl.handleContainerClose` for its part validates nothing at all, not even the container id, and goes straight to `ServerPlayer.doCloseContainer`. ## Why hashes, and why only in one direction `HashedStack.create` produces either `HashedStack.EMPTY` or a `HashedStack.ActualItem` of item holder, count and a `HashedPatchMap` — one CRC32C integer per *added* component plus the plain set of *removed* ones. Each integer comes from running the component's own codec into `HashOps.CRC32C_INSTANCE`, a `DynamicOps` whose output **is** the hash ([codecs, NBT and JSON](../foundations/codecs-nbt-json.md) owns that mechanism). Nothing is serialised on the way and there is no intermediate byte form. `HashedPatchMap.matches` then checks the removed set, the added count, and each component's hash in turn. Two things follow. The client is *asserting a belief*, not authoring state — a hash cannot be turned back into an item, so a client that lies here can only fail to match — and the traffic is 128 integers rather than 128 full `DataComponentPatch`es. The asymmetry is exact: **only the client ever calls `HashedStack.create`, and only the server ever calls `HashedStack.matches`.** Hashing is not free-standing on the server either: `ServerPlayer`'s `ContainerSynchronizer` carries a 256-entry component-hash cache shared by every menu that player opens, and `ContainerSynchronizer.createSlot` hands each new `RemoteSlot.Synchronized` a `HashedPatchMap.HashGenerator` backed by it. One channel is deliberately outside all of this. `DataSlot` and `ContainerData` — furnace progress, enchanting cost, lectern page — travel as `ClientboundContainerSetDataPacket`, whose id and value are written as **shorts**, and they carry two independent baselines: `DataSlot.checkAndClearUpdateFlag` for the listeners and `AbstractContainerMenu.remoteDataSlots` for the network, compared separately. The network comparison is a plain integer test, so a furnace's progress bar is not covered by the hash-agreement silence and is re-sent every time it changes. ## The state id, and the three places it moves `AbstractContainerMenu.incrementStateId` is a wrapping 15-bit counter — it masks to 32767 — and it answers exactly one question: *has the client applied every slot correction I have sent?* It has three call sites in the whole game. Two are inside `ServerPlayer`'s synchronizer, behind `ContainerSynchronizer.sendInitialData` and `ContainerSynchronizer.sendSlotChange`; `ContainerSynchronizer.sendCarriedChange` and `ContainerSynchronizer.sendDataChange` do not bump it — and `ClientboundSetCursorItemPacket` carries no id whatever, while `ClientboundContainerSetDataPacket` carries the container's but never a state id. The third is `CraftingMenu.slotChangedCraftingGrid`, below. The client never generates one: `AbstractContainerMenu.setItem` and `AbstractContainerMenu.initializeContents` simply store whatever arrived and quote it back on the next click. A click quoting a stale id means corrections are still in flight, and the server stops diffing and resends everything. ## Where in the tick a broadcast happens Not one place, and the difference is observable. Packets are drained **before any level ticks** ([the server tick](../server/server-tick.md)), so a click and any correction it produces both happen at the top of the tick and reach the client the same tick. `ServerPlayer.tick` then calls `AbstractContainerMenu.broadcastChanges` again from the level's **entity** phase, which runs *before* the block-entity phase ([the level tick](../server/server-level-tick.md)). And `ServerPlayer.doTick` — driven by the connection after every level has finished — repeats only the `AbstractContainerMenu.stillValid` distance test, without a broadcast. So the distance test happens twice a tick and only the first is accompanied by a broadcast. A hopper that pushes an item into a chest whose menu is open therefore runs in the block-entity phase, after that tick's only broadcast, and **nothing calls back into the menu to say so** — `SimpleContainer.setChanged` is empty, `BlockEntity.setChanged` marks the chunk and re-derives the comparator output and stops there, `Inventory.setChanged` only bumps a counter. You see the hopper's item one tick late. Closing has its own surprise. The cursor belongs to the menu, not the player, so closing one would destroy it: `AbstractContainerMenu.removed` rescues it explicitly, dropping it in the world if the player has been removed or has disconnected and calling `Inventory.placeItemBackInInventory` otherwise, the whole method gated on being a `ServerPlayer`, which is what makes that safe. The rescue sends one `ClientboundSetPlayerInventoryPacket` per slot it fills, and runs *before* `AbstractContainerMenu.transferState`, which copies both the listener baseline and the remote beliefs across every container-and-slot pair the closing menu and `InventoryMenu` share. For a chest that is the 36 main and hotbar slots — not armour, not the offhand, not the 2×2 grid, not the crafting result — so changes to those four are re-sent and nothing else is. ## The seven click kinds `ContainerInput` has seven values, and the button number means something different in every one. `ClickAction` — `ClickAction.PRIMARY` and `ClickAction.SECONDARY` — is *not* on the wire; it is derived inside `AbstractContainerMenu.doClick` on both sides and handed to the item override hooks `ItemStack.overrideStackedOnOther` and `ItemStack.overrideOtherStackedOnMe`, which is how a `BundleItem` intercepts a click before ordinary slot logic runs. | value | the gesture | what the button number means | packets per gesture | |---|---|---|---| | `ContainerInput.PICKUP` | the ordinary click | 0 left, 1 right — and on `AbstractContainerMenu.SLOT_CLICKED_OUTSIDE`, drop all or drop one | 1 | | `ContainerInput.QUICK_MOVE` | shift-click | 0 or 1, the same in a real slot — and outside the window, the drop-all and drop-one of `ContainerInput.PICKUP` | 1, or one per matching slot for the shift-double-click sweep | | `ContainerInput.SWAP` | a hotbar key, or `Inventory.SLOT_OFFHAND` for the offhand key | the destination index: 0–8, or 40 | 1 | | `ContainerInput.CLONE` | creative middle-click | ignored, but the player must pass `Player.hasInfiniteMaterials` | 1 | | `ContainerInput.THROW` | Q over a slot, cursor empty — and also a click *outside* the window with an empty cursor, which the server's branch then ignores because it demands a non-negative index | 0 drops one, 1 (control-Q) drops the stack and keeps going while the slot yields the same item | 1 | | `ContainerInput.QUICK_CRAFT` | drag-painting | a packed mask that `AbstractContainerMenu.getQuickcraftHeader` and `AbstractContainerMenu.getQuickcraftType` split into a phase (start, continue, end) and a mode (even split, one each, clone) | **painted slots plus two** — `AbstractContainerScreen.quickCraftToSlots` sends a start, one continue per slot, and an end | | `ContainerInput.PICKUP_ALL` | double-click to collect | 0 scans forwards, 1 backwards, over two passes that skip already-full stacks first | 1 | An unknown id on the wire is not rejected. `ContainerInput`'s id mapper is built with a zero-out-of-bounds strategy, so a malformed click **decodes as `ContainerInput.PICKUP`**. ## Two paths that are not this protocol at all **Creative mode is a parallel protocol, not a variation.** `CreativeModeInventoryScreen` overrides `AbstractContainerScreen.slotClicked` and drives a menu of its own, and its writes go up as `ServerboundSetCreativeModeSlotPacket`. That is the one packet in the game whose *item* data the server adopts — a rename, a sign and a jigsaw block are adopted as text: `ServerGamePacketListenerImpl.handleSetCreativeModeSlot` takes the client's `ItemStack` verbatim into the slot through `Slot.setByPlayer`, behind only a `Player.hasInfiniteMaterials` check, a feature-flag check, a slot range of 1 to 45 and a count cap — then writes the same stack into the remote belief with `AbstractContainerMenu.setRemoteSlot` to suppress the echo. A negative slot number means *drop it in the world instead*, and that branch is the one place here with a rate limiter on it. **The crafting result is a second, unsuppressed channel.** `CraftingMenu.slotChangedCraftingGrid` recomputes the result slot and then sends a `ClientboundContainerSetSlotPacket` **straight down the connection**, bumping the state id itself and bypassing `ContainerSynchronizer` entirely. It is reached from `CraftingMenu.slotsChanged`, `CraftingMenu.finishPlacingRecipe` and `InventoryMenu.slotsChanged`, and the first of those fires mid-click, because `TransientCraftingContainer` calls `AbstractContainerMenu.slotsChanged` from its own `Container.setItem` and from any `Container.removeItem` that took something. So the one path that transmits *during* a click is also the one path `AbstractContainerMenu.suppressRemoteUpdates` does not cover — that flag guards only `AbstractContainerMenu.synchronizeSlotToRemote` and its two siblings. [Recipes](recipes.md) is where the recomputation itself lives. Everything else on the wire is bookkeeping around those two: `ClientboundOpenScreenPacket` and `ClientboundContainerClosePacket` for the lifetime, `ClientboundContainerSetContentPacket` and `ClientboundSetCursorItemPacket` for a resync, `ServerboundContainerButtonClickPacket` for the lectern, enchanting, loom and stonecutter buttons, `ServerboundContainerSlotStateChangedPacket` for crafter toggles, `ServerboundSelectBundleItemPacket` for a bundle, and `ServerboundSetCarriedItemPacket` with `ClientboundSetHeldSlotPacket` for the hotbar selection — which, despite the name, has nothing to do with the cursor. A structure chest fills itself on first open through `RandomizableContainer.unpackLootTable` ([loot tables](loot-tables.md)), and `ContainerLevelAccess` — `ContainerLevelAccess.NULL` on the client — is the position capability a block-anchored menu tests distance against. None of the click protocol is data-driven: `BuiltInRegistries.MENU` supplies only the type. ## Where to look `ServerGamePacketListenerImpl.handleContainerClick` · `MultiPlayerGameMode.handleContainerInput` · `AbstractContainerMenu.doClick` · `AbstractContainerMenu.moveItemStackTo` · `AbstractContainerMenu.broadcastChanges` · `AbstractContainerMenu.setRemoteSlotUnsafe` · `RemoteSlot.Synchronized` · `HashedStack` · `HashedPatchMap` · `HashOps` · `ContainerSynchronizer` · `ContainerListener` · `Container` · `Slot` · `ChestMenu` · `InventoryMenu` · `CraftingMenu.slotChangedCraftingGrid` · `ContainerInput` · `ClickAction` · `DataSlot` · `MenuType` · `MenuProvider` · `MenuScreens` · `ServerPlayer.openMenu` · `ServerGamePacketListenerImpl.handleSetCreativeModeSlot` · `CreativeModeInventoryScreen` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Recipes > Verified against **Minecraft 26.2** · Part VII · Eight planks go around the empty centre of a crafting table, a chest appears in the result slot, and the client is never told which recipe it was. You lay eight planks around the empty middle square of a crafting table and a chest appears in the slot on the right. What the server did was take a trimmed copy of the grid, scan every crafting recipe it has loaded in alphabetical order until one matched, call a `Recipe.assemble` that ignored your planks entirely, and push the answer down the wire in a packet written by hand, outside the menu's ordinary bookkeeping. What it never did was tell you *which* recipe matched — and it could not have, because **no `Recipe` ever crosses the wire**. Every `RecipeSerializer` is a record of a `MapCodec` and a `StreamCodec`, and no game packet uses the stream half: the only reference to `Recipe.STREAM_CODEC` anywhere in the tree is inside `RecipeHolder.STREAM_CODEC`, which has no call sites at all. For the recipes it has unlocked the client holds the whole *contents* — pattern, dimensions, ingredients, result — as a `RecipeDisplay`. What it is denied is the **identity**, plus every recipe it has not unlocked, plus any authority over the outcome. ## The cast | class | what it decides | thread | |---|---|---| | `Recipe` | ten methods and no result getter — a result leaves through `Recipe.assemble`, `Recipe.display`, or a stonecutter's `StonecutterRecipe.resultDisplay` | data, and a `Recipe` object never leaves the server | | `RecipeManager` | the loaded set, four indexes derived from it, and the server's `RecipeAccess` | the background executor for the scan, server main for everything after | | `RecipeMap` | the immutable store, holding exactly two indexes: `RecipeMap.byType` and `RecipeMap.byKey` | built off-thread, swapped in and read on server main | | `Ingredient` | whether one stack satisfies one slot — a `HolderSet` of items wearing a predicate face | both sides | | `CraftingInput` | the trimmed grid a crafting recipe is matched against, and the presence index a shapeless one is matched with | server main | | `ResultSlot` | the order of the endgame: award first, then look the recipe up again, then decrement | server main, mirrored on the client's own menu | | `ServerRecipeBook` | which recipes this player has unlocked, and which of them still glow | server main | | `ClientRecipeContainer` | everything the client knows about recipes *as recipes*: seven `RecipePropertySet`s and the stonecutter's input set | client, rebuilt wholesale from each packet | ## Loading: one scan, one swap, and four indexes built later `RecipeManager` is a `SimplePreparableReloadListener` over a `RecipeMap`, so it takes the ordinary two-phase shape of [the resource system](../foundations/resource-system.md) — and then does something unusual with the second phase. ```mermaid flowchart TD W["Worker: RecipeManager.prepare scans data/ns/recipe with SimpleJsonResourceReloadListener.scanDirectory, parsing each file through Recipe.CODEC into a sorted map"] --> M["RecipeMap.create, one RecipeHolder per file, keyed by a ResourceKey in Registries.RECIPE"] M --> A["server main: RecipeManager.apply swaps the field and logs a count. That is all it does."] A --> GAP["until the next call, the four indexes below still describe the PREVIOUS recipe set"] GAP --> F["MinecraftServer calls RecipeManager.finalizeRecipeLoading itself, in its constructor and again at the end of reloadResources"] F -->|"an ingredient is dropped unless every item in it is enabled"| P1["seven RecipePropertySets, one per key in RECIPE_PROPERTY_SETS"] F -->|"input and result display both enabled"| P2["SelectableRecipe.SingleInputSet, the stonecutter's own index"] F -->|"result and crafting station both enabled"| P3["allDisplays: a flat list whose POSITION is the RecipeDisplayId"] P3 --> P4["recipeToDisplay: recipe key to the displays it produced"] ``` Three things in that picture are worth saying out loud. **The scan is sorted, and the sort is the whole ordering story.** `RecipeManager.prepare` accumulates into a sorted map keyed by `Identifier`, and `Identifier.compareTo` compares the *path* first and the namespace only to break a tie. So *foo:acacia_boat* sorts ahead of *minecraft:zzz*, matching is deterministic across restarts, and the same order fixes the numbering of every display id below. **The indexes are not built by `RecipeManager.apply`.** `RecipeManager.finalizeRecipeLoading` has exactly two call sites, both of them in `MinecraftServer`, and neither is inside the reload listener. Between the swap and that call the four derived indexes are **empty** — a reload builds a fresh `RecipeManager`, whose constructor sets all four to their empty values — so the recipe book, the property sets and the stonecutter index describe nothing at all. Nothing can catch the game in that state: the swap and the call are five statements apart in one lambda on the server thread. **A `RecipeDisplayId` is a list index, not an identifier.** It is a record wrapping a single int, and the int is the position the entry took in the flat display list. Reload an unchanged pack and every id comes back identical; add one recipe near the front and everything after it shifts. The server does not try to work out which ids moved — `ServerRecipeBook.sendInitialRecipeBook` re-sends the player's whole book with the replace flag set. Nor is the list one entry per recipe: `Recipe.display` returns a *list*, and `TransmuteRecipe` returns one display per legal material count — up to `TransmuteRecipe.MAX_MATERIAL_COUNT`, eight — so a single recipe can occupy eight consecutive ids. The same walk decides what a recipe *is*, and it forgives more than you would expect. A non-special recipe whose `PlacementInfo.isImpossibleToPlace` — the usual cause being an ingredient tag that resolved to nothing, which `Ingredient.CODEC` cannot reject because only a *literal* empty list is illegal — is logged as unplaceable and then **kept**. It stays in `RecipeMap`, it still matches a manual craft, and it still gets a `RecipeDisplayEntry`, one whose ingredient list is present but empty; and `RecipeDisplayEntry.canCraft` on an empty ingredient list is trivially *true*, so the book paints it as craftable out of thin air. Only gate six of the auto-fill, below, stops the click. ### It is not only shaped and shapeless `RecipeSerializers` registers twenty-one serializers and fourteen of them are crafting-table recipes. **Nine** of those fourteen are `CustomRecipe`s — Java, not data. `CustomRecipe` hard-codes `Recipe.isSpecial` true, `Recipe.group` empty and `PlacementInfo.NOT_PLACEABLE`, and not one of the nine overrides `Recipe.display`, so a special recipe contributes nothing to the display list in the first place. Only eight of the nine are *named* special: `DecoratedPotRecipe` registers as *crafting_decorated_pot* and is a `CustomRecipe` all the same. Of the five that remain, `ShapedRecipe` and `ShapelessRecipe` are the pair everyone knows, and `DyeRecipe`, `ImbueRecipe` and `TransmuteRecipe` are genuine `NormalCraftingRecipe`s — data-driven, with hand-written matching, hand-built placement info, and the exotic `SlotDisplay` variants to draw themselves with (`SlotDisplay.OnlyWithComponent` and `SlotDisplay.DyedSlotDemo` for the first, `SlotDisplay.WithAnyPotion` for the second). So *special* and *neither shaped nor shapeless* are not the same set, and the difference matters, because it is *special* that the book cannot show. ## Eight planks: the trace ```mermaid sequenceDiagram participant CraftM as CraftingMenu participant CI as CraftingInput participant RM as RecipeManager participant ResultC as ResultContainer participant Wire as the network participant ResultS as ResultSlot participant SRB as ServerRecipeBook Note over CraftM: the tick the eighth plank lands CraftM->>CI: asCraftInput, trimming the empty border rows and columns CraftM->>RM: getRecipeFor CRAFTING, this input, this level, no hint RM-->>CraftM: the first RecipeHolder that matches, in id order, or nothing CraftM->>ResultC: setRecipeUsed, refused under LIMITED_CRAFTING if the book has not unlocked it CraftM->>ResultC: setItem 0, the assembled stack CraftM->>Wire: ClientboundContainerSetSlotPacket, written by hand, bumping the state id Note over ResultS: some later tick, the player clicks the result ResultS->>ResultC: checkTakeAchievements first, then awardUsedRecipes on the container ResultC->>SRB: addRecipes, and then the container nulls its stored holder SRB->>Wire: ClientboundRecipeBookAddPacket ResultS->>RM: getRecipeFor again, which it does whether or not the holder survived ResultS->>CraftM: removeItem one per occupied cell, then place the remainders Note over CraftM: every one of those removals re-enters slotsChanged ``` **The grid changes.** Writing a plank into the menu's `TransientCraftingContainer` calls `AbstractContainerMenu.slotsChanged`, and both crafting overrides of it end in the same static, `CraftingMenu.slotChangedCraftingGrid`. They reach it differently: `CraftingMenu.slotsChanged` goes through `ContainerLevelAccess.execute`, while `InventoryMenu.slotsChanged` calls the static directly, gated only on having a `ServerLevel`. A `CraftingMenu` built with the two-argument constructor holds `ContainerLevelAccess.NULL`, whose `ContainerLevelAccess.execute` runs nothing — and that instance is the client's copy, which is why the client never matches anything. **Trimming.** `CraftingContainer.asPositionedCraftInput` produces a `CraftingInput` with the empty border rows and columns removed *and* the offset beside it, which is why a shaped recipe works anywhere in the grid; `CraftingContainer.asCraftInput` is the same call with the offset thrown away, and it is what matching uses. Its constructor also fills a `StackedItemContents`, but accounts every stack as **one** item: that index is a presence set for shapeless matching, not the arithmetic the auto-fill does. **Matching is a linear scan.** `RecipeMap.getRecipesFor` exits immediately on an empty input, then streams the recipes of that `RecipeType` and filters them through `Recipe.matches`, and `RecipeManager.getRecipeFor` takes the first. `ShapedRecipePattern.matches` compares the ingredient count, then demands exactly matching trimmed dimensions, then tries the **mirrored** layout before the straight one — unless the pattern is symmetrical, which `Util.isSymmetrical` settles once in the constructor. The chest's ring of planks is symmetrical, so only the straight pass ever runs, and a one-column pattern is always symmetrical too. `ShapelessRecipe.matches` rejects on count, short-circuits the single-slot case, and otherwise hands the presence index to the bipartite search in `StackedContents`, which it reaches through `StackedItemContents.canCraft`. Three accelerations sit on top of that scan, and each belongs to a different caller. `RecipeManager.getRecipeFor` takes an optional **hint** and tests it before scanning; the auto-fill supplies one through `CraftingMenu.finishPlacingRecipe`, so the recipe it has just laid out is the first thing re-matched — and the base `AbstractCraftingMenu.finishPlacingRecipe` is a no-op, so the player's own 2×2 grid never gets one. `RecipeManager.CachedCheck` remembers the last successful key and re-hints with it — `AbstractFurnaceBlockEntity` holds one per block entity and `CampfireBlock` hands one to `CampfireBlockEntity.cookTick`. And `RecipeCache`, ten entries held statically by `CrafterBlock` and keyed on the grid contents, caches **misses** as well as hits, and invalidates on object identity: it keeps a weak reference to the manager and wipes itself the moment the level hands back a different one, which works because a reload builds a new `RecipeManager`. **The gate, and then the result.** `RecipeCraftingHolder.setRecipeUsed` returns false — leaving the result slot empty — when `GameRules.LIMITED_CRAFTING` is on, the recipe is not special, and `ServerRecipeBook.contains` says no. Limited crafting is enforced *here*, in the result slot, not in matching. Past it, `Recipe.assemble` produces the stack — for everything but the hand-written recipes it ignores its input entirely and materialises a stored `ItemStackTemplate` ([items and stacks](items-and-stacks.md)) — `ItemStack.isItemEnabled` filters that against the level's feature flags, and `ResultContainer.setItem` stores it. **Pushing it.** `AbstractContainerMenu.setRemoteSlot` forces the server's belief about slot zero and then a `ClientboundContainerSetSlotPacket` is sent by hand, outside the diffing that [containers and menus](containers-and-menus.md) describes, incrementing the state id on its own way past. It is sent even when the result is empty. **Taking it, in an order that surprises.** `ResultSlot.remove` counts what was taken, and then `ResultSlot.onTake` runs `ResultSlot.checkTakeAchievements` *before* anything is consumed. That calls `ItemStack.onCraftedBy`, which awards `Stats.ITEM_CRAFTED` and runs `Item.onCraftedBy`, and then `RecipeCraftingHolder.awardUsedRecipes`, which fires `CriteriaTriggers.RECIPE_CRAFTED` for *every* recipe, special ones included, and then — for a non-special recipe only — calls `ServerPlayer.awardRecipes` and so `ServerRecipeBook.addRecipes`, unlocking it, firing `CriteriaTriggers.RECIPE_UNLOCKED`, sending `ClientboundRecipeBookAddPacket`, and **nulling the stored holder**. The next thing `ResultSlot.onTake` does is look the recipe up all over again — not because of that null, but because `ResultSlot.getRemainingItems` never consults the stored holder on any path, special recipes included. `CraftingRecipe.getRemainingItems` — whose default implementation is spelled `CraftingRecipe.defaultCraftingReminder`, the typo Mojang's — maps each slot through `Item.getCraftingRemainder`, an `ItemStackTemplate` or nothing. Then one item leaves each occupied cell and the remainder goes back into the emptied slot, or merges with what is left there, or goes to the inventory, or is dropped. Every one of those removals re-enters `AbstractContainerMenu.slotsChanged`, so the result slot is recomputed eight times on the way out. A crafter block runs the same machinery through `RecipeCache` and fires its own advancement trigger, `CriteriaTriggers.CRAFTER_RECIPE_CRAFTED`, not the player's. ## What the client actually gets `ClientboundUpdateRecipesPacket` goes out twice: once from `PlayerList` as a player joins, once to everybody from `PlayerList.reloadResources`. It carries two things. The `RecipePropertySet`s are flat sets of items, and `RecipePropertySet.test` is **item identity only** — the client's slot predicate ignores components entirely. The stonecutter's `SelectableRecipe.SingleInputSet` is written by `SelectableRecipe.SingleInputEntry.noRecipeCodec`, which serialises the input `Ingredient`'s contents and the option's `SlotDisplay` and drops the recipe on the floor: `SelectableRecipe.noRecipeCodec` decodes an empty optional in its place. Those sets exist so that menus can answer *may this item go in this slot* — and route a shift-click on the strength of it — without knowing a single recipe. `SmithingMenu` builds its three input slots out of the three smithing sets, `AbstractFurnaceMenu` holds whichever of `RecipePropertySet.FURNACE_INPUT`, `RecipePropertySet.BLAST_FURNACE_INPUT` and `RecipePropertySet.SMOKER_INPUT` its type was constructed with — the fourth set, `RecipePropertySet.CAMPFIRE_INPUT`, reaches no menu at all and is read by `CampfireBlock` on a right-click — and `StonecutterMenu` asks the stonecutter set the same question through `SelectableRecipe.SingleInputSet.acceptsInput`. The book gets something far richer and still anonymous: a `RecipeDisplayEntry` per display, carrying the display id, the `RecipeDisplay` itself, a group index, a `RecipeBookCategory`, and an optional ingredient list used for one thing only — deciding whether the entry lights up. `RecipeDisplay` and `SlotDisplay` are dispatched registries of their own (`Registries.RECIPE_DISPLAY`, `Registries.SLOT_DISPLAY`, and `Registries.RECIPE_BOOK_CATEGORY` for the categories `RecipeBookCategories` fills), and *their* stream codecs are very much used: five `RecipeDisplay` types, one per station shape, and eleven registered `SlotDisplay` variants, which `SlotDisplay.resolve` turns into concrete stacks against a `SlotDisplayContext`. The chest's single ingredient reaches the client as a `SlotDisplay.TagSlotDisplay` over *minecraft:planks* ([tags](../foundations/tags.md)) — everything needed to draw the recipe, and nothing at all about its name. ## The recipe book: unlocked, glowing, and filled in for you `ServerRecipeBook` stores four things: a display resolver, the settings, `ServerRecipeBook.known` — an *identity* set of recipe keys — and `ServerRecipeBook.highlight`, the subset still new enough to glow. It is saved in the player NBT as `ServerRecipeBook.Packed` and read back by `ServerRecipeBook.loadUntrusted`, which validates every key against the live `RecipeManager` and logs and drops the ones that no longer resolve ([codecs](../foundations/codecs-nbt-json.md)). `ClientRecipeBook` never sees any of that. It holds `RecipeDisplayEntry`s by display id, and `ClientRecipeBook.rebuildCollections` groups them into `RecipeCollection`s by category and then by group index, which is why one button in the book cycles through all twelve kinds of plank. The tabs are narrower than the recipe types. `RecipeBookType` has four values — crafting, furnace, blast furnace and smoker — and exactly five menus extend `RecipeBookMenu` to claim them, `CraftingMenu` and `InventoryMenu` both answering `RecipeBookType.CRAFTING`. `StonecutterMenu` and `SmithingMenu` are not `RecipeBookMenu`s at all, so the stonecutter and the smithing table have no book and no auto-fill, even though `RecipeBookCategories.STONECUTTER` and `RecipeBookCategories.SMITHING` exist to categorise them — and the anvil and the grindstone were never recipes at all ([enchanting](enchanting.md)). Craftability is decided on the client and then decided again on the server. `RecipeCollection.selectRecipes` asks `RecipeDisplayEntry.canCraft` against a `StackedItemContents` that `RecipeBookComponent` fills from the player's inventory, purely to choose which entries glow. A lying client gains nothing by it, because the placement re-checks and `CraftingMenu.slotChangedCraftingGrid` runs a full `Recipe.matches` afterwards regardless. Auto-fill itself runs entirely on the server. Clicking an entry sends `ServerboundPlaceRecipePacket` carrying nothing but a container id, a `RecipeDisplayId` and a shift flag, and `ServerGamePacketListenerImpl.handlePlaceRecipe` puts it through seven gates: 1. the player is not a spectator, and the packet's container id is the open menu's; 2. `AbstractContainerMenu.stillValid` still holds for the open menu; 3. `RecipeManager.getRecipeFromDisplay` resolves the index to a `RecipeManager.ServerDisplayInfo`; 4. `ServerRecipeBook.contains` says this player has unlocked the parent recipe; 5. the open menu really is a `RecipeBookMenu`; 6. the recipe's `PlacementInfo.isImpossibleToPlace` says no — which catches `PlacementInfo.NOT_PLACEABLE` and any placement whose ingredients came out empty; 7. and only then does `RecipeBookMenu.handlePlacement` run. `AbstractCraftingMenu.handlePlacement` raises the flag that suppresses re-matching while it shuffles — a `CraftingMenu` override, so the 2×2 grid inside `InventoryMenu` re-matches on every single write — and calls `ServerPlaceRecipe.placeRecipe`, which **counts before it clears**: a dry run of emptying the grid back into the inventory, a tally of what the player has, a calculation of how many crafts that allows, clamped to the smallest stack size among the items chosen, and only then the real clear and the layout through `PlaceRecipeHelper.placeRecipe`. Whether it may drop items in order to clear the grid is simply `Player.isCreative`, so for a survival player with a full inventory the whole call returns `RecipeBookMenu.PostPlaceAction.NOTHING` — no fill *and* no ghost. When the ingredients merely are not there it returns `RecipeBookMenu.PostPlaceAction.PLACE_GHOST_RECIPE` instead, and the server sends `ClientboundPlaceGhostRecipePacket` carrying the `RecipeDisplay` itself. One filter runs underneath all of that and catches players out. `Inventory.isUsableForCrafting` rejects any stack that is damaged, enchanted or renamed, and it gates both halves of the auto-fill: the tally, through `StackedItemContents.accountSimpleStack`, and the actual pull, through `Inventory.findSlotMatchingCraftingIngredient`. `CraftingInput`'s constructor deliberately goes around it by calling `StackedItemContents.accountStack` directly. So the book can grey out a recipe that a manual craft with those very items would have accepted without complaint. ## Where to look `Recipe` · `RecipeType` · `RecipeSerializer` · `RecipeSerializers` · `RecipeHolder` · `RecipeManager` · `RecipeMap` · `RecipeAccess` · `ClientRecipeContainer` · `Ingredient` · `PlacementInfo` · `RecipePropertySet` · `RecipeInput` · `CraftingInput` · `ShapedRecipePattern` · `ShapelessRecipe` · `NormalCraftingRecipe` · `AbstractCookingRecipe` · `CustomRecipe` · `RecipeDisplay` · `SlotDisplay` · `RecipeDisplayEntry` · `RecipeDisplayId` · `AbstractCraftingMenu` · `CraftingMenu` · `RecipeBookMenu` · `ResultSlot` · `ResultContainer` · `RecipeCraftingHolder` · `ServerRecipeBook` · `ClientRecipeBook` · `ServerPlaceRecipe` · `StackedItemContents` Before this page: [containers and menus](containers-and-menus.md), for the synchroniser that the result slot goes around, and [items and stacks](items-and-stacks.md) for `ItemStackTemplate` and crafting remainders. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Enchantments > Verified against **Minecraft 26.2** · Part VII · A player hits a zombie with a Fire Aspect sword, and everything that makes the zombie burn is data. You swing a Fire Aspect sword at a zombie and the zombie catches fire. Go looking for the class that does that — the one with the *on hit, set them alight* method — and there is nothing to find. There are no enchantment subclasses. The forty-three vanilla enchantments are JSON files in the built-in data pack, `Enchantments` is a bag of forty-three registry keys that only four files outside the data generator ever name, and the whole *Fire Aspect is melee only* rule is **one loot condition**, sitting on one entry of a `DataComponentMap` that no item ever holds. That is the pattern the rest of the page is about. An enchantment is a named modifier that holds no code; every behaviour in the game an enchantment can change is a component key that some other system asks about at a well-defined moment. ## The cast | class | what it decides | thread | |---|---|---| | `Enchantment` | the record — a description, a definition, an exclusive set and a map of effects. No behaviour of its own | built at data-pack load, read on both sides | | `Enchantment.EnchantmentDefinition` | what it can go on, what it costs, which slots it counts in | data-pack load | | `EnchantmentEffectComponents` | the thirty-one keys that name every moment an enchantment can change | static, both sides | | `EnchantmentHelper` | the static hook surface — walks stacks and slots, calls into the record, holds no state | server main for every effect, with some read-only entry points on the client | | `ConditionalEffect` / `TargetedConditionalEffect` | whether this effect fires here, and on whom | server main | | `EnchantmentEntityEffect` | the thing that finally happens | server main only — the signature demands a `ServerLevel` | | `ItemEnchantments` | the id-to-level map on the stack, under one of two components | both sides | | `EnchantedItemInUse` | the stack, its slot, its owner and a break callback, built by the slot-aware walk and handed to the effects that act on the world | server main | ## A record with a definition and a bag of components `Enchantment` is a record of four things: a description `Component`, an `Enchantment.EnchantmentDefinition`, a `HolderSet` of enchantments it is exclusive with, and a `DataComponentMap` of effects. `Enchantment.getEffects` looks a component type up in that map and returns an empty list if it is absent, which is the whole dispatch mechanism. The definition is where the enchanting rules live. It carries the supported items and a *narrower, optional* set of primary items — both normally item tags — so `Enchantment.isPrimaryItem` falls back to the supported set when the second is absent, while `Enchantment.isSupportedItem` only ever asks the first. It carries the weight, the maximum level, two `Enchantment.Cost` curves (a base plus a per-level-above-first increment, read by `Enchantment.Cost.calculate`), the anvil cost, and a list of `EquipmentSlotGroup` — the slots in which this enchantment counts at all, tested by `Enchantment.matchingSlot`. The codec bounds the weight to 1–1024 and the maximum level to 1–255, which is `Enchantment.MAX_LEVEL`; `Enchantment.getMinLevel` is the one number that is not data, hardcoded to 1. What a stack carries is not that record but `ItemEnchantments`, an immutable id-to-level map under `DataComponents.ENCHANTMENTS` — or, for an enchanted book's inert set, under `DataComponents.STORED_ENCHANTMENTS`. It is changed only through `ItemEnchantments.Mutable`, whose `ItemEnchantments.Mutable.upgrade` merges by maximum and clamps at 255. `EnchantmentInstance`, a holder-and-level pair with a `EnchantmentInstance.weight` delegate, is what the weighted selection path is built on — and that path, along with the table, the anvil, the grindstone, the providers and `/enchant`, is a separate machine with its own arithmetic, on [enchanting](enchanting.md). ## Thirty-one keys, three registries and one curve `EnchantmentEffectComponents` registers thirty-one component types into `BuiltInRegistries.ENCHANTMENT_EFFECT_COMPONENT_TYPE`. Twenty-four hold a list of `ConditionalEffect` (or `TargetedConditionalEffect`); the other seven are plain — `EnchantmentEffectComponents.ATTRIBUTES`, two sound lists, two unconditional values, and the two `Unit`-valued flags `EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP` and `EnchantmentEffectComponents.PREVENT_ARMOR_CHANGE`, true by being present. Three registries supply the effect objects, and **they are not disjoint**. `EnchantmentValueEffect` has six shapes and modifies a running number (`AddValue`, `MultiplyValue`, `SetValue`, `RemoveBinomial`, `ScaleExponentially` and `AllOf.ValueEffects`). `EnchantmentEntityEffect` has fifteen and does something to an entity — `Ignite`, `DamageEntity`, `ApplyMobEffect`, `SummonEntityEffect`, `AllOf.EntityEffects` and ten more. And it *extends* `EnchantmentLocationBasedEffect`, whose registry has sixteen entries: those same fifteen ids plus one. The odd one out is *attribute*, `EnchantmentAttributeEffect`, which installs an `AttributeModifier` ([attributes](../entities/attributes.md)) and is the only effect that is location-based without also being an entity effect. `LevelBasedValue` turns a level into a number, and six shapes are in its dispatch registry (`LevelBasedValue.Linear`, `LevelBasedValue.Clamped`, `LevelBasedValue.Fraction`, `LevelBasedValue.LevelsSquared`, `LevelBasedValue.Exponent`, `LevelBasedValue.Lookup`). `LevelBasedValue.Constant` is deliberately **not** among them: it is the other arm of an either-codec, which is what makes a bare float legal anywhere a curve is expected. `ConditionalEffect` is an effect plus an optional `LootItemCondition`. `TargetedConditionalEffect` adds two `EnchantmentTarget` fields — which side of the fight the enchantment *lives on* (`TargetedConditionalEffect.enchanted`) and which side it *lands on* (`TargetedConditionalEffect.affected`) — except for the equipment-drops variant, whose codec reads only the first and pins the second to `EnchantmentTarget.VICTIM`. Both implement `Validatable`, and this is where the effect codecs do something unusual: those twenty-four list components wrap their element codec in `Validatable.listValidatorForContext`, so a condition is checked **at decode time** against the parameter set the hook will actually supply. A *post_attack* effect asking about a block state fails to load rather than failing quietly at runtime. ## Seven families of moment Everything above is inert until something calls `EnchantmentHelper`. The enchantment package barely calls anything and everything calls it, so the artefact worth keeping is a table of who calls what: every entry point with its callers is [the enchantment hook table](../../reference/enchantment-hooks.md), and these are the seven kinds of moment it falls into. | family | a representative hook or two | who makes it real | |---|---|---| | damage and protection | `EnchantmentHelper.modifyDamage`, `EnchantmentHelper.getDamageProtection` | `ServerPlayer.getEnchantedDamage`, `LivingEntity.getDamageAfterMagicAbsorb` | | post-attack effects | `EnchantmentHelper.doPostAttackEffectsWithItemSource`, `EnchantmentHelper.doPostPiercingAttackEffects` | `Player.itemAttackInteraction`, `LivingEntity.postPiercingAttack` | | durability and drops | `EnchantmentHelper.processDurabilityChange`, `EnchantmentHelper.processEquipmentDropChance` | `ItemStack.hurtAndBreak`, `Mob.dropCustomDeathLoot` | | projectiles and the weapon in hand | `EnchantmentHelper.processProjectileCount`, `EnchantmentHelper.getPiercingCount` | `ProjectileWeaponItem.draw`, the `AbstractArrow` constructor | | location and tick effects | `EnchantmentHelper.runLocationChangedEffects`, `EnchantmentHelper.tickEffects` | `LivingEntity.onChangedBlock`, `LivingEntity.baseTick` | | experience and repair | `EnchantmentHelper.processBlockExperience`, `EnchantmentHelper.modifyDurabilityToRepairFromXp` | `Block.tryDropExperience`, `ExperienceOrb.repairPlayerItems` | | the flag questions | `EnchantmentHelper.has`, `EnchantmentHelper.hasTag` | `ArmorSlot.mayPickup` for Curse of Binding, four blocks for the four *prevents* tags | The location row is the one that keeps state: `LivingEntity.activeLocationDependentEnchantments` remembers which location-based effects are running in which slot, so the system can tell *became active* from *still active*, and so an attribute effect can be taken off cleanly by `EnchantmentHelper.stopLocationBasedEffects` when the armour comes off. ## How one hook fires Six of those seven rows are the same shape underneath. A system reaches a moment and asks `EnchantmentHelper`; the helper picks which stacks to walk, filtering by slot if it was given one; the record picks which effect entries apply; a loot condition decides whether this one fires. Only then does anything happen. The flag row is the exception and is the reason the shape is worth drawing: `EnchantmentHelper.has` and `EnchantmentHelper.hasTag` build no context and run no condition — they ask the record whether the key is present at all. ```mermaid flowchart TD Caller["a system reaches a moment: Player.itemAttackInteraction, Block.tryDropExperience, LivingEntity.baseTick"] Caller --> EH["an EnchantmentHelper entry point"] EH --> Walk["walk one stack, or every EquipmentSlot of one entity"] Walk --> Slot["on the slot-aware walk, keep entries whose Enchantment.matchingSlot accepts this slot"] Slot --> Comp["read the list under one EnchantmentEffectComponents key"] Comp --> Target["for the targeted keys, keep entries whose enchanted target matches this pass"] Target --> Ctx["build a LootContext on that hook's parameter set"] Ctx --> Cond["ConditionalEffect.matches runs the LootItemCondition"] Cond -- "no" --> Drop["nothing happens"] Cond -- "yes" --> Apply["apply: fold a value, or run the effect on the affected entity"] ``` The `LootContext` in the middle is the same machinery loot tables and `/execute if predicate` use, and `Enchantment` builds five of them — `Enchantment.damageContext` plus four private siblings for items, entities, locations and block hits, one per parameter set the components name. [Contexts and predicates](contexts-and-predicates.md) owns that half. ## Fire Aspect, from the click to the flame Fire Aspect as data is forty-three lines of JSON: supported and primary items are tags, weight 2, maximum level 2, anvil cost 4, dynamic cost curves, one slot (*mainhand*), and **one effect** — a `EnchantmentEffectComponents.POST_ATTACK` entry whose enchanted target is the attacker, whose affected target is the victim, whose effect is `Ignite` with a linear duration of four seconds per level, behind one damage-source predicate requiring a *direct* hit. ```mermaid sequenceDiagram participant SGPL as ServerGamePacketListenerImpl participant Player as Player participant EH as EnchantmentHelper participant Ench as Enchantment participant Ignite as Ignite participant Entity as Entity participant SED as SynchedEntityData SGPL->>Player: handleAttack passes the range checks, then Player.attack Player->>Player: createAttackSource, whose direct and causing entity are one Player->>EH: on a hit that landed, itemAttackInteraction calls doPostAttackEffectsWithItemSource EH->>EH: the victim's whole equipment first, then the attacker's main hand EH->>Ench: doPostAttack for the main-hand stack, in the ATTACKER pass Ench->>Ench: damageContext, then TargetedConditionalEffect.matches asks is the hit direct Ench->>Ignite: apply, with the victim as the affected target Ignite->>Entity: igniteForSeconds, raised only if the new value is larger Entity->>SED: baseTick sets shared flag zero Note over SED: the flame travels as ClientboundSetEntityDataPacket, the enchantment never does Note over Entity: one point of fire damage every twentieth tick from here on ``` **The swing, and the source that is the whole melee rule.** `ServerGamePacketListenerImpl.handleAttack` runs the range checks and calls `Player.attack` — unless the main-hand item has `DataComponents.PIERCING_WEAPON`, in which case the packet is dropped and none of this happens (see [the sword swing](../player/the-sword-swing.md) for the spear's path). `Player.createAttackSource` then asks `ItemStack.getDamageSource`, and every branch of it reaches the single-entity `DamageSource` constructor — the one that sets the direct entity and the causing entity to the same object, making `DamageSource.isDirect` true. An arrow's source has an arrow as the direct entity and a player as the causing entity, so it is false. That one comparison, read by one loot condition in one JSON file, is why Fire Aspect never fires through a bow. **The damage, then the hook.** `ServerPlayer.getEnchantedDamage` — the override, not `Player`'s base version, which returns its argument unchanged — folds Sharpness and friends in through `EnchantmentHelper.modifyDamage`, and the hit goes through `Entity.hurtOrSimulate` to `LivingEntity.hurtServer` ([damage and death](../entities/damage-and-death.md)). Only if that returned true does `Player.itemAttackInteraction` call `EnchantmentHelper.doPostAttackEffectsWithItemSource`. **Three branches, not two.** The helper walks the *victim's* whole equipment first, with `EnchantmentTarget.VICTIM` as the pass — that is Thorns' lane, and why Thorns works from a chestplate while Fire Aspect does not work from boots. Then, for a living causing entity, it walks the attacker's **main hand only**, in the `EnchantmentTarget.ATTACKER` pass, keeping enchantments whose declared slots include that slot. A third branch handles a causing entity that is not living: a slotless pass with no filter at all, reached through `EnchantmentHelper.doPostAttackEffectsWithItemSourceOnBreak` with a break callback, whose only vanilla caller is `ThrownTrident`. **An attacker's armour can never contribute a post-attack effect.** **The condition and the target.** `Enchantment.doPostAttack` keeps the entries whose enchanted target matches the pass, builds the enchanted-damage context — victim, level, origin, damage source, and both attacking entities as optional parameters — and runs the predicate. The *affected* field then picks who receives the effect: attacker, direct entity, or victim. Fire Aspect says victim, and if that works out to null the effect is dropped in silence. **The burn.** `Ignite.apply` calls `Entity.igniteForSeconds`, which floors to ticks and hands `Entity.igniteForTicks` a number it applies **only if it is larger than the counter already there** — while clearing any freeze regardless, so re-hitting a burning target with a weaker Fire Aspect does nothing except thaw it. No damage happens here. `Entity.baseTick` deals it, one point every twentieth tick, on a `ServerLevel`, skipped in lava and replaced by `Entity.clearFire` for a fire-immune entity. The same method sets shared flag zero through `Entity.setSharedFlagOnFire` ([synched entity data](../entities/synched-entity-data.md)), which becomes `Entity.displayFireAnimation` on the client — whose own `Entity.baseTick`, finding no `ServerLevel`, clears the fire counter instead of burning. ## Questions the pattern raises **Where does Fortune live, if not in a hook?** In the loot table. `ApplyBonusCount` and `BonusLevelTableCondition` read the *tool* parameter out of a `LootContext` and call `EnchantmentHelper.getItemEnchantmentLevel` on it — a level, not an effect. Looting is the same trick from the other end: `EnchantedCountIncreaseFunction` reads the *attacking entity* parameter and calls `EnchantmentHelper.getEnchantmentLevel`, the overload that walks a `LivingEntity`'s equipment and keeps the best. Fortune has no effect component whatsoever, and Looting's only one is an *equipment_drops* entry that has nothing to do with mob loot. Mending inverts it once more: `ExperienceOrb.repairPlayerItems` asks `EnchantmentHelper.getRandomItemWith` for a stack carrying `EnchantmentEffectComponents.REPAIR_WITH_XP`, so the orb, not the item, drives the repair. **Does anything enchantment-shaped run on the client?** No *effect* can: `EnchantmentEntityEffect` and `EnchantmentLocationBasedEffect` both demand a `ServerLevel`. But two *values* do. `Enchantment.modifyUnfilteredValue` takes only a `RandomSource`, and its two users are `Enchantment.modifyCrossbowChargeTime` and `Enchantment.modifyTridentSpinAttackStrength`. `CrossbowItem.getChargeDuration` is called by three entity renderers, by `ItemInHandRenderer` and by the `CrossbowPull` item property, so **Quick Charge is evaluated on the render thread every frame a crossbow is being drawn**; and `MultiPlayerGameMode.releaseUsingItem` runs `TridentItem.releaseUsing` on the client's own copy, which asks `EnchantmentHelper.getTridentSpinAttackStrength`, so **Riptide's strength is computed client-side too** — which is what lets the riptide push be predicted at all. Everything else the client does is drawing: `ItemEnchantments.addToTooltip` for the tooltip, `ItemStack.hasFoil` for the glint, and `EnchantmentHelper.forEachModifier` for the attribute lines — which means the client evaluates the `LevelBasedValue` curve itself. **What actually crosses the wire?** Usually an id and nothing else. `Registries.ENCHANTMENT` is in `RegistryDataLoader.SYNCHRONIZED_REGISTRIES` with the full `Enchantment.DIRECT_CODEC`, but `RegistrySynchronization.packRegistry` sends a bare `Identifier` for every element whose pack the client already has — for a vanilla client against a vanilla server, all forty-three. Full definitions cross only for a data pack's custom or overridden enchantments. Beyond that, `ClientboundUpdateTagsPacket` for `EnchantmentTags` and, per stack, the `DataComponents.ENCHANTMENTS` component: registry ids and levels. **Why is an enchanted book inert?** Because `EnchantmentHelper.runIterationOnItem` — the private walk under every hook — reads `DataComponents.ENCHANTMENTS` and nothing else. A book's set lives under `DataComponents.STORED_ENCHANTMENTS`, and the routing between the two, in `EnchantmentHelper.getComponentType`, is keyed on the exact item `Items.ENCHANTED_BOOK`. The *component* is not so exclusive: `ItemStack` puts `DataComponents.STORED_ENCHANTMENTS` in any stack's tooltip, and `AnvilMenu` decides it is being handed a book by testing for that component rather than for the item — so a data pack can make a stick priced like one, but never make it behave like one. **Is the main hand really the main hand?** No — it is a label. `EnchantmentHelper.doPostAttackEffectsWithItemSourceOnBreak` and `EnchantmentHelper.doPostPiercingAttackEffects` both hand `EquipmentSlot.MAINHAND` to the slot filter regardless of where the weapon came from, and `KineticWeapon.damageEntities` reaches `LivingEntity.stabAttack` with whichever slot the *use* was in. An off-hand spear's enchantments are therefore tested against the main-hand slot group. **Three more small ones.** `ItemStack.isEnchantable` reads *two* components: `DataComponents.ENCHANTABLE` must be present, and `DataComponents.ENCHANTMENTS` must be present *and empty*, so an item with no enchantments component at all is not enchantable either. The two `EnchantmentHelper.forEachModifier` overloads test different things — the `EquipmentSlot` one asks `Enchantment.matchingSlot`, the `EquipmentSlotGroup` one asks whether the definition declares that exact group — and `ItemStack.forEachModifier` uses one of each. And the instance method is spelled `Enchantment.modifyArmorEffectivness`, Mojang's typo, while the helper beside it is `EnchantmentHelper.modifyArmorEffectiveness`. **And does Fire Aspect cook the loot?** Not through the enchantment. That is a loot-table condition on `EnchantmentTags.SMELTS_LOOT`, a tag whose only member is Fire Aspect ([loot tables](loot-tables.md)). `EnchantmentTags` holds twenty-nine tags. Twenty-five fall into five families — the seven exclusivity sets, the tooltip order, pool membership for the table and for mob, trade and loot equipment, the behaviour flags (curse, smelts-loot and the four *prevents* tags), and the seven biome trade tables — and the last four are the trading and treasure axes the villager and the loot tables sort on. **What does a whole enchantment look like, then?** `Enchantments.LUNGE` is the only user of `EnchantmentEffectComponents.POST_PIERCING_ATTACK` in the game, and its single effect is an `AllOf.EntityEffects` of four — a `ChangeItemDamage`, an `ApplyExhaustion` scaled per level, an `ApplyEntityImpulse` forward with its vertical component scaled away, and a `PlaySoundEffect` holding three sounds, indexed by level rather than shuffled — behind a four-clause condition checking that the user is not riding, not elytra-flying, not in water, and either not a player, in creative, or fed. One JSON file, four effect objects, four predicates, no Java. That is what an enchantment is. ## Where to look `Enchantment` · `Enchantment.EnchantmentDefinition` · `Enchantment.Cost` · `EnchantmentEffectComponents` · `ConditionalEffect` · `TargetedConditionalEffect` · `EnchantmentTarget` · `LevelBasedValue` · `EnchantmentValueEffect` · `EnchantmentEntityEffect` · `EnchantmentLocationBasedEffect` · `EnchantmentAttributeEffect` · `AllOf` · `Ignite` · `EnchantmentHelper` · `EnchantedItemInUse` · `ItemEnchantments` · `EnchantmentInstance` · `Enchantments` · `EnchantmentTags` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Enchanting: the five paths, and what each one is allowed to do > Verified against **Minecraft 26.2** · Part VII · A player reads three offers off an enchanting table and buys one, and then the same sword picks up enchantments four other ways — an anvil, a grindstone running backwards, a spawning pillager, and a command. A sword goes in the left slot of an enchanting table and three lapis in the right, and the table answers with three lines of Standard Galactic Alphabet, three level numbers, and — if you hover — one enchantment named outright. None of that is guessed. The server has already run the entire selection, and it ships the answer to the client as ten integers. One of those ten is `Player.enchantmentSeed`, and it is the reason the page is worth a lecture: **one number per player, saved in the player file, carried across death and dimension change, sent to the client, and re-rolled by nothing in the game except the enchanting table itself.** Spend thirty levels at an anvil and come back and the table is offering exactly what it offered before — and the gibberish is in the same handwriting, because the client is drawing it from that same number. The table is one of five paths that change what a stack is enchanted with — four of them adding and the grindstone taking away — and they differ far more than the shared vocabulary suggests. This page is about those differences. A sixth writer hides outside all of them, in the crafting grid: `RepairItemRecipe` carries every curse from both inputs onto the tool it makes ([recipes](recipes.md)). What an enchantment *is* — the record, the effect components, the hooks that fire in combat — is [the next page along](enchantments.md) and is not re-taught here. ## The cast | class | what it decides | thread | |---|---|---| | `EnchantmentMenu` | the three offers, the clue, and what the click costs | server main, with a client copy that can only say no | | `EnchantmentHelper` | the cost curve, the weighted selection, and the write every path ends in | whichever side asks | | `Player` | the seed and the levels | server main | | `AnvilMenu` | the merge arithmetic and the price | server main | | `GrindstoneMenu` | the only removal a player can reach, and the refund | server main | | `EnchantmentProvider` | what a mob's spawn equipment gets | server main | | `EnchantRandomlyFunction` | chest loot and villager trades | server main | | `EnchantCommand` | the operator's path, and the fewest checks | server main | `EnchantWithLevelsFunction`, `SetEnchantmentsFunction` and `EnchantedCountIncreaseFunction` are the other three loot functions, named below where they differ; `EnchantmentScreen` is the client half of the table and gets its own section. ## The five paths at a glance | | enchanting table | anvil | grindstone | providers and loot | `/enchant` | |---|---|---|---|---|---| | **what it costs** | 1, 2 or 3 levels and the same count of lapis | the full price in levels, and a chance the anvil chips | pays *you*, in orbs at the block | nothing | nothing | | **the gate on the item** | `ItemStack.isEnchantable` — enchantable *and* not already enchanted | `EnchantmentHelper.canStoreEnchantments` | damageable or already enchanted | `DataComponents.ENCHANTABLE`, except `SingleEnchantment` and `EnchantRandomlyFunction` | any non-empty main-hand item | | **which item filter** | `Enchantment.isPrimaryItem` — the narrow set | `Enchantment.canEnchant` — the supported set | n/a | `Enchantment.isPrimaryItem` for the selection paths, `Enchantment.canEnchant` for `EnchantRandomlyFunction`, none at all for `SingleEnchantment` | `Enchantment.canEnchant` | | **the level ceiling** | whatever the cost brackets allow | clamped to `Enchantment.getMaxLevel` | n/a | clamped, except `SetEnchantmentsFunction` | rejected above `Enchantment.getMaxLevel` | | **exclusivity** | filtered out mid-selection | dropped, and it raises the price | curses survive, everything else goes | filtered, or ignored by flag | rejected with an error | | **randomness** | the player's saved seed | none in the arithmetic; a 12% roll for the chip | none in the strip; a roll on the refund | the level's random source | none | | **decided on** | server, with the click predicted | server, with the price synced | server | server | server | Five paths, one row that would be the same everywhere: the last step. Not the same method — the table, `/enchant` and `EnchantRandomlyFunction` go through `ItemStack.enchant`, the grindstone and the providers call `EnchantmentHelper.updateEnchantments` themselves, and the anvil writes with `EnchantmentHelper.setEnchantments` — but the same *decision*, and that is where the page starts. ## The one question all five ask `EnchantmentHelper.getComponentType` is the private line under every one of those three entry points, and it does the thing worth knowing before any of the five paths make sense. It **routes by item identity**: the component the write lands in is `DataComponents.STORED_ENCHANTMENTS` if the stack is `Items.ENCHANTED_BOOK` and `DataComponents.ENCHANTMENTS` otherwise — a hard identity test against one item, not a tag. That is why every path that can be handed a plain `Items.BOOK` transmutes it *first*, through `ItemStack.transmuteCopy` or by building a fresh stack. The transmute is not cosmetic: enchant a plain book and the levels would land in the active component and the book would start *working*. `EnchantmentHelper.updateEnchantments` adds one more rule of its own: it **silently does nothing if the component is absent**, returning `ItemEnchantments.EMPTY` on a null read. In practice every item gets `DataComponents.ENCHANTMENTS` from `DataComponents.COMMON_ITEM_COMPONENTS`, so the case only arises when an item definition replaces the default component initializer or a patch removes the component from a stack — and then the four paths through it become a no-op, with no error anywhere ([data components](../foundations/data-components.md)). The merge itself is `ItemEnchantments.Mutable.upgrade`: keep the higher level, cap at 255, ignore a level of zero. Only `ItemEnchantments.Mutable.set` can lower a level, and only the anvil — where it clamps an over-maximum level down — and `SetEnchantmentsFunction` reach for it. The grindstone does not lower anything; it removes. ## What it costs, and who pays The enchanting table's headline number is not its price. The level *requirement* for a slot is the cost the table computed for it — up to thirty at the bottom slot — but the amount `Player.onEnchantmentPerformed` actually subtracts is the slot's index plus one, and the lapis consumed is the same one, two or three. | what the forum says | what the decompile does | |---|---| | the bottom offer costs thirty levels | `EnchantmentMenu.clickMenuButton` requires thirty levels and takes **three** | | more bookshelves make better enchantments | more bookshelves raise the *cost*, and `EnchantmentHelper.getEnchantmentCost` floors the bottom slot at twice the shelf count | | the anvil's "Too Expensive" is a level cap | it is a result cap — at a price of forty or more `AnvilMenu.createResult` empties the output slot unless the player has infinite materials | The anvil is the opposite: it charges the whole displayed price, through `Player.giveExperienceLevels` with a negative amount, in `AnvilMenu.onTake`, and then rolls a small chance to damage or destroy the block. The price is a prior-work tax read from `DataComponents.REPAIR_COST` on **both** inputs, plus one per repair material consumed, plus `Enchantment.getAnvilCost` times the resulting level for every enchantment transferred (halved with a floor of one when the addition is a book), plus one for a rename. The **larger** of the two inputs' `DataComponents.REPAIR_COST` is then doubled and incremented by `AnvilMenu.calculateIncreasedRepairCost` and written onto the result — the whole of the prior-work spiral, and the one step a pure rename skips. Two cases escape that arithmetic: an input stack of more than one item sets the price to a flat **40** the moment any enchantment actually transfers, which — forty being exactly the threshold at which the result is withheld — makes enchanting a stack not expensive but forbidden outside creative; and a rename with no other change is capped at 39, which is why renaming never hits "Too Expensive". The grindstone runs the transaction backwards. It strips everything not in `EnchantmentTags.CURSE`, turns an emptied `Items.ENCHANTED_BOOK` back into a plain `Items.BOOK` with `ItemStack.transmuteCopy`, and rebuilds `DataComponents.REPAIR_COST` from zero, so a clean item leaves with its prior work erased. The refund is the sum of `Enchantment.getMinCost` at each stripped level, halved upward with a random bonus of up to one less than that half again, and it arrives as orbs from `ExperienceOrb.award` at the block — on the ground, not in the player ([hunger and experience](../player/hunger-and-experience.md)). ## What each path is allowed to add Two predicates are doing the work, and the difference between them is the difference between the enchantments an axe is *offered* and the enchantments an axe can *hold*. `Enchantment.canEnchant` asks whether the item's *type* is in the definition's supported set; `Enchantment.isSupportedItem` asks exactly the same question of a stack and is called from nowhere but the method below. `Enchantment.isPrimaryItem` asks the supported question **and** the narrower primary-items question on top, falling back to the supported set when the definition names no primary items. The narrow one lives in `EnchantmentHelper.getAvailableEnchantmentResults`, which is `EnchantmentHelper.selectEnchantment`'s own — so the table, the cost-based providers and chest loot all use it, and the anvil and `/enchant` do not. In vanilla exactly five enchantments declare a narrower primary set than their supported one. Three of them are melee enchantments whose supported set reaches axes and whose primary set stops at swords and spears, which is why no enchanting table has ever offered Sharpness on an axe while every anvil will put it there. A fourth does the same to the mace, and the fifth is Thorns, offered only on a chestplate and wearable anywhere. The anvil uses `Enchantment.canEnchant`, overridden to true when the target is an `Items.ENCHANTED_BOOK` or the player has infinite materials, so books collect anything. Its arithmetic per transferred enchantment is short: the same level on both sides merges to one higher, different levels take the maximum, and the winner is clamped to `Enchantment.getMaxLevel`. An enchantment the target cannot take is dropped and **costs nothing**; one that conflicts with something already on the result is dropped *and* adds one to the price per conflicting pair — the anvil's only punitive rule. If nothing survives, the result slot is emptied. ### The ceilings, and who ignores them `/enchant` is the shortest path and, contrary to its reputation, not the laxest. `EnchantCommand` rejects a level above `Enchantment.getMaxLevel` before it looks at any target, then per target requires a `LivingEntity` whose `LivingEntity.getMainHandItem` is non-empty, then checks `Enchantment.canEnchant` and `EnchantmentHelper.isEnchantmentCompatible` against what the stack already carries — and from there it is the same tail as everything else. What it skips is the primary filter, the enchantability component and the cost, not the supported-items or level rules. It also accepts a level of zero, which passes every check, reports success, and changes nothing. The genuine ceiling-breaker is elsewhere. `SetEnchantmentsFunction` writes through `ItemEnchantments.Mutable.set`, whose only clamp is 255, with no reference to `Enchantment.getMaxLevel` at all: a loot table can hand out Sharpness 200 and nothing else on this page can. Exclusivity, by contrast, is one static method everywhere — `Enchantment.areCompatible`, wrapped by `EnchantmentHelper.isEnchantmentCompatible` and `EnchantmentHelper.filterCompatibleEnchantments` — and it is **symmetric**, failing if either side's exclusive set names the other, and failing an enchantment against itself. ## Where the randomness comes from The anvil and the grindstone roll a die each — for the chip and for the refund — but only the table and the provider and loot paths roll one to decide *what you get*, and they roll it in the same place: `EnchantmentHelper.selectEnchantment`, a short method with four distinct sources of variance stacked on one another. ```mermaid flowchart TD A["a cost arrives: three from the table, a sampled IntProvider from a provider, a NumberProvider from a loot table"] --> B{"does the stack have DataComponents.ENCHANTABLE"} B -- no --> Z["empty list, and the caller adds nothing"] B -- yes --> C["raise the cost by one plus two independent rolls scaled by the enchantability value"] C --> D["scale by a triangular span of plus or minus 15 percent, round, clamp to at least 1"] D --> E["getAvailableEnchantmentResults keeps an enchantment only if it is primary for this item, or the item is a plain book"] E --> F["for each survivor, take the highest level whose min and max cost bracket contains the value"] F --> G{"any candidates at all"} G -- no --> Z G -- yes --> H["weighted pick by Enchantment.getWeight"] H --> I{"a fresh roll under 50 is at most the cost"} I -- no --> Y["the list, out"] I -- yes --> J["drop every candidate incompatible with the last pick"] J --> K{"anything left"} K -- no --> Y K -- yes --> L["weighted pick again, then halve the cost"] L --> I ``` The enchantability perturbation is the first place the item matters: `Enchantable.value` — gold's is famously high — widens two independent rolls that only ever push the cost **up**. The span that follows is triangular rather than flat, so the extremes are rare. The bracket test in `EnchantmentHelper.getAvailableEnchantmentResults` walks levels downward and stops at the first fit, so a high cost buys a high level of one enchantment rather than more of them. Buying more is the loop's job: the cost halves after every extra pick, so by the third or fourth pass the roll is nearly always lost — while from a cost of forty-nine up the first extra is certain. The table adds one more layer. `EnchantmentMenu.slotsChanged` seeds its `RandomSource` with `Player.enchantmentSeed` for the three costs, then re-seeds it with the seed **plus the slot number** before each selection, which is why the three offers are independent of each other and yet reproducible. `EnchantmentHelper.getEnchantmentCost` returns zero outright for an item with no `DataComponents.ENCHANTABLE`, and a slot whose cost came out below its own index plus one is zeroed too. Bookshelves reach it as a plain integer. `EnchantmentMenu` walks `EnchantingTableBlock.BOOKSHELF_OFFSETS` — a fixed list of thirty-two offsets, the outer ring of a five-by-five footprint at two heights — and `EnchantingTableBlock.isValidBookShelf` requires the block at the offset to be in `BlockTags.ENCHANTMENT_POWER_PROVIDER` **and** the block between it and the table to be in `BlockTags.ENCHANTMENT_POWER_TRANSMITTER` ([tags](../foundations/tags.md)). That between position halves the X and Z offsets but leaves Y alone, so the upper ring's gap is checked at the bookshelf's own height, not the table's. The clamp to fifteen happens inside `EnchantmentHelper.getEnchantmentCost`, not in the walk. **Fifteen** — the shelf count above which nothing changes, and twice which is the floor on the bottom offer (`EnchantmentHelper.getEnchantmentCost`). ## What is decided on which side The clue you hover is not a hint about what you might get: it is a genuine member of the exact list you *will* get. `EnchantmentMenu.slotsChanged` runs the selection for real, shows one entry of the result at random and throws the rest away, and `EnchantmentMenu.clickMenuButton` runs the same selection again from the same seed and slot and applies all of it. The one wrinkle is the plain book, which has one random entry deleted from its list before either use — unless the list has only one entry, which survives. ```mermaid sequenceDiagram participant EScr as EnchantmentScreen participant EM as EnchantmentMenu participant EH as EnchantmentHelper participant Player as Player participant SGPL as ServerGamePacketListenerImpl participant SP as ServerPlayer Note over EM: the sword lands in slot 0 and slotsChanged runs on the server EM->>EM: walk BOOKSHELF_OFFSETS, count the valid shelves EM->>EH: getEnchantmentCost three times, from a stream seeded with the player seed EM->>EH: selectEnchantment per slot, re-seeded with the seed plus the slot EH-->>EM: a list per slot, one entry of which becomes the clue EM->>SP: broadcastChanges SP-->>EScr: the changed data slots, of ten: three costs, the seed, six clues Note over EScr: EnchantmentNames.initSeed makes the alphabet stable for this seed EScr->>EM: clickMenuButton on the client copy, whose level access is NULL EM-->>EScr: true only if the lapis and the levels are really there EScr->>SGPL: ServerboundContainerButtonClickPacket, via MultiPlayerGameMode SGPL->>EM: clickMenuButton on the server copy EM->>EH: selectEnchantment again, same seed and slot, same list EM->>Player: onEnchantmentPerformed, take slot plus one levels, re-roll the seed EM->>EH: updateEnchantments once per entry, through ItemStack.enchant Note over EM,SP: consume the lapis, award Stats.ENCHANT_ITEM, fire CriteriaTriggers.ENCHANTED_ITEM EM->>EM: slotsChanged again, three fresh offers from the new seed SP-->>EScr: broadcastChanges, then the ten values again, all different ``` The predicted click is the sharpest thing on that diagram. `EnchantmentScreen.mouseClicked` calls `EnchantmentMenu.clickMenuButton` on its own local menu and only sends the packet if that call returns true. On the client the menu's level access is `ContainerLevelAccess.NULL`, whose evaluation returns an empty optional without running the action at all — so the entire enchanting body is skipped, and what the client really evaluates is the guard in front of it: the lapis count, the level requirement, and `Player.hasInfiniteMaterials`. The affordability check is real on both sides; the enchanting is real on one. [Containers and menus](containers-and-menus.md) has the data-slot and button-click machinery in general. The ten slots are ordinary `DataSlot` entries — three `DataSlot.shared` views onto the cost array, one `DataSlot.standalone` holding the seed — and they reach the client one `ClientboundContainerSetDataPacket` each as `AbstractContainerMenu.broadcastChanges` diffs them against its remote copy. The clue slots carry a **numeric registry id** that `EnchantmentScreen` resolves against its own registry copy — the same registry copy that `ItemEnchantments`' stream codec needs to name the enchantments on any stack the client is sent. The seed slot is the only route by which `Player.enchantmentSeed` ever reaches a client: it is written to the player file as *XpSeed*, re-rolled on read if it loads back as zero, copied unconditionally by `ServerPlayer.restoreFrom` across death and dimension change, and named in no packet of its own. `EnchantmentNames.initSeed` then seeds one shared `RandomSource` with it per frame and `EnchantmentNames.getRandomName` draws three or four words from a fixed list in the *alt* font — same seed, same three lines, every time. ## The paths that never show a player anything The provider path runs at spawn. `Mob.enchantSpawnedEquipment` calls `EnchantmentHelper.enchantItemFromProvider`, which looks a provider up in `Registries.ENCHANTMENT_PROVIDER` and hands the stack's mutable enchantment map to `EnchantmentProvider.enchant`. `EnchantmentsByCost` and `EnchantmentsByCostWithDifficulty` go through `EnchantmentHelper.selectEnchantment`, so a mob's gear is rolled by exactly the arithmetic the table uses, with the regional difficulty widening the cost; `SingleEnchantment` skips selection entirely, upgrading one named enchantment to a sampled level clamped only to that enchantment's own range, never asking whether the item supports it. Six of the seven providers `VanillaEnchantmentProviders` registers are that third kind. The loot path runs wherever a loot table does, and villager trades are on it: a `VillagerTrade` carries a list of `LootItemFunction`s applied to what it gives, and the librarian's enchanted book is `EnchantRandomlyFunction` with compatibility checking turned *off*, followed by a filter that discards the trade if the result somehow is not an enchanted book. `EnchantWithLevelsFunction` is the chest-loot one and calls `EnchantmentHelper.enchantItem` — the same selection again; `SetEnchantmentsFunction` is the deterministic one. Both random ones can set `DataComponents.ADDITIONAL_TRADE_COST` when the context offers `LootContextParams.ADDITIONAL_COST_COMPONENT_ALLOWED`, which is how a strong enchantment makes a trade dearer ([loot tables](loot-tables.md), [contexts and predicates](contexts-and-predicates.md)). `EnchantedCountIncreaseFunction` sits in the same package and is the odd one out: it adds nothing, reading a level off the killer with `EnchantmentHelper.getEnchantmentLevel` to multiply a drop count. It consumes this page's output rather than producing any. One more producer belongs in nobody's mental model of enchanting. `CreativeModeTabs` builds the creative enchanted books with `EnchantmentHelper.createBook` — maximum level only in the tab, every level in the search, through `CreativeModeTabs.generateEnchantmentBookTypesOnlyMaxLevel` and `CreativeModeTabs.generateEnchantmentBookTypesAllLevels`. ## Where to look `EnchantmentMenu.slotsChanged` · `EnchantmentMenu.clickMenuButton` · `EnchantingTableBlock.BOOKSHELF_OFFSETS` · `EnchantingTableBlock.isValidBookShelf` · `EnchantmentHelper.getEnchantmentCost` · `EnchantmentHelper.selectEnchantment` · `EnchantmentHelper.getAvailableEnchantmentResults` · `EnchantmentHelper.filterCompatibleEnchantments` · `EnchantmentHelper.updateEnchantments` · `ItemStack.enchant` · `Enchantment.areCompatible` · `Enchantment.isPrimaryItem` · `Enchantment.canEnchant` · `AnvilMenu.createResult` · `AnvilMenu.onTake` · `GrindstoneMenu` · `EnchantmentProvider` · `VanillaEnchantmentProviders` · `EnchantRandomlyFunction` · `EnchantWithLevelsFunction` · `SetEnchantmentsFunction` · `EnchantCommand` · `Player.onEnchantmentPerformed` · `EnchantmentScreen.mouseClicked` · `EnchantmentNames` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Contexts and predicates > Verified against **Minecraft 26.2** · Part VII · A command asks *is this true, here, of this entity?* — and the machinery that answers is the same machinery that decides what a chest contains. You type `/execute if predicate example:in_the_rain run say wet`. Before anything can be tested, the server has to turn *here* into something a data-pack file is allowed to interrogate: a position, maybe an entity, nothing else. It does that by building a small typed map of parameters and handing it to an object loaded out of a registry, which returns a boolean. That machinery is usually called the loot system, because most of it lives in `net/minecraft/world/level/storage/loot` — but the keys and the sets it is built from live in `net/minecraft/util/context` and know nothing about loot at all. **Twelve of the twenty-six parameter sets never roll a loot table**, and the set that is enforced is always the *caller's*: a loot table's own declared *type* is read exactly once, by the load-time validator, and never consulted again while the game is running. ## Two packages, one machine ```mermaid flowchart TD subgraph U["net/minecraft/util/context — knows nothing about loot"] CK["ContextKey, an Identifier plus a static type"] CKS["ContextKeySet, the required keys and the allowed keys"] CMap["ContextMap, the checked bag of values"] end subgraph L["net/minecraft/world/level/storage/loot"] LPar["LootParams, the immutable inputs"] LCtx["LootContext, one invocation"] Users["LootItemCondition, NumberProvider, LootItemFunction"] end Slot["SlotSource, in world/item/slot"] CK -->|"declared required or optional by ContextKeySet.Builder"| CKS CKS -->|"ContextMap.Builder.create validates against it"| CMap CMap -->|"wrapped, with a ServerLevel beside it"| LPar LPar -->|"plus a random source and a resolver"| LCtx LCtx -->|"getParameter and getOptionalParameter"| Users LCtx -->|"the same interface, from outside the package"| Slot ``` The dividing line is the whole argument of this page. `ContextKey`, `ContextKeySet` and `ContextMap` are three small general-purpose classes in `net/minecraft/util/context` with no dependency on items, blocks, entities or loot. Everything below them is in the loot package, and everything *above* them is whoever wants a question answered. A loot table is one such caller. The others are commands, entity selectors, advancement triggers, villager trades, every enchantment effect — and, on the *client*, `SlotDisplayContext`, which builds a `ContextMap` of its own so a recipe book entry can resolve itself into stacks. ## The cast | class | package | what it owns | when | |---|---|---|---| | `ContextKey` | *util/context* | one parameter's name and its Java type, and nothing else | — | | `ContextKeySet` | *util/context* | which keys a call site must supply, and which it may | built once at class-init | | `ContextMap` | *util/context* | the values, and the only place the contract is enforced | server main for loot; the client builds its own for a recipe display | | `LootParams` | *storage/loot* | the `ServerLevel`, the map, the dynamic-drop callbacks, the luck | server main | | `LootContext` | *storage/loot* | one invocation: the random source, the reference resolver, the visited stack | server main | | `LootItemCondition` | *storage/loot/predicates* | a predicate over a `LootContext` — the boolean answer | server main | | `NumberProvider` | *storage/loot/providers* | a float over a `LootContext` — the numeric answer | server main | | `ValidationContext` | *storage/loot* | at load, whether an element asked for a key its set does not have | background executor | > **For a 1.21-era reader.** *LootContextParam* is `ContextKey` and > *LootContextParamSet* is `ContextKeySet`, both moved out to > `net/minecraft/util/context`, and the predicate library that consumes them > has left *critereon* for `net/minecraft/advancements/predicates` and > `net/minecraft/advancements/triggers`. > [The drift table](../../reference/naming-drift.md) has the rest. ## A key is a name with a type welded to it `ContextKey` is an `Identifier` and a phantom type parameter. It has no value, no default, no validation and no registry — `ContextKey.vanilla` just makes one in the *minecraft* namespace, and only seventeen exist: the fifteen static fields of `LootContextParams`, plus the two on `SlotDisplayContext` that let a client draw a recipe. The type parameter is what makes the rest of the system safe: `LootContextParams.ORIGIN` is a key of `Vec3`, `LootContextParams.TOOL` a key of `ItemInstance` (the read-only item view), `LootContextParams.ENCHANTMENT_LEVEL` a key of a boxed *int*, and a reader gets that type back without a cast. A data-pack author never writes a key's name directly. They write a *target* — *this*, *attacker*, *target_entity*, *tool*, *block_entity* — and `LootContextArg` turns it into a key, out of three enums nested in `LootContext`: `LootContext.EntityTarget` with six entity keys, `LootContext.BlockEntityTarget` and `LootContext.ItemStackTarget` with one each. All three read through `LootContextArg.SimpleGetter`, which uses the **optional** accessor — so a target the current set does not carry evaluates to nothing rather than throwing. ## A set is a contract, and the caller signs it `ContextKeySet` holds two sets: `ContextKeySet.required` and `ContextKeySet.allowed`, the second the union of required and optional, and its `ContextKeySet.Builder` refuses to make one key both in either order. `LootContextParamSets` registers twenty-six of them into a private bi-map, which is what `LootContextParamSets.CODEC` reads when a data pack names a set by id — and the ids do not always match the field names: `LootContextParamSets.PIGLIN_BARTER` is *barter* and `LootContextParamSets.ALL_PARAMS` is *generic*. Every set's keys are tabulated in [Loot context parameter sets](../../reference/loot-context-params.md). `LootContextParamSets.ALL_PARAMS` deserves its own warning, because it is the default a loot table with no declared *type* gets (`LootTable.DEFAULT_PARAM_SET`) and the set standalone predicate and item-modifier files are validated against — and it is **not all of them**. It declares eleven of the fifteen keys, all of them required, and omits `LootContextParams.INTERACTING_ENTITY`, `LootContextParams.TARGET_ENTITY`, `LootContextParams.ENCHANTMENT_LEVEL` and `LootContextParams.ENCHANTMENT_ACTIVE`. The practical consequence: a standalone predicate file that asks about the interacting or target entity, or about whether an enchantment is active, *is* flagged at load; one that asks about a block state or a damage source is not, even though `LootContextParamSets.COMMAND` — the set `/execute if predicate` actually builds — carries neither. ## Three ways a parameter can be missing `ContextMap.Builder.create` is where the contract is enforced, and it is the only place: it throws if the values collected include a key the set does not allow, and again if the set requires a key that is absent. Note what it compares — the keys the **caller** supplied against the set the **caller** named. Nothing on this path ever sees the loot table. 1. **At build time.** `ContextMap.Builder.create` throws, naming the offending keys. That is a programming error rather than a data one, and it takes the tick down with it. 2. **At read time.** `LootContext.getParameter` goes to `ContextMap.getOrThrow` and throws; `LootContext.getOptionalParameter` returns nothing, and most conditions and functions degrade quietly rather than fail. 3. **At load time.** `ValidationContext.validateContextUsage` compares what an element declares it reads — `LootContextUser.getReferencedContextParams`, overridden by twenty-seven classes — against `ContextKeySet.allowed`, and reports the difference. The third is the loose one, in two ways. It checks against *allowed*, not *required*, so an element that reads an optional key passes validation and can still take path 2 at runtime. And what `ReloadableServerRegistries.validateLootRegistries` does with the collected problems is **log them as warnings**: a predicate that asks for a parameter it cannot have loads fine and misbehaves later. The same validation is a hard error in exactly two places, both applied by a codec — `Validatable.validatorForContext` for `VillagerTrade`, and its list form `Validatable.listValidatorForContext` for every conditional effect in `EnchantmentEffectComponents`. Those two build a `ValidationContext` with no resolver, so `ValidationContext.allowsReferences` is false and a `ConditionReference` inside a trade or an enchantment effect is rejected outright. ## Inputs, then one invocation `LootParams` is the immutable half: a `ServerLevel`, the `ContextMap`, a map of `LootParams.DynamicDrop` callbacks keyed by `Identifier`, and a float of luck. The `ServerLevel` is the mechanical guarantee that none of this runs on the client — `LootParams.Builder` takes one in its constructor and `LootContext.Builder.create` dereferences the server off it for the registries, so a `ClientLevel` cannot produce a context at all. `LootContext` is the per-invocation half, and it adds three things `LootParams` does not have: the chosen `RandomSource`, a `HolderGetter.Provider` that resolves references to other loaded elements, and a set of `LootContext.VisitedEntry` used as a recursion guard. `LootContext.pushVisitedElement` returns false when the element is already present, which is how `ConditionReference` detects a cycle — it logs an infinite loop and answers false. The guard is a stack, not a ledger: the entry is popped afterwards, so naming the same predicate twice in one evaluation is fine, and only genuine re-entrancy trips it. Both command call sites seed it with the top-level predicate before testing, so a predicate that references itself by name is caught on the first hop. **Named random sequences belong to the context, not to the table.** `LootContext.Builder.create` takes an optional `Identifier` and picks a random source three ways, in order: an explicit source or non-zero seed handed to the builder, else `MinecraftServer.getRandomSequence` for the named sequence, else `Level.getRandom`. `RandomSequences` is a `SavedData` that derives each sequence's seed from the world seed, a salt and the sequence id, which is what makes a named sequence reproducible across restarts. A loot table supplies that identifier from its own field — and so does `TradeSet.randomSequence`, which is why a villager's trade selection is reproducible by the same mechanism and has nothing to do with loot. The seeded-chest half of the story belongs to [loot tables](loot-tables.md). ## What reads a context `LootContextUser` — *what did you read out of the map* — has six sub-interfaces, and two of them carry the traffic, both reached by codec through a registry of types. `LootItemCondition` is a predicate over a `LootContext` with twenty registered types, from `LootItemEntityPropertyCondition` and `LocationCheck` to `EnchantmentActiveCheck` and `ConditionReference`; `NumberProvider` returns a float and has eight, from `ConstantValue` and `UniformGenerator` to `EnchantmentLevelProvider`. Their codecs are forgiving in the same shape: `LootItemCondition.DIRECT_CODEC` accepts a bare *list* as an implicit `AllOfCondition`, and `NumberProviders.CODEC` accepts a bare number as a `ConstantValue` and an untagged object as a `UniformGenerator`. A third family, `SlotSource` in `net/minecraft/world/item/slot`, reads a context through the same interface from outside the loot package. `ContextAwarePredicate` is the bridge the advancement system uses: a list of `LootItemCondition` composed into one predicate, entered through `ContextAwarePredicate.matches`. The *player* half of every trigger goes through it — `EntityPredicate.wrap` folds an `EntityPredicate` into a `LootItemEntityPropertyCondition` — but the rest of `net/minecraft/advancements/predicates` need not: a trigger instance can hold an `ItemPredicate` or a `LocationPredicate` outright and test it with no context at all, which `ConsumeItemTrigger` and `DistanceTrigger` both do. ## Who asks, and with which set | caller | set | when | |---|---|---| | `BlockBehaviour.BlockStateBase.getDrops` | `LootContextParamSets.BLOCK` | any block break ([block breaking](../blocks/block-breaking.md)) | | `Block.dropFromBlockInteractLootTable` | `LootContextParamSets.BLOCK_INTERACT` | beehives, cave vines, carving a pumpkin, sweet berries | | `LivingEntity.dropFromLootTable` | `LootContextParamSets.ENTITY` | death ([damage and death](../entities/damage-and-death.md)) | | `LivingEntity.dropFromShearingLootTable` | `LootContextParamSets.SHEARING` | sheep, mooshrooms, snow golems, bogged | | `LivingEntity.dropFromGiftLootTable` | `LootContextParamSets.GIFT` | hero gifts, cat gifts, chicken eggs, sniffer digs | | `LivingEntity.dropFromEntityInteractLootTable` | `LootContextParamSets.ENTITY_INTERACT` | brushing an armadillo | | `RandomizableContainer.unpackLootTable` | `LootContextParamSets.CHEST` | first touch of a structure container | | `ContainerEntity.unpackChestVehicleLootTable` | `LootContextParamSets.CHEST` | first touch of a chest minecart or chest boat | | `FishingHook.retrieve` | `LootContextParamSets.FISHING` | reeling in — luck is the hook's plus the owner's | | `BrushableBlockEntity` | `LootContextParamSets.ARCHAEOLOGY` | brushing suspicious sand | | `PiglinAi.getBarterResponseItems` | `LootContextParamSets.PIGLIN_BARTER` | bartering | | `Mob.createEquipmentParams` | `LootContextParamSets.EQUIPMENT` | the `EquipmentUser.equip` path, on mob spawn | | `VaultBlockEntity` | `LootContextParamSets.VAULT` | a trial chamber vault's display item and its reward | | `TrialSpawner.ejectReward`, `TrialSpawnerStateData.getDispensingItems` | `LootContextParamSets.EMPTY` | a trial spawner's reward and its dispensed items | | `AdvancementRewards.grant` | `LootContextParamSets.ADVANCEMENT_REWARD` | advancement loot | | `Enchantment.damageContext` and its four siblings | the four enchanted sets plus `LootContextParamSets.HIT_BLOCK` | [an enchantment effect's condition](enchantments.md) | | `LootCommand` | block, entity, chest or fishing | `/loot` | | `ItemCommands.applyModifier` | `LootContextParamSets.COMMAND` | `/item … with` | | **`ExecuteCommand`** | `LootContextParamSets.COMMAND` | **`/execute if predicate`** | | **`EntitySelectorOptions`** | `LootContextParamSets.SELECTOR` | **a selector's *predicate* argument** | | **`EntityPredicate.createContext`** | `LootContextParamSets.ADVANCEMENT_ENTITY` | **every advancement trigger that tests an entity** | | **`ItemUsedOnLocationTrigger`, `AnyBlockInteractionTrigger`** | `LootContextParamSets.ADVANCEMENT_LOCATION` | **placing or using an item on a block** | | **`DefaultBlockInteractionTrigger`** | `LootContextParamSets.BLOCK_USE` | **right-clicking a block, for an advancement** | | **`AbstractVillager.addOffersFromTradeSet`** | `LootContextParamSets.VILLAGER_TRADE` | **rolling and filtering a villager's offers** | The bold rows are the ones with no loot table anywhere in sight: a boolean, or in the villager's case a `MerchantOffers`, is the whole output. `ValidationContextSource` even keeps a cached `LootContextParamSets.ADVANCEMENT_ENTITY` context around, because so much of the advancement tree validates against it. Count *sets* rather than rows and the picture is starker. Fourteen of the twenty-six are named above by a caller that goes on to roll a `LootTable`. **The other twelve never do.** Six are the bold rows — `LootContextParamSets.COMMAND` counts among them, because its other caller, `/item … with`, applies an item modifier rather than a table. Five are the enchantment sets, built only by `Enchantment` to decide whether an effect fires. The twelfth is `LootContextParamSets.ALL_PARAMS`, which is never used to build a `ContextMap` at all: it exists solely as a validation context and as `LootTable.DEFAULT_PARAM_SET`. That is why this page is not called *loot tables*. ## The trace: `/execute if predicate` ```mermaid sequenceDiagram participant ExecC as ExecuteCommand participant LootP as LootParams participant CMap as ContextMap participant LootC as LootContext participant LIC as LootItemCondition Note over ExecC: the argument already holds a Holder, resolved at parse time ExecC->>LootP: LootParams.Builder on the level, ORIGIN required, THIS_ENTITY optional LootP->>CMap: ContextMap.Builder.create against LootContextParamSets.COMMAND CMap-->>LootP: throws on an unexpected key, or on an absent required one ExecC->>LootC: LootContext.Builder.create with no random sequence Note over LootC: the random source is Level.getRandom, the resolver is the reloadable registries ExecC->>LootC: pushVisitedElement, seeding the recursion guard with this predicate ExecC->>LIC: test LIC->>LootC: getParameter and getOptionalParameter LIC-->>ExecC: a boolean, and the branch is taken or not ``` Four details in that picture are worth pulling out. **The predicate is resolved before the command runs.** The argument type is `ResourceOrIdArgument.LootPredicateArgument`, which accepts *either* a registry id *or* an inline SNBT object and hands back a `Holder` — a reference in the first case, a direct holder over a freshly parsed condition in the second. An unknown id fails at parse time, as a command syntax error, not at execution. **`LootContextParamSets.COMMAND` is thin.** It requires `LootContextParams.ORIGIN` and optionally allows `LootContextParams.THIS_ENTITY`, and that is all: a predicate run from `/execute` cannot see a block state, a damage source or a tool, whatever the standalone-file validator let through. The selector's version, `LootContextParamSets.SELECTOR`, differs in exactly one way — it makes the entity *required*, because a selector always has one. **There is no random sequence.** `ExecuteCommand` passes an empty optional, so `Level.getRandom` is what a `LootItemRandomChanceCondition` inside the predicate draws from — not correlated between runs, not reproducible across restarts. **The selector path is the same code with one difference.** `EntitySelectorOptions` builds its context *per candidate entity*, inside the predicate it hands the parser, and looks the condition up itself through the reloadable registries rather than through the argument type — so a missing predicate there is a silent false, not a syntax error. ## None of this crosses the wire `LootDataType` is the three loot registries expressed as data: `LootDataType.TABLE` over `Registries.LOOT_TABLE`, `LootDataType.PREDICATE` over `Registries.PREDICATE` and `LootDataType.MODIFIER` over `Registries.ITEM_MODIFIER`. Each pairs a registry key with a codec and a `LootDataType.ContextGetter` — the function that says which `ContextKeySet` an element of that type is validated against. Predicates and item modifiers get the constant `LootContextParamSets.ALL_PARAMS`; tables get `LootTable.getParamSet`, and that context getter is its only caller in the game. That is the sense in which a table's declared type is never checked at runtime. All three live in `RegistryLayer.RELOADABLE`, loaded by `ReloadableServerRegistries.reload` on a background executor — one task per type, each scanning the data packs, registering, loading that registry's tags, and only then validating ([the resource system](../foundations/resource-system.md), [identifiers and registries](../foundations/identifiers-and-registries.md), and [codecs](../foundations/codecs-nbt-json.md) for the files themselves). None of the three appears in `RegistryDataLoader.SYNCHRONIZED_REGISTRIES`, the list `RegistrySynchronization.isNetworkable` tests, so none is ever packed for a client. What crosses the wire is only the *result* — a container packet, an item entity, a command's success. The two dependants outside this part are Part XIII's commands, which own `/execute if predicate` and the selector argument, and the advancement system, whose triggers build a context per tested entity. ## Where to look `ContextKey` · `ContextKeySet` · `ContextMap` · `LootContextParams` · `LootContextParamSets` · `LootParams` · `LootContext` · `LootContextArg` · `LootContextUser` · `LootItemCondition` · `LootItemConditions` · `ConditionReference` · `NumberProvider` · `NumberProviders` · `SlotSource` · `ContextAwarePredicate` · `EntityPredicate` · `Validatable` · `ValidationContext` · `ValidationContextSource` · `LootDataType` · `ReloadableServerRegistries` · `ExecuteCommand` · `EntitySelectorOptions` · `ResourceOrIdArgument` · `RandomSequences` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Loot tables > Verified against **Minecraft 26.2** · Part VII · A player opens a dungeon chest for the first time, and every item in it comes into existence between the click and the screen. You break into a mossy cobblestone room, there is a chest against the wall, and you right-click it. Between that click and the inventory screen the server does something it will do exactly once for this chest: it looks up a loot table, rolls it, scatters the results across the empty slots, and throws the recipe away. Until that moment the chest is **genuinely empty on disk** — the region file holds a table key and a seed and no items at all. And the roll is a one-shot in the strictest sense: `RandomizableContainer.unpackLootTable` clears the stored key *before* it rolls, and the thing that triggers it is not "a player opens this chest" but "anything reads this container". A hopper underneath taking one item, or a comparator behind the wall asking how full it is, will commit the roll with **no player present, and therefore no luck, for good**. The typed parameters the table is handed, the predicates it tests and the sets those belong to are not loot machinery — they are the general context engine that enchantment effects, advancement triggers, `/execute if predicate` and villager trade filters all run on, and [contexts and predicates](contexts-and-predicates.md) is where they are explained. Loot is that engine's oldest and largest client. This page is the worked example: what a table *is*, how one draw picks an item, and why the chest was empty. ## The cast | class | what it decides | thread | |---|---|---| | `LootTable` | the parameter set, an optional random sequence, the pools, the table's own functions, and the ways out — `LootTable.fill` into a container, `LootTable.getRandomItems` into a list, and the unsplit `LootTable.getRandomItemsRaw` a nested table uses | server main | | `LootPool` | whether the pool runs at all, and how many draws it makes | server main | | `LootPoolEntryContainer` | the entry algebra — `ComposableEntryContainer.expand` answers *did I contribute* | server main | | `LootPoolSingletonContainer` | weight and quality — one of the two places luck reaches anything | server main | | `LootItemFunction` | forty-three stack-to-stack transforms, composed into one per level | server main | | `RandomizableContainer` | the stored table key and seed, and the one-shot unpack | server main | | `LootContext` | which random source this draw uses, and the recursion guard | server main | | `ReloadableServerRegistries` | loading the three loot registries and validating them | the background executor | ## The chest was empty before you got there Nothing wrote items into that chest at world generation. `MonsterRoomFeature` places the room, then places up to two chests, giving each up to three tries at a position — every try needing an air block with exactly one solid horizontal neighbour — and for each chest that lands calls `RandomizableContainer.setBlockEntityLootTable` with `BuiltInLootTables.SIMPLE_DUNGEON` and a fresh long from the feature's own random. What lands on the live `ChestBlockEntity` is a **key and a seed**, and nothing else. Structure pieces do the same through `StructurePiece.createChest` and `StructurePiece.createDispenser`; `BuiltInLootTables` holds a hundred and seventeen named keys plus two per-dye-colour sets for sheep, spread over thirteen path prefixes — chests and dispensers, gameplay drops, shearing and brushing, harvesting and carving, decorated pots, equipment, archaeology, spawners and a charged creeper's. The seed reaches disk on the next save, and from then on the emptiness is self-perpetuating. `RandomizableContainer.trySaveLootTable` writes the key — and the seed only when it is non-zero — and answers *yes, I handled this*, so `ChestBlockEntity.saveAdditional` never writes an item list. `RandomizableContainer.tryLoadLootTable` answers the same way on the way back in, and `ChestBlockEntity.loadAdditional` **skips reading items entirely**. A chest nobody has touched has no inventory in any file the game has ever written. ## From the click to the screen ```mermaid sequenceDiagram participant SPGM as ServerPlayerGameMode participant CBE as ChestBlockEntity participant SP as ServerPlayer participant RCont as RandomizableContainer participant LT as LootTable participant LPool as LootPool participant ChestM as ChestMenu SPGM->>CBE: ChestBlock.useWithoutItem resolves a menu provider, and a single chest is its own CBE->>SP: openMenu, whose body is ServerPlayer's SP->>CBE: createMenu, guarded by canOpen CBE->>RCont: unpackLootTable, with the opening player Note over RCont: setLootTable to null BEFORE the roll, one shot RCont->>LT: fill, on the CHEST set, with the stored seed LT->>LPool: addRandomItems, once per pool LPool-->>LT: stacks, each through createStackSplitter LT->>LT: getAvailableSlots then shuffleAndSplitItems then setItem CBE->>ChestM: threeRows over the now-filled container SP->>ChestM: ClientboundOpenScreenPacket goes first, then initMenu ChestM-->>SP: sendAllDataToRemote, one ClientboundContainerSetContentPacket ``` **The click** arrives as `ServerPlayerGameMode.useItemOn` and reaches `ChestBlock.useWithoutItem` ([block interaction](../blocks/block-interaction.md)), which asks `ChestBlock.getMenuProvider` for something to open. A single chest *is* its own provider — the combiner hands back the `ChestBlockEntity`. A double chest is the interesting case: it gets an anonymous provider wrapped round a `CompoundContainer` that requires **both** halves to pass `RandomizableContainerBlockEntity.canOpen`, unpacks **both** loot tables itself, and never enters either block entity's own menu factory. **Opening** goes through `ServerPlayer.openMenu`, which closes whatever menu was already open, allocates a container id, and calls `RandomizableContainerBlockEntity.createMenu`. Both gates on opening are in one predicate: `RandomizableContainerBlockEntity.canOpen` adds a spectator clause to the lock check it inherits from `BaseContainerBlockEntity.canOpen`, and the spectator half bites only *while a table is still pending* — so a spectator cannot commit the roll by peering into an unopened chest, though they can open one that has already been rolled. **The unpack** is `RandomizableContainer.unpackLootTable`, and its order matters more than anything else on this page. It looks the key up through `ReloadableServerRegistries.Holder.getLootTable` — which answers `LootTable.EMPTY` for a missing key, never null — fires `CriteriaTriggers.GENERATE_LOOT` if a `ServerPlayer` is doing the opening, and **then clears the stored key**, before a single die is rolled. It builds the parameters with `LootContextParams.ORIGIN` at the block centre and, *only if a player is present*, that player's `Player.getLuck` and `LootContextParams.THIS_ENTITY`. Then it calls `LootTable.fill` with the container, those parameters and the stored seed. **The screen** comes last and is no part of the roll. `ServerPlayer.openMenu` sends `ClientboundOpenScreenPacket` and then calls `ServerPlayer.initMenu`, which attaches the listener and the synchronizer; attaching a synchronizer runs `AbstractContainerMenu.sendAllDataToRemote`, and that is the single `ClientboundContainerSetContentPacket` carrying the freshly rolled contents ([containers and menus](containers-and-menus.md)). ## One roll, drawn `LootTable.fill` runs every pool of the table, each pool makes some number of independent draws, and each draw picks at most one entry. That draw is the engine's smallest complete unit, and it is a funnel with three fan-outs in it. ```mermaid flowchart TD A["LootPool.addRandomItems"] --> B{"the pool conditions, all of them"} B -->|"any fails"| Z["the pool contributes nothing"] B -->|"all pass"| C["draws equals rolls plus floor of bonusRolls times luck"] C --> D["ONE DRAW, repeated that many times"] D --> E["expand every entry container, in declaration order, into candidates"] E --> F["AlternativesEntry, an or: stops at the first child that contributes"] E --> G["SequentialEntry, an and: stops at the first child that does not"] E --> H["EntryGroup: every child expands, contribution ignored"] E --> I["TagEntry in expand mode: one candidate per item in the tag"] E --> J["NestedLootTable: one candidate that will run another whole table"] F --> K["weight is floor of weight plus quality times luck, clamped at zero"] G --> K H --> K I --> K J --> K K --> L{"is that above zero?"} L -->|"no"| M["dropped from this draw entirely"] L -->|"yes"| N["kept, and added to the running total"] N --> O{"how many candidates survived?"} O -->|"none, or the total is zero"| Z2["this draw yields nothing"] O -->|"exactly one"| P["taken, consuming no randomness at all"] O -->|"two or more"| Q["one nextInt over the total, then walk subtracting weights"] P --> R["the entry's own functions"] Q --> R R --> S["then the pool's functions"] S --> T["then the table's functions"] T --> U["once per fill: createStackSplitter drops disabled items and cuts oversized stacks"] U --> V["getAvailableSlots shuffles the empty slot numbers"] V --> W["shuffleAndSplitItems breaks multi-count stacks up until they roughly fill them"] W --> X["setItem, or a logged warning and a silent discard once the slots run out"] ``` **The algebra is boolean, not weighted.** `ComposableEntryContainer.expand` returns a plain *did I contribute*, and each composite folds its children into one of those in its own `CompositeEntryBase.compose` — the two-child case literally calling `ComposableEntryContainer.or` or `ComposableEntryContainer.and`, and the longer cases a hand-written loop with the same short-circuit. So `AlternativesEntry` behaves like a boolean or and `SequentialEntry` like a boolean and — neither is a weighted choice between branches, and the validator reports an `AlternativesEntry` whose non-final children carry no conditions, because every later alternative is then unreachable. Nine entry types are registered in `LootPoolEntries`: the leaves `LootItem`, `EmptyLootItem`, `TagEntry`, `NestedLootTable`, `DynamicLoot` and `SlotLoot`, and the composites `AlternativesEntry`, `SequentialEntry` and `EntryGroup`. `TagEntry` has two modes and they are not variations on a theme: expanded, it becomes one weighted candidate *per item in the tag* — and those candidates are built bare, so the entry's own functions never run on them; unexpanded, it is a single candidate that emits **every** item in the tag at once, with its functions intact. **Luck touches exactly two things**, and neither is what players think it is. `LootPool.bonusRolls` is multiplied by luck and floored to add whole extra draws, and `LootPoolSingletonContainer.EntryBase.getWeight` is *weight + quality × luck*, floored, then clamped at zero. Because a candidate whose weight comes out at zero or below is discarded rather than merely made rare, a **negative quality with enough luck removes an entry from the pool altogether**. Everything else players call luck is something else entirely: Fortune is `ApplyBonusCount` and `BonusLevelTableCondition`, both reading `LootContextParams.TOOL` and asking `EnchantmentHelper` for a level on it; Looting is `EnchantedCountIncreaseFunction` and `LootItemRandomChanceWithEnchantedBonusCondition`, both reading `LootContextParams.ATTACKING_ENTITY` and asking about the killer's gear. **Functions apply innermost first.** Each level wraps the output consumer with `LootItemFunction.decorate` over its own `LootItemFunctions.compose`, so as the call stack unwinds a drop passes the entry's functions, then the pool's, then the table's. Forty-two of the forty-three registered functions extend `LootItemConditionalFunction`, whose `LootItemConditionalFunction.apply` is final and hands the stack back untouched when its own conditions fail — which is why a function with a failing condition is a no-op and not a veto on the drop. They also **mutate the stack in place and return it**, which is safe because every leaf hands out something of its own: `LootItem` and `TagEntry` construct fresh stacks and `SlotLoot` emits copies. `DynamicLoot` is the exception: it calls straight out to a callback the caller registered with `LootParams.Builder.withDynamicDrop`, and the one `ShulkerBoxBlock.getDrops` registers hands back the block entity's live stacks uncopied. ## The scatter `LootTable.fill` does not simply place what it rolled. `LootTable.getAvailableSlots` collects the container's empty slot numbers and shuffles them; `LootTable.shuffleAndSplitItems` then pulls the multi-count stacks out of the result list and repeatedly splits one — taking a random amount between one and half its count — until the number of pieces roughly matches the slot count. That is why one rolled stack of arrows arrives as several partial ones in unrelated slots — how many depends on how much of the chest is free. If the pieces outnumber the free slots, the remainder is **logged as a warning and silently discarded**. Above that sits `LootTable.createStackSplitter`, which every public `LootTable.getRandomItems` and `LootTable.fill` wraps its output in: it drops items the level's feature flags disable, and cuts anything at or over its maximum stack size into stack-sized pieces. `NestedLootTable` deliberately calls `LootTable.getRandomItemsRaw` instead, so a nested table's results are split once by the outer table rather than twice. ## Where the randomness comes from `LootContext.Builder` resolves the random source in a fixed order, and the zero is load-bearing. `LootContext.Builder.withOptionalRandomSeed` installs a seeded source **only when the seed is non-zero**; failing that, the table's declared random sequence is fetched from the server's per-world `RandomSequences` through `MinecraftServer.getRandomSequence`; failing that, the level's own random is used. `LootTable.RANDOMIZE_SEED` names the zero, and nothing in the game reads the constant. So a seed of zero means *unseeded*, is indistinguishable from having no seed at all, and is never written to NBT — which is why a chest given a loot table by command rolls unpredictably where a structure chest, carrying a seed, rolls the same contents whoever opens it and whenever. Neither rolls twice: the key is gone after the first unpack either way. Named random sequences are the other half: a table that declares one draws from a stored, saved sequence rather than the level random, which is what keeps the same table in the same world reproducible across a restart. Villager trades use that mechanism from outside the loot package — `AbstractVillager.addOffersFromTradeSet` builds its context with `TradeSet.randomSequence`. Loading is the only part of any of this that is not on the server thread. `ReloadableServerRegistries.reload` schedules one load per `LootDataType` on the background executor, builds a registry for each, loads that registry's tags, freezes the layer and only then validates ([the resource system](../foundations/resource-system.md)). Rolling is server main everywhere, and the guarantee is a type rather than a thread check: the parameters are built from a `ServerLevel`, so a `ClientLevel` cannot produce them at all. No client class references the loot package. ## Questions players ask **Did I just lose the loot by putting a hopper under it?** Yes, and the answer is precise about which reads count. **Five** — the container methods `RandomizableContainerBlockEntity` overrides so that they unpack first: `RandomizableContainerBlockEntity.isEmpty`, `RandomizableContainerBlockEntity.getItem`, `RandomizableContainerBlockEntity.removeItem`, `RandomizableContainerBlockEntity.removeItemNoUpdate` and — the surprising one — `RandomizableContainerBlockEntity.setItem`. A comparator gets there through `AbstractContainerMenu.getRedstoneSignalFromContainer`, which walks `Container.getItem` over every slot. A hopper gets there the same way before it takes anything, and so does a hopper pointing *into* the chest — it asks whether the destination is full before it pushes, so that one commits the roll by reading as well. `Clearable.clearContent` and `Container.getContainerSize` do not unpack, and neither does saving — which is why `/data get block` on an unopened chest reports the loot table key instead of committing the roll. **Can I reference the same table twice?** Yes. The recursion guard in `LootContext` is a **stack, not a ledger**: `LootContext.pushVisitedElement` adds the table on the way in and `LootContext.popVisitedElement` takes it off on the way out, so two pools pointing at the same nested table each get items, and so do two rolls of one pool. Only genuine re-entrancy — a table inside itself — trips it, and it is logged as an infinite loop rather than passing silently. **Why does a shulker box in my inventory say the contents are unknown?** `DataComponents.CONTAINER_LOOT` carries a `SeededContainerLoot` and is declared persistent with no network codec of its own, so `ByteBufCodecs.fromCodecWithRegistries` supplies one from the persistence codec and the component **does** reach the client ([data components](../foundations/data-components.md)). What it cannot carry is the contents: the client has no loot registry at all, and `SeededContainerLoot.addToTooltip` does not try — it prints the unknown-contents line and nothing else, on either side. **Does the type declared on the table do anything?** Not at roll time. A table's parameter set is read during load-time validation and never compared against the incoming parameters; whether a chest table works is entirely down to what the *caller* put in the map. That, and the twenty-six sets, are next door in [contexts and predicates](contexts-and-predicates.md). Two callers on the other side of that door are worth naming here: `BlockBehaviour.BlockStateBase.getDrops` for every block broken ([block breaking](../blocks/block-breaking.md)) and `LivingEntity.dropFromLootTable` for every mob killed ([damage and death](../entities/damage-and-death.md)). `EnchantWithLevelsFunction` and `EnchantRandomlyFunction` are the loot side of [enchanting](enchanting.md). And `EquipmentUser.equip` is the one caller that compares the looked-up table against `LootTable.EMPTY` **by identity** to decide whether to bother. ## Where to look `RandomizableContainer.unpackLootTable` · `RandomizableContainerBlockEntity` · `ChestBlock.getMenuProvider` · `LootTable.fill` · `LootTable.getRandomItemsRaw` · `LootPool.addRandomItems` · `ComposableEntryContainer` · `LootPoolSingletonContainer.EntryBase` · `AlternativesEntry` · `TagEntry` · `NestedLootTable` · `DynamicLoot` · `LootItemFunctions` · `LootItemConditionalFunction` · `LootTable.createStackSplitter` · `LootContext.Builder` · `RandomSequences` · `BuiltInLootTables` · `ReloadableServerRegistries` · `SeededContainerLoot` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # VIII · The player > Verified against **Minecraft 26.2** · Part VIII · The one entity a human is steering: what it is made of, when it runs, and the four things it does that the rest of the world does differently. Everything in Parts IV to VII happens to the world. This part is about the one object in it that argues back. A player is an entity like any other — same base class, same tick, same synched data — and then almost every rule is bent for it: it is ticked twice instead of once, it is the only thing the server simulates and then contradicts, its inventory reports seven slots it does not store and aliases an eighth, and its melee combat has three separate entry points of which the famous one is the least interesting. A player recognises the part by the friction: the snap back after a laggy jump, the swing that does more damage if you wait, the food bar that empties from sprinting and not from walking, the effect timer that keeps counting while the connection is down. ## The shape of the part Part VIII is a trunk and four branches. Two pages say what a player *is* and when it runs; everything after them is one thing a player *does*, and they are independent of each other — except the spear, which is the sword swing's sequel and should not be watched before it. ```mermaid flowchart TD PA["Player anatomy — what a player is made of"] TT["The two-phase tick — when it runs, and what is thrown away"] IM["Input to movement — walking, and being believed"] SS["The sword swing — one integer, and a number rebuilt"] SP["The spear — the same hit, twice, neither through Player.attack"] HE["Hunger and experience — two bars the server owns"] SE["Status effects — a list of things happening to you"] PA -- "eight classes, forty-three slots" --> TT TT -- "phase two is where the player acts" --> IM TT --> SS TT --> HE TT --> SE SS -- "and two other melee paths" --> SP ``` ## Before you start [Part VI](../entities/README.md) is the hard prerequisite, and two of its pages in particular. [Entity anatomy](../entities/entity-anatomy.md), because a player is a `LivingEntity` with three rungs added on the server and four on the client, and this part never re-teaches the base; and **[authority](../entities/authority.md)**, because every page here rests on it — a `Player` is client-authoritative on *both* sides, which is why the server's own answer for your movement is thrown away in favour of the number you sent it. If you watch one page from another part first, watch that one. Then [the server tick](../server/server-tick.md) and [the level tick](../server/server-level-tick.md), because half this part's timing claims are about which phase something ran in — including the fact that a player's own physics run *after* every level has finished. [Players and sessions](../server/players-and-sessions.md) owns how a `ServerPlayer` comes to exist at all. And [Part VII](../items/README.md) for the inventory this part stops at the edge of: [using an item](../items/using-an-item.md) in particular, because the spear is an item you *use*. ## Watch in this order 1. [Player anatomy](player-anatomy.md) — the vocabulary page: eight classes, two game-mode objects, forty-three slots. There is an abstract class between `LivingEntity` and `Player` — `Avatar` — with no instance fields at all, and the main-hand *item* is not stored anywhere: it is the selected hotbar slot, aliased. 2. [The two-phase tick](the-two-phase-tick.md) — one player, one tick, twice. The connection records where you are, runs the whole physics pipeline, and then puts you back: the server keeps the velocity and throws the position away. 3. [Input to movement](input-to-movement.md) — W is pressed. A movement key held for less than a tick never happened, sending move packets faster makes the anti-cheat *stricter*, and the packet that reports your key presses cannot move you but can move a minecart. 4. [The sword swing](the-sword-swing.md) — left-click on a pig. The attack packet carries one integer and the server rebuilds the rest, applying the cooldown twice in two different shapes and multiplying the mace's fall bonus by the critical hit. 5. [The spear](the-spear.md) — the 26.2 combat change, and the part's most surprising lecture. Two components on one item: a stab whose packet has no target in it, and a charge whose damage comes from closing speed and which ignores the attack cooldown entirely. 6. [Hunger and experience](hunger-and-experience.md) — two bars the server owns outright. Walking costs exactly zero exhaustion, and not one of the named thresholds in the file the system is built on is read by anything — `FoodData` writes every number as a literal instead. 7. [Status effects](status-effects.md) — the part's closer, and the cleanest statement of the server/client split in the book: the client never runs a single one of an effect's hooks, only counts it down — and an infinite effect is never re-sent, because −1 never satisfies the re-send test. Watched as lectures, one and two are the pair to keep together, and four and five are the other pair. Six and seven can be watched in either order, or skipped and returned to. ## Reference this part uses [Attributes](../../reference/attributes.md), because reach, attack damage, attack speed, sweeping ratio and knockback are all attributes — and attack damage and knockback, the two that decide what a hit is worth, are not synced to the client at all. [Packets](../../reference/packets.md) for the movement, attack and health packets by name. [Data components](../../reference/components.md) for the components that make an item a weapon — eight of them on a spear. Then [game rules](../../reference/gamerules.md), [level data and rules](../../reference/level-data-and-rules.md) and [diagram lanes](../../reference/lanes.md). The part stops where the player stops being a player: how a hit is resolved once it lands is [damage and death](../entities/damage-and-death.md) in Part VI, what your client is *told* about everyone else is [what the client is told](../networking/what-the-client-is-told.md) in Part IX, and the ledger behind the block you already saw break is [prediction and acknowledgement](../client/prediction-and-acks.md) in Part X. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Player anatomy > Verified against **Minecraft 26.2** · Part VIII · You open your own inventory and look at what you are made of: five classes deep, forty-three slots wide, and one of those slots is an alias. You are a `LivingEntity` that a human is steering through a socket. Almost everything on this page follows from that sentence: the class ladder exists to separate *what any living thing does* from *what a thing with an inventory and a game mode does* from *what a thing with a connection does*. But two of the rungs are not where a reader expects them. **There is an abstract class between `LivingEntity` and `Player` that holds no instance state at all**, and **the main-hand item is not stored anywhere** — the hotbar slot you are looking at and the item `LivingEntity.getMainHandItem` returns are the same bytes, aliased through the equipment container a horse also has. ## The cast | class | what it decides | thread | |---|---|---| | `Avatar` | the player-shaped hitbox and the two cosmetic synched values — and nothing else | both main threads | | `Player` | everything a reader means by *the player*: inventory, abilities, experience, sleep, reach | both | | `ServerPlayer` | the connection, the advancements, the statistics, and every *last sent* field | server main | | `LocalPlayer` | the one a human steers: input, prediction, what to send | client main | | `RemotePlayer` | every other player on your screen — interpolated, never derived | client main | | `Inventory` | thirty-six stacks, and a window onto `EntityEquipment` for the rest | both | | `ServerPlayerGameMode` / `MultiPlayerGameMode` | what the current `GameType` allows, one object per side | server / client main | | `Mannequin` | the other `Avatar`: a posable dummy that gets the whole skin pipeline | both | ## The ladder, and the class 26.2 put in the middle ```mermaid flowchart TD E["Entity"] --> LE["LivingEntity"] LE --> AV["Avatar — no instance fields"] AV --> P["Player — abstract"] AV --> M["Mannequin"] P --> SP["ServerPlayer"] P --> ACP["AbstractClientPlayer"] ACP --> LP["LocalPlayer"] ACP --> RP["RemotePlayer"] M --> CM["ClientMannequin"] ``` `Entity` and `LivingEntity` belong to [Part VI](../entities/entity-anatomy.md). The rung above them, **`Avatar`** (`world/entity`), is fifty-seven lines and **no instance fields at all**. It owns the player-shaped dimensions (`Avatar.POSES`, `Avatar.STANDING_DIMENSIONS`, `Avatar.CROUCH_BB_HEIGHT`, `Avatar.SWIMMING_BB_WIDTH`, `Avatar.SWIMMING_BB_HEIGHT`), the 1.62 eye height (`Avatar.DEFAULT_EYE_HEIGHT`), the two cosmetic synched values (`Avatar.DATA_PLAYER_MAIN_HAND`, `Avatar.DATA_PLAYER_MODE_CUSTOMISATION`) read back through `Avatar.getMainArm` and `Avatar.isModelPartShown`, and one abstract method, `Avatar.getProfile`, returning a `ResolvableProfile`. That is the whole class. It exists for the renderer. `AvatarRenderer` is generic over *an `Avatar` that is also a `ClientAvatarEntity`*, and exactly two classes satisfy that: `AbstractClientPlayer` and **`ClientMannequin`**. The swap is worth knowing, because it is the same server/client split `Player` has, one class lower: `Mannequin` (`world/entity/decoration`) holds a mutable static factory, `Mannequin.constructor`, and `ClientMannequin.registerOverrides` replaces it during client startup, so a mannequin spawned into a `ClientLevel` is really a `ClientMannequin` with the full `PlayerModel` and skin pipeline behind it. The mannequin is therefore a **sibling of `Player`**, not of `ArmorStand`; only the package is shared with the armour stand. It is posable (`Mannequin.VALID_POSES`), profiled (`Mannequin.DATA_PROFILE`), describable (`Mannequin.DATA_DESCRIPTION`) and optionally immovable (`Mannequin.DATA_IMMOVABLE`). **`Player`** itself is abstract for one method above all: `Player.gameMode`, returning a nullable `GameType`. A long tail of its other methods are empty hooks that exist so the two sides can disagree — `Player.onUpdateAbilities`, `Player.awardStat`, `Player.triggerRecipeCrafted`, `Player.crit`, `Player.magicCrit`, `Player.sendSystemMessage`, `Player.doCloseContainer`, `Player.openTextEdit`, `Player.sendMerchantOffers`, `Player.handleCreativeModeItemDrop`. On `Player` they do nothing; the subclass with somewhere to send a packet overrides them. ## What `Player` owns | what | the fields | who explains it | |---|---|---| | storage | `Player.inventory`, `Player.enderChestInventory` (a `PlayerEnderChestContainer`) | below | | the open window | `Player.inventoryMenu` (final) and `Player.containerMenu`, which *is* `Player.inventoryMenu` when nothing is open | [containers and menus](../items/containers-and-menus.md) | | what the mode allows | `Player.abilities` | below | | the food bar | `Player.foodData` | [hunger and experience](hunger-and-experience.md) | | experience | `Player.experienceLevel`, `Player.experienceProgress`, `Player.totalExperience`, `Player.enchantmentSeed`, `Player.lastLevelUpTime`, `Player.takeXpDelay` | [hunger and experience](hunger-and-experience.md) | | sleep | `Player.sleepCounter`, `Player.startSleepInBed` / `Player.stopSleepInBed`, the `Player.BedSleepingProblem` refusals, `Player.SLEEP_DURATION` (100) and `Player.WAKE_UP_DURATION` (10) | `ServerLevel` owns the *everyone is asleep* half | | the two combat clocks | `LivingEntity.attackStrengthTicker` and `LivingEntity.itemSwapTicker`, declared one rung up but read, reset and incremented only here | [the sword swing](the-sword-swing.md) | | cooldowns | `Player.cooldowns`, built by `Player.createItemCooldowns`, which only `ServerPlayer` overrides | [using an item](../items/using-an-item.md) | | four synched values | `Player.DATA_PLAYER_ABSORPTION_ID`, `Player.DATA_SCORE_ID`, `Player.DATA_SHOULDER_PARROT_LEFT`, `Player.DATA_SHOULDER_PARROT_RIGHT` | [synched entity data](../entities/synched-entity-data.md) | | addressing | `Player.ENDER_SLOT_OFFSET` (200), `Player.HELD_ITEM_SLOT` (499), `Player.CRAFTING_SLOT_OFFSET` (500), decoded by `Player.getSlot` | commands and containers | | the odds and ends | `Player.gameProfile`, `Player.lastDeathLocation`, `Player.fishing`, `Player.reducedDebugInfo`, `Player.lastItemInMainHand`, `Player.hurtDir`, `Player.jumpTriggerTime`, `Player.wasUnderwater` | — | None of those four synched values is the hand, which went up to `Avatar`; and a *player's* skin is not synched data at all: it arrives out of band, from the tab-list entry. A mannequin's does travel as synched data, in `Mannequin.DATA_PROFILE`. **Reach is two attributes, not one.** `Player.blockInteractionRange` and `Player.entityInteractionRange` read `Attributes.BLOCK_INTERACTION_RANGE` and `Attributes.ENTITY_INTERACTION_RANGE` ([attributes](../entities/attributes.md)), whose defaults — 4.5 and 3.0 — live on the attributes themselves. `Player.DEFAULT_BLOCK_INTERACTION_RANGE` and `Player.DEFAULT_ENTITY_INTERACTION_RANGE` name the same two numbers and are read by nothing. The checks the server makes are `Player.isWithinBlockInteractionRange` and `Player.isWithinEntityInteractionRange`. Note which class supplies which: `Player.createAttributes` adds the *block* range, `Attributes.BLOCK_BREAK_SPEED`, `Attributes.SUBMERGED_MINING_SPEED`, `Attributes.SNEAKING_SPEED`, `Attributes.MINING_EFFICIENCY`, `Attributes.SWEEPING_DAMAGE_RATIO` and the waypoint attributes, while the *entity* range comes from `LivingEntity.createLivingAttributes`. Two smaller seams: `Player.permissions` returns `PermissionSet.NO_PERMISSIONS` and both sides override it — it is what `Player.canUseGameMasterBlocks` consults alongside `Abilities.instabuild` for `Permissions.COMMANDS_GAMEMASTER` — and `Player` implements `ContainerUser`, which is how a chest decides you are still close enough to keep it open (`Player.getContainerInteractionRange`). ## Forty-three slots, and one of them is an alias `Inventory` implements `Container` in a particular shape: **one `Inventory.items` list of thirty-six stacks (`Inventory.INVENTORY_SIZE`) plus a reference to the player's `EntityEquipment`.** Slots at or above thirty-six are not stored here at all. `Inventory.EQUIPMENT_SLOT_MAPPING` routes them into the equipment object — the four armour indices, `Inventory.SLOT_OFFHAND` (40), `Inventory.SLOT_BODY_ARMOR` (41) and `Inventory.SLOT_SADDLE` (42) — so `Inventory.getContainerSize` is **forty-three**, and a player carries the same body-armour and saddle slots a horse does. `Player.createEquipment` returns a **`PlayerEquipment`**, which overrides the map so that `EquipmentSlot.MAINHAND` resolves to `Inventory.getSelectedItem`. That is the alias: the held item is not stored twice, and the main hand *is* the selected hotbar slot seen through the equipment interface. The rest of the class is the vocabulary the whole game uses to put things in a player: `Inventory.add`, `Inventory.getFreeSlot`, `Inventory.getSlotWithRemainingSpace`, `Inventory.placeItemBackInInventory`, `Inventory.findSlotMatchingItem`, `Inventory.contains`, `Inventory.removeItem`, `Inventory.clearOrCountMatchingItems`, `Inventory.dropAll`, `Inventory.getSuitableHotbarSlot`, `Inventory.addAndPickItem` and `Inventory.pickSlot` (pick-block), and `Inventory.fillStackedContents` (the recipe book). `Inventory.save` and `Inventory.load` cover the thirty-six — the equipment half is persisted by `LivingEntity` — and `Inventory.setSelectedSlot` throws rather than accept a non-hotbar index. ## `Abilities`, `GameType`, and the one method that connects them `Abilities` is five public booleans and two floats: `Abilities.invulnerable`, `Abilities.flying`, `Abilities.mayfly`, `Abilities.instabuild`, `Abilities.mayBuild`, plus `Abilities.getFlyingSpeed` and `Abilities.getWalkingSpeed`. It does not serialise itself by hand — it packs into the record `Abilities.Packed` and `Abilities.Packed.CODEC` does the work, through `Abilities.pack` and `Abilities.apply`. `GameType` is the four-constant enum (`GameType.SURVIVAL`, `GameType.CREATIVE`, `GameType.ADVENTURE`, `GameType.SPECTATOR`) with `GameType.DEFAULT_MODE`, a `GameType.CODEC` and a `GameType.STREAM_CODEC`. The method that matters is **`GameType.updatePlayerAbilities`**: the single place in the game that decides which abilities a mode grants. Both sides call it — the server from `ServerPlayerGameMode`, the client from `MultiPlayerGameMode` on login, on respawn and on a mode-change event. `GameType.isBlockPlacingRestricted` is what sets `Abilities.mayBuild`, and `GameType.isSurvival` is true for `GameType.ADVENTURE` too. ## The two game-mode objects | | `ServerPlayerGameMode` (`server/level`) | `MultiPlayerGameMode` (`client/multiplayer`) | |---|---|---| | owns the mode | `ServerPlayerGameMode.getGameModeForPlayer` | `MultiPlayerGameMode.getPlayerMode` | | changes it | `ServerPlayerGameMode.changeGameModeForPlayer` | `MultiPlayerGameMode.setLocalMode` | | breaking state | `ServerPlayerGameMode.isDestroyingBlock`, `ServerPlayerGameMode.destroyProgressStart`, `ServerPlayerGameMode.hasDelayedDestroy` | `MultiPlayerGameMode.isDestroying`, `MultiPlayerGameMode.destroyProgress`, `MultiPlayerGameMode.destroyDelay` | | the block hooks | `ServerPlayerGameMode.handleBlockBreakAction`, `ServerPlayerGameMode.useItemOn`, `ServerPlayerGameMode.useItem` | `MultiPlayerGameMode.startDestroyBlock`, `MultiPlayerGameMode.continueDestroyBlock`, `MultiPlayerGameMode.useItemOn`, `MultiPlayerGameMode.useItem` | | attacking | — (`ServerGamePacketListenerImpl` handles it) | `MultiPlayerGameMode.attack`, `MultiPlayerGameMode.interact` | | containers | — | `MultiPlayerGameMode.handleContainerInput` | Neither object is held by `Player` itself, and only one of them is held by a player at all: `Minecraft.gameMode` holds the client one — `LocalPlayer` has no such field — while `ServerPlayer.gameMode` is a field on the server player. [Block interaction](../blocks/block-interaction.md) and [block breaking](../blocks/block-breaking.md) own the block halves of both columns, and [prediction and acknowledgement](../client/prediction-and-acks.md) owns the ledger they share — which, note, `MultiPlayerGameMode` does not hold either: `MultiPlayerGameMode.startPrediction` reaches for `ClientLevel.getBlockStatePredictionHandler` per call. ## The three sides of one player **`ServerPlayer`** is everything that needs a server: `ServerPlayer.connection` (the `ServerGamePacketListenerImpl`), `ServerPlayer.gameMode`, `ServerPlayer.advancements`, `ServerPlayer.stats`, `ServerPlayer.recipeBook` (a `ServerRecipeBook`), `ServerPlayer.chunkTrackingView` and `ServerPlayer.lastSectionPos` — what the client has been sent ([tickets and loading](../world/tickets-and-loading.md)) — `ServerPlayer.respawnConfig`, `ServerPlayer.camera`, `ServerPlayer.textFilter`, `ServerPlayer.wardenSpawnTracker`, `ServerPlayer.enderPearls`, `ServerPlayer.containerSynchronizer`, and a row of mirror fields that exist so the server can notice a change: the `ServerPlayer.lastSentHealth`, `ServerPlayer.lastSentFood` and `ServerPlayer.lastSentExp` trio, which turn a difference into one packet, and a second row led by `ServerPlayer.lastRecordedArmor`, which turns one into a scoreboard criterion update instead. It also remembers what the client *said*: `ServerPlayer.lastClientInput` (an `Input`) and `ServerPlayer.lastKnownClientMovement`, both explained by [input to movement](input-to-movement.md). [Players and sessions](../server/players-and-sessions.md) owns this object's lifecycle; it is constructed during the *configuration* phase by `PrepareSpawnTask`, before the play listener exists. **`AbstractClientPlayer`** adds the tab-list entry (`AbstractClientPlayer.playerInfo`, fetched lazily from the connection), the per-frame animation state `AvatarRenderer` reads (`AbstractClientPlayer.clientAvatarState`), `AbstractClientPlayer.getSkin` and the field-of-view modifier. **`LocalPlayer`** is the one the human steers: `LocalPlayer.connection` (a `ClientPacketListener`), `LocalPlayer.input` (a `ClientInput` at construction, swapped for a `KeyboardInput` by the connection on login and respawn), `LocalPlayer.lastSentInput`, the last-sent position block, `LocalPlayer.recipeBook` (a `ClientRecipeBook`), `LocalPlayer.dropSpamThrottler`, `LocalPlayer.permissions`, `LocalPlayer.autoJumpEnabled`, `LocalPlayer.startedUsingItem` and the view-bob fields `LocalPlayer.yBob` / `LocalPlayer.xBob`. **`RemotePlayer`** is every *other* player on the client: it sets `Entity.noPhysics`, interpolates through `RemotePlayer.lerpDeltaMovement`, and has an **empty `RemotePlayer.updatePlayerPose`** — another player's pose is told to you, not derived. Which of the three is allowed to decide anything is [Part VI's authority](../entities/authority.md), stated once there: a `Player` is client-authoritative on *both* sides, and the server runs the physics anyway. [The two-phase tick](the-two-phase-tick.md) is what that costs. ## What a player is on disk `Player.addAdditionalSaveData` and `Player.readAdditionalSaveData` are where a player becomes a file: the inventory as a sparse slot/stack list, the selected slot, the sleep timer, the four experience fields including the enchanting seed, the score, the abilities through `Abilities.Packed`, the ender chest, and the last death location. `ServerPlayer` adds the game-type history through `ServerPlayer.storeGameTypes`, the thrown ender pearls through `ServerPlayer.saveEnderPearls`, the vehicle through `ServerPlayer.saveParentVehicle`, and `ServerPlayer.SavedPosition` — which is read *before* the entity exists, by the configuration-phase spawn task. ## Questions players ask **Why does the creative flag not come from my abilities?** `Player.isCreative` and `Player.isSpectator` do not read `Abilities` at all; they compare `Player.gameMode` against `GameType` constants. The flags themselves are read almost entirely through two accessors on `Player`: `Player.hasInfiniteMaterials`, which reads `Abilities.instabuild`, has more call sites than every other ability accessor combined — it is the most widely read of them, not the narrowest — with `Player.preventsBlockDrops` next and `Player.mayBuild`, `Player.isSwimming` and `Player.isPushedByFluid` well behind. **Whose game mode arrives late — and why is it yours?** On the client every player's mode comes from the tab list: `AbstractClientPlayer.gameMode` resolves through `AbstractClientPlayer.getPlayerInfo`, and returns null when there is no entry. For *another* player that window cannot be observed — `ClientPacketListener` refuses to spawn a `RemotePlayer` whose `PlayerInfo` has not arrived, and a `PlayerInfo` starts at `GameType.DEFAULT_MODE`, so their mode is survival until an update packet says otherwise, never null. The one player who really does have a null window is **you**: `LocalPlayer` is built during login, before your own tab-list entry is sent. What covers it is a second, independent source — `MultiPlayerGameMode.localPlayerMode`, set from the spawn info on login and respawn and from the game-event packet — and that is the one that drives `Abilities`, block breaking and the creative screen. **Why does building permission survive a packet that says otherwise?** `Abilities.mayBuild` never goes on the wire, and nothing recomputes it on receipt. `ClientboundPlayerAbilitiesPacket` carries four flag bits and two floats, none of them the build permission; the client's copy is written only by `MultiPlayerGameMode` on a mode change, so an abilities packet with no mode change leaves it as it was. The other direction is smaller still: `ServerboundPlayerAbilitiesPacket` carries only the flying bit. **Why does the save file say *flySpeed* when the accessor is `Abilities.getFlyingSpeed`?** Because `Abilities.Packed.CODEC` names the keys, and it does not match the accessors. While reading that class, note the misspelled constant: `Abilities.DEFAULY_FLYING`. **Why does item ticking need two callers?** Because of the forty-three slots. `Inventory.tick` runs over the thirty-six ordinary ones from `Player.aiStep`, and `EntityEquipment.tick` covers the other seven from `LivingEntity.aiStep`; and phase two walks all forty-three separately, offering each stack to `ServerPlayer.synchronizeSpecialItemUpdates` for the map-update packet a filled map needs. **What crosses the wire about the player itself?** `ClientboundLoginPacket` and `ClientboundRespawnPacket`, both carrying a `CommonPlayerSpawnInfo` built by `ServerPlayer.createCommonSpawnInfo` — which is also where the client's *local* game mode comes from; `ClientboundPlayerAbilitiesPacket`; `ClientboundGameEventPacket.CHANGE_GAME_MODE` and `ClientboundPlayerInfoUpdatePacket.Action.UPDATE_GAME_MODE` for a mode change; `ClientboundSetHeldSlotPacket` and the serverbound `ServerboundSetCarriedItemPacket` for the hotbar; and `ClientboundSetPlayerInventoryPacket`, built by `Inventory.createInventoryUpdatePacket`. **Can a data pack redefine any of this?** Almost none of it. `Player.createAttributes` supplies the attribute defaults, and game rules and server properties set the starting `GameType`. The player is one of the few systems in the game a data pack cannot redefine. **Which names will I hunt for under other spellings?** The renderer is `AvatarRenderer`, and pick-block is `Inventory.addAndPickItem`. ## Where to look `Player` · `Avatar` · `ServerPlayer` · `AbstractClientPlayer` · `LocalPlayer` · `RemotePlayer` · `Inventory` · `PlayerEquipment` · `Abilities` · `GameType` · `ServerPlayerGameMode` · `MultiPlayerGameMode` · `PrepareSpawnTask` · `Mannequin` · `ClientMannequin` · `AvatarRenderer` · `ClientAvatarEntity` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The two-phase tick > Verified against **Minecraft 26.2** · Part VIII · One server tick of one player — which happens twice, from two different callers, and the second half throws its own answer away. Every other entity on the server is ticked once, by the level it stands in. A player is ticked twice: once from the level's entity loop, and once from its own connection, after every level in the game has finished. The two halves share almost no work — the first never calls up into `Player.tick` at all, and the one block they have in common is the container-validity check — and the second half is stranger still. **The connection records where the player is, runs the entire physics pipeline, and then puts the player back where it found them.** The server simulates your movement in full, every tick, and deletes the result: what it keeps is the *velocity*, because that is the number the anti-cheat compares your reported motion against. ## The cast | class | what it decides | thread | |---|---|---| | `ServerLevel` | phase one: ticks the player in entity order, inside the level tick | server main | | `ServerPlayer` | both halves — `ServerPlayer.tick` and `ServerPlayer.doTick` overlap in one block only | server main | | `ServerGamePacketListenerImpl` | phase two: the record–simulate–snap-back bracket | server main | | `Player` | `Player.tick` and `Player.aiStep`, reached only from phase two | both | | `Inventory` | the thirty-six ordinary slots' per-tick item hook | both | | `FoodData` | hunger, regeneration and starvation, last of the three | server main | | `AbstractContainerMenu` | the open window, diffed against what the client was told | server main | | `LocalPlayer` | the client's own single tick, gated on the level having loaded | client main | ## Phase one: what the world does to this player `ServerPlayer.tick` is called by the level's entity loop — through `ServerLevel.tickNonPassenger` when the player is walking, and through `ServerLevel.tickPassenger` and `Entity.rideTick` when mounted — and players are ticked there whether or not their chunk is entity-ticking ([the level tick](../server/server-level-tick.md)). It runs late in `ServerLevel.tick`: after the block ticks and the chunk source, before the block entities. It does **not** call `Player.tick`. What it does instead is the outside world's business with the player: `ServerPlayerGameMode.tick` for block-breaking progress and the delayed destroy, the invulnerability countdown, `AbstractContainerMenu.broadcastChanges` on the open menu followed by closing it if it is no longer valid, dragging the camera entity along when one is set, the per-tick advancement criteria and a flush of the dirty ones, the warden spawn tracker, and `ServerPlayer.updatePlayerAttributes`. It is not quite connection-free: its very first statement is the connection's client-load timeout. ## Phase two: what this player would do if it simulated itself `ServerPlayer.doTick` is called by `ServerGamePacketListenerImpl.tickPlayer`, from the connection tick, *after* every level has ticked. This is the half that calls up into `Player.tick` and `LivingEntity.tick`, so **the player's physics are simulated here**. It then ticks `FoodData.tick`, the play-time statistics, `ServerPlayer.synchronizeSpecialItemUpdates` over all forty-three slots, and every *has this changed since I last sent it* comparison that produces `ClientboundSetHealthPacket` and `ClientboundSetExperiencePacket`. Most of it — including `Player.tick` — sits behind a gate that a spectator in unloaded chunks fails. `Player.aiStep`, reached from inside that, is where `Inventory.tick` runs over the thirty-six ordinary slots, immediately before `EntityEquipment.tick` covers the other seven from `LivingEntity.aiStep`. It is also the item and orb pickup sweep, gated on being alive and not a spectator, and it takes **one** experience orb per tick, chosen at random from those touching. ## The trace: one player, one tick, twice ```mermaid sequenceDiagram participant SL as ServerLevel participant SP as ServerPlayer participant SPGM as ServerPlayerGameMode participant ACM as AbstractContainerMenu participant SGPL as ServerGamePacketListenerImpl participant Player as Player participant Inv as Inventory participant FD as FoodData Note over SL: phase 1 — the entity loop, inside the level tick SL->>SP: tick — and no call up to Player.tick SP->>SPGM: tick — block-breaking progress and delayed destroy SP->>ACM: broadcastChanges — diff the open menu, then stillValid SP->>SP: updatePlayerAttributes — creative reach modifiers on and off Note over SGPL: phase 2 — the connection tick, after every level SGPL->>SGPL: resetPosition — record this position as firstGood and lastGood SGPL->>SP: doTick — the simulation half SP->>Player: Player.tick, then LivingEntity.tick — physics, to be discarded Player->>Inv: tick — ItemStack.inventoryTick for the 36 ordinary slots SP->>FD: tick — hunger, regeneration, starvation SP->>SGPL: ClientboundSetHealthPacket — only if a watched field differs SGPL->>SP: absSnapTo(firstGood) — put the position back, keep the rotation ``` ## The bracket, and what survives it `ServerGamePacketListenerImpl.tickPlayer` is a bracket around one call. It **records** the player's current position into the `firstGood…` and `lastGood…` fields, runs `ServerPlayer.doTick`, and then snaps the player back to the recorded position with `Entity.absSnapTo`, keeping only the rotation. The rest of the method is the anti-cheat that rides along in the same bracket: the *floating too long* kick, and the same record-and-check done again for the vehicle the player is steering. The authoritative position moves in `ServerGamePacketListenerImpl.handleMovePlayer` or in a teleport, never here. What survives the snap-back is `Entity.getDeltaMovement` — exactly what the anti-cheat subtracts from the client's reported displacement ([input to movement](input-to-movement.md)) — plus everything non-positional the tick did: drowning, burning, effects, hunger, the last-sent diffs. Both halves run every tick whether or not a packet arrived, and packets are drained before either of them. Everything the client must be *told* about its own player is written during phase two, and it leaves at once: `Connection.tick` flushes the channel on the line after it has run the listener that called `ServerPlayer.doTick` ([the server tick](../server/server-tick.md)). The pairing that makes this necessary is [Part VI's authority](../entities/authority.md): `Player.isClientAuthoritative` is an unconditional yes on **both** sides, which denies a `ServerPlayer` local-instance authority, while `Entity.canSimulateMovement` and `Entity.isEffectiveAi` are overridden true on the server anyway. So the pipeline runs and its answer is not believed. ## The client's single tick `LocalPlayer.tick` runs from `ClientLevel`'s entity tick on the main thread, with its entire body gated on the connection reporting that the level has loaded. `Minecraft.gameMode` is ticked separately, and *earlier* in `Minecraft.tick` than the entity tick. `ClientInput.tick` is called from inside `LocalPlayer.aiStep`, so input is **sampled inside the tick**, not pushed from the key callback — though the method doing the sampling is `KeyboardInput.tick`; `ClientInput.tick` itself is empty. Netty threads mostly do not touch player state: fifty-two of the sixty-one game handlers open by deferring to the owning thread ([the server tick](../server/server-tick.md) covers the mechanism). The exceptions are worth knowing, because they are not all trivial. Two really do touch nothing — the ping reply and an empty custom-payload hook. But all three chat handlers reach `ServerGamePacketListenerImpl.tryHandleChat`, which reads `ServerPlayer.getChatVisibility` and calls `ServerPlayer.resetLastActionTime` **on the Netty thread** before handing the rest to `MinecraftServer.execute`. ## Questions players ask **If the server ticks my player from the connection, does a silent client stop being ticked?** No. `ServerPlayer.doTick` runs every tick regardless of traffic, and so does `ServerPlayer.tick`; the one thing that stops phase two is `MinecraftServer.isPaused`, which only an integrated server reports. What stops a silent client moving is not a missing tick — it is the snap-back, which undoes every position the simulation produced. **Why does fall damage come from the packet handler?** Because inside `Entity.move`, the fall-damage branch is gated on local-instance authority, which is false for a `ServerPlayer`. The damage is applied instead by `Entity.doCheckFallDamage`, called on the movement-packet path with the client's own reported delta. **Which half does the thing I am looking for?** If it is the world acting on the player — the menu's change broadcast, the breaking timer, the spectator camera, the advancement criteria — phase one. If it is the player acting — physics, hunger, effects, item ticking, the packets that report a changed number — phase two. **Is a mounted player different?** Only in who calls phase one: `ServerLevel.tickPassenger` through `Entity.rideTick` rather than `ServerLevel.tickNonPassenger`. Phase two is unchanged, and the movement packets a passenger sends are treated very differently — see [input to movement](input-to-movement.md). ## Where to look `ServerPlayer.tick` · `ServerPlayer.doTick` · `ServerGamePacketListenerImpl.tickPlayer` · `Entity.absSnapTo` · `Player.tick` · `Player.aiStep` · `Inventory.tick` · `FoodData.tick` · `LocalPlayer.tick` · `ServerLevel.tickNonPassenger` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Input to movement > Verified against **Minecraft 26.2** · Part VIII · W is pressed: the key becomes a boolean, the boolean becomes a velocity, the velocity becomes a packet, and the server decides whether to believe it. You press W. Nothing happens for up to a twentieth of a second, because the key was not pushed anywhere — it was written into a boolean that the next tick will read. That tick turns seven booleans into a velocity, the velocity into a position, and the position into a packet; the server then decides whether to believe the packet. It usually does. The player is the one entity the server does not control, and everything here exists to reconcile that. The surprising part is what the server does while deciding. **It runs the whole physics pipeline for your player every tick and throws the position away.** It simulates in order to know what your velocity *ought* to be, because that is the number the anti-cheat subtracts from what you reported. ## The cast | class | what it decides | thread | |---|---|---| | `KeyboardHandler` | that a key went down, when nothing is in the way of it | client main | | `KeyMapping` | what that key is bound to, and whether it is held | client main | | `KeyboardInput` | seven booleans and a normalised vector, once per tick | client main | | `LocalPlayer` | the movement itself, and what is worth sending | client main | | `ServerGamePacketListenerImpl` | whether to believe it, and where the player really is | server main | | `ServerPlayer` | what the client last said: the input, and the known movement | server main | Who is *allowed* to decide any of this is [Part VI's authority](../entities/authority.md), and this page assumes it: a `Player` is client-authoritative on both sides, which denies a `ServerPlayer` local-instance authority, while `Entity.canSimulateMovement` and `Entity.isEffectiveAi` are overridden true on the server anyway. Two consequences run through everything below — `Entity.checkFallDamage` inside `Entity.move` is gated on local-instance authority and therefore never fires for a `ServerPlayer`, so `Entity.doCheckFallDamage` on the packet path does the work; and the ground flag is only updated unconditionally for an authoritative instance, so on the server it needs real vertical motion. ## What each side holds ### On the client - **`KeyMapping`** — one object per bindable action. `KeyMapping.ALL` is the by-name registry, `KeyMapping.MAP` the physical-key reverse index rebuilt by `KeyMapping.resetMapping`. Each holds `KeyMapping.isDown` and `KeyMapping.clickCount`. **`KeyMapping.consumeClick` is a counter drain, not an edge test** — most call sites loop on it, a few take one click per tick — but the movement keys never use it; they are polled with `KeyMapping.isDown`. `KeyMapping.Category` is a *record*, with constants like `KeyMapping.Category.MOVEMENT` and a public `KeyMapping.Category.register` for mods. - **`ToggleKeyMapping`** — `Options.keyShift` and `Options.keySprint` are these; hold-versus-toggle lives entirely in `ToggleKeyMapping.setDown`, driven by `Options.toggleCrouch` and `Options.toggleSprint`. Nothing downstream knows the difference. The screen-focus machinery is theirs too: `KeyMapping.setAll`, `KeyMapping.releaseAll`, `KeyMapping.resetToggleKeys` and `KeyMapping.restoreToggleStatesOnScreenClosed`, which consults `ToggleKeyMapping.shouldRestoreStateOnScreenClosed`. That is the answer to why a sneak *toggle* survives opening the inventory when a held sneak does not. - **`Options`** — the movement bindings: `Options.keyUp`, `Options.keyDown`, `Options.keyLeft`, `Options.keyRight`, `Options.keyJump`, `Options.keyShift`, `Options.keySprint`. Plus `Options.autoJump` and `Options.sprintWindow` (the double-tap window in ticks, default seven, zero to disable). - **`Input`** (`world/entity/player`) — a **shared** record of seven booleans (forward, backward, left, right, jump, shift, sprint) with `Input.EMPTY` and an `Input.STREAM_CODEC` that packs all seven into one byte; the bit values are named `Input.FLAG_FORWARD` … `Input.FLAG_SPRINT`. - **`ClientInput`** — `ClientInput.keyPresses` (an `Input`) plus `ClientInput.moveVector` (a `Vec2`, where `Vec2.x` is the *left* impulse and `Vec2.y` the *forward* one). `ClientInput.makeJump` is how auto-jump fakes a press. `ClientInput.tick` is empty; the subclass that actually reads the keyboard is **`KeyboardInput`**, whose `KeyboardInput.tick` builds a fresh `Input` from the seven `KeyMapping.isDown` values, maps each pair to −1, 0 or +1 with `KeyboardInput.calculateImpulse`, and normalises the resulting vector. - **`LocalPlayer`** — the send-tracking block: `LocalPlayer.xLast`, `LocalPlayer.yLast`, `LocalPlayer.zLast`, `LocalPlayer.yRotLast`, `LocalPlayer.xRotLast`, `LocalPlayer.lastOnGround`, `LocalPlayer.lastHorizontalCollision`, `LocalPlayer.positionReminder` (against `LocalPlayer.POSITION_REMINDER_INTERVAL`, twenty), `LocalPlayer.lastSentInput`, `LocalPlayer.wasSprinting`, `LocalPlayer.sprintTriggerTime`, `LocalPlayer.autoJumpEnabled`, `LocalPlayer.autoJumpTime`, `LocalPlayer.crouching`. `LocalPlayer.input` starts as a bare `ClientInput` and is replaced with a `KeyboardInput` by `ClientPacketListener` on login and on respawn — which is why a respawned player's input object is a different one. ### On the server `ServerGamePacketListenerImpl` holds the whole judgement: `ServerGamePacketListenerImpl.firstGoodX` and its siblings (where `ServerGamePacketListenerImpl.tickPlayer` found the player), `ServerGamePacketListenerImpl.lastGoodX` and its siblings (the last accepted position), `ServerGamePacketListenerImpl.awaitingPositionFromClient`, `ServerGamePacketListenerImpl.awaitingTeleport`, `ServerGamePacketListenerImpl.awaitingTeleportTime`, `ServerGamePacketListenerImpl.clientIsFloating`, `ServerGamePacketListenerImpl.aboveGroundTickCount`, `ServerGamePacketListenerImpl.receivedMovePacketCount`, `ServerGamePacketListenerImpl.knownMovePacketCount`, `ServerGamePacketListenerImpl.receivedMovementThisTick`, and the vehicle equivalents (`ServerGamePacketListenerImpl.lastVehicle`, `ServerGamePacketListenerImpl.vehicleFirstGoodX`, `ServerGamePacketListenerImpl.vehicleLastGoodX`, `ServerGamePacketListenerImpl.clientVehicleIsFloating`). `ServerPlayer.lastKnownClientMovement` is the observed per-tick displacement, read back through `ServerPlayer.getKnownMovement` and `ServerPlayer.getKnownSpeed`, and `ServerPlayer.lastClientInput` is the raw key state. **Almost none of the thresholds have names.** The numbers in the movement checks are inline literals; the only named ones nearby are `ServerGamePacketListenerImpl.MAXIMUM_FLYING_TICKS` (80) and `ServerGamePacketListenerImpl.CLIENT_LOADED_TIMEOUT_TIME` (60). ## Sampled once a tick, judged once a tick Keys are **sampled inside the tick, not pushed from the callback.** The GLFW callback builds a `KeyEvent` and immediately defers to the client's main thread with `BlockableEventLoop.execute`; the *movement* half of `KeyboardHandler.keyPress` sets `KeyMapping.isDown` and bumps `KeyMapping.clickCount`, and does even that only when no screen is open. Everything else the method does — the screen's own key handling, the debug keys, the pause — never reaches a `KeyMapping` at all. Releases are always delivered, which is the asymmetry the toggle-restoring machinery above exists to repair. The read happens once per game tick, deep inside `LocalPlayer.aiStep`, which calls `ClientInput.tick`. Mouse look is the exception: `MouseHandler.handleAccumulatedMovement` runs **per frame**, in `Minecraft.runTick` after the tick loop, gated on the window being active and the mouse grabbed, and `MouseHandler.turnPlayer` calls `Entity.turn` directly — after cubing the sensitivity, applying `Options.smoothCamera` through a `SmoothDouble` and honouring the two invert options. Rotation is therefore finer-grained than position. On the server the ordering is the whole story: 1. `MinecraftServer.processPacketsAndTick` drains `PacketProcessor` **before** `MinecraftServer.tickServer`. Every movement packet for the tick is applied first, ahead of any level ticking. 2. The levels tick. 3. `MinecraftServer.tickChildren` reaches its connection phase, and `ServerGamePacketListenerImpl.tick` runs `ServerGamePacketListenerImpl.tickPlayer` — the simulate-and-discard step, and the place the floating check is enforced. A paused server short-circuits before it. ## The trace: W is pressed ```mermaid sequenceDiagram participant KH as KeyboardHandler participant KM as KeyMapping participant KI as KeyboardInput participant LP as LocalPlayer participant LE as LivingEntity participant SGPL as ServerGamePacketListenerImpl participant SP as ServerPlayer KH->>KM: set — isDown = true#59; nothing else happens yet LP->>KI: tick — from inside aiStep: poll seven keys into one Input KI->>LP: applyInput — moveVector becomes xxa/zza, jump becomes jumping LP->>LE: travel — travelInAir, then Entity.move: the client is authoritative LP->>SGPL: ServerboundPlayerInputPacket — only when the key set changed LP->>SGPL: ServerboundMovePlayerPacket.PosRot — sendPosition decides which variant SGPL->>SGPL: moved too quickly? — squared delta vs getDeltaMovement, budget 100 or 300 SGPL->>SP: move(MoverType.PLAYER) — where the server applies the position you reported SGPL->>SGPL: moved wrongly? — residual over 0.0625, or a new collider SGPL->>LP: ClientboundPlayerPositionPacket — rubber-band, awaiting an ack SGPL->>SP: doTick — simulate the whole tick, then absSnapTo(firstGood…) and discard ``` **The client half.** `KeyboardInput.tick` builds an `Input` from the seven keys and a normalised `Vec2`. `LocalPlayer.applyInput` — overriding a `LivingEntity.applyInput` that does nothing but decay them — turns that into the `LivingEntity.xxa` and `LivingEntity.zza` movement fields, after passing it through `LocalPlayer.modifyInput`: a flat 0.98 scaling, then the item-use slowdown (from `LocalPlayer.itemUseSpeedMultiplier`), `Attributes.SNEAKING_SPEED` when moving slowly, and `LocalPlayer.modifyInputSpeedForSquareMovement`, the diagonal correction. From there it is ordinary [movement and collision](../entities/movement-and-collision.md): `Player.travel` — a real override, handling passengers, the swimming look nudge and the creative-flight damping — then `LivingEntity.travel` → `LivingEntity.travelInAir` → `LivingEntity.handleRelativeFrictionAndCalculateMovement` → `Entity.moveRelative` → `Entity.move`. Sprint is decided in `LocalPlayer.aiStep` before that, and it is a **rising edge, not a release**. `LocalPlayer.aiStep` snapshots the forward impulse *before* ticking the input, so the value it later tests is the previous tick's; `LocalPlayer.canStartSprinting` requires the current tick's. The pair means the double-tap window is armed on the first *press* and consumed on the second, and a release only matters because sneaking, using an item or walking backwards clears `LocalPlayer.sprintTriggerTime` outright. `LocalPlayer.shouldStopRunSprinting` ends it. Auto-jump is `LocalPlayer.updateAutoJump` (called from `LocalPlayer.move`) setting `LocalPlayer.autoJumpTime`, which makes the *next* tick call `ClientInput.makeJump`. **What goes on the wire.** `LocalPlayer.sendPosition` picks the variant: `ServerboundMovePlayerPacket.PosRot` when both changed, `ServerboundMovePlayerPacket.Pos` or `.Rot` for one, `ServerboundMovePlayerPacket.StatusOnly` when only the ground or collision flag changed, and **nothing at all** otherwise — except that a position is re-sent every twenty ticks regardless, via `LocalPlayer.positionReminder`. "Changed" is not the same test for the two halves: rotation compares exactly, while position must have moved by more than 2×10⁻⁴ blocks. And the whole method sits behind `LocalPlayer.isControlledCamera`, so while spectating another entity a client sends no move packets at all, not even the reminder. The two booleans ride in one byte (`ServerboundMovePlayerPacket.FLAG_ON_GROUND`, `ServerboundMovePlayerPacket.FLAG_HORIZONTAL_COLLISION`). `ServerboundPlayerInputPacket` is sent only when the key set *changes*, and `ServerboundClientTickEndPacket` — a zero-byte singleton — closes every client tick that has a level and is not paused. **The server half.** `ServerGamePacketListenerImpl.handleMovePlayer` begins with `ServerGamePacketListenerImpl.containsInvalidValues`, which rejects **NaN** coordinates and non-finite *rotations* — an infinite coordinate survives it and is clamped instead, to ±3×10⁷ horizontally by `ServerGamePacketListenerImpl.clampHorizontal` and ±2×10⁷ vertically by `ServerGamePacketListenerImpl.clampVertical`. It then discards the position entirely while a teleport is outstanding; short-circuits a sleeping player, teleporting them back if they claim to have moved more than a block; and for a passenger applies rotation only — snapping the position back with `Entity.absSnapTo` and re-registering the chunk position, which is not quite "returns early". Then two checks: - *moved too quickly*: the squared distance from `firstGood…` minus `Entity.getDeltaMovement().lengthSqr()` against a budget of **100 per packet, or 300 while fall-flying**, scaled by how many move packets arrived since the last tick. Both sides are squared, so 100 is a hundred blocks *squared* — about ten blocks a tick. The whole check, and the packet counter it uses, is gated on `TickRateManager.runsNormally`, so a frozen or stepping world does no speed checking at all. It is also skipped for the singleplayer host, during a dimension change, and when `GameRules.PLAYER_MOVEMENT_CHECK` is off (`GameRules.ELYTRA_MOVEMENT_CHECK` covers the elytra case). Failure teleports the player back and returns. - The move is then actually applied — `Entity.move` with `MoverType.PLAYER` — and *moved wrongly* measures what is left over: a residual above `0.0625` while not changing dimension, sleeping, creative, spectating or inside `LivingEntity.isInPostImpulseGraceTime` (the mace and wind-charge exemption, closed by `ServerGamePacketListenerImpl.tryResetCurrentImpulseContext`). The rubber-band that follows is a **disjunction**: either that failure with a demonstrably clear old box, *or* `ServerGamePacketListenerImpl.isEntityCollidingWithAnythingNew` reporting the player ended up inside a collider it was not already inside — which fires whether or not the residual check failed. Both arms are additionally suppressed for a no-physics or sleeping player. Accepting means `Entity.absSnapTo`, `ServerChunkCache.move`, `Entity.setOnGroundWithMovement`, `Entity.doCheckFallDamage`, `ServerGamePacketListenerImpl.handlePlayerKnownMovement` and `ServerPlayer.checkMovementStatistics` — the walked-distance statistics are computed from the *client's reported* delta, never from a simulation. The server also **infers the jump**: a packet that reports leaving the ground while moving upward calls `LivingEntity.jumpFromGround` on the player's behalf. **Where velocity comes from.** The reported delta is stored by `ServerPlayer.setKnownMovement`, and `ServerGamePacketListenerImpl.handleClientTickEnd` zeroes it if no move packet arrived that tick. That is what everything downstream reads when it wants the player's velocity — whether a swing sweeps, what a spear's charge does, the speed a fired projectile inherits, leash physics — and it is why a client that stops sending is treated as stationary rather than as still coasting. **The teleport handshake.** `ServerGamePacketListenerImpl.teleport` bumps `ServerGamePacketListenerImpl.awaitingTeleport`, moves the player with `Entity.teleportSetPosition`, records `ServerGamePacketListenerImpl.awaitingPositionFromClient` and sends `ClientboundPlayerPositionPacket` (a `PositionMoveRotation` plus a set of `Relative` flags saying which fields are deltas). `ServerGamePacketListenerImpl.updateAwaitingTeleport` **re-sends after more than twenty ticks** if no acknowledgement arrives, and until it does, every incoming move packet contributes rotation only. The client replies with `ServerboundAcceptTeleportationPacket` *and* an immediate `ServerboundMovePlayerPacket.PosRot`, then calls `BlockStatePredictionHandler.onTeleport` to drop its outstanding block predictions ([block interaction](../blocks/block-interaction.md)). On the receiving end `ClientPacketListener.handleMovePlayer` applies the position only when the player is not a passenger, and never interpolates: it passes the interpolate flag as a literal false, so your own player is always snapped, and the 4096-blocks-squared jump test that gates interpolation is reached only on the entity-teleport path. `ClientboundPlayerRotationPacket` is the rotation-only sibling. **Elytra** is its own round trip: `LocalPlayer.aiStep` asks `Player.tryToStartFallFlying` and, if it says yes, sends `ServerboundPlayerCommandPacket.Action.START_FALL_FLYING` — the server runs the same method on receipt. The server may disagree and call `LivingEntity.stopFallFlying`, and the flight itself is `LivingEntity.updateFallFlying` and `LivingEntity.travelFallFlying`. ## What it calls, and what crosses the wire - **Called by:** `Minecraft.tick` (client, via `ClientLevel` and `Minecraft.handleKeybinds`); `PacketProcessor` and `MinecraftServer.tickChildren` (server). - **Calls into:** `LivingEntity.travel` and `Entity.move` ([movement and collision](../entities/movement-and-collision.md)); `ServerChunkCache.move`, which is what makes chunks load as you walk ([tickets and loading](../world/tickets-and-loading.md)). - **Crosses the network as:** `ServerboundMovePlayerPacket` and its four variants, `ServerboundPlayerInputPacket`, `ServerboundPlayerCommandPacket` (whose `ServerboundPlayerCommandPacket.Action` is seven values — start and stop sprinting, start and stop riding-jump, `ServerboundPlayerCommandPacket.Action.STOP_SLEEPING`, `ServerboundPlayerCommandPacket.Action.OPEN_INVENTORY`, `ServerboundPlayerCommandPacket.Action.START_FALL_FLYING`; **there is no sneak action** — sneaking reaches the server through `ServerboundPlayerInputPacket`, which calls `Entity.setShiftKeyDown`), `ServerboundMoveVehiclePacket`, `ServerboundAcceptTeleportationPacket`, `ServerboundClientTickEndPacket`; and back, `ClientboundPlayerPositionPacket`, `ClientboundPlayerRotationPacket`, `ClientboundMoveVehiclePacket`. - **Data-driven by:** almost nothing — `GameRules.PLAYER_MOVEMENT_CHECK` and `GameRules.ELYTRA_MOVEMENT_CHECK` ([level data and rules](../../reference/level-data-and-rules.md)), plus the movement attributes. ## Questions players ask **Why does the server bother simulating me at all?** `ServerGamePacketListenerImpl.tickPlayer` records the player's position into the `firstGood…` and `lastGood…` fields, calls `ServerPlayer.doTick` — the whole `LivingEntity.aiStep` / `LivingEntity.travel` / `Entity.move` pipeline runs server-side — and then puts the player back with `Entity.absSnapTo`, keeping the rotation. The simulation exists for `Entity.getDeltaMovement`, the *expected* distance the check subtracts. On foot the authoritative position moves only in `ServerGamePacketListenerImpl.handleMovePlayer` or through a teleport; the exception is riding, where the vehicle repositions you through `Entity.rideTick` every tick. [The two-phase tick](the-two-phase-tick.md) is that bracket seen from the other side. **If `ServerboundPlayerInputPacket` never moves me, what is it for?** Two things. `ServerPlayer.setLastClientInput` feeds `ServerPlayer.getLastClientMoveIntent`, and both `NewMinecartBehavior` and `OldMinecartBehavior` read it to nudge a stalled cart along the rider's intended direction — so the packet that cannot move a player *can* move a minecart. And the handler sets the sneak flag directly, which is why there is no sneak action on `ServerboundPlayerCommandPacket`. Boats are steered client-side (`LocalPlayer.rideTick` → `AbstractBoat.setInput`) and the *result* ships as `ServerboundMoveVehiclePacket`. **Does sending move packets faster help me cheat?** It does the opposite. The per-packet budget normally scales with how many packets arrived since the last tick, but above five the code clamps the count to one — so a flood gets a one-packet budget for a many-packet displacement. There is no throttle or kick for the flood itself; only chat, commands and item drops have a `TickThrottler`. **Why is a passenger barely checked?** A passenger's own move packet contributes rotation and a chunk re-registration and nothing else, and the *vehicle's* packet is judged by a cut-down copy of the same code: a flat budget of 100, no elytra case, no game rule, and no horizontal-collision flag. **What counts as floating, and why does creative flight not trip it?** `ServerGamePacketListenerImpl.getMaximumFlyingTicks` returns an effectively unbounded budget below a gravity of 10⁻⁵ and otherwise stretches the eighty-tick budget as gravity falls, so the kick scales with gravity and only upward. `ServerGamePacketListenerImpl.clientIsFloating` is separately suppressed by spectator mode, `Abilities.mayfly`, the server's own allow-flight setting, `MobEffects.LEVITATION`, fall-flying and riptide — and the condition that actually defines *floating* is having no blocks anywhere below. **Why did my quick tap do nothing?** Movement polls `KeyMapping.isDown` once a tick, so a press shorter than a tick never happened. The keys that use `KeyMapping.consumeClick` behave the other way: three taps inside one tick can fire three times. **Why does brushing a wall sometimes cancel my sprint and sometimes not?** `LocalPlayer.isHorizontalCollisionMinor` is a client-only override that measures the angle against `LocalPlayer.MINOR_COLLISION_ANGLE_THRESHOLD_RADIAN`, about eight degrees; a graze shallower than that is forgiven. The server has no equivalent. **What is quietly wrong here?** Three things, all harmless and all worth knowing. The vertical residual in the *moved wrongly* check is dead code — the guard that zeroes it is a disjunction true for every finite double, so the 0.0625 test is horizontal-only in practice, in both the player and the vehicle handler. `Options.autoJump` is read inside a networking method: `LocalPlayer.autoJumpEnabled` is refreshed in `LocalPlayer.sendPosition`, which does not run for a passenger or a non-camera player, so the setting quietly stops tracking in those states. And `ServerboundPlayerCommandPacket` carries an entity id the server never validates against the sender. ## Where to look `KeyboardHandler` · `KeyMapping` · `ToggleKeyMapping` · `Options` · `ClientInput` · `KeyboardInput` · `Input` · `LocalPlayer` · `MouseHandler` · `ServerboundMovePlayerPacket` · `ServerboundPlayerInputPacket` · `ServerGamePacketListenerImpl` · `ClientboundPlayerPositionPacket` · `PositionMoveRotation` · `Relative` · `PacketProcessor` · `TickThrottler` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The sword swing > Verified against **Minecraft 26.2** · Part VIII · Left-click on a pig: the client picks a target and sends one integer, and the server rebuilds every part of the hit from scratch. You put the crosshair on a pig and click. The client has already decided what you are looking at — earlier in this same tick — checks a handful of reasons not to swing, and sends the smallest packet in melee combat: **`ServerboundAttackPacket` is a record of one int, the entity id.** No hand, no sneak flag, no hit position, no damage. Everything else the server re-derives: the weapon from your main hand, the geometry from the target's bounding box measured against your eye, and the damage from an attribute, a cooldown curve applied twice in two different shapes, and a multiplication order in which the mace's fall bonus lands *before* the critical hit and is therefore multiplied by it. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | what you are looking at, and whether the click swings at all | client main | | `LocalPlayer` | the raycast, and which range each candidate is judged against | client main | | `MultiPlayerGameMode` | sends the attack, and predicts almost nothing | client main | | `ServerGamePacketListenerImpl` | resolves the id, re-checks the range and the item | server main | | `Player` | `Player.attack`: one method, and the order inside it is load-bearing | both (only the server's answer counts) | | `LivingEntity` | the swing animation state, and the two attack clocks | both | | `AttackRange` | a weapon's own reach, with a minimum as well as a maximum | — | ## Picking: what is under the crosshair `Minecraft.pick` runs once per tick inside `Minecraft.tick`, in this order: `MultiPlayerGameMode.tick` → **`Minecraft.pick`** → the GUI → `Minecraft.handleKeybinds`, which drains `Options.keyAttack` into `Minecraft.startAttack` and finishes with `Minecraft.continueAttack` for held-down mining. So the hit result a click uses was computed *earlier in the same tick*. `Minecraft.pick` also runs per frame, for the crosshair and the block outline, but that value is not what the attack sees. It asks the camera entity, and for the local player that is `LocalPlayer.raycastHitResult`. If the **active** item — the one being used, if any, else the main hand — carries an `AttackRange` (`DataComponents.ATTACK_RANGE`), that component's own search runs first; and if it finds nothing, the classic algorithm runs **as well**: a block clip out to the greater of the two ranges, an entity sweep with `ProjectileUtil.getEntityHitResult` over the bounding box expanded along the view direction and inflated by one, each candidate inflated by `Entity.getPickRadius` (**zero** for everything but projectiles) — and the entity wins only if it is strictly nearer than the block. Then `LocalPlayer.filterHitResult` discards each against *its own* range: `Attributes.ENTITY_INTERACTION_RANGE` (3.0) for the entity, `Attributes.BLOCK_INTERACTION_RANGE` (4.5) for the block. That is where the two reaches diverge. `AttackRange` is worth a second look, because it is a reach *floor* as well as a ceiling: a minimum and a maximum, separate creative values, a hitbox margin and a mob factor. `AttackRange.isInRange` is the test, `AttackRange.defaultFor` falls back to `Attributes.ENTITY_INTERACTION_RANGE`, and `AttackRange.effectiveMinRange` / `AttackRange.effectiveMaxRange` apply the mob factor — only for non-players. ## Deciding: most branches do not swing `Minecraft.startAttack` is the branch point. It returns early — no swing, no packet — while `Minecraft.missTime` is running, when there is no hit result at all (setting a ten-tick miss time), while `LocalPlayer.isHandsBusy`, for a disabled item, and when `Player.cannotAttackWithItem` refuses with a tolerance of **zero**; spectators take a branch of their own. Two branches do swing: the piercing short-circuit to `MultiPlayerGameMode.piercingAttack` when the item carries `DataComponents.PIERCING_WEAPON` — [the spear](the-spear.md) — and the tail of the hit-result switch: entity to `MultiPlayerGameMode.attack`, block to `MultiPlayerGameMode.startDestroyBlock` ([block breaking](../blocks/block-breaking.md)), a miss on an air block to `Player.resetAttackStrengthTicker` and the ten-tick miss time. Even the entity branch is conditional — a weapon with its own `AttackRange` that the hit falls outside of swings but sends no attack packet at all. The miss time itself only exists outside creative, and opening any screen parks it at a very large number. On the server, `ServerGamePacketListenerImpl.handleAttack` requires the client to have loaded and the player not to be a spectator, resolves the id with `ServerLevel.getEntityOrPart`, checks the world border, applies `Player.isWithinAttackRange` with a **3.0-block server buffer** — applied to *both* ends, so a weapon's minimum range effectively vanishes server-side — rejects a piercing weapon (that path arrives elsewhere), checks the item is enabled, re-checks `Player.cannotAttackWithItem` with a tolerance of **five ticks**, more lenient than the client's zero, and calls `Player.attack`. Attacking something absurd — an `ItemEntity`, an `ExperienceOrb`, an unattackable `AbstractArrow`, yourself — is a **disconnect**, not a rejection; failing the range check is a silent drop. The packet is drained from `PacketProcessor` at the **top** of the tick, before `MinecraftServer.tickServer` and therefore before any level ticks. That ordering matters: `Player.attack` runs before the victim's `LivingEntity.baseTick` decrements `Entity.invulnerableTime` for the tick. (The counter is declared on `Entity` and decremented in `LivingEntity.baseTick`, which skips a `ServerPlayer` — because `ServerPlayer.tick` decrements it itself, in phase one.) Each of the resulting feedback packets is written and flushed on its own: the connection suspends flushing only across `MinecraftServer.tickChildren`, and the attack was handled before that bracket opened. ## The trace: one click, one integer, one round trip ```mermaid sequenceDiagram participant MC as Minecraft participant LP as LocalPlayer participant MPGM as MultiPlayerGameMode participant SGPL as ServerGamePacketListenerImpl participant Player as Player participant LE as LivingEntity participant SL as ServerLevel MC->>LP: raycastHitResult — AttackRange first, then the classic pick MC->>MPGM: attack — after cannotAttackWithItem and the range test MPGM->>SGPL: ServerboundAttackPacket — one varint: the entity id LP->>SGPL: ServerboundSwingPacket — from the branches that swing at all SGPL->>SGPL: isWithinAttackRange — AttackRange plus a 3.0 buffer both ways SGPL->>Player: attack — the server recomputes damage from nothing but the id Player->>LE: hurtOrSimulate — into Part VI#59; returns did-it-land Player->>Player: causeExtraKnockback, doSweepAttack, itemAttackInteraction SL->>MC: ClientboundDamageEventPacket — a damage type and three ids, no amount ``` ## The damage: one number, two curves, one order `Player.attack` is a single method, and everything interesting about melee combat is the order in which it touches one float. ```mermaid flowchart TD S["s = Player.getAttackStrengthScale, read with a partial tick of 0.5"] BASE["base = Attributes.ATTACK_DAMAGE — or the riptide value while auto-spinning"] BOOST["boost = Player.getEnchantedDamage minus base"] BL["boost × s — linear in the cooldown"] BQ["base × (0.2 + s² × 0.8) — quadratic in the same cooldown"] RESET["Player.onAttack — resets the attack ticker, after the scale was read"] GATE["either term above zero? — otherwise nothing below runs"] ITEM["plus Item.getAttackDamageBonus — the mace's fall bonus lands here"] CRIT["× 1.5 if full strength and Player.canCriticalAttack"] TOTAL["total = that, plus the linear boost"] HURT["Entity.hurtOrSimulate — its boolean gates the knockback, sweep and durability"] S --> BL S --> BQ BOOST --> BL BASE --> BQ BQ --> RESET RESET --> GATE GATE --> ITEM ITEM --> CRIT CRIT --> TOTAL BL --> TOTAL TOTAL --> HURT ``` Read that picture for the two things it makes obvious. **The cooldown is applied twice, differently** — a quadratic ramp on the base damage, a linear one on the enchantment bonus, both from the same scale read with the same 0.5 partial tick. And **the item bonus is inside the crit**, because `Item.getAttackDamageBonus` is added between the sprint check and the multiplication. The gates along the way are as particular as the arithmetic. `Player.cannotAttack` comes first: the target must be attackable and must not claim the interaction for itself. `Player.deflectProjectile` can end the attack outright. Sprint knockback needs the scale above 0.9, plays a sound, and adds a flat **0.5** to the knockback later. `Player.canCriticalAttack` needs falling, not on the ground, not climbing, not in water, not mobility-restricted, not a passenger, **not sprinting**, a `LivingEntity` target — and full strength as well. `Player.isSweepAttack` needs full strength, *not* a crit, *not* sprint knockback, on the ground, moving slower than 2.5× the walking speed, and something in `ItemTags.SWORDS`. If the hit landed, the tail runs in order: `Player.causeExtraKnockback` — which is also where the attacker's own motion is damped and sprinting cancelled, using `LivingEntity.getKnockback` computed from `Attributes.ATTACK_KNOCKBACK` through the enchantments and halved — then `Player.doSweepAttack`, `Player.attackVisualEffects`, `LivingEntity.setLastHurtMob`, `Player.itemAttackInteraction`, `Player.damageStatsAndHearts`, and `Player.causeFoodExhaustion` of 0.1. If it did not land, a no-damage sound. Either way `Player.postPiercingAttack` runs at the end. `Player.itemAttackInteraction` is itself three steps in a particular order: `ItemStack.hurtEnemy` (the item's own hook and the use statistic, *not* durability), then `EnchantmentHelper.doPostAttackEffectsWithItemSource`, then `ItemStack.postHurtEnemy`, which is where `Weapon`'s per-attack durability cost is applied. `Weapon` (`DataComponents.WEAPON`) is a pair: that cost, and `Weapon.disableBlockingForSeconds`, the axe's shield-breaking rule, read back through `LivingEntity.getSecondsToDisableBlocking`. `Player.doSweepAttack` damages every *living* entity in a box around the **primary target** inflated by (1, 0.25, 1), for candidates within three blocks of the **attacker**, excluding the attacker, the primary target, allies and marker armour stands. Each takes `1.0 + Attributes.SWEEPING_DAMAGE_RATIO × base`, run through `Player.getEnchantedDamage` and then scaled by the attack-strength scale, plus a flat 0.4 knockback. Its sweep *sound* is unguarded; the damage and the `ParticleTypes.SWEEP_ATTACK` particles sit behind the server check. `Entity.hurtOrSimulate` is the wrapper that branches on the side — `Entity.hurtServer` on the server, `Entity.hurtClient` on the client. Armour, invulnerability frames, `DataComponents.BLOCKS_ATTACKS` and knockback resistance are all [damage and death](../entities/damage-and-death.md). ## Questions players ask **Why does mashing do less damage?** Because of the quadratic. At half charge the base damage is 0.2 + 0.25 × 0.8 = 40% of full, while the enchantment bonus is at 50%. The vocabulary behind it is small: `LivingEntity.attackStrengthTicker` and `LivingEntity.itemSwapTicker` are declared on `LivingEntity` but read, reset and incremented only from `Player`; `Player.getCurrentItemAttackStrengthDelay` is twenty divided by `Attributes.ATTACK_SPEED`; `Player.resetAttackStrengthTicker` clears both clocks and `Player.resetOnlyAttackStrengthTicker` clears one. `Player.tick` also resets both when the main-hand *item type* changes; what distinguishes the swap ticker is that `Player.onAttack` clears the attack ticker and leaves it alone, because it exists only to drive the held-item swap animation. Both sides then reset twice: once mid-`Player.attack`, and once more after it — the client in `MultiPlayerGameMode.attack`, the server on the swing packet that follows, because `ServerPlayer.swing` resets the ticker too. **Why does my sword make no sound until the server answers?** `ClientLevel.playSeededSound` plays a sound only when the excluded player *is* the local player, and `Player.playServerSideSound` excludes nobody — so every hit sound the attacker hears arrives as a `ClientboundSoundPacket`, one round trip late. **Does the client predict any of this against a mob?** Almost none. `Entity.hurtClient` returns false and neither `LivingEntity` nor `Mob` overrides it, so on the client `Entity.hurtOrSimulate` reports that the hit did not land and the entire block after it is skipped: no predicted knockback, no sweep, no visual effects, no durability, no exhaustion. The exceptions are the eight classes that do override it, and every one of them is something you can hit that is not a mob: another *player* (`RemotePlayer`), a boat or minecart (`VehicleEntity`), a painting or leash knot (`BlockAttachedEntity`), an item frame, an end crystal, a shulker bullet, a dropped item and an experience orb. Against those the whole block runs locally. `Player.getEnchantedDamage` does nothing on `Player` either; it returns its argument unchanged and only `ServerPlayer` overrides it. With `Attributes.ATTACK_DAMAGE` not being client-syncable ([attributes](../entities/attributes.md)), the client's damage figure is never authoritative wherever that block does run. **Can a weapon be too close to swing?** On the client, yes — `AttackRange` has a minimum. On the server, no: the 3.0-block leniency is subtracted from the minimum as well as added to the maximum, so the floor does not survive the round trip. **How does my client know how badly the pig was hurt?** It does not. `ClientboundDamageEventPacket` carries no amount at all — a damage-type holder, three entity ids and an optional source position — and the victim's red flash, hurt sound and invulnerability window are reconstructed from that. Health bars come from [synched entity data](../entities/synched-entity-data.md). **Are sweep and knockback enchantment effects?** They are attributes. `Attributes.SWEEPING_DAMAGE_RATIO` defaults to zero, so a vanilla sweep does 1.0 — scaled by the attack-strength ratio, so slightly less than 1.0 anywhere in the sweep's legal window below full charge. And `Attributes.ATTACK_KNOCKBACK` defaults to zero, so for an unenchanted sword the *entire* attacker-side knockback is the sprint bonus of 0.5. **Why does my swing look different with a different weapon?** Because swing duration is a data component — `ItemStack.getSwingAnimation` returns a `SwingAnimation` — not a constant six ticks; `MobEffects.MINING_FATIGUE` stretches it and haste shortens it. The animation state itself is `LivingEntity.swinging`, `LivingEntity.swingingArm`, `LivingEntity.swingTime` and `LivingEntity.attackAnim`. The swing is not echoed to the swinger: `ServerGamePacketListenerImpl.handleAnimate` broadcasts `ClientboundAnimatePacket` to trackers only — but crit particles *are* sent back, because they go to the trackers of the **attacker** while naming the **victim**. **Is this the only way to hit something in melee?** No. Two other paths end in damage and neither goes through `Player.attack`: a `PiercingWeapon` short-circuits before the hit-result switch, and a `KineticWeapon` is reached from item *use* rather than attack. Both are [the spear](the-spear.md). ## Where to look `Minecraft.startAttack` · `LocalPlayer.raycastHitResult` · `MultiPlayerGameMode.attack` · `ServerboundAttackPacket` · `ServerGamePacketListenerImpl.handleAttack` · `Player.attack` · `Player.baseDamageScaleFactor` · `Player.doSweepAttack` · `Player.itemAttackInteraction` · `AttackRange` · `Weapon` · `ProjectileUtil` · `ClientboundDamageEventPacket` · `SwingAnimation` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The spear > Verified against **Minecraft 26.2** · Part VIII · Two ways to hit something with the same item: jab it, and the client sends no target at all; charge it and run, and the damage comes from how fast the gap is closing. A spear is one item with two weapons in it. Left-click and you **stab**: the client sends a packet with no entity id in it, and the server does its own raycast and hits *everything* along the ray. Hold right-click and you **charge**: the spear becomes an item you are using, like a bow, except that what it does each tick is look for entities in front of you and hurt them in proportion to the closing speed. Neither path goes anywhere near `Player.attack`, the method [the sword swing](the-sword-swing.md) is about, and the second one has a property no other melee attack in the game has — **a charging spear ignores the attack-strength cooldown entirely**, because the code that applies the cooldown curves is skipped for the item you are currently using. ## The cast | class | what it decides | thread | |---|---|---| | `PiercingWeapon` | the stab: who can be hit along a ray, and what a hit does | server main (sounds: both) | | `KineticWeapon` | the charge: three speed conditions, and the damage from closing speed | server main | | `Item.Properties.spear` | the seven spears, and the combat components that make one | — | | `Minecraft` / `MultiPlayerGameMode` | the client's short-circuit, and the packet with no target | client main | | `ServerGamePacketListenerImpl` | `ServerboundPlayerActionPacket.Action.STAB`, and the piercing rejection in the ordinary attack handler | server main | | `LivingEntity.stabAttack` | the shared tail: damage, two knockbacks, dismount, durability | server main | | `Player.stabAttack` | the override that adds the cooldown curves — sometimes | server main | | `SpearUseGoal` / `SpearAttack` | how a zombie or a piglin does the same thing | server main | ## What an item needs to be a spear `Item.Properties.spear` is one builder call per material, and the seven spears — `Items.WOODEN_SPEAR` through `Items.NETHERITE_SPEAR` — differ almost only in the numbers it is given; the wooden one gets its own sounds and the netherite one is additionally fire-resistant. What it attaches is the interesting part, because it is *both* weapons at once plus the reach to use them: | component | what the spear gets | |---|---| | `DataComponents.PIERCING_WEAPON` | knockback yes, dismount no, a use sound and a hit sound | | `DataComponents.KINETIC_WEAPON` | a contact cooldown of ten ticks, a delay, three conditions, and a damage multiplier | | `DataComponents.ATTACK_RANGE` | `AttackRange.minReach` of 2.0 and `AttackRange.maxReach` of 4.5 — 2.0 and 6.5 in creative — with a hitbox margin and a mob factor | | `DataComponents.MINIMUM_ATTACK_CHARGE` | 1.0: no partial-charge stab | | `DataComponents.SWING_ANIMATION` | `SwingAnimationType.STAB`, with a per-material duration | | `DataComponents.DAMAGE_TYPE` | `DamageTypes.SPEAR`, as a delayed holder component | | `DataComponents.USE_EFFECTS` | `UseEffects.canSprint` **true** — the one item that lets you run while using it | | `DataComponents.WEAPON` | a durability cost of one per attack | | attribute modifiers | `Attributes.ATTACK_DAMAGE` from the material, and an `Attributes.ATTACK_SPEED` derived from the swing duration | That `UseEffects` override is why a spear feels unlike every other held-down item: [hunger and experience](hunger-and-experience.md) explains the component, and `LocalPlayer.isSlowDueToUsingItem` is the reader that a spear turns off. ## Two entries, one exit ```mermaid flowchart TD CLICK["left-click: Minecraft.startAttack"] HAS["main hand has PIERCING_WEAPON?"] NORMAL["the ordinary path: MultiPlayerGameMode.attack, then Player.attack"] PA["MultiPlayerGameMode.piercingAttack — plays the sound, resets the ticker locally"] PKT["ServerboundPlayerActionPacket, Action.STAB — no entity id, a dummy position"] SGPL["handlePlayerAction: not a spectator, cannotAttackWithItem with a 5-tick tolerance"] PW["PiercingWeapon.attack — the server's own raycast"] USE["right-click: Item.use sees KINETIC_WEAPON, startUsingItem for 72000 ticks"] TICK["every use tick: ItemStack.onUseTick, server side only"] KW["KineticWeapon.damageEntities — ticksUsed, look vector, closing speed"] RAY["ProjectileUtil.getHitEntitiesAlong — every entity on the ray, filtered by PiercingWeapon.canHitEntity"] STAB["stabAttack — damage, two knockbacks, dismount, durability"] CLICK --> HAS HAS -- "no" --> NORMAL HAS -- "yes" --> PA PA --> PKT PKT --> SGPL SGPL --> PW USE --> TICK TICK --> KW PW --> RAY KW --> RAY RAY --> STAB ``` Two things in that picture are worth stopping on. The **client tells the server nothing about the target** on the stab path: the packet is a `ServerboundPlayerActionPacket` carrying `ServerboundPlayerActionPacket.Action.STAB`, whose block position and direction are dummies, and every question about what was hit is answered by the server's own raycast. And the ordinary attack handler *refuses* a piercing weapon — `ServerGamePacketListenerImpl.handleAttack` checks for `DataComponents.PIERCING_WEAPON` and drops out — so the two paths cannot be confused for one another even by a client that tries. ## The stab `PiercingWeapon.attack` takes the attacker's `Attributes.ATTACK_DAMAGE`, the weapon in the given slot and `LivingEntity.getAttackRangeWith`, and walks `ProjectileUtil.getHitEntitiesAlong` with the block-collider clip context — so a wall stops the ray, but a crowd does not. **Every** entity along it is stabbed — in the order the ray walk happened to append them, which is not sorted by distance — each through `LivingEntity.stabAttack` with the same damage figure. `PiercingWeapon.canHitEntity` is the filter, and it is a projectile-shaped test rather than a melee one: the target must not be `Entity.isInvulnerableToPiercingWeapon`, must be alive, and must satisfy `Entity.canBeHitByProjectile`. Player against player defers to `Player.canHarmPlayer`, and an entity riding the same vehicle as the attacker is not hit. An `Interaction` short-circuits the whole filter to *hittable* before any of those tests, same vehicle included. Afterwards the attacker gets `LivingEntity.onAttack` — which on a `Player` resets the attack-strength ticker — and `LivingEntity.postPiercingAttack`, the hook that runs `EnchantmentHelper.doPostPiercingAttackEffects`; only then does the weapon play `PiercingWeapon.makeHitSound` if anything was hit and `PiercingWeapon.makeSound` regardless, and swing the arm. The client half did the swing and `LivingEntity.onAttack` itself a round trip earlier, but not `LivingEntity.postPiercingAttack`, which does nothing off a `ServerLevel`. ## The charge A kinetic weapon is *used*, not swung. `Item.use` sees `DataComponents.KINETIC_WEAPON`, calls `LivingEntity.startUsingItem` and plays the sound; `Item.getUseDuration` returns **72000** for it, the same effectively-endless duration a bow gets, so the charge ends only when you release ([using an item](../items/using-an-item.md)). Starting also allocates `LivingEntity.recentKineticEnemies`, a server-side map of who has been hit and when, which `LivingEntity.stopUsingItem` throws away. Each use tick, `ItemStack.onUseTick` diverts to `KineticWeapon.damageEntities` — **and skips the item's own `Item.onUseTick` when it does**. What that method computes is a speed argument, not a swing: - **How long you have been charging.** Ticks used must be at least `KineticWeapon.delayTicks`; everything below is measured from there. - **How fast you are going, along your look vector.** `KineticWeapon.getMotion` reads `Entity.getKnownSpeed` — the *reported* movement from [input to movement](input-to-movement.md) — scaled to blocks per second, taking the **root vehicle's** motion for a non-player passenger. - **How fast the gap is closing.** The target's own projected speed is subtracted, floored at zero, and that relative speed is what the damage is built from. - **Whether you already hit them.** `LivingEntity.wasRecentlyStabbed` against `KineticWeapon.contactCooldownTicks` — ten for a spear — is why running through a crowd does not hit the same mob every tick. Three independent `KineticWeapon.Condition`s then decide what the hit *is*: `KineticWeapon.dismountConditions`, `KineticWeapon.knockbackConditions` and `KineticWeapon.damageConditions`, each a maximum duration and a speed bar — measured against the attacker's own projected speed for the first two, and against the closing speed for damage. A spear's three come from the builder with different windows, and for all seven materials they nest the same way: damage has the longest window and the lowest bar, dismount the shortest window and much the highest. So a charge that has run too long can still hurt when it can no longer knock a target off a horse — a wooden spear dismounts for five seconds, knocks back for ten and damages for fifteen. If any of the three passes, the damage is the attacker's **base** `Attributes.ATTACK_DAMAGE` plus the floor of relative speed × `KineticWeapon.damageMultiplier` — base value, so the modifiers a sword swing would pick up are not in it. A landed charge broadcasts an entity event, and it is the part of the telling that is *about the charge*, alongside the ordinary damage sync, knockback and durability every hit sends: `LivingEntity.onKineticHit` plays a local hit sound, throttled to ten ticks by a bare literal in `LivingEntity.onKineticHit` — the `KineticWeapon.HIT_FEEDBACK_TICKS` constant that names that number is read by nothing — and `LivingEntity.getTicksSinceLastKineticHitFeedback` feeds the animation. A `ServerPlayer` also trips `CriteriaTriggers.SPEAR_MOBS_TRIGGER` with the number of living entities stabbed this charge. ## The tail, and the cooldown that is not applied Both paths end in a method called *stabAttack*, which exists twice. `LivingEntity.stabAttack` is the general one: it returns false off a `ServerLevel`, runs the damage through `EnchantmentHelper.modifyDamage`, calls `Entity.hurtServer`, applies two knockbacks — a flat one and `LivingEntity.getKnockback` — dismounts the target if the caller asked, runs `ItemStack.hurtEnemy` and the post-attack enchantment effects, and plays the attack sound. `Player.stabAttack` overrides it, and the override is where the spear becomes strange. It computes the enchantment boost the way `Player.attack` does, and then applies the two cooldown curves — the linear one to the boost, the quadratic `Player.baseDamageScaleFactor` to the base — **only if the player is not currently using an item in that slot.** A stab qualifies, so a stab is charged like a sword swing. A kinetic charge does not: while you are holding the spear out, both curves are skipped and every tick's hit lands at full base damage. The rest of the override is the familiar tail — `Player.deflectProjectile` can still end it, the knockbacks are the same two, `Player.itemAttackInteraction` applies the durability cost, and `Player.causeFoodExhaustion` charges the same 0.1 a sword does. ## Questions players ask **Why does the server never ask which mob I stabbed?** Because it does not trust the answer and does not need it. The stab packet is an action, not a target: the server raycasts from the player's own look vector with the weapon's `AttackRange`, and hits everything on the line. That also puts the stab in the small company of melee attacks whose hit count is not one, beside the sword's sweep and the spear's own charge. **Does a spear work while I am moving?** It is the only weapon that *requires* it. The charge's damage is built from closing speed, and the `UseEffects` override exists so you can sprint while charging — the two halves of the same design. **Why did my charge stop hurting the same mob?** `KineticWeapon.contactCooldownTicks` remembers it for ten ticks. The map is allocated when you start using the spear and dropped when you stop, so releasing and re-charging clears everyone. **Can a mob do this?** Yes, both ways round. `SpearUseGoal` drives the charge for a goal-based mob and `SpearApproach`, `SpearAttack` and `SpearRetreat` do it for a brain-based one — zombies, zombified piglins and piglins are the users in the tree, and `Piglin` treats a kinetic weapon like a crossbow when deciding what it is holding. Both read `KineticWeapon.computeDamageUseDuration` to know how long to hold it. The thresholds are easier for them: the speed conditions are scaled by an action factor of **0.2** for anything that is not a player, against 1.0 for you. **Is any of this data-driven?** The shape is; the values are not. `PiercingWeapon` and `KineticWeapon` are ordinary data components with codecs and stream codecs, so a data pack can describe a weapon without code — but every built-in spear's numbers are hard-coded in `Item.Properties.spear`, not read from JSON. One field is not a combat number at all: `KineticWeapon.forwardMovement` — 0.38 for a spear — is read **only** by `SpearAnimations`, and only by its *third-person* methods, which is a rendering offset living in the middle of a combat component. ## Where to look `PiercingWeapon` · `KineticWeapon` · `KineticWeapon.Condition` · `Item.Properties.spear` · `Minecraft.startAttack` · `MultiPlayerGameMode.piercingAttack` · `ServerboundPlayerActionPacket` · `LivingEntity.stabAttack` · `Player.stabAttack` · `LivingEntity.recentKineticEnemies` · `ProjectileUtil.getHitEntitiesAlong` · `SpearUseGoal` · `SpearAnimations` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Hunger and experience > Verified against **Minecraft 26.2** · Part VIII · Two bars above the hotbar that the server owns outright: one you empty by sprinting, one you fill by mining — and they meet in the enchanting table. The food bar and the experience bar look like the same kind of thing: a number the server keeps and sends you. They are, and they are also the two counters a player argues with most — the meal that does not seem to fill you, the levels that vanish into an anvil. Both are worth following for the same reason: neither is quite what the interface suggests. **There is no method called *eat*** on `Player` or `LivingEntity` — eating is a walk over the components of a stack that happens to end in `FoodData.eat` — and the experience packet is change-detected on the **total** alone, so every mutation that changes only your level has to poison the last-sent value or the bar will not move. ## The cast | class | what it decides | thread | |---|---|---| | `FoodData` | the food bar, saturation and exhaustion — and by type, only on the server | server main | | `FoodProperties` | how much a given item is worth, as a data component | both | | `Consumable` | how long eating takes, what it sounds like, and what else it applies | both | | `Player` | the four experience fields, the level curve, and the enchanting seed | both | | `ExperienceOrb` | a value and a multiplicity, wandering toward you | both | | `ServerPlayer` | the change detection that turns any of this into a packet | server main | Both halves hang off `ServerPlayer.doTick`, the connection-driven half of [the two-phase tick](the-two-phase-tick.md); the level's entity tick touches essentially none of it. The order inside that half matters: item use is resolved, then `Player.aiStep` runs `ServerPlayer.tickRegeneration` — which *is* the Peaceful refill, its whole body gated on that difficulty — and the orb pickup, then **`FoodData.tick`**, and then the change-detection block that emits the packets. So a meal eaten this tick and a Hunger effect's exhaustion from this tick are both visible to `FoodData.tick` in the same tick, and the resulting health and food reach the client in that tick too. ## The food bar is four numbers and a pile of literals **`FoodData`** (`world/food`) is a value bag with no back-reference to the player: `FoodData.foodLevel` (20), `FoodData.saturationLevel` (5.0), `FoodData.exhaustionLevel` and `FoodData.tickTimer`. Its surface is the two `FoodData.eat` overloads — one taking a `FoodProperties`, one taking a nutrition and saturation pair — plus `FoodData.addExhaustion` (which caps at 40), `FoodData.needsFood`, `FoodData.hasEnoughFood`, `FoodData.setFoodLevel`, `FoodData.setSaturation`, the two accessors, and `FoodData.tick` — whose signature is `FoodData.tick(ServerPlayer)`, **server-only by type**. It saves as four loose keys in the player tag, not a sub-compound. **`FoodConstants`** names every threshold in the system — `FoodConstants.MAX_FOOD`, `FoodConstants.HEAL_LEVEL`, `FoodConstants.HEALTH_TICK_COUNT`, `FoodConstants.HEALTH_TICK_COUNT_SATURATED`, `FoodConstants.EXHAUSTION_DROP`, `FoodConstants.EXHAUSTION_HEAL`, `FoodConstants.EXHAUSTION_SPRINT`, `FoodConstants.EXHAUSTION_MINE`, `FoodConstants.EXHAUSTION_ATTACK`, `FoodConstants.SPRINT_LEVEL`, `FoodConstants.SATURATION_FLOOR` and the saturation-quality ladder from `FoodConstants.FOOD_SATURATION_POOR` to `FoodConstants.FOOD_SATURATION_SUPERNATURAL` — and **none of them is referenced by anything.** Only `FoodConstants.saturationByModifier` has call sites; `FoodData` writes every threshold as an inline literal, so the constants file and the behaviour can drift apart without a compile error. What `FoodData.tick` does with those literals is one exhaustion rule and a three-way mutually exclusive chain: ```mermaid flowchart TD EX["exhaustion above 4.0?"] EX -- "yes" --> DRAIN["spend 4.0 of exhaustion, and take 1.0 off saturation — or one off the food bar once saturation is spent and the difficulty is not Peaceful"] EX -- "no" --> CHAIN DRAIN --> CHAIN["then at most one of the three"] CHAIN --> FAST["heal fast: every 10 ticks, at a full bar, hurt, with saturation left and the game rule on — the heal is funded by that saturation"] CHAIN --> SLOW["heal slowly: every 80 ticks, at 18 or more food, hurt, and the game rule on"] CHAIN --> STARVE["starve: every 80 ticks at zero food, and not gated on the game rule at all"] ``` The game rule is `GameRules.NATURAL_HEALTH_REGENERATION`, which lives in `world/level/gamerules` with typed `GameRule` lookups ([level data and rules](../../reference/level-data-and-rules.md)). The starvation hit only lands if health is above five hearts, or above half a heart on Normal, or unconditionally on Hard: five hearts is the floor on Easy and Peaceful, half a heart on Normal, and death on Hard. `DamageTypes.STARVE` is declared with zero exhaustion, so starving does not feed itself. ## Eating is a component walk **`FoodProperties`** (`DataComponents.FOOD`) is three things: nutrition, saturation and *can always eat*. The duration is not on it — that lives on **`Consumable`** (`DataComponents.CONSUMABLE`), which owns `Consumable.consumeSeconds` (with `Consumable.consumeTicks` derived from it), the `ItemUseAnimation`, the sound, the particles and a list of `ConsumeEffect`s: `ApplyStatusEffectsConsumeEffect`, `RemoveStatusEffectsConsumeEffect`, `ClearAllStatusEffectsConsumeEffect`, `TeleportRandomlyConsumeEffect`, `PlaySoundConsumeEffect`. `FoodProperties` reaches the player by implementing **`ConsumableListener`**, and `Consumable.onConsume` walks every component of that type on the stack. It is not the only implementation — `PotionContents`, `SuspiciousStewEffects` and `OminousBottleAmplifier` implement it too, and `PotionContents` is how drinking applies an effect, which is where this page and [status effects](status-effects.md) meet in one method. Two routes reach `FoodData.eat` without any of that: `CakeBlock.eat`, and the saturation effect, both using the raw nutrition-and-saturation overload. ```mermaid sequenceDiagram participant LE as LivingEntity participant IStack as ItemStack participant Cons as Consumable participant FP as FoodProperties participant FD as FoodData participant SP as ServerPlayer participant CPL as ClientPacketListener LE->>LE: updateUsingItem — the zero check is server side only SP->>CPL: ClientboundEntityEventPacket(9) — sent first, so the client replays the meal LE->>IStack: finishUsingItem — the item decides what finishing means IStack->>Cons: onConsume — walks every ConsumableListener on the stack Cons->>FP: onConsume — the food component is one such listener FP->>FD: eat — nutrition and pre-multiplied saturation, clamped Cons->>Cons: onConsumeEffects — server only#59; then consume(1) SP->>FD: tick — exhaustion drain, then regen or starvation SP->>CPL: ClientboundSetHealthPacket — when health, food or zero-saturation changed ``` `Consumable.canConsume` consults `Player.canEat` only when the stack has `DataComponents.FOOD` and the user is a player — potions and milk are ungated — and `Player.canEat` itself passes for *invulnerable abilities*, *can always eat*, or `FoodData.needsFood`. An item whose `Consumable.consumeTicks` is zero is consumed instantly with no animation. After the food lands, `ItemStack.finishUsingItem` applies `DataComponents.USE_REMAINDER` and `DataComponents.USE_COOLDOWN` ([using an item](../items/using-an-item.md)). The *decision* to finish is server-only, but the client replays the meal: the server announces it with an entity event, `Player.handleEntityEvent` turns that back into `LivingEntity.completeUsingItem`, and the client therefore runs `FoodProperties.onConsume` and its `FoodData.eat` locally, with no side guard on it. `ClientPacketListener.handleSetHealth` then overwrites food and saturation outright, and routes health through `LocalPlayer.hurtTo`, which works out the delta first so the damage flash still plays. The client also *reads* its food data for two decisions of its own: sprinting is gated on having more than six food *or* being able to fly, and the HUD's food-bar jitter reads saturation. Eating slowdown is a third component again. **`UseEffects`** (`DataComponents.USE_EFFECTS`) is on *every* item — `UseEffects.canSprint`, `UseEffects.interactVibrations`, `UseEffects.speedMultiplier` — and its slowdown half is genuinely client-side: `LocalPlayer.isSlowDueToUsingItem` and `LocalPlayer.itemUseSpeedMultiplier` are its only readers, which is how a spear overrides the component and lets you sprint while charging ([the spear](the-spear.md)). The vibration half is not client-side: `ItemStack.causeUseVibration` reads the same component server-side to decide whether using an item emits a game event. ## The other bar `Player.experienceLevel`, `Player.experienceProgress`, `Player.totalExperience` and `Player.enchantmentSeed`, plus `Player.takeXpDelay` and `Player.lastLevelUpTime` — which exists only to throttle the level-up sound — are the whole of it. The arithmetic is `Player.giveExperiencePoints`, `Player.giveExperienceLevels` and `Player.getXpNeededForNextLevel` — the three-segment curve with corners at levels 15 and 30. Where orbs come from is worth naming, because two game rules gate it: `LivingEntity.dropExperience` requires the experience not to have been consumed already, and either an always-dropper or a recent player kill with `GameRules.MOB_DROPS` on and the entity's own `LivingEntity.shouldDropExperience` agreeing; a player's own death drop is `Player.getBaseExperienceReward`, seven per level capped at 100, unless `GameRules.KEEP_INVENTORY` — or unless you are a spectator, which drops nothing. **`ExperienceOrb`** is an `Entity` with `ExperienceOrb.DATA_VALUE` synched and `ExperienceOrb.count`, `ExperienceOrb.age`, `ExperienceOrb.health` and `ExperienceOrb.followingPlayer` unsynched — though `ExperienceOrb.age` and `ExperienceOrb.followingPlayer` are still mutated by the client's own tick, which runs the follow behaviour locally. `ExperienceOrb.health` is not: only `ExperienceOrb.hurtServer` ever writes it. `ExperienceOrb.awardWithDirection` splits an amount into denominations via `ExperienceOrb.getExperienceValue` (a fixed ladder from 2477 down to 1) and calls `ExperienceOrb.tryMergeToExisting` for each; `ExperienceOrb.award` is a one-line delegate to it. Merging is by **count, not value**: an orb carries one value and a multiplicity. The merge candidate search picks a *random* group number below `ExperienceOrb.ORB_GROUPS_PER_AREA` and only merges into orbs whose id is congruent to it — which caps how many orbs collapse into one entity rather than reducing the scan. ## Questions players ask **Why does walking cost nothing?** Because it is multiplied by zero, out loud: `ServerPlayer.checkMovementStatistics` multiplies distance by a literal zero on both the walking and the crouching branch, while `FoodConstants.EXHAUSTION_WALK` documents the intent and is referenced by nothing. The exhaustion economy is sprinting, jumping, swimming, mining, attacking, the Hunger effect, the `ApplyExhaustion` enchantment effect, and being hurt — the last of which is data-driven, since `DamageSource.getFoodExhaustion` reads the damage type. And in creative or spectator the whole economy is disabled in one line: `Player.causeFoodExhaustion` returns immediately for invulnerable abilities. **Why does my saturation sit above my food bar on Peaceful?** `ServerPlayer.tickRegeneration` raises saturation directly toward 20, while `FoodData.eat` clamps saturation to the food level. Only one of the two respects the clamp. **Why does the saturation shown by the HUD lag?** Because it is sent but not change-detected. `ClientboundSetHealthPacket` carries a full float, yet the server only notices whether saturation became *zero* — so your client's saturation does not update until health or food moves. **Why does enchanting update the bar when the level packet is change-detected on the total?** Because the code forces it. `ServerPlayer.lastSentExp` is compared against `Player.totalExperience`, so every mutation that changes only the level — `ServerPlayer.setExperienceLevels`, `Player.giveExperienceLevels`, enchanting, respawn — has to poison the last-sent value to make the packet go out. **Why does an orb repair my pickaxe before it reaches my bar?** Because `ExperienceOrb.playerTouch` runs `ExperienceOrb.repairPlayerItems` first and only the remainder becomes experience — and that method calls itself with the leftover. One orb entity can also be picked up many times: it carries a count, decremented per touch behind a two-tick `Player.takeXpDelay`, and a player absorbs **one orb per tick**, chosen at random from those it is touching. The pickup sweep buckets orbs separately from items for exactly that purpose. **Why is the Standard Galactic gibberish stable until I enchant?** Because `Player.onEnchantmentPerformed` subtracts the level cost *and* re-rolls `Player.enchantmentSeed`, while `AnvilMenu` — which also spends levels, through `Player.giveExperienceLevels` — does not. A seed that loads back as zero is re-rolled on read. [Enchanting](../items/enchanting.md) owns what the seed is for. **What crosses the wire?** `ClientboundSetHealthPacket` (health, food and saturation together, to that player only), `ClientboundSetExperiencePacket` (progress, level and total), and `ClientboundTakeItemEntityPacket` for the orb pickup animation. The data-driven side is `DataComponents.FOOD`, `DataComponents.CONSUMABLE`, `DataComponents.USE_EFFECTS`, `Registries.CONSUME_EFFECT_TYPE`, `EnchantmentEffectComponents.REPAIR_WITH_XP` for mending, and the three game rules above. ## Where to look `FoodData` · `FoodConstants` · `FoodProperties` · `Consumable` · `ConsumableListener` · `ConsumeEffect` · `UseEffects` · `Foods` · `Player.giveExperiencePoints` · `ExperienceOrb` · `ServerPlayer.tickRegeneration` · `ClientboundSetHealthPacket` · `ClientboundSetExperiencePacket` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Status effects > Verified against **Minecraft 26.2** · Part VIII · You drink a potion of Poison: the server starts hurting you on a rhythm, and your client never runs a single one of the effect's hooks. Poison II lands. Your health starts dropping in steps, the swirls appear, the icon in the corner counts down, and if the connection stutters the number in the corner keeps counting anyway. That last part is the whole page. **The client never runs a `MobEffect` hook.** It counts durations down, advances a blend factor, unhides a masked effect and spawns particles from a list the server synched. It does *read* the effects it holds — jump boost in `LivingEntity.getJumpBoostPower`, slow falling in `LivingEntity.getEffectiveGravity`, levitation inside `LivingEntity.travel` — because that is shared movement code your own player runs unguarded. But every attribute modifier, every pulse of damage, every regeneration tick happens on the server behind an explicit server-side guard, and the client's copy of the duration is corrected by a re-send every six hundred ticks. An **infinite** effect is never re-sent at all, because its duration is −1 and −1 never satisfies the test. ## The cast | class | what it decides | thread | |---|---|---| | `MobEffect` | what the effect *does*, and on what rhythm | server main (the client reads only its colour and blend durations) | | `MobEffectInstance` | duration, amplifier, flags, and the masked effect underneath | both main threads | | `MobEffects` | the forty built-in holders | — | | `LivingEntity` | `LivingEntity.activeEffects`, the tick, and the three server-guarded hooks | both | | `AttributeInstance` | where an effect's modifier actually lands | server main | | `ServerPlayer` | who gets told, and how often | server main | | `MobEffectUtil` | the questions the rest of the game asks about effects | both | ## What an effect is **`MobEffect`** is the behaviour singleton: a category, a colour, a particle factory, blend durations and a map of `MobEffect.AttributeTemplate`s. Its hooks are the interesting part, because one of them is false by default. | hook | when it runs | |---|---| | `MobEffect.shouldApplyEffectTickThisTick` | every tick, to ask whether this is a pulse — **false by default** | | `MobEffect.applyEffectTick` | on a pulse; returning false ends the effect | | `MobEffect.applyInstantaneousEffect` | never from the tick — only from the splash potion, the lingering cloud and the drink | | `MobEffect.onEffectAdded` | when it lands on an entity that did not already have it | | `MobEffect.onEffectStarted` | on every successful add, a refresh of an existing effect included | | `MobEffect.onMobHurt` | when the holder takes damage | | `MobEffect.onMobRemoved` | when the holder goes | The default *false* is why each effect has its own rhythm: the overrides give poison a pulse every *25 ≫ amplifier* ticks, regeneration every *50 ≫ amplifier*, wither every *40 ≫ amplifier*, and hunger every tick. Attribute modifiers go on as `AttributeInstance.addPermanentModifier` with an amount linear in amplifier + 1, computed by `MobEffect.AttributeTemplate.create` ([attributes](../entities/attributes.md)). **`MobEffectInstance`** is the per-entity half: duration, amplifier, the ambient, visible and show-icon flags, a private blend state, and **`MobEffectInstance.hiddenEffect`** — the stack that lets a stronger, shorter effect temporarily mask a weaker, longer one, built by `MobEffectInstance.update`. `MobEffectInstance.INFINITE_DURATION` is −1, and `MobEffectInstance.compareTo` is what orders the icons in the HUD. `LivingEntity.canBeAffected` is the veto, and it consults three entity tags as well as the effect itself. **`MobEffects`** is forty entries, and every one is a `Holder`, not a bare `MobEffect` — including `MobEffects.BREATH_OF_THE_NAUTILUS`. Some point at attributes a reader would not expect: `MobEffects.JUMP_BOOST` modifies `Attributes.SAFE_FALL_DISTANCE`, and `MobEffects.INVISIBILITY` modifies `Attributes.WAYPOINT_TRANSMIT_RANGE`. On the entity itself: `LivingEntity.activeEffects` (a plain unordered map), `LivingEntity.effectsDirty`, and two synched values — `LivingEntity.DATA_EFFECT_PARTICLES`, which is a **list of `ParticleOptions`** rather than a packed colour, and `LivingEntity.DATA_EFFECT_AMBIENCE_ID`. `MobEffectUtil` is the shared question-asking surface: `MobEffectUtil.hasDigSpeed`, `MobEffectUtil.hasWaterBreathing`, `MobEffectUtil.shouldEffectsRefillAirsupply`, `MobEffectUtil.addEffectToPlayersAround` and the duration formatter the inventory screen uses. ## The trace: Poison II, on both sides at once Effects are ticked from `LivingEntity.tickEffects`, the last call `LivingEntity.baseTick` makes before it copies this tick's rotations into last tick's — which for a player means inside `ServerPlayer.doTick`, the connection-driven half of [the two-phase tick](the-two-phase-tick.md), not the level's entity tick. ```mermaid sequenceDiagram participant LE as LivingEntity participant MEI as MobEffectInstance participant ME as MobEffect participant AttrI as AttributeInstance participant SP as ServerPlayer participant CPL as ClientPacketListener LE->>MEI: update — masks any weaker instance as hiddenEffect LE->>ME: addAttributeModifiers — from LivingEntity.onEffectAdded, server-guarded ME->>AttrI: addPermanentModifier — amount linear in amplifier + 1 SP->>CPL: ClientboundUpdateMobEffectPacket — amplifier, duration, four flag bits Note over LE: every tick after this LE->>MEI: tickServer — count down, and ask for a pulse MEI->>ME: shouldApplyEffectTickThisTick — every 25 ≫ amplifier for poison MEI->>ME: applyEffectTick — the pulse itself, and false here ends the effect Note over LE: the client, in its own tick LE->>MEI: tickClient — count down, unhide, advance the blend ``` The client branch of `LivingEntity.tickEffects` never calls `MobEffect.applyEffectTick` and never touches an attribute. It does not even remove an expired effect: it keeps a zero-duration instance until told otherwise. ## Questions players ask **Why does my duration sometimes jump?** Because it was wrong and got corrected. All of `LivingEntity.onEffectAdded`, `LivingEntity.onEffectUpdated` and `LivingEntity.onEffectsRemoved` are server-guarded, so no attribute modifier is ever applied client-side and attribute values arrive by their own sync; the client's *duration* is a local countdown that drifts, re-sent every six hundred ticks. Two holes in that: an **infinite** effect's duration is −1 and never satisfies the re-send test, so it is never re-sent, and the re-send only ever reaches the affected player or a player riding them. **Why can I not see how long a mob's effect has left?** Because you were never told. A client watching a mob it is not riding receives no `MobEffectInstance` at all — only `LivingEntity.DATA_EFFECT_PARTICLES`, the synched particle list, which is why other entities have swirls and no numbers. **Where did my weaker effect go?** Under the stronger one. `MobEffectInstance.hiddenEffect` is a stack, and both of its codecs are recursive, so both the save and the wire *can* carry the chain. The packet does not: **`ClientboundUpdateMobEffectPacket` never uses that stream codec at all**, writing an entity id, a `MobEffect` holder, an amplifier, a duration and a flags byte by hand — so the client rebuilds an instance with no hidden effect under it, and learns about the masked one only when `MobEffectInstance.downgradeToHiddenEffect` surfaces it and triggers a re-send. **Why does Nausea swim in and out, but Poison just starts?** Blending is a pure render quantity: `MobEffectInstance`'s blend state ticks only on the client, is never saved and never sent, and only `MobEffects.NAUSEA` and `MobEffects.DARKNESS` use it. The blend *bit* is set only when an effect is first added — an update clears it, and the client responds by skipping the blend. **Why are a beacon's swirls so faint?** Twice over. The default particle factory bakes ambience into the `ParticleOptions` itself — alpha 38 of 255 instead of opaque — so an ambient effect really does synch a different, fainter particle; and it also makes them rarer, because the client spawns one particle from the synched list with a probability that an invisible entity divides by about four and a further five when **every** effect on the entity is ambient, which is what `LivingEntity.DATA_EFFECT_AMBIENCE_ID` records. **Does an effect pulse on its own clock or the world's?** Both, depending on whether it ends. `MobEffectInstance.tickServer` counts an infinite-duration effect's pulses off the entity's age and a finite one off its own countdown. **What happens when one effect adds another?** The rest of that tick's effects are silently skipped. `LivingEntity.tickEffects` catches a concurrent-modification error and drops it, so an effect that adds or removes another quietly aborts the loop it was in. **What crosses the wire?** `ClientboundUpdateMobEffectPacket` and `ClientboundRemoveMobEffectPacket` for the effects you hold, and `LivingEntity.DATA_EFFECT_PARTICLES` through [synched entity data](../entities/synched-entity-data.md) for everyone else's swirls. Effects themselves are code, registered into `BuiltInRegistries.MOB_EFFECT` by `MobEffects` with no JSON behind them; what is data-driven is the ways they land — `PotionContents`, `SuspiciousStewEffects`, `ApplyStatusEffectsConsumeEffect` and its siblings — are [using an item](../items/using-an-item.md) and [hunger and experience](hunger-and-experience.md). ## Where to look `MobEffect` · `MobEffectInstance` · `MobEffects` · `MobEffectCategory` · `MobEffectUtil` · `LivingEntity.tickEffects` · `LivingEntity.activeEffects` · `LivingEntity.canBeAffected` · `ClientboundUpdateMobEffectPacket` · `ClientboundRemoveMobEffectPacket` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # IX · Networking > Verified against **Minecraft 26.2** · Part IX · One socket, four languages, and everything the two halves of the game say to each other across it. Almost every part before this one had a single machine to describe. This one has two, and a wire between them that neither trusts. Singleplayer runs the same wire — an integrated server on its own threads, talking to the client through an in-memory channel — so the split is not a multiplayer feature bolted on the side, it is the shape of the program. A player recognises the part by its failures: the rubber-band after a laggy jump, the block that comes back, the *Connection lost* screen with a reason string on it, the chat message that arrives with a grey bar down its left edge. ## The shape of the part Part IX is **one wire and three passengers**. Two lectures carry bytes and are really one lecture in two halves — the transport, then what travels on it. The other three are unrelated systems the wire carries or is, and each has a different shape: a state machine, a policy, and a protocol written against an adversary. ```mermaid flowchart TD TC["The connection — bytes to a handler call, and back"] PSC["Packets and stream codecs — what the thing crossing the wire is"] PP["Protocol phases — one socket, four languages in turn"] WCT["What the client is told — the server's choosing"] CS["Chat and signing — a message that has to prove who sent it"] TC -- "one round trip, two threads" --> PSC PSC -- "a different codec table per phase" --> PP PP --> WCT PP --> CS ``` The spine is the pair at the top: read them together and the rest of the part is applications. *Protocol phases* is what the wire *is* over the life of a connection; *what the client is told* and *chat and signing* are the two the part spends longest on, and neither needs the other. They are not the same size of traffic: entity tracking and chunk sending are most of what a connection ever carries, and chat is a few packets a minute. ## Before you start [Part III](../server/README.md), and not optionally. Two of this part's claims are really facts about somebody else's loop, and Part IX states the consequence and links rather than teaching them a third time: [the server tick](../server/server-tick.md) owns what happens after every level has ticked, and [the level tick](../server/server-level-tick.md) owns the phase in which broadcasts go out — before the entity phase, which is why one broadcast carries this tick's block changes but the *previous* tick's entity movement. [Part I's anatomy](../anatomy/anatomy.md) for the two-loops figure: the client's frame loop and the server's tick loop are different clocks, and the client drains its inbound packets once per **frame**, not once per tick. That one fact is behind half of what looks like network jitter. Part X's *client loop* is the deeper version of it, and this part does not wait for that page: where the arithmetic matters, the pages here state the consequence and link forward. Then [Part II](../foundations/README.md) for two objects this part assumes whole: [codecs](../foundations/codecs-nbt-json.md), because a packet codec is the same idea specialised to a byte buffer, and [components](../foundations/text-components.md), because a chat message is one. And [authority](../entities/authority.md) from Part VI, which is the premise under *what the client is told*: the server does not send the client the truth, it sends the client what it is not allowed to be wrong about. ## Watch in this order 1. [The connection](the-connection.md) — bytes land on a socket, and some milliseconds later a method runs on the game thread. The lecture with the round-trip diagram: two threads, two codec layers, one hop. 2. [Packets and stream codecs](packets-and-stream-codecs.md) — the second half of the same lecture. What the thing crossing the wire is, now that you have watched it travel. 3. [Protocol phases](protocol-phases.md) — a login, from clicking a server in the list to standing in the world. Four languages over one socket, and the player object built *after* the configuration task named for preparing it has reported itself finished. 4. [What the client is told](what-the-client-is-told.md) — a creeper walks into view. Not a trace but a policy: every gate a change passes before it becomes a packet, and the things the server decides never to say. 5. [Chat and signing](chat-and-signing.md) — the part's closer, and the only system in the book designed against an adversary. What each check catches, and whether it kills the message, the chain, or the connection. One and two are the pair to keep together. Four and five can be watched in either order, and neither strictly needs three: they are applications of the play phase and never name the phase machinery. Three comes first because it is where the wire's own story ends. ## Reference this part uses [Packets](../../reference/packets.md) above all — the catalogue this part narrates, and the page to keep open beside every lecture in it. [Registries](../../reference/registries.md) for the registry data that crosses during configuration, [components](../../reference/components.md) where a packet carries a stack, and [diagram lanes](../../reference/lanes.md) for the abbreviations these pages' figures use. [The threads](../../reference/threads.md) names the Netty event loop and the two game threads by their real names. Where the part stops: what the *client* does with what it is told is [the client level](../client/the-client-level.md) and [prediction and acknowledgement](../client/prediction-and-acks.md) in Part X, and how a `ServerPlayer` comes to exist at all is [players and sessions](../server/players-and-sessions.md) in Part III. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The connection > Verified against **Minecraft 26.2** · Part IX · you swing at a pig and the server answers: one round trip, from a value on one thread to bytes on a wire to a method call on another. You swing. A small immutable value is handed to `Connection.send` on the client's main thread, and some milliseconds later a method runs on the server's game thread with that value as its argument; the server's answer makes the same trip in reverse. Now close the server list and open a singleplayer world. Every sentence above is still true. The integrated server is another thread in the same process, and the client reaches it through a real Netty channel with a real `PacketEncoder` and a real `PacketDecoder` in it: **singleplayer serialises every packet to bytes and parses them back again.** The local pipeline swaps the length-prefix framing for an in-memory hand-off and never installs a cipher or a compressor, and that is the whole of the difference. There is no local shortcut: the same encoder runs, the same decoder runs, and the value your click produced is rebuilt from bytes on the other side. ## The cast | class | what it decides | thread | |---|---|---| | `Connection` | the channel, the current `PacketListener`, and when a fault kills the link | Netty event loop, plus one call a tick from a game thread | | `PacketEncoder` | one packet becomes the bytes of one frame, or is skipped | the sender's Netty event loop | | `PacketDecoder` | one frame becomes one packet, and a terminal one dismantles the codec | the receiver's Netty event loop | | `PacketProcessor` | the queue that carries a decoded packet to a game thread | filled from Netty, drained by its owner | | `PacketListener` | whether this packet should be handled at all — asked twice | both | | `TickablePacketListener` | the only way a listener gets time when no packet has arrived | a game thread | | `ServerConnectionListener` | the server's accept side, and which connections get ticked | server main | | `EventLoopGroupHolder` | NIO, Epoll, KQueue or in-memory, and the threads that run them | any | ## One packet there, and one back ```mermaid sequenceDiagram participant CPL as ClientPacketListener participant Conn as Connection participant PEnc as PacketEncoder participant Wire as the network participant PDec as PacketDecoder participant SGPL as ServerGamePacketListenerImpl Note over Conn,PDec: one instance of each of these at each end Note over CPL: client main thread — a value, not yet any bytes CPL->>Conn: send — no packet queue, Netty owns the buffering Note over Conn,PEnc: the sender's Netty event loop, joined by sendPacket Conn->>PEnc: write, or write and flush PEnc->>Wire: the phase's one codec writes a VarInt id, then the fields Note over Wire: compress, prepender, encrypt, then decrypt, splitter, decompress Note over PDec,SGPL: the receiver's Netty event loop Wire->>PDec: exactly one whole frame PDec->>Conn: channelRead0, at the tail of the pipeline Conn->>SGPL: shouldHandleMessage, then Packet.handle SGPL->>SGPL: ensureRunningOnSameThread queues the pair and aborts the call Note over SGPL: server main thread — processQueuedPackets, before the tick SGPL->>SGPL: shouldHandleMessage again, then the handler from the top SGPL->>Conn: send the reply — written, not flushed, inside the tick's bracket Note over Conn: the connection phase flushes the channel Conn->>PEnc: the same handlers, the other direction PEnc->>Wire: clientbound bytes Wire->>PDec: one frame, on the client's Netty event loop PDec->>Conn: channelRead0 again Conn->>CPL: shouldHandleMessage, then Packet.handle CPL->>CPL: ensureRunningOnSameThread queues the pair and aborts the call Note over CPL: client main thread — the drain, once per frame CPL->>CPL: the handler from the top, a frame later at the earliest ``` Four things in that picture are worth stopping on: the framing, the hop, the drain, and the fact that `Connection` appears once but exists twice. **Framing is separate from decoding.** `Varint21FrameDecoder` reads at most three length bytes, refuses anything wider and refuses a zero length, and emits exactly one frame or nothing at all. Everything downstream of it may assume it is looking at one whole packet, which is why the codec layer never has to handle a half-arrived value. **`Connection.channelRead0` calls `Packet.handle` directly, on the Netty thread.** There is no automatic hop. `PacketListener.shouldHandleMessage` is consulted first — which is how a listener being torn down ignores what is still arriving — and `Connection.receivedPackets` counts only the packets that pass it. Three other outcomes live in that method: a packet arriving before any listener is set is an illegal state; a packet whose listener is of the wrong shape is a cast failure and an *invalid_packet* kick; and a rejected schedule — the `PacketProcessor` closed because the game is shutting down — becomes a *server_shutdown* kick. **The hop is the handler's own first line.** `PacketUtils.ensureRunningOnSameThread` asks the `PacketProcessor` whether this is the right thread; if it is not, it enqueues the listener-and-packet pair and throws the singleton `RunningOnDifferentThreadException`, a stackless exception that `Connection.channelRead0` catches and drops on the floor. **The handler body then runs again from the top** when the queue is drained, which is why a handler method must do nothing observable before that line. A handler that touches no game state — the pong bookkeeping, the chunk-batch clock, the keep-alive answer — simply omits it and runs on Netty; the client's play listener has nine, listed in [threads](../../reference/threads.md#the-nine-client-handlers-that-never-hop). The unknown-custom-payload fallback is not one of them, and looks as though it should be: `ClientCommonPacketListenerImpl` hops first and dispatches to it afterwards, so it runs on the main thread like everything else. **The drain has a phase of its own, and the two sides do not schedule it alike.** The server drains before the tick proper, so every packet that arrived since last time enters the world at one point ([the server tick](../server/server-tick.md)); the client drains **once per frame**, not once per tick, because its tick is a sub-step of the frame loop ([two loops and a wire between them](../anatomy/anatomy.md#two-loops-and-a-wire-between-them), and [the client loop](../client/the-client-loop.md) for the arithmetic). The consequence for a packet is that its handling latency on the client is a frame, not a tick — and that packet handling and *execute*-style task scheduling are two different queues drained at two different moments on both sides. **The drain re-asks the same question.** `PacketProcessor.ListenerAndPacket` calls `PacketListener.shouldHandleMessage` a second time before dispatching, and logs and drops if the answer has changed. That is the gate that matters: a packet can wait between the two checks, and a disconnect arriving in between must not be able to run its handler. **Errors on re-dispatch go somewhere else entirely.** The drain catches exceptions only — a bare out-of-memory error is not caught at all — and routes what it does catch to `PacketListener.onPacketError`, which by default raises a reported crash. The one special case is a `ReportedException` *caused by* an out-of-memory error: that is rethrown, but not untouched. `PacketUtils.makeReportedException` delegates both steps to `PacketUtils.fillCrashReport`, which first decorates the report with an *Incoming Packet* category naming the type and its terminal and skippable flags, then lets the listener add its own detail. ## The pipeline, in both directions Two directions through one list of handlers. Inbound runs head to tail; outbound runs tail to head. Handlers in *italics* are added later, if at all. | inbound order | handler | added by | |---|---|---| | 1 | `"timeout"` — a read timeout of thirty seconds | the connect or accept site, before serialization | | 2 | `"legacy_query"` — `LegacyQueryHandler` | `ServerConnectionListener.startTcpServerListener` only, and only if the server replies to status; removes itself on the first modern byte | | 3 | *`"decrypt"`* — `CipherDecoder` | `Connection.setEncryptionKey` | | 4 | `"splitter"` — `Varint21FrameDecoder` | `Connection.configureSerialization` | | 5 | *`"decompress"`* — `CompressionDecoder` | `Connection.setupCompression`, inserted directly *after* `"splitter"` | | 6 | an unnamed flow-control handler | `Connection.configureSerialization` | | 7 | `"decoder"` or `"inbound_config"` | `Connection.configureSerialization` | | 8 | *`"bundler"`* — `PacketBundlePacker` | `Connection.setupInboundProtocol`, only for a protocol with a bundle | | 9 | `"packet_handler"` — the `Connection` itself | `Connection.configurePacketHandler` | Outbound, from the game outwards: `"packet_handler"`, then `"hackfix"` — an anonymous pass-through that `Connection.configurePacketHandler` adds immediately before it, whose write method does nothing but call its superclass — then *`"unbundler"`* (`PacketBundleUnpacker`), `"encoder"` or `"outbound_config"`, *`"compress"`*, `"prepender"`, *`"encrypt"`*, and the socket. Which side gets a live codec at birth is decided by direction. The end that will *receive* the handshake — the server — is built with a real `"decoder"` and a dead `"outbound_config"` placeholder; the end that will *send* it gets a real `"encoder"` and a dead `"inbound_config"`. Only the live one is built from `Connection.INITIAL_PROTOCOL`, which is `HandshakeProtocols.SERVERBOUND`. The placeholder is a bare `UnconfiguredPipelineHandler` holding no protocol at all, which is the entire point of it. `HandlerNames` is a class of constants for most of those names, and **nothing references it**: every name the pipeline is actually built with is a string literal in `Connection`, `ServerConnectionListener`, `ProtocolSwapHandler` and `UnconfiguredPipelineHandler`. It has already drifted, which is what an index no code reads does — it has no entry for *hackfix*, and it carries `HandlerNames.LATENCY`, a handler that exists only on the local pipeline behind a debug flag. Cite it for the names, not for the list. ### The threads underneath it `EventLoopGroupHolder` owns the event-loop groups: four instances behind two accessors, `EventLoopGroupHolder.local` for the in-memory channel and `EventLoopGroupHolder.remote`, which tries KQueue and then Epoll **only if the native-transport flag is set** and otherwise goes straight to NIO. That flag is the client's option and the server property of the same name, so switching it off really does change transport rather than hint at it. The class lives in `server/network` and the client uses it too: `ConnectScreen` and the server list ask for a group, and the server list hands the one it got to `ServerStatusPinger`, which never asks for its own. ## Singleplayer runs the same pipeline `Connection.configureInMemoryPipeline` builds the local variant, and the differences are smaller than almost anyone assumes. - `"splitter"` and `"prepender"` become `LocalFrameDecoder` and `LocalFrameEncoder`, which do nothing but `HiddenByteBuf.pack` and `HiddenByteBuf.unpack` — no length prefix, because the buffer never becomes a byte stream. - **No read timeout on either side**, so a wedged integrated server hangs rather than disconnecting. - No legacy query handler, and **never** a cipher or a compression handler — though by two different mechanisms. Compression is refused at the installation sites, which both test `Connection.isMemoryConnection`. Encryption is not: neither `Connection.setEncryptionKey` nor either side's key handler asks. What prevents it sits one gate further up, in `ServerLoginPacketListenerImpl`, where the decision to *ask* for encryption requires authentication **and** a non-memory connection, so `ClientboundHelloPacket` is never sent and the ciphers are never reached. - Everything else is identical, with one debug exception. `PacketEncoder` and `PacketDecoder` are still there, still running the same `StreamCodec`s, and singleplayer pays the full serialisation cost; the only handler that exists *only* on the local pipeline is `ServerConnectionListener.LatencySimulator`, installed when `SharedConstants.DEBUG_FAKE_LATENCY_MS` is positive. The integrated server binds its channel through `EventLoopGroupHolder.local` and `ServerConnectionListener.startMemoryChannel` hands back an address that `Connection.connectToLocalServer` dials. ## Sending, and the two flushes Outbound is the mirror image and shorter. `Connection.send` reaches `Connection.sendPacket`, which checks whether the calling thread is already the channel's event loop and, if not, schedules the write onto it — so a packet sent from the game thread crosses the same boundary an inbound packet does, just without a queue of its own. `Connection.doSendPacket` then chooses between a write and a write-and-flush, and between a real future and Netty's void promise; `Connection.flushChannel` performs the same hop for a bare flush. `PacketSendListener` is the callback that rides along. `PacketSendListener.thenRun` runs something once the packet is really on the wire — which is how compression and the client's encryption are installed *after* the packet that announced them, and how a disconnect waits for its own kick message to leave. `PacketSendListener.exceptionallySend` sends a fallback packet when the write fails. Both run on the event loop, which is why "disconnect after sending" is not a game-thread operation. ### Two writes per client per tick The server does not flush per packet. `ServerCommonPacketListenerImpl.send` turns a send into a write with no flush while `ServerCommonPacketListenerImpl.suspendFlushing` has set the flag — but only for a caller on the server thread, because the flag is tested together with the thread check, so anything sent from another thread flushes on its own regardless of the bracket. The bracket is opened around the whole server tick, and two different things then empty the buffer inside it. **Two** — writes to the socket per client per tick. The first is `Connection.tick` itself, which flushes the channel unconditionally in the middle of its own body, and runs inside the bracket: everything the levels produced leaves there, including the block changes that were collected during the tick and only became packets in the chunk source's step ([the level tick](../server/server-level-tick.md#the-broadcast-which-is-why-entities-are-a-tick-behind)). The second is `ServerCommonPacketListenerImpl.resumeFlushing`, which both clears the flag and flushes the channel itself, and therefore carries everything the server does after the connection phase — the ordering of which belongs to [the server tick](../server/server-tick.md#the-two-writes-each-client-gets). ### `Connection.tick`, the one call from a game thread `Connection.tick` is also the only place a listener gets time on a game thread without a packet having arrived. It drains `Connection.pendingActions` (the one queue `Connection` owns, holding closures rather than packets, and mattering only in the window before the channel exists); ticks the listener if it is a `TickablePacketListener`; calls `Connection.handleDisconnection` if the channel has died; flushes; every twentieth tick runs `Connection.tickSecond`, which rolls the packet-rate averages; and last, samples bandwidth. Note the order: the flush is in the middle, and the disconnect check happens before it. Its callers differ by side. The server has one, `MinecraftServer.tickConnection`, which walks `ServerConnectionListener.tick`. The client has three, because the client has more than one connection: `MultiPlayerGameMode.tick` ticks the play connection, `Minecraft.tick` ticks `Minecraft.pendingConnection` — the one still handshaking or logging in, and therefore the one that drives a login — and `ServerStatusPinger` ticks the connections it opened to ping the servers in the list. Note the clock: the pending connection is ticked at tick rate, not at the frame rate the drain above runs on. ## A phase change is a message written down the pipeline The pipeline is **reconfigured by writing through it**, not by editing it from outside. `Connection.setupInboundProtocol` validates that the new listener's direction and phase match the `ProtocolInfo`, assigns `Connection.packetListener`, and then builds an `UnconfiguredPipelineHandler.InboundConfigurationTask` — a closure that will replace the current handler with a new `PacketDecoder` and turn auto-read back on, optionally adding `"bundler"` after it. That task is *written down the channel*, where `UnconfiguredPipelineHandler.Inbound` recognises it and runs it with the right context, and `Connection.syncAfterConfigurationChange` blocks the caller until it completes. `Connection.setupOutboundProtocol` is the mirror image, and also records whether the new outbound protocol is the login one, for the benefit of the disconnect path. ### Getting back to unconfigured, which nobody asks for Returning to the unconfigured state is automatic, and it is asymmetric. `ProtocolSwapHandler.handleInboundTerminalPacket` fires when a packet whose `Packet.isTerminal` is true passes through `PacketDecoder`: it turns auto-read *off*, inserts a fresh `UnconfiguredPipelineHandler.Inbound` under the name `"inbound_config"`, and removes the decoder. `ProtocolSwapHandler.handleOutboundTerminalPacket` does the equivalent on the encoder side — but there is no incoming flow to stop, so it leaves auto-read alone and simply puts an `UnconfiguredPipelineHandler.Outbound` in the encoder's place. The bundler and unbundler remove themselves on the same signal, and `PacketBundlePacker` treats a terminal packet arriving *inside* a bundle as a decode error rather than a swap. So a phase change reads: terminal packet, codecs self-destruct and inbound reads stop, the game thread installs the new protocol, a configuration task travels the pipeline in order with the byte stream, reads resume. The unnamed flow-control handler between `"splitter"` and the decoder is what makes turning auto-read off actually stop delivery mid-batch. Which phases exist, and what ends each of them, is [protocol phases](protocol-phases.md). The server's first listener is installed by `Connection.setListenerForServerboundHandshake`, which refuses if one already exists and refuses on a connection that is not receiving serverbound traffic in the handshake protocol — so it is server-side by construction. The client has no equivalent: its first listener arrives with the pair of protocols that `Connection.initiateServerboundConnection` installs around `ClientIntentionPacket`, and `Connection.initiateServerboundStatusConnection` is the same code with a different intent. ## Compression and encryption arrive behind the packet that announces them **Compression** is `Connection.setupCompression`, which inserts `CompressionDecoder` after `"splitter"` and `CompressionEncoder` after `"prepender"`, or re-thresholds them if they already exist; a negative threshold removes both. The server turns it on during login, sending `ClientboundLoginCompressionPacket` with a send-listener that installs the handlers only *after* that packet is on the wire, and the client installs its own side when it handles the packet. Both sides skip it entirely on a memory connection. The asymmetry worth knowing is that the server validates that a compressed frame really was above the threshold and the client does not; the frame ceilings the two handlers enforce are in [packets and stream codecs](packets-and-stream-codecs.md). **Encryption** is `Connection.setEncryptionKey`, which inserts `CipherDecoder` before `"splitter"` and `CipherEncoder` before `"prepender"` — so decryption is the first thing that happens to inbound bytes and encryption the last thing before outbound bytes leave. Neither is ever removed. The server installs its ciphers synchronously the moment it handles the key packet; the client attaches its own to the *send* of that packet, so the key packet itself goes out in the clear and everything after it does not. ## How a connection dies `Connection.exceptionCaught` is the funnel, and it has four outcomes. - A `SkipPacketException` is **logged and swallowed**: the codec layer has already decided this one packet may be dropped, and the connection survives. It is the one marker that does not end the connection — an empty interface, implemented by `SkipPacketDecoderException` and `SkipPacketEncoderException`, one per direction. - A timeout — the thirty-second read timeout expiring — disconnects with *disconnect.timeout*, the "Timed out" a player sees. - Any other fault, the **first** time: the listener is asked for a `DisconnectionDetails` through `PacketListener.createDisconnectionInfo`; if this end is the one sending clientbound traffic it tries to tell the peer why, with either `ClientboundLoginDisconnectPacket` or `ClientboundDisconnectPacket` depending on `Connection.sendLoginDisconnect`, and disconnects once that packet has gone. `Connection.setReadOnly` is not the last step but an immediate one, taken on both branches the moment the write is handed over and long before it completes. - Any other fault, the **second** time — a fault while handling a fault, which `Connection.handlingFault` detects — skips all of that and disconnects immediately. `Connection.setReadOnly` is how a pending disconnect ignores the rest of the stream: it turns auto-read off and leaves the peer's remaining packets unread. `Connection.handleDisconnection` is the other end of the story. It runs from `Connection.tick`, only once the channel is really closed, reports to the listener — or to `Connection.disconnectListener`, the client's connect-attempt fallback, if the connection never got a real one — and is guarded by `Connection.disconnectionHandled` so that it reports exactly once. **Keep-alive is the real timeout on a live connection.** `ServerCommonPacketListenerImpl.keepConnectionAlive` sends a challenge every fifteen seconds and disconnects if the previous one was never answered, or if the answer carries the wrong id, with an exemption for the singleplayer host. It stops sending them once the listener has closed itself behind a terminal packet; from that moment `ServerCommonPacketListenerImpl.checkIfClosed` gives the protocol swap another fifteen seconds and then times the connection out. The thirty-second read timeout exists only on socket connections, so the singleplayer host has neither clock running against them: no read timeout on the in-memory pipeline, and an exemption from the keep-alive. ## Questions players ask **Which phase am I in?** `Connection` cannot tell you: there is no protocol field and no getter. The answer is distributed between the two codec handlers currently in the pipeline and the listener object, and every place `Connection` names a `ConnectionProtocol` is a comparison rather than a reading: `Connection.validateListener` checks a new listener against the protocol it is being installed for, `Connection.setupOutboundProtocol` asks only whether this is *login*, and the handshake entry point asks only whether the listener is the initial one. **Does login happen on the Netty thread or the game thread?** Both, and that is the surprise. None of `ServerHandshakePacketListenerImpl`, `ServerStatusPacketListenerImpl`, `ServerLoginPacketListenerImpl` or the client's `ClientHandshakePacketListenerImpl` contains a single `PacketUtils.ensureRunningOnSameThread` call, so every state transition and the encryption setup happen on the event loop. But `ServerLoginPacketListenerImpl` is a `TickablePacketListener`, and its tick — on the server thread, through `Connection.tick` — is what runs the ban and whitelist checks, switches compression on, and sends the terminal packet that ends the phase. Login is a three-thread state machine; [protocol phases](protocol-phases.md) walks it. **Does the server drop packets when it is behind?** Not for being late. `Connection` keeps no outbound packet queue at all — once the channel exists, everything written goes into Netty's own buffer and backpressure is Netty's water marks — and inbound, the `PacketProcessor`'s queue is unbounded and each drain empties it. What a slow server costs you is latency, not messages, until the keep-alive gives up. **Why does kicking someone sometimes stall the caller?** Not for the obvious reason. `Connection.disconnect` does block on the channel close, but the deliberate kicks never call it from the game thread: `/kick` and the ban commands go through `ServerCommonPacketListenerImpl.disconnect`, which defers `Connection.disconnect` to a `PacketSendListener.thenRun` callback on the event loop. What stalls the game thread is the next line — `MinecraftServer.executeBlocking` running `Connection.handleDisconnection`, so the kicking tick does not continue until the player has been removed. The one place that does block on the close from a game thread is `ServerLoginPacketListenerImpl.disconnect`, reached from that listener's tick. **Is a flood of packets rate-limited?** Only if the server was configured for it. `RateKickingConnection` overrides `Connection.tickSecond` to kick a client whose average received-packet rate exceeds `RateKickingConnection.rateLimitPacketsPerSecond`, and the two accept sites build one only when the server's rate limit is above zero — which it is not by default. An ordinary socket gets a plain `Connection`. **Why does one client's bug crash a singleplayer world but not a server?** `ServerConnectionListener.tick` catches a per-connection failure and kicks that client with an internal-server-error message — unless `Connection.isMemoryConnection`, in which case it raises a fresh reported crash and takes the integrated server down. **Why is the network graph in the debug screen client-side only?** `Connection.bandwidthDebugMonitor` is inbound-only and socket-only, set from `ConnectScreen` and the Realms connect path and nowhere else, so no server ever has one. `MonitoredLocalFrameDecoder` exists for the singleplayer case and is **never installed**: the local pipeline is always built with a null monitor. That is the transport: one wire, two ends, a thread hop at each of them, and a picture that does not change between singleplayer and a public server. What actually crosses — what a packet class has to declare, how its fields become bytes, how the far side knows which class to build, and what stops a hostile sender from allocating a gigabyte — is the other half of this lecture, [packets and stream codecs](packets-and-stream-codecs.md). ## Where to look `Connection` · `PacketListener` · `TickablePacketListener` · `PacketProcessor` · `PacketUtils` · `PacketSendListener` · `ProtocolInfo` · `UnconfiguredPipelineHandler` · `ProtocolSwapHandler` · `PacketDecoder` · `PacketEncoder` · `Varint21FrameDecoder` · `HandlerNames` · `CompressionDecoder` · `CipherBase` · `EventLoopGroupHolder` · `ServerConnectionListener` · `RateKickingConnection` · `DisconnectionDetails` · `ServerCommonPacketListenerImpl` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Packets and stream codecs > Verified against **Minecraft 26.2** · Part IX · Someone says hello in chat, and you stop the message on its way out of the server to ask what it actually is. [The connection](the-connection.md) hands you a frame: a length, then a run of bytes that a handler on a Netty thread is about to turn into a method call on the other side. This page is what is inside that frame. A chat line leaving the server is a `ClientboundSystemChatPacket` — a record of a `Component` and a boolean — and it has no write method, no byte layout of its own, and no number. The number that goes on the wire in front of it is written down nowhere in the game: not on the packet, not on its `PacketType`, not in any table a human maintains. **A packet's id is the position of one line in a chain of registration calls.** Swap two of those lines and the whole protocol renumbers — and because a handful of packet types are registered into several phases, *the same packet type is a different number in each phase it appears in*. > **For a 1.21-era reader.** `Packet` no longer knows how to write itself. > There is no *write* method on the interface and no buffer constructor it > is obliged to have. Serialisation is a *STREAM_CODEC* static field that > the protocol description reads — which is why one packet class can have > two of them, and why a packet class need not own one at all. ## The cast | class | what it decides | thread | |---|---|---| | `Packet` | that a message is a value with a type, one handler method and two flags — and nothing at all about bytes | any | | `PacketType` | which message this is: a direction and a name, and no number | — | | `StreamCodec` | one value's bytes — an encoder and a decoder over a `ByteBuf`, composed out of its fields' codecs | Netty | | `ByteBufCodecs` | the primitive vocabulary every packet codec is built from, and where the read limits sit | Netty | | `IdDispatchCodec` | one codec for a whole phase: a var-int id, then delegate to the entry that id names | Netty | | `ProtocolInfo` | what a configured connection holds — the phase, the direction, that one codec, and the bundler | Netty | | `ProtocolInfoBuilder` | the registration chain, and therefore every packet number in the game | class-load, or the configuration-to-play swap | | `RegistryFriendlyByteBuf` | that a registry id on the wire means something, by carrying the `RegistryAccess` it is relative to | Netty | The catalogue of *which* packets exist is generated, not written: [reference/packets.md](../../reference/packets.md) lists all 232 packet types across the eight `*PacketTypes` classes that declare them. ## A packet is a value, a name and a direction `Packet` is an interface with four methods and one static helper. | member | what it says | |---|---| | `Packet.type` | the `PacketType`, always a constant from one of the eight `*PacketTypes` classes | | `Packet.handle` | hands this packet to one named method on the phase's listener interface | | `Packet.isSkippable` | default false. True for the chat-shaped packets, so a failure to encode one is dropped rather than fatal | | `Packet.isTerminal` | default false. True for the seven packets that end a protocol phase | | `Packet.codec` | a static convenience: `StreamCodec.ofMember` under a friendlier name | Exactly one packet refuses to be handled at all: `BundleDelimiterPacket` makes `Packet.handle` final and throws, because a delimiter is consumed by the pipeline and must never reach a listener. The skippable set is the five chat-shaped packets — `ClientboundSystemChatPacket`, `ClientboundPlayerChatPacket`, `ClientboundDisguisedChatPacket`, `ClientboundPlayerCombatKillPacket` and `ClientboundTagQueryPacket`. The terminal set is exactly the seven transition packets drawn on [protocol phases](protocol-phases.md) and no others; what the flag *does* to the pipeline is [the connection](the-connection.md)'s business. Beware the namesake: `ServerboundResourcePackPacket.Action.isTerminal` asks whether a resource-pack *response* is a final answer, and that packet is not terminal. **`PacketType` is a record of two things** — a `PacketFlow` and an `Identifier`. A direction and a name; no number, no version, no size. `PacketFlow` is the two-constant enum `PacketFlow.SERVERBOUND` / `PacketFlow.CLIENTBOUND`, with `PacketFlow.getOpposite` and `PacketFlow.id`. Three shapes of packet class coexist in the tree, and two of them carry the argument. The modern one is a record whose *STREAM_CODEC* is a `StreamCodec.composite` naming each component's codec and accessor — `ClientboundSystemChatPacket` is one. The older one is a plain class with a private buffer constructor and a private write method, joined into a codec by `Packet.codec`; `ServerboundSwingPacket`, `ClientboundKeepAlivePacket` and `ClientboundSetHealthPacket` are these, and `StreamMemberEncoder` exists chiefly so that second form can bind a member reference as its encoder half — `CustomPacketPayload` is its one other client. The third shape is the one with no fields to serialise at all: a singleton whose codec is `StreamCodec.unit`, fourteen of them, of which `ServerboundFinishConfigurationPacket` is the one this part's login trace turns on. ## From two fields to a numbered blob ```mermaid flowchart TB subgraph V["the value"] F1["content, a Component"] F2["overlay, a boolean"] end subgraph C["one stream codec per component"] SC1["ComponentSerialization.TRUSTED_STREAM_CODEC"] SC2["ByteBufCodecs.BOOL"] end F1 --> SC1 F2 --> SC2 SC1 --> COMP["ClientboundSystemChatPacket.STREAM_CODEC, a StreamCodec.composite of two codec-and-getter pairs plus the constructor"] SC2 --> COMP COMP --> ENTRY["one addPacket call in GameProtocols.CLIENTBOUND_TEMPLATE, pairing that codec with GamePacketTypes.CLIENTBOUND_SYSTEM_CHAT"] ENTRY --> WRAP["mapStream, applied when the protocol is bound, wraps every call in a fresh RegistryFriendlyByteBuf"] WRAP --> DISP["IdDispatchCodec for the whole clientbound play phase. The id is this entry's index in the chain"] DISP --> OUT["a VarInt id, then the two fields in argument order"] ``` Read it downwards and you have the page. **Nothing above the `ProtocolInfoBuilder.addPacket` line knows anything about ids**, and nothing below it knows anything about chat. **One codec serves an entire phase.** `ProtocolInfo.codec` is a single `StreamCodec` — an `IdDispatchCodec` — and not a table the encoder walks: encoding looks the `PacketType` up in a map, and a type never registered in *this* protocol is an encoder error naming the unknown packet, which is how a configuration-phase packet sent during play fails. Decoding reads the id, bounds-checks it and delegates — and then **must have consumed the frame exactly**, or `PacketDecoder` raises an error naming how many bytes were left over. An under-read corrupts nothing visible until much later, so it is caught at the one point where the answer is still knowable. All of that runs on the Netty event loop, inside `PacketEncoder` and `PacketDecoder`, never on a game thread; the framing, the compression, the ciphers and the later hop to the game thread are [the connection](the-connection.md)'s. ## The codec layer is small, and composition is all of it `net/minecraft/network/codec` is a handful of files and does all of the composing; the byte-level encodings themselves live outside it, in `VarInt`, `VarLong`, `Utf8String`, `LpVec3` and `FriendlyByteBuf`. `StreamCodec` is one interface extending `StreamEncoder` and `StreamDecoder`, so `StreamEncoder.encode` takes a buffer and a value and `StreamDecoder.decode` takes a buffer and returns one. It is deliberately *not* a `Codec`: a packet is written once, read once and must be small, so it gets hand-laid bytes rather than a document in some format — the distinction is [codecs, NBT and JSON](../foundations/codecs-nbt-json.md)'s. The constructors and combinators are `StreamCodec.of`, `StreamCodec.ofMember`, `StreamCodec.unit`, `StreamCodec.map`, `StreamCodec.mapStream`, `StreamCodec.apply`, `StreamCodec.dispatch`, `StreamCodec.recursive` and `StreamCodec.cast` — plus **`StreamCodec.composite` in twelve arities**, one through twelve pairs of codec and getter followed by a constructor. Fields encode and decode strictly in argument order, and *that ordering is the format specification*: there is no other statement anywhere of what a packet's bytes look like. `StreamCodec.CodecOperation` lets `StreamCodec.apply` read left to right, and `StreamCodec.dispatch` is how registry-dispatched values travel — `ConsumeEffect.STREAM_CODEC`, `SlotDisplay.STREAM_CODEC` and `RecipeDisplay.STREAM_CODEC` are built with it. `ByteBufCodecs` is the primitive library underneath: SCREAMING\_CASE constants for the fixed things, lowerCamel factories for the parameterised ones. | what | the names | |---|---| | numbers | `ByteBufCodecs.BOOL`, `ByteBufCodecs.BYTE`, `ByteBufCodecs.SHORT`, `ByteBufCodecs.UNSIGNED_SHORT`, `ByteBufCodecs.INT`, `ByteBufCodecs.VAR_INT`, `ByteBufCodecs.LONG`, `ByteBufCodecs.VAR_LONG`, `ByteBufCodecs.FLOAT`, `ByteBufCodecs.DOUBLE` | | bytes and text | `ByteBufCodecs.BYTE_ARRAY`, `ByteBufCodecs.LONG_ARRAY`, `ByteBufCodecs.STRING_UTF8`, `ByteBufCodecs.byteArray`, `ByteBufCodecs.stringUtf8`, `ByteBufCodecs.PLAYER_NAME` | | tags and JSON | `ByteBufCodecs.TAG`, `ByteBufCodecs.COMPOUND_TAG`, `ByteBufCodecs.TRUSTED_TAG`, `ByteBufCodecs.TRUSTED_COMPOUND_TAG`, `ByteBufCodecs.lenientJson` | | shapes and identities | `ByteBufCodecs.VECTOR3F`, `ByteBufCodecs.QUATERNIONF`, `ByteBufCodecs.RGB_COLOR`, `ByteBufCodecs.CONTAINER_ID`, `ByteBufCodecs.GAME_PROFILE`, `ByteBufCodecs.GAME_PROFILE_PROPERTIES` | | structure | `ByteBufCodecs.optional`, `ByteBufCodecs.collection`, `ByteBufCodecs.list`, `ByteBufCodecs.map`, `ByteBufCodecs.either`, `ByteBufCodecs.lengthPrefixed` | | registries | `ByteBufCodecs.idMapper`, `ByteBufCodecs.registry`, `ByteBufCodecs.holder`, `ByteBufCodecs.holderRegistry`, `ByteBufCodecs.holderSet` | Two are worth naming for what they encode rather than for what they are. `ByteBufCodecs.ROTATION_BYTE` is one byte meaning 1/256 of a full turn — a little over a degree, with `Mth.packDegrees` and `Mth.unpackDegrees` doing the arithmetic — and `ByteBufCodecs.OPTIONAL_VAR_INT` spends zero for absent and value-plus-one otherwise. The bridge to the disk-and-JSON codecs of Part II is `ByteBufCodecs.fromCodec` and its relatives, which run an ordinary `Codec` into a carrier format and put the result on the wire. The combinator underneath takes the ops as an argument and is format-agnostic, and its six NBT entry points all pass `NbtOps`, so **a `Codec` on the wire almost always means a compound tag**. Almost: the combinator is public, and two packets call it with `JsonOps` and carry JSON strings instead — the server-list response, `ClientboundStatusResponsePacket`, and the login kick, `ClientboundLoginDisconnectPacket`. They are the two a player is likeliest to have seen. Beneath it, `ByteBufCodecs.tagCodec` takes an `NbtAccounter` supplier — which is exactly what *trusted* turns out to mean, below. `IdDispatchCodec` is the class that makes a protocol out of a pile of codecs: a list of serialisers, a type-to-int map, a var-int written in front and a delegation behind it. `IdDispatchCodec.DontDecorateException` is the marker meaning *rethrow me as I am, do not wrap me*, and `IdDispatchCodec.Builder.build` refuses a duplicate `PacketType` outright, so registering one type twice in a protocol fails loudly rather than silently shadowing an id. Loudly, but not always early: the check runs when the template is bound, which for the four static phases is class-load and for play is the first connection's configuration-to-play switch. ## Which buffer, and why play needs its own ```mermaid flowchart LR RAW["ByteBuf, what the pipeline hands the codec"] --> ID["status serverbound binds the identity function and never wraps at all"] RAW --> FBB["FriendlyByteBuf for handshaking, status clientbound, login and configuration"] FBB --> RFBB["RegistryFriendlyByteBuf for play, adding one field, a RegistryAccess"] ``` `FriendlyByteBuf` is a `ByteBuf` decorator declaring a hundred and fifty-two readers and writers, a hundred and twenty-one of which add a wire format the plain buffer knows nothing about — `FriendlyByteBuf.readVarInt`, `FriendlyByteBuf.writeUtf`, `FriendlyByteBuf.readIdentifier`, `FriendlyByteBuf.writeResourceKey`, `FriendlyByteBuf.readNbt`, `FriendlyByteBuf.readCollection`, `FriendlyByteBuf.readEnumSet`, `FriendlyByteBuf.readBlockPos`, `FriendlyByteBuf.readBlockHitResult`, `FriendlyByteBuf.readWithCodec` and so on. It holds the two length constants `FriendlyByteBuf.MAX_STRING_LENGTH` and `FriendlyByteBuf.MAX_COMPONENT_STRING_LENGTH`, and also `FriendlyByteBuf.limitValue`, the wrapper an old-style packet puts round a collection constructor to get the cap `ByteBufCodecs.collection` gives for free. **`RegistryFriendlyByteBuf` extends it and adds exactly one field**, a `RegistryAccess`, behind `RegistryFriendlyByteBuf.registryAccess`. It exists because a numeric registry id means nothing on its own — item number 37 is only an item relative to the registry set the server sent during configuration ([identifiers and registries](../foundations/identifiers-and-registries.md)). Every codec that writes a registry id needs one: `ByteBufCodecs.registry`, `ByteBufCodecs.holderRegistry`, `ByteBufCodecs.holder`, `ByteBufCodecs.holderSet`, `ByteBufCodecs.fromCodecWithRegistries` and `ByteBufCodecs.registryFriendlyLengthPrefixed` — and therefore `ItemStack.STREAM_CODEC`, `DataComponentPatch.STREAM_CODEC` ([data components](../foundations/data-components.md)), `ComponentSerialization.STREAM_CODEC` and `HashedStack.STREAM_CODEC`. The wrapper is a throwaway, not a pipeline object: `ProtocolInfoBuilder` applies the decorator — `RegistryFriendlyByteBuf.decorator` for play — through `StreamCodec.mapStream`, so a fresh view is constructed round the raw buffer on every single encode and every single decode. Below all of it the actual encodings live in `VarInt`, `VarLong`, `Utf8String` and `LpVec3`, the quantised position behind `Vec3.LP_STREAM_CODEC`. Two everyday values are worth naming because they are *not* special-cased: `Identifier.STREAM_CODEC` is a plain UTF-8 string under the ordinary 32,767-character cap — an identifier on the wire is text, never an interned number — and `UUIDUtil.STREAM_CODEC` is two longs. ## Where a packet's number comes from `ProtocolInfo` is what a configured connection actually holds: `ProtocolInfo.id` (a `ConnectionProtocol`), `ProtocolInfo.flow`, `ProtocolInfo.codec` — the single phase-wide `StreamCodec` — and a nullable `ProtocolInfo.bundlerInfo`. It is built by `ProtocolInfoBuilder`, whose `ProtocolInfoBuilder.addPacket` and `ProtocolInfoBuilder.withBundlePacket` are the registration calls and whose `ProtocolInfoBuilder.buildUnbound` yields an `UnboundProtocol` or a `SimpleUnboundProtocol` — a protocol that knows everything except which buffer type to wrap the bytes in. `UnboundProtocol.bind` supplies that. Underneath it, `ProtocolCodecBuilder` is the layer that talks to `IdDispatchCodec`. **The id is a registration index.** `ProtocolCodecBuilder.add` appends to a list and `IdDispatchCodec.Builder.build` walks that list assigning 0, 1, 2 in call order, so a packet's wire number is literally its position in the `ProtocolInfoBuilder.addPacket` chain in `GameProtocols`, `ConfigurationProtocols`, `LoginProtocols`, `StatusProtocols` or `HandshakeProtocols`. `ProtocolCodecBuilder.add` also asserts that the type's `PacketFlow` matches the protocol's, so a clientbound type cannot be registered into a serverbound protocol. Those numbers are readable from outside for one reason. `ProtocolInfo.DetailsProvider` and `ProtocolInfo.Details` let tooling enumerate a phase, and `ProtocolInfo.Details.listPackets` hands a `ProtocolInfo.Details.PacketVisitor` each `PacketType` with its network id. The tooling is the data generator: `PacketReport` walks every template and writes every packet's id in every phase into a report — the only place in the project those numbers are written down. There are four registration entry points, one per direction and per context-or-not: `ProtocolInfoBuilder.serverboundProtocol`, `ProtocolInfoBuilder.clientboundProtocol`, `ProtocolInfoBuilder.contextServerboundProtocol` and `ProtocolInfoBuilder.contextClientboundProtocol`. The context ones let a codec ask the *connection* a question, and in 26.2 exactly one protocol uses one: `GameProtocols.SERVERBOUND_TEMPLATE`, whose context is `GameProtocols.Context` and whose only question is `GameProtocols.Context.hasInfiniteMaterials`. Every other template is a `SimpleUnboundProtocol`; `CodecModifier` is the hook a context-aware codec is installed through. **When the description is built** splits the same way. Handshaking, status, login and configuration bind their buffers eagerly at class-load — and the serverbound status protocol binds the identity function, so it is the one phase and direction that never wraps its bytes at all. Play binds **per connection**, at the configuration-to-play transition: on the client that really is the first moment a `RegistryAccess` exists, while on the server the registries have been loaded since startup and the rebind happens because the protocol changed, not because the registries arrived. ## A bundle is two empty markers round ordinary packets `ClientboundBundlePacket` has a `PacketType` and **no wire id at all**. `ProtocolInfoBuilder.withBundlePacket` registers only the *delimiter*, `ClientboundBundleDelimiterPacket`, serialised with `StreamCodec.unit` — an id and a zero-byte body — and records a `BundlerInfo` beside the codec list, so a bundle on the wire is two empty markers with ordinary, individually numbered packets between them. `PacketBundleUnpacker` explodes an outgoing bundle into delimiter-packets-delimiter and `PacketBundlePacker` collects an incoming run back up; `BundlePacket` holds the sub-packets and `BundlerInfo` the logic, split between `BundlerInfo.unbundlePacket` outgoing and its nested `BundlerInfo.Bundler` incoming, with `BundlerInfo.BUNDLE_SIZE_LIMIT` caps a bundle at 4,096 sub-packets. Only the clientbound play protocol declares a bundle at all. **What a bundle buys is atomicity against the client's tick.** `ClientPacketListener.handleBundlePacket` hops to the main thread once for the whole bundle and then handles the sub-packets inline, so the client can never tick or render with half a bundle applied. There are only two senders, both in `ServerEntity`: `ServerEntity.addPairing`, which collects what `ServerEntity.sendPairingData` writes into a list and sends the result as one bundle, so an entity never appears mid-initialisation ([what the client is told](what-the-client-is-told.md)); and the motion-plus-power pair sent for a hurtling projectile. ## What stops a hostile sender The frame limit does most of the work, and it is not on this page: a frame length is at most three var-int bytes, and `Varint21FrameDecoder` refuses a wider prefix or a zero length before any codec sees anything ([the connection](the-connection.md)). Above that sit two separate collection defences, and only the second is famous. `ByteBufCodecs.readCount` is the first — it compares the declared count against the codec's own maximum and refuses outright, which is what makes the three-argument `ByteBufCodecs.collection` different from the two-argument one whose maximum is effectively unbounded. Behind it, `ByteBufCodecs.MAX_INITIAL_COLLECTION_SIZE` clamps the *allocation* to 65,536 entries whatever the count says, so even an accepted count cannot force a huge array up front. `ByteBufCodecs.lengthPrefixed` bounds the bytes instead of the count, handing the inner codec a slice it physically cannot read past. *Trusted* is a real distinction in the codec library, and a statement about the read budget rather than about direction: `ByteBufCodecs.fromCodecTrusted` gives the NBT reader an unlimited heap quota where plain `ByteBufCodecs.fromCodec` gives it the default. Hence the pairs `ByteBufCodecs.TAG` / `ByteBufCodecs.TRUSTED_TAG`, `ByteBufCodecs.COMPOUND_TAG` / `ByteBufCodecs.TRUSTED_COMPOUND_TAG` and `ComponentSerialization.STREAM_CODEC` / `ComponentSerialization.TRUSTED_STREAM_CODEC`. The rule is direction: the server wrote it, so the client may trust it ([codecs, NBT and JSON](../foundations/codecs-nbt-json.md) has the serverbound half). **Exactly one packet lets a client hand the server an arbitrary item**, and it is fenced three ways. `ServerboundSetCreativeModeSlotPacket` uses `ItemStack.OPTIONAL_UNTRUSTED_STREAM_CODEC`, which differs from `ItemStack.OPTIONAL_STREAM_CODEC` only in using `DataComponentPatch.DELIMITED_STREAM_CODEC`: every component's payload is length-prefixed, and `ByteBufCodecs.lengthPrefixed` hands the inner codec a slice and advances the outer reader past the whole region before delegating, so a component that lies about its own length cannot mis-frame the ones after it. That is containment, not recovery — nothing catches a component that throws, and one bad component still fails the whole packet. It is then wrapped in `ItemStack.validatedStreamCodec`, which re-encodes the decoded stack against `NullOps` purely to collect its errors. And the registration carries `GameProtocols.HAS_INFINITE_MATERIALS`, a `CodecModifier` that refuses the packet whenever its context says the connection is not in creative — a `SkipPacketDecoderException` one way and a `SkipPacketEncoderException` the other, landing on one side only because the client's context answers `GameProtocols.Context.hasInfiniteMaterials` true unconditionally while the server's answers from the real player. The packet is refused **in the decoder**, before any handler exists to fool. The ordinary container click is defended by carrying nothing to validate. `ServerboundContainerClickPacket` sends a `HashedStack` — either `HashedStack.EMPTY` or `HashedStack.ActualItem`, with an item holder, a count and a `HashedPatchMap`, itself two halves rather than one: `HashedPatchMap.addedComponents`, a map of component type to a hash, and `HashedPatchMap.removedComponents`, a bare set. A removal is as much a part of the claim as an addition, and `HashedStack.matches` checks both. Client-supplied component *contents* never cross the wire at all; see [containers and menus](../items/containers-and-menus.md). The limits in one table: | limit | value | where | |---|---|---| | frame length prefix | three var-int bytes | `Varint21FrameDecoder` | | compressed frame | 2 MiB | `CompressionDecoder.MAXIMUM_COMPRESSED_LENGTH` | | decompressed frame | 8 MiB | `CompressionDecoder.MAXIMUM_UNCOMPRESSED_LENGTH` | | default string | 32,767 chars | `FriendlyByteBuf.MAX_STRING_LENGTH` | | component as string | 262,144 | `FriendlyByteBuf.MAX_COMPONENT_STRING_LENGTH` | | player name | 16 | `ByteBufCodecs.PLAYER_NAME` | | collection allocation | 65,536 | `ByteBufCodecs.MAX_INITIAL_COLLECTION_SIZE` | | sub-packets in a bundle | 4,096 | `BundlerInfo.BUNDLE_SIZE_LIMIT` | | slots in one click | 128 | `ServerboundContainerClickPacket.MAX_SLOT_COUNT` (named, but the codec passes the literal) | | var-int / var-long | 5 / 10 bytes | `VarInt.read`, `VarLong.read` | ## Custom payloads, the only extension point The packet set is code, fixed at compile time, and no data pack adds to it. The one seam is `CustomPacketPayload`, with `CustomPacketPayload.Type`, `CustomPacketPayload.createType` and `CustomPacketPayload.codec`, carried by `ClientboundCustomPayloadPacket` and `ServerboundCustomPayloadPacket`. The route in is `CustomPacketPayload.FallbackProvider`, the codec handed to the dispatch as the map miss, with `CustomPacketPayload.TypeAndCodec` as the registration pair; `BrandPayload` is vanilla's own use of the mechanism. An unrecognised payload decodes to `DiscardedPayload` — a record of the identifier and **nothing else**. Its decoder checks the remaining length against a per-direction maximum and then skips every byte, and its encoder writes nothing. The payload is discarded, not held. ## Questions players ask **Why do packet ids move — between versions, and between phases?** Because nobody chose them. They are indexes into a registration chain, so inserting one line in `GameProtocols` shifts every packet declared after it, and the *common*, *cookie* and *ping* types, registered into several phases, count from zero again in each. A `PacketType` is a name and a direction; a number is a property of a phase, not of a type — which is why nothing in the game reads a packet number as a constant, and why `PacketReport` is the only place they are ever written out. **Why can one packet class have several codecs?** `ClientboundCustomPayloadPacket` declares `ClientboundCustomPayloadPacket.GAMEPLAY_STREAM_CODEC` and `ClientboundCustomPayloadPacket.CONFIG_STREAM_CODEC`; `ClientboundShowDialogPacket` declares `ClientboundShowDialogPacket.STREAM_CODEC` and `ClientboundShowDialogPacket.CONTEXT_FREE_STREAM_CODEC`. The same `PacketType` is registered with the first in `GameProtocols` and with the second in `ConfigurationProtocols`. Type and encoding are genuinely separate things. **Why does a malformed chat message not disconnect me?** Because `SkipPacketException` is a marker and the skip is a decision two layers up. `SkipPacketEncoderException` and `SkipPacketDecoderException` implement it and `IdDispatchCodec.DontDecorateException`; `PacketEncoder` turns a failure on a `Packet.isSkippable` packet into one; `PacketDecoder` drains the rest of the frame and **rethrows**, because the frame was already delimited and nothing was ever misaligned, so the drain only satisfies Netty's bookkeeping. What keeps the connection alive is `Connection.exceptionCaught` logging it and returning ([the connection](the-connection.md)). **Is a modern packet safer than an old one?** Measurably. `FriendlyByteBuf.readCollection` applies its constructor to the raw decoded count with no cap of its own, so only the frame limit bounds it, where `ByteBufCodecs.collection` refuses the count and then clamps the allocation. The hand-written shape is still in the tree, and `FriendlyByteBuf.limitValue` is what one of those must remember to use. Which packets a phase registers, and in what order, is therefore the whole definition of that phase — so the next page is [protocol phases](protocol-phases.md), where nine codec tables — one per direction per phase, bar handshaking's serverbound-only one — become the four languages a joining connection speaks in turn. ## Where to look `Packet` · `PacketType` · `PacketFlow` · `StreamCodec` · `ByteBufCodecs` · `IdDispatchCodec` · `ProtocolInfo` · `ProtocolInfoBuilder` · `ProtocolCodecBuilder` · `GameProtocols` · `GamePacketTypes` · `FriendlyByteBuf` · `RegistryFriendlyByteBuf` · `VarInt` · `BundlerInfo` · `PacketBundlePacker` · `CustomPacketPayload` · `HashedStack` · `PacketReport` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Protocol phases > Verified against **Minecraft 26.2** · Part IX · A login: from clicking a server in the list to standing in the world. Click a server in the multiplayer list and one TCP connection opens, and over the next second it speaks four different languages in turn. Each is a `ConnectionProtocol`; each has its own packet set, and all but handshaking a listener at both ends — handshaking is serverbound only, so the client has nothing to listen with; and each hands over to the next by a packet marked *terminal*, which tears its own codec out of the pipeline as it passes. What a 1.21-era reader will not expect is where the work happens. Every server-side handler in the handshake and login phases runs on the Netty event loop, and the thing that actually advances a login is a **tick**. And the `ServerPlayer` — the object, its save data, its position, the chunks under it — is *prepared* during configuration, by a task named for it, and **constructed after the client has already acknowledged that configuration is over**, by which point the server is encoding play packets to a player that does not yet exist. ## The cast | class | role | thread | |---|---|---| | `ConnectionProtocol` | the five phases — a bare enum of labels; the codec lives in a `ProtocolInfo`, the behaviour in a `PacketListener` | — | | `Connection` | one channel, and `Connection.setupInboundProtocol` / `Connection.setupOutboundProtocol` at every transition | Netty | | `ServerHandshakePacketListenerImpl` | the three-way switch and the version gate | Netty | | `ServerLoginPacketListenerImpl` | the login state machine; its handlers set a volatile state and its `ServerLoginPacketListenerImpl.tick` acts on it | Netty, ticked from Server | | `ServerConfigurationPacketListenerImpl` | the serial task queue, and the handler that finally builds the player | mixed — see below | | `ClientHandshakePacketListenerImpl` | the client's side of handshake and login, including the session-service call | Netty, client IO pool | | `ClientConfigurationPacketListenerImpl` | accumulates registries and tags, then constructs the `ClientPacketListener` | Netty, then Render | | `PrepareSpawnTask` | finds a spawn, tickets its chunks, waits — and later, on request, spawns the player | Server | ## The five phases ```mermaid stateDiagram-v2 direction LR [*] --> HANDSHAKING : TCP accept HANDSHAKING --> STATUS : ClientIntentionPacket, intent STATUS HANDSHAKING --> LOGIN : ClientIntentionPacket, intent LOGIN or TRANSFER STATUS --> [*] : pong, then the server hangs up LOGIN --> CONFIGURATION : ClientboundLoginFinishedPacket, ServerboundLoginAcknowledgedPacket CONFIGURATION --> PLAY : ClientboundFinishConfigurationPacket, ServerboundFinishConfigurationPacket PLAY --> CONFIGURATION : ClientboundStartConfigurationPacket, ServerboundConfigurationAcknowledgedPacket PLAY --> [*] : disconnect note right of HANDSHAKING : every transition packet is terminal, so the codec that decoded it is already gone ``` `ConnectionProtocol` is five constants — `ConnectionProtocol.HANDSHAKING`, `ConnectionProtocol.STATUS`, `ConnectionProtocol.LOGIN`, `ConnectionProtocol.CONFIGURATION`, `ConnectionProtocol.PLAY` — each carrying only a string `ConnectionProtocol.id`. There is no number, no packet table and no lookup by id. What a phase *is* lives in two other places: the packet set, bound as a `ProtocolInfo`, and the listener that handles it. | phase | serverbound listener | clientbound listener | `ProtocolInfo` | |---|---|---|---| | `ConnectionProtocol.HANDSHAKING` | `ServerHandshakePacketListenerImpl`, or `MemoryServerHandshakePacketListenerImpl` in singleplayer | none — there is no clientbound handshake protocol | `HandshakeProtocols.SERVERBOUND`, one packet | | `ConnectionProtocol.STATUS` | `ServerStatusPacketListenerImpl` | reached from `ServerStatusPinger` | `StatusProtocols.SERVERBOUND` binds a **raw buffer**; `StatusProtocols.CLIENTBOUND` a `FriendlyByteBuf` | | `ConnectionProtocol.LOGIN` | `ServerLoginPacketListenerImpl` | `ClientHandshakePacketListenerImpl` | `LoginProtocols.SERVERBOUND` / `LoginProtocols.CLIENTBOUND` | | `ConnectionProtocol.CONFIGURATION` | `ServerConfigurationPacketListenerImpl` | `ClientConfigurationPacketListenerImpl` | `ConfigurationProtocols.SERVERBOUND` / `ConfigurationProtocols.CLIENTBOUND` | | `ConnectionProtocol.PLAY` | `ServerGamePacketListenerImpl` | `ClientPacketListener` | **not pre-bound** — `GameProtocols.SERVERBOUND_TEMPLATE` and `GameProtocols.CLIENTBOUND_TEMPLATE` are bound per connection | The bindings in the last column are the per-phase codec tables that [packets and stream codecs](packets-and-stream-codecs.md) builds, and the swap itself is the pipeline surgery [the connection](the-connection.md) performs; this page is what the swaps are *for*. The first four bind their buffers once, at class load, because they need no registries. Play cannot: its codecs write registry ids, so both templates are bound per connection with `RegistryFriendlyByteBuf.decorator` at the configuration-to-play switch. On the client that is genuinely the first moment a `RegistryAccess` exists; on the server the registries have been there since startup and the rebind is only because the protocol changed. The serverbound play template is also the one protocol with a context object, `GameProtocols.Context`, whose single question is `GameProtocols.Context.hasInfiniteMaterials` — which is why it is an `UnboundProtocol` where the other eight templates are a `SimpleUnboundProtocol`. Eight, not four: every phase but handshaking declares one per direction. Two listeners sit under the phases. `ServerCommonPacketListenerImpl` is the shared base of the server's configuration and play listeners and holds everything legal in both — keep-alive, latency, custom payloads, resource-pack responses and the flush suspension — with `ClientCommonPacketListenerImpl` its client counterpart; that inheritance is why the *common* packets in [reference/packets.md](../../reference/packets.md) belong to no single phase. And the state that crosses a phase change is a `CommonListenerCookie`: on the server a small record of profile, latency, client information and a transferred flag; on the client a much larger one carrying registries, feature flags, cookies, chat state and the server brand. ## Handshake The handshake is one packet and a three-way switch. `ClientIntentionPacket` carries the protocol version, the address the client dialled and a `ClientIntent`, and `ServerHandshakePacketListenerImpl.handleIntention` branches on it. `ClientIntent.STATUS` installs the status listener, or disconnects at once if the server does not reply to status. `ClientIntent.TRANSFER` disconnects if the server does not accept transfers, and otherwise joins `ClientIntent.LOGIN` in `ServerHandshakePacketListenerImpl.beginLogin`, which compares the client's protocol version against this build's and refuses a mismatch — *outdated_client* below the 1.16.4 protocol number, *incompatible* above it. The two refusals on the login path first install the **login** clientbound protocol, purely so they can send `ClientboundLoginDisconnectPacket` and have the client render a reason. The status refusal is the exception and the rudest: the status clientbound protocol is installed before the branch, and then the connection is dropped with no packet at all. The client does not wait for any of that. Whichever of its three entry points opened the connection — `ConnectScreen` for a listed or direct server, `Minecraft` for the integrated server's memory channel, `RealmsConnect` — it sends `ServerboundHelloPacket` immediately after the intention packet; there is no round trip between them. The profile id the hello carries is decoded and then never read — the server mints or fetches identity for itself. All of this runs on the Netty thread, and the intention packet, being terminal, has already torn out the codec that decoded it. ## Status, the phase nobody logs in through `ConnectionProtocol.STATUS` is two packets each way and a deliberate dead end. `ServerStatusPacketListenerImpl` answers exactly one `ServerboundStatusRequestPacket` — a second one disconnects the caller — and answers `ServerboundPingRequestPacket` with `ClientboundPongResponsePacket` and then **hangs up**. A status connection is expected to be thrown away, which is why `ServerStatusPinger` opens one per server in the list and why `Connection.initiateServerboundStatusConnection` exists as a separate entry point. ## Login ```mermaid stateDiagram-v2 direction LR [*] --> HELLO HELLO --> KEY : online mode over a socket, ClientboundHelloPacket sent HELLO --> VERIFYING : singleplayer profile, or offline mode KEY --> AUTHENTICATING : ServerboundKeyPacket, ciphers installed now AUTHENTICATING --> VERIFYING : the User Authenticator thread stores the profile VERIFYING --> WAITING_FOR_DUPE_DISCONNECT : tick, a player with this profile is still in the world VERIFYING --> PROTOCOL_SWITCHING : tick, bans and whitelist pass, ClientboundLoginFinishedPacket sent WAITING_FOR_DUPE_DISCONNECT --> PROTOCOL_SWITCHING : tick, the old connection is gone PROTOCOL_SWITCHING --> ACCEPTED : ServerboundLoginAcknowledgedPacket, configuration begins NEGOTIATING : NEGOTIATING, declared and never assigned note left of VERIFYING : the three tick transitions are the server-thread work that advances the login ``` `ServerLoginPacketListenerImpl` has no thread hop anywhere, which is why its `ServerLoginPacketListenerImpl.state` field is volatile: the packet handlers run on the Netty thread and set the state, and `ServerLoginPacketListenerImpl.tick` — reached from `MinecraftServer.tickConnection` through `ServerConnectionListener.tick` and `Connection.tick` — reads it on the server thread and does the login. The tick is also where `ServerLoginPacketListenerImpl.MAX_TICKS_BEFORE_LOGIN`, six hundred ticks, is enforced: a client that has not reached the end of the phase in thirty seconds is disconnected for a slow login. **Three branches out of the hello.** If the name matches the singleplayer profile, verification starts at once with no encryption. If the server uses authentication and this is not a memory connection, the state becomes `ServerLoginPacketListenerImpl.State.KEY` and `ClientboundHelloPacket` carries the server's RSA public key and a four-byte challenge. Otherwise — offline mode — the profile is minted from the name by `UUIDUtil.createOfflineProfile` and nothing is encrypted. **Both sides authenticate, and the client goes first.** ```mermaid sequenceDiagram participant CHPL as ClientHandshakePacketListenerImpl participant SLPL as ServerLoginPacketListenerImpl participant Auth as User Authenticator thread SLPL->>CHPL: ClientboundHelloPacket, RSA public key and a four-byte challenge CHPL->>CHPL: generate the AES secret, digest over server id, secret and key CHPL->>Auth: joinServer on the client IO pool, before the key packet is sent CHPL->>SLPL: ServerboundKeyPacket, secret and challenge RSA-encrypted, ciphers attached to the send SLPL->>SLPL: validate the challenge, recover the secret, Connection.setEncryptionKey now SLPL->>Auth: hasJoinedServer on a fresh thread named for user authentication Auth-->>SLPL: the authenticated profile, state VERIFYING Note over SLPL: the next server tick runs bans, whitelist, compression, duplicates ``` The client generates the AES secret and computes a digest over the server id, the secret and the server's public key; if the server asked for authentication it calls the session service on its IO pool *before* the key packet goes anywhere, and sends `ServerboundKeyPacket` from that callback, attaching its own ciphers to the send. The server validates the challenge, recovers the secret, recomputes the digest, installs its ciphers *immediately and synchronously*, and only then starts its own session-service call. An unauthenticated connection is already encrypted. **Authentication is a plain thread with two fallbacks.** It calls the session service, reports login activity and on success stores the profile and flips the state to `ServerLoginPacketListenerImpl.State.VERIFYING`. On failure it disconnects — unless the server is a singleplayer host, in which case both a null result and an unreachable authentication service fall back to an offline profile. That is how a LAN world admits a guest whose account cannot be checked. A login over a real socket against an authenticating server starts exactly one such thread; an offline-mode, memory or singleplayer-profile login starts none. **The tick does the real login.** `ServerLoginPacketListenerImpl.verifyLoginAndFinishConnectionSetup` runs on the server thread: `PlayerList.canPlayerLogin` for bans, whitelist and capacity; the compression switch; and `PlayerList.disconnectAllPlayersWithProfile` for a duplicate login, after which the machine waits in `ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT` until the old connection has actually died. It also compares the authenticated profile against `Connection.getIntendedProfileId`, which is set in exactly one place, `ServerConnectionListener.acceptChannel` — and nothing in the tree calls that, so it is an embedder's hook. **Login ends with a terminal packet in each direction**, and the two sides install their codecs in mirror order. `ClientboundLoginFinishedPacket` then `ServerboundLoginAcknowledgedPacket` — but the server installs *outbound* configuration when the acknowledgement arrives, in `ServerLoginPacketListenerImpl.handleLoginAcknowledgement`, whereas the client installs *inbound* configuration before sending it and outbound immediately after. Both packets are terminal, so the codecs tear themselves out as they pass ([the connection](the-connection.md)). The client then volunteers two packets straight away: its own `BrandPayload` and `ServerboundClientInformationPacket`, which is where the server learns the language it will pick a code of conduct in. What disconnects a login: a version mismatch, a ban, a full whitelist-only server, a failed session check on a non-singleplayer host, an unexpected custom-query answer (`ServerLoginPacketListenerImpl.State.NEGOTIATING` is declared and never assigned — `ClientboundCustomQueryPacket` decodes every payload as `DiscardedQueryPayload`, and an answer just disconnects), or six hundred ticks. ## Configuration ```mermaid flowchart LR S["startConfiguration: BrandPayload, server links, enabled features, outside the queue"] --> R["SynchronizeRegistriesTask"] R --> C["ServerCodeOfConductConfigurationTask, if the server has one"] C --> P["ServerResourcePackConfigurationTask, if the server has one"] P --> W["returnToWorld appends the last two"] W --> PS["PrepareSpawnTask: Preparing, then Ready"] PS --> J["JoinWorldTask sends ClientboundFinishConfigurationPacket, terminal"] J --> F["handleConfigurationFinished: outbound play, the gate again, then spawnPlayer"] ``` `SynchronizeRegistriesTask` is the reason configuration exists, and the queue around it is strictly serial. `ServerConfigurationPacketListenerImpl.startConfiguration` sends three things outside the queue — the server's `BrandPayload`, `ClientboundServerLinksPacket` if there are links, and `ClientboundUpdateEnabledFeaturesPacket` — then queues the registry task, a code-of-conduct task if the server has one and a resource-pack task if it has one, before `ServerConfigurationPacketListenerImpl.returnToWorld` appends `PrepareSpawnTask` and `JoinWorldTask` and starts the first. Each `ConfigurationTask` finishes before the next begins: `ServerConfigurationPacketListenerImpl.finishCurrentTask` rejects a completion naming the wrong task type, and an exception out of any task disconnects the client. **Registry and tag sync.** The task begins with `ClientboundSelectKnownPacks`, a list of `KnownPack` records naming, by namespace, id and version, those of the server's packs that declare one — `PackLocationInfo.knownPackInfo` is an optional, so a world's own datapack is simply absent from the request; the client matches them against its bundled vanilla repository through `KnownPacksManager.trySelectingPacks` and replies with the subset it recognises. If that reply is not *exactly* the requested list — same packs, same order — the server discards the negotiation and re-sends everything; it is all or nothing, never a per-pack intersection. Then one `ClientboundRegistryDataPacket` **per registry**, walking `RegistryDataLoader.SYNCHRONIZED_REGISTRIES`, each element a `RegistrySynchronization.PackedRegistryEntry` whose data is omitted when the element came from a pack the client already has — the entire point of the negotiation — written as NBT with the registry's own element codec. Then one `ClientboundUpdateTagsPacket` covering the **networkable** registries and the static ones too — the surprise, since the data packets are dynamic-only — with empty payloads dropped ([tags](../foundations/tags.md)). On the client, `RegistryDataCollector` accumulates the contents and the tags and only resolves them at `ClientConfigurationPacketListenerImpl.handleConfigurationFinished`, loading them on a background executor against the negotiated packs — and blocking on the result, so the load is dispatched away rather than genuinely asynchronous — before constructing the `ClientPacketListener` with the finished `RegistryAccess`. In singleplayer the result is narrowed to the server's own objects, so both sides share instances. **The seam is not where it looks.** Four of the server's configuration handlers hop to the main thread — `ServerConfigurationPacketListenerImpl.handleSelectKnownPacks`, `ServerConfigurationPacketListenerImpl.handleConfigurationFinished` and, from the common base, the resource-pack response and `ServerCommonPacketListenerImpl.handleCustomClickAction` — while the client-information and code-of-conduct handlers, like the keep-alive and ping handlers they inherit, stay on the Netty thread. That last one is load-bearing: accepting a code of conduct finishes a task, which **starts the next task on the Netty thread**, and the next task may be `PrepareSpawnTask`, whose first act is to read a player save file and resolve a spawn position. `ServerConfigurationPacketListenerImpl.tick` runs each tick to drive the current task and keep the spawn chunks loaded. **`PrepareSpawnTask` is two states, and the player is born in neither.** Its `PrepareSpawnTask.Preparing` state reads the save file for a stored position, resolves a level, runs `PlayerSpawnFinder.findSpawn`, takes a `TicketType.PLAYER_SPAWN` ticket at `PrepareSpawnTask.PREPARE_CHUNK_RADIUS` and waits — which is what `ConfigurationTask.tick` exists for. When the chunks arrive it becomes `PrepareSpawnTask.Ready`, reports itself finished and does nothing further except re-arm the ticket every tick through `PrepareSpawnTask.keepAlive`. `JoinWorldTask` then sends the terminal packet. Only when the *client's* `ServerboundFinishConfigurationPacket` arrives does `ServerConfigurationPacketListenerImpl.handleConfigurationFinished` swap the outbound protocol to play, re-run the duplicate-player check and `PlayerList.canPlayerLogin` — because a ban or a full server can arrive in the seconds a configuration takes — and call `PrepareSpawnTask.spawnPlayer`, which constructs the `ServerPlayer`, reads the save data into it — a second read, the first having happened when the task started — and hands it to `PlayerList.placeNewPlayer`, which installs the inbound play protocol. [Players and sessions](../server/players-and-sessions.md) owns the rest of that story. Everything between the join task and that handler is a server holding a ticket on chunks for a player that does not exist. What disconnects a configuration: a task that throws on start or on tick, a completion for the wrong task, a ban or a full server at the second check, and an exception while placing the player. ## Play, and the way back The play protocol is installed from two different places on each side: the server swaps outbound a line into the finish handler and inbound inside `PlayerList.placeNewPlayer`; the client swaps inbound, sends the finish packet, then swaps outbound. That is why the server can be encoding play packets while still nominally in the configuration listener. Chat session keys are not part of any of this: they are negotiated in play, after the client learns the server's mode from `ClientboundLoginPacket` ([chat and signing](chat-and-signing.md)). **A reconfigure does not re-run configuration.** `ServerGamePacketListenerImpl.switchToConfig` removes the player from the world, sends `ClientboundStartConfigurationPacket` and swaps outbound; the client's `ClientPacketListener.handleConfigurationStart` flushes its chat queue, stashes the chat state and carries the registry access, feature flags, brand and server links forward in a rebuilt cookie, then answers `ServerboundConfigurationAcknowledgedPacket`; and `ServerGamePacketListenerImpl.handleConfigurationAcknowledged` installs a configuration listener **without** calling `ServerConfigurationPacketListenerImpl.startConfiguration`. No registries, tags, feature flags or brand are re-sent, and none need to be. The player parks in configuration with an empty queue until `ServerConfigurationPacketListenerImpl.returnToWorld` re-queues the spawn and join tasks. Both directions are reachable in vanilla only from `DebugConfigCommand`. ## What the phases leave unused **Seven packets are terminal**, and four of them are the two handshakes that bracket configuration; `ClientIntentionPacket` is one of the seven, so the very first packet of a connection already tears out the codec that decoded it. **The creative-inventory packet is filtered at the codec, asymmetrically.** `GameProtocols.HAS_INFINITE_MATERIALS` refuses to encode or decode the packet when its context says the connection is not in creative — but the client's own context answers `GameProtocols.Context.hasInfiniteMaterials` true unconditionally, while the server's is `ServerGamePacketListenerImpl` answering from the real player, so the symmetric modifier bites on exactly one side ([packets and stream codecs](packets-and-stream-codecs.md)). Compression is asymmetric the same way: the server validates that a compressed frame really was above the threshold; the client does not. **Cookies, transfers and the chat reset are proxy hooks.** A `ServerboundCookieResponsePacket` arriving at any server listener is an unexpected query and a disconnect, and nothing in the tree constructs `ClientboundStoreCookiePacket` or `ClientboundCookieRequestPacket`. Of the transfer machinery only `ClientboundTransferPacket` has a vanilla caller, `/transfer`: it sends the client to another server, which sees a `ClientIntent.TRANSFER` handshake and a transferred flag in its cookie, and `ClientCommonPacketListenerImpl.shouldHandleMessage` keeps accepting store-cookie and transfer packets while a transfer is in flight, which is what lets a proxy's trailing state land. `ClientboundResetChatPacket` is registered and handled and never sent. The rest is fully implemented on the client and unused by the server. > **For a 1.21-era reader.** The assumption that "packet handlers run on > the game thread" is exactly backwards for the first two phases: the > handshake and login listeners run to completion on the Netty thread, and > the first `PacketUtils.ensureRunningOnSameThread` in a connection's life > is in configuration. The connection is encrypted before it is > authenticated — the server installs both ciphers while handling the key > packet, before its own session-service call has begun. ## Where to look `ConnectionProtocol` · `ProtocolInfo` · `ProtocolInfoBuilder` · `ServerHandshakePacketListenerImpl` · `ServerStatusPacketListenerImpl` · `ServerLoginPacketListenerImpl` · `ServerConfigurationPacketListenerImpl` · `ClientHandshakePacketListenerImpl` · `ClientConfigurationPacketListenerImpl` · `ConfigurationTask` · `SynchronizeRegistriesTask` · `PrepareSpawnTask` · `JoinWorldTask` · `RegistrySynchronization` · `KnownPack` · `Crypt` · `ServerCommonPacketListenerImpl` · `CommonListenerCookie` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # What the client is told > Verified against **Minecraft 26.2** · Part IX · a creeper walks into view: everything the server decides to say, in order, and everything it decides not to. A creeper three chunks away steps across a section boundary, and inside that tick the server decides a player may know about it. One bundled packet goes out, and the position in that packet is not where the creeper is. It is where the creeper's tracker last *said* it was — stale by at least the entity's update interval and, for an entity sitting in a chunk that is loaded but not ticking, stale by no bounded amount at all. That is not a bug being tolerated. Every viewer has to start dead reckoning from an identical base, so **the server sends the base rather than the truth**, and the rest of this page is that trade made over and over: which chunks a player is sent and how fast, which entities they are told about, and what counts as a change worth a packet. ## The cast | class | what it decides | thread | |---|---|---| | `ChunkMap` | which players can see which chunks and which entities — it owns both maps | Server | | `ChunkMap.TrackedEntity` | one entity's audience, `ChunkMap.TrackedEntity.seenBy`, and the range test that fills it | Server | | `ServerEntity` | the change detector: the dead-reckoning baseline, the interval gate, the packet shape | Server | | `ChunkTrackingView` | which chunks a player has been sent, and what a movement turns into | Server | | `PlayerChunkSender` | how many chunks leave for one player this tick | Server | | `ChunkHolder` | the per-chunk block-change batch, one flush a tick | Server | | `EntityType` | the per-type tracking range, update interval and delta exclusion | static, from the builder | | `ChunkBatchSizeCalculator` | the client's answer — how many chunks a tick it wants | client Netty thread, and it never hops | ## One entity's tick, and the gates it does not pass ```mermaid flowchart TD TICK["ChunkMap.tick walks every tracked entity, in the chunk-source phase, before entities tick"] --> SEC{"did the entity change section"} SEC -- yes --> UP["ChunkMap.TrackedEntity.updatePlayer, once per player in the level"] UP --> G1{"gate 1: three conjuncts, all required"} G1 -- "horizontal range, and Entity.broadcastToPlayer, and ChunkMap.isChunkTracked" --> IN["seenBy gains the connection: ServerEntity.addPairing sends the introduction bundle"] G1 -- "any one false" --> OUT["seenBy loses it: ClientboundRemoveEntitiesPacket"] SEC -- no --> G2{"gate 2: is the change detector called at all"} IN --> G2 OUT --> G2 G2 -- "changed section, or Entity.needsSync, or the chunk is in entity-ticking range" --> SC["ServerEntity.sendChanges"] G2 -- "none of the three" --> MUTE["nothing, and the call counter does not advance"] SC --> FREE["past gate 3 only: a changed passenger list, an item frame every tenth call, and Entity.hurtMarked knockback"] SC --> G3{"gate 3: three disjuncts, any one opens it"} G3 -- "the call count is a multiple of EntityType.updateInterval, or Entity.needsSync, or the synched data is dirty" --> D["three decisions"] G3 -- "none of the three" --> WAIT["wait for a later call"] D --> D1{"relative or absolute"} D1 -- "the delta fits a short, precision is not demanded, the ground flag held, it was not riding, and the teleport delay is within ServerEntity.FORCED_TELEPORT_PERIOD" --> REL["ClientboundMoveEntityPacket.Pos, .Rot or .PosRot"] D1 -- otherwise --> ABS["ClientboundEntityPositionSyncPacket, and the teleport delay resets"] D --> D2["head yaw: its own ClientboundRotateHeadPacket, whenever it moved by a byte"] D --> D3["velocity: ClientboundSetEntityMotionPacket, only for a tracked-delta type, a needsSync, or an elytra flight"] ``` The figure is the page. Three gates stand between an entity moving and a player hearing about it, and each is a three-term test: gate 1 is a conjunction, where all three must hold, and gates 2 and 3 are disjunctions, where any one term is enough. What comes out the bottom is not a description of the entity but a description of the *difference* between the entity and what this viewer was last told. The sections below are one per gate, then one per feed that does not go through them at all. > **For a 1.21-era reader.** `PlayerChunkSender` is in `server/network`, not > `server/level`. And routine movement no longer travels as > `ClientboundTeleportEntityPacket`: that packet survives, but `ServerEntity` > never touches it, and an absolute position is > `ClientboundEntityPositionSyncPacket`. ### Gate 1: who is allowed to see it `ChunkMap.tick` re-tests visibility only when something moved between sections — the entity's own section against the tracker's last, and, for players who changed section, every entity against that player. `ChunkMap.move` does the same eagerly the moment a player crosses a boundary ([tickets and loading](../world/tickets-and-loading.md)). The test itself is three conjuncts. **The distance is horizontal.** `ChunkMap.TrackedEntity.updatePlayer` compares squared *x/z* distance against the smaller of the entity's effective range and the player's view distance in blocks. **Y is ignored entirely** — an entity directly above you, at any height, is in range. **The range is the maximum over the whole vehicle stack.** `ChunkMap.TrackedEntity.getEffectiveRange` takes the largest `EntityType.clientTrackingRange` among the entity and all its indirect passengers, then scales it by `MinecraftServer.getScaledTrackingDistance`, which both server classes override: `DedicatedServer` applies the *entity-broadcast-range-percentage* property, and `IntegratedServer` applies the client's own **Entity Distance** video option. In singleplayer a graphics slider decides how far away mobs are tracked. **And the chunk must already be there.** `Entity.broadcastToPlayer` looks like a per-entity hiding hook and is not: it defaults to true and is overridden exactly once, by `ServerPlayer`, to make spectators see only what they are spectating and to keep a spectator out of everyone else's view. And `ChunkMap.isChunkTracked` is false while the chunk is still queued in `PlayerChunkSender`, so an entity is never sent before the ground it stands on — the ordering is guaranteed rather than hoped for. One case never reaches the test at all: `ChunkMap.TrackedEntity.updatePlayer` returns immediately for the player's own entity. **You never track yourself.** That is why `ServerEntity.Synchronizer` has three verbs and not one — `ServerEntity.Synchronizer.sendToTrackingPlayers`, `ServerEntity.Synchronizer.sendToTrackingPlayersAndSelf` and `ServerEntity.Synchronizer.sendToTrackingPlayersFiltered` — and why damage, knockback and synched data must all reach for the self-directed one. ### The introduction is one bundle ```mermaid sequenceDiagram participant CM as ChunkMap participant CMTE as ChunkMap.TrackedEntity participant SE as ServerEntity participant CPL as ClientPacketListener CM->>CMTE: the creeper changed section, so re-test every player CMTE->>CMTE: range, veto and chunk-tracked all hold, so seenBy gains this connection CMTE->>SE: addPairing SE->>SE: sendPairingData fills one list, in a fixed order SE->>CPL: one ClientboundBundlePacket Note over SE,CPL: add-entity at the tracker baseline, then synched data, attributes, equipment, passengers, leash CPL->>CPL: the bundle is applied inside one task, so nothing renders half-built ``` `ServerEntity.sendPairingData` produces the list and `ServerEntity.addPairing` sends it as a single `ClientboundBundlePacket`, so the creeper can never be seen half-initialised. The synched values are not read fresh: they come from `ServerEntity.trackedDataValues`, the cached snapshot of the entity's non-default values, refreshed whenever dirty data is flushed ([synched entity data](../entities/synched-entity-data.md)). Only the syncable attributes go ([attributes](../entities/attributes.md)), and equipment, passengers and leash links go only if there are any. The add packet is the page's hook, and it has three exceptions and two refusals. Paintings, item frames and leash knots build their own `ClientboundAddEntityPacket` from their real position, bypassing `ServerEntity` entirely — they are the three `BlockAttachedEntity` subclasses, and a block-attached entity has no dead reckoning to agree about. `EnderDragonPart` refuses outright, and `ChunkMap` never asks it. `Marker` throws outright if anyone asks — which nobody does, because its tracking range is zero and `ChunkMap` never tracks it. Everything else reads its position from `ServerEntity.getPositionBase` and its rotations and motion from the last-sent fields, however old they are. ### Gate 2: whether the detector is called at all `ChunkMap.tick` calls `ServerEntity.sendChanges` when *any* of three things is true: the entity changed section, `Entity.needsSync` is set, or its chunk is in entity-ticking range. Two public fields on `Entity` do the forcing. `Entity.needsSync` appears at this gate, again at gate 3 and again in the velocity decision — three of the cascade's terms are the same flag — and is set by being pushed, by being loaded from disk, and by a couple of dozen classes for their own reasons. `Entity.syncPosition` is subtler: it re-phases the call counter to the next interval boundary, so a bounced entity syncs at once rather than up to an interval late. The two counters inside `ServerEntity` are deliberately out of step. `ServerEntity.tickCount` advances on every call, gate 3 open or shut, so `ServerEntity.FORCED_POS_UPDATE_PERIOD` counts *calls*. `ServerEntity.teleportDelay` advances only inside gate 3, so `ServerEntity.FORCED_TELEPORT_PERIOD` counts *gated* calls — the forced absolute sync is rarer than the forced position packet by however long the interval gate stays shut. This is also where a distant entity goes quiet, and the silence is conditional. Being out of entity-ticking range only suppresses the detector while the entity *also* stays inside its section and leaves `Entity.needsSync` clear. Either of those breaks it, which is why a far-off mob can freeze for a long time and then correct itself in one jump. ### Gate 3, and the position it chooses | condition | result | |---|---| | squared position delta below `ServerEntity.TOLERANCE_LEVEL_POSITION` and rotation within `ServerEntity.TOLERANCE_LEVEL_ROTATION` | nothing sent | | otherwise, and no forcing condition | `ClientboundMoveEntityPacket.Pos`, `.Rot` or `.PosRot` | | every `ServerEntity.FORCED_POS_UPDATE_PERIOD` calls, gated or not | a position packet regardless | | delta beyond what a short can hold — about eight blocks | absolute sync | | `ServerEntity.teleportDelay` past `ServerEntity.FORCED_TELEPORT_PERIOD` | absolute sync | | the entity just dismounted, or its ground flag flipped | absolute sync | | `Entity.getRequiresPrecisePosition` | absolute sync | | the entity is a passenger | rotation only — the base is silently re-set, and the next free call forces an absolute sync | Rotations are single bytes, so one unit is a little over a degree, and the dead-reckoning base advances only when something was actually sent: that is what keeps the two sides' arithmetic identical. An arrow never takes a partial path — `AbstractArrow` is excluded from the position-only and rotation-only branches, so every open gate sends it a full position-and-rotation packet. A minecart on the new movement behaviour skips the table altogether: `ServerEntity.handleMinecartPosRot` diverts it into `ClientboundMoveMinecartPacket`, which carries a list of steps rather than one position. The first term in that decision has exactly one caller in the whole game, and it is a happy ghast. `Entity.setRequiresPrecisePosition` is asked for by a ghast on its still timeout and by nothing else — a large ridable platform is the one entity whose rounding error a player has to stand on. Velocity is the third decision and a separate channel. When the entity wants deltas — it is in the `EntityType.trackDeltas` set, `Entity.needsSync` is set, or it is a `LivingEntity` currently elytra-flying — `ServerEntity` compares the current delta movement against `ServerEntity.lastSentMovement` and sends `ClientboundSetEntityMotionPacket`, bundled with `ClientboundProjectilePowerPacket` for a hurtling projectile. `EntityType.trackDeltas` looks like a third tracking parameter but is a hardcoded exclusion list: players, llama spit, the wither, bats, item frames, leash knots, paintings, end crystals and evoker fangs are out, everything else is in. ### What goes out around the gates Four feeds ignore gate 3, and between them they explain most of what still feels responsive about a distant mob. Only gate 3: all four are inside `ServerEntity.sendChanges`, which gate 2 decides whether to call at all, so none of them helps a mob outside entity-ticking range. `Entity.hurtMarked` sends a motion packet to the trackers *and* the entity itself, which is why knockback is immediate on a creeper whose position otherwise updates slowly. A changed passenger list is diffed on every call and goes out *filtered*, and the filter is the surprise: it excludes the player whose own passenger status changed, because that player has already been told directly by `ServerPlayer.startRiding` or `ServerPlayer.removeVehicle`. An `ItemFrame` iterates *every player in the level* — not its trackers — every tenth call, to flush its synched data and, if it holds a map, push map updates. Equipment *changes* do not pass through `ServerEntity`. `ClientboundSetEquipmentPacket` comes from `LivingEntity.handleEquipmentChanges`, and it goes to the trackers *without* the self-directed variant, so a player is never sent their own equipment. A straight main-hand to off-hand swap does not even get that far: `LivingEntity.handleHandSwap` compresses it into a one-byte entity event. And damage crosses without a number — `ClientboundDamageEventPacket` carries the source, not the amount, and the health bar moves because of a separate synched value ([damage and death](../entities/damage-and-death.md)). ## Chunks arrive on a loop the client paces Chunk visibility is the same kind of decision one level up: made per player rather than per entity, and paced by a loop whose set point the client supplies. ### Which chunks enter and leave `ChunkMap.applyChunkTrackingView` diffs the player's old and new `ChunkTrackingView`; entering chunks are queued with `PlayerChunkSender.markChunkPendingToSend`, leaving chunks are dropped with `ClientboundForgetLevelChunkPacket` — unless they were still only pending, in which case they leave the queue silently, because you cannot forget what was never delivered. A chunk that merely *becomes* ready is queued by `ChunkMap.onChunkReadyToSend`, and a moved centre sends `ClientboundSetChunkCacheCenterPacket` first. The region is neither a disc nor a square. `ChunkTrackingView.isWithinDistance` shrinks each axis delta by a small buffer *before* the squared compare, so it reaches a chunk further along each axis than it does diagonally, and `ChunkTrackingView.Positioned` iterates one chunk beyond the view distance for exactly that reason. `ChunkTrackingView.difference` is what turns a movement into an enter/leave pair. ### The rate the client asks for Then, once per tick, `PlayerChunkSender.sendNextChunks` runs per player from the phase `MinecraftServer.tickChildren` reaches after every level has ticked ([the server tick](../server/server-tick.md)): - it stops if too many batches are unacknowledged — `PlayerChunkSender.maxUnacknowledgedBatches` **starts at one** and is raised to `PlayerChunkSender.MAX_UNACKNOWLEDGED_BATCHES` on the first reply, so the first batch after login is a hard round-trip barrier; - it accumulates `PlayerChunkSender.batchQuota` by the client's desired rate and stops if it is below one; - it takes that many chunks **nearest first** from `PlayerChunkSender.pendingChunks` — or, on a memory connection, or whenever fewer are pending than the budget allows, all of them at once; - and it brackets them with `ClientboundChunkBatchStartPacket` and `ClientboundChunkBatchFinishedPacket`. The client times the bracket in `ChunkBatchSizeCalculator`, clamps the sample against the running average by `ChunkBatchSizeCalculator.CLAMP_COEFFICIENT`, folds it into a weighted mean against `ChunkBatchSizeCalculator.MAX_OLD_SAMPLES_WEIGHT`, and reports a rate back in `ServerboundChunkBatchReceivedPacket`. The server clamps that between `PlayerChunkSender.MIN_CHUNKS_PER_TICK` and `PlayerChunkSender.MAX_CHUNKS_PER_TICK` and uses it as next tick's budget. It is a closed control loop, and it is the client that sets the set point. **Seven milliseconds** — the client time per tick that `ChunkBatchSizeCalculator.getDesiredChunksPerTick` divides by its running estimate of nanoseconds per chunk. It starts pessimistic, at two milliseconds a chunk — three and a half chunks a tick against `PlayerChunkSender.START_CHUNKS_PER_TICK`, nine. That opening figure is never actually sent: the client folds the first real batch into the average before it answers, so the first number the server hears is already measured. What is being measured is narrower than it looks. `ClientPacketListener.handleChunkBatchStart` and `ClientPacketListener.handleChunkBatchFinished` are two of the nine handlers on the client's play listener that never hop off the network thread, so the loop is timing packet decode, not mesh building (the nine are listed in [threads](../../reference/threads.md#the-nine-client-handlers-that-never-hop)). ### What a chunk packet carries A chunk packet — `ClientboundLevelChunkWithLightPacket` — carries only the client-facing heightmaps, every section's paletted block states and biomes, a block-entity entry per block entity holding `BlockEntity.getUpdateTag` rather than its save data, and the light layers. See [chunk anatomy](../world/chunk-anatomy.md) and [lighting](../world/lighting.md). ## Block changes: one flush a tick, two audiences `ServerLevel.sendBlockUpdated` sends nothing. It marks a section dirty on the `ChunkHolder` — in `ChunkHolder.changedBlocksPerSection`, one short set per section — and adds the holder to a set on `ServerChunkCache`, unless the chunk is loaded but not ticking, in which case nothing is recorded and the change is never broadcast to anyone. Once a tick `ServerChunkCache.broadcastChangedChunks` drains that set through `ChunkHolder.broadcastChanges`, early in the level tick and before entities move ([the level tick](../server/server-level-tick.md)) — so one broadcast carries this tick's block changes and the previous tick's entity-driven ones. **Light goes first, and to a strictly smaller audience.** If either of `ChunkHolder.skyChangedLightSectionFilter` and `ChunkHolder.blockChangedLightSectionFilter` is non-empty, one `ClientboundLightUpdatePacket` goes only to players for whom this chunk is on the *border* of their sent region (`ChunkMap.isChunkOnTrackedBorder`). A player standing in the middle of their own loaded area is never sent light for the chunk they are standing in: their own light engine is expected to derive it ([lighting](../world/lighting.md)). **Then blocks, to everyone tracking the chunk.** Exactly one changed block in a section becomes a `ClientboundBlockUpdatePacket`, two or more become a `ClientboundSectionBlocksUpdatePacket`, and every change within the tick collapses into at most one packet per section. **And block entities alongside the blocks, not after them.** The check runs inside the same per-section loop, immediately after that section's own update packet — interleaved, not a third pass. `ChunkHolder.broadcastBlockEntityIfNeeded` calls `BlockEntity.getUpdatePacket`, which returns null by default, so only overriding types produce a `ClientboundBlockEntityDataPacket`. The fallback is not "it rides the chunk packet instead": the chunk packet carries `BlockEntity.getUpdateTag`, which is *also* empty by default, and an empty tag is stored as nothing at all. A block entity that overrides neither tells the client its position and its type and nothing else — which is why chest contents are invisible until the chest is opened ([block entities](../blocks/block-entities.md)). ### The rest of the block-shaped traffic It is small: `ClientboundBlockEventPacket` from the deferred event set ([pistons and block events](../blocks/pistons-and-block-events.md)), `ClientboundBlockDestructionPacket` for other players' mining progress, `ClientboundChunksBiomesPacket` when biomes are re-sent, and `ClientboundBlockChangedAckPacket`, sent at most once per connection per tick and on any tick where the client sent a block action, a use-on or a use — **including an unsequenced abort, which produces an ack of zero and settles nothing**. The rules that receipt obeys belong to [prediction and acknowledgement](../client/prediction-and-acks.md). ## The level's own feeds Entities and chunks are the two big feeds. The level itself has several small ones, all bypassing the change detectors entirely — most on `ServerLevel`, though time comes from `MinecraftServer` and the view distances from `PlayerList`: - **Time**, once a second. `MinecraftServer.forceGameTimeSynchronization` runs every twentieth tick, and the packet carries a game time plus a map of clock updates rather than a single day-time number ([environment attributes and timelines](../world/environment-attributes-and-timelines.md)). - **Weather**, on change. `ServerLevel.advanceWeatherCycle` broadcasts rain- and thunder-level changes and the start/stop pair as `ClientboundGameEventPacket`s, and `PlayerList` re-sends the same set to a joining player. - **Sounds, level events, particles and entity events**, each with its own helper and its own audience. Two radii are worth naming because they are not the tracking distance: another player's mining progress reaches everyone within thirty-two blocks except the miner, and a block event reaches sixty-four. - **View distances**, as `ClientboundSetChunkCacheRadiusPacket` and `ClientboundSetSimulationDistancePacket` — the two integers that are the client's entire knowledge of the ticket system. Receiving the first also rebuilds the client's chunk storage array. - **The debug feed.** `ServerLevel` owns a set of per-subscriber debug synchronizers that push neighbour updates, POI state, chunk sends and entity tracking to a client that has opted in. Everything in the next section is invisible *except* through that channel. ## What the client is never told | what never crosses | the server-side owner | where it is explained | |---|---|---| | all AI — targets, goals, brains, paths | `Mob.goalSelector`, `Mob.targetSelector`, `Mob.getTarget`, `Brain`, `Mob.navigation` | [AI, goals and brains](../entities/ai-goals-and-brains.md) | | scheduled block and fluid ticks — the client's equivalents are empty | `ServerLevel.blockTicks`, `ServerLevel.fluidTicks` | [scheduled ticks](../world/scheduled-ticks.md) | | points of interest, except through the debug channel | `ChunkMap.poiManager` | [points of interest](../world/points-of-interest.md) | | the ticket graph — the client gets a radius and a simulation distance, as two integers | `TicketStorage`, `DistanceManager`, `ChunkHolder.ticketLevel`, `FullChunkStatus` | [tickets and loading](../world/tickets-and-loading.md) | | worldgen, and **the world seed** — `ClientLevel` gets only a biome zoom seed | `ChunkGenerator`, `RandomState`, `ServerLevel.structureManager`, `StructureStart` | [the generation pipeline](../world/chunk-generation-pipeline.md) | | the worldgen heightmaps, and any block-entity field outside `BlockEntity.getUpdateTag` | `BlockEntity` | [block entities](../blocks/block-entities.md) | | non-syncable attributes, loot tables and loot seeds, the natural spawn state, raids and the dragon fight | `AttributeMap`, `LootTable`, `NaturalSpawner` | [attributes](../entities/attributes.md) | | game rules — they reach the client only on request, and only for a player with the command permission | `GameRules` | [level data and rules](../../reference/level-data-and-rules.md) | | the creeper's fuse length and its swell counter — of its three synched values, none is the counter | `Creeper` | [synched entity data](../entities/synched-entity-data.md) | | everything outside the disc: entities past tracking range, chunks past the view, and every other level on the server | `ChunkMap`, `MinecraftServer.levels` | — | ## Questions players ask **Why does a mob above me appear out of nowhere?** Because visibility ignores Y. The test is a horizontal disc, so an entity directly overhead is in range at any height, while one a few blocks further out is not, at any height. **Why do I see mobs further away in singleplayer after changing a graphics setting?** `IntegratedServer` scales every tracking range by the client's Entity Distance video option. On a dedicated server the same hook reads the *entity-broadcast-range-percentage* property instead. **Why does a distant mob freeze and then jump?** Its chunk is out of entity-ticking range, so gate 2 is shut — until the mob crosses a section boundary or something sets `Entity.needsSync`, at which point one packet carries the whole accumulated difference. **Why is knockback instant when the same mob's walking looks choppy?** `Entity.hurtMarked` is checked past gate 3, while the position it is knocked into waits for the interval. Past gate 3 and no further: a mob outside entity-ticking range that has not changed section fails gate 2, and its knockback waits with everything else. **Why can I not see what is in a chest until I open it?** Because `BlockEntity.getUpdateTag` is empty by default, so the chunk packet carries the chest's position and type and nothing else, and `BlockEntity.getUpdatePacket` is null by default, so no update packet follows it. **Why does the first bit of world take a moment, and then the rest floods in?** The first chunk batch is a synchronous round trip: one batch in flight until the client's first acknowledgement, ten after it. **Why do I never see my own armour appear on my own body?** Equipment goes to the trackers without the self-directed variant, and you are not one of your own trackers. ## Choosing what the client may be wrong about Every gate above is a decision *not* to say something, and the server can afford them because the receiver is not passive. `ClientLevel` never admits a chunk is missing, runs its own light engine and its own clock, ticks entities it does not own — `Creeper.tick` runs locally, swell counter and all — and guesses at block placements through a sequence-numbered ledger. That half of the story is Part X's: [the client level](../client/the-client-level.md) for what the receiver fakes and simulates, [prediction and acknowledgement](../client/prediction-and-acks.md) for what it guesses, and [authority](../entities/authority.md) for which side is allowed to move what. The client also applies a whole burst of these packets once per frame, before that frame's ticks, so at a high frame rate it takes the server's updates far more often than it ticks — the two-loops figure in [anatomy](../anatomy/anatomy.md) is the shape, [the client loop](../client/the-client-loop.md) the detail. Which is the design constraint under all of it: because the client will happily simulate in the absence of data, **the server's job is not to keep the client correct — it is to choose what the client is allowed to be wrong about.** ## Where to look `ChunkMap.tick` · `ChunkMap.TrackedEntity.updatePlayer` · `ChunkMap.isChunkTracked` · `ServerEntity.addPairing` · `ServerEntity.sendPairingData` · `ServerEntity.sendChanges` · `VecDeltaCodec` · `ChunkTrackingView.difference` · `PlayerChunkSender.sendNextChunks` · `ChunkBatchSizeCalculator` · `ChunkHolder.broadcastChanges` · `ServerChunkCache.broadcastChangedChunks` · `ServerLevel.sendBlockUpdated` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Chat and signing > Verified against **Minecraft 26.2** · Part IX · A player presses T, types a line and hits enter: the message is signed on the way out, taken apart on the way in, and verified again by every client that draws it. A player presses T, types *hey* and hits enter. Before the line leaves the machine, `ChatScreen.normalizeChatMessage` has squeezed the whitespace and cut it to 256 characters, and `ClientPacketListener.sendChat` has taken a timestamp, a random salt and the signatures of the twenty messages the player most recently saw, and signed all of it with a key Mojang issued to that account. The server pulls the packet apart on the Netty thread, hands the cryptography to the Server thread, filters it, decorates it, broadcasts it — and every receiving client verifies the signature again before drawing a character. Every one of those steps can say no, and *no* does not mean the same thing twice. Forge a signature and you stay connected: you get a red line and your **chain** dies, so everything else you say this session fails too. Miscount which messages you have *seen* — a number nothing in the game shows you — and the server closes the connection mid-sentence. **The bookkeeping is defended harder than the cryptography is**, and that is the right way round. ## The cast | class | what it decides | thread | |---|---|---| | `ServerGamePacketListenerImpl` | the order the checks run in, and which failure closes the connection | Netty for the window, the characters and the chat-visibility refusal, Server for everything after | | `LastSeenMessagesValidator` | whether the client's twenty-slot acknowledgement still matches the server's mirror | Netty, inside a lock on itself | | `SignedMessageChain.Decoder` | whether this message continues the sender's chain — and whether the chain survives the answer | Server | | `PlayerChatMessage` | what a signature covers: the chain link, the content, the timestamp, the salt, the window | wherever a message is built | | `MessageSignatureCache` | which of those signatures travel as a small index instead of 256 bytes | both sides, one 128-slot cache each | | `PlayerList` · `OutgoingChatMessage` | who gets a copy, and whether it goes as a signed message or a disguised one | Server | | `SignedMessageValidator.KeyBased` | whether the receiving client believes the sender said this | Render | | `ChatTrustLevel` | secure, modified or not secure — the tag drawn beside the line | Render | ## A message is not the text you see A message on the wire is a `PlayerChatMessage`: a `SignedMessageLink` saying where in the sender's chain it sits, a `MessageSignature`, a `SignedMessageBody` of exactly four fields, and — optionally — a `Component` to display *instead of* the signed string. That `Component` is Part II's subject ([text components](../foundations/text-components.md)); all this page needs from it is that it is a different object from the signed text and that the signature does not cover it. Vanilla's *decorator* never produces one: `MinecraftServer.getChatDecorator` is hard-coded to `ChatDecorator.PLAIN`, so `PlayerChatMessage.withUnsignedContent` always finds the decorated copy equal to the original and drops it. But vanilla sends unsigned content by another road entirely — `MessageArgument.resolveChatMessage`, the message argument behind `/msg`, `/say` and `/tell`, sets it on every message it resolves, because it has expanded the entity selectors in the text. Type a selector into a whisper and the recipient sees a string the signature does not cover. ## One line, typed and delivered ```mermaid sequenceDiagram participant CScr as ChatScreen participant CPL as ClientPacketListener participant SGPL as ServerGamePacketListenerImpl participant PL as PlayerList participant RCPL as ClientPacketListener participant CLis as ChatListener Note over RCPL: RCPL is the recipient's client, CPL the sender's CScr->>CPL: whitespace squeezed, cut to 256 characters CPL->>CPL: timestamp, salt, the last-seen window, then sign CPL->>SGPL: ServerboundChatPacket SGPL->>SGPL: Netty thread, apply the last-seen update, check the characters Note over SGPL: everything below is a task queued on the Server thread SGPL->>SGPL: SignedMessageChain.Decoder.unpack, which verifies the signature SGPL->>SGPL: start the filter, decorate at once, join them in a FutureChain SGPL->>PL: broadcastChatMessage, bound to ChatType.CHAT PL->>RCPL: ClientboundPlayerChatPacket, signatures packed to cache ids RCPL->>RCPL: check the global index, unpack the cache ids, verify the signature RCPL->>CLis: handlePlayerChatMessage, trust level, blocklist, delay queue CLis->>RCPL: markMessageAsProcessed RCPL->>SGPL: ServerboundChatAckPacket, once the offset passes 64 ``` Four things in that picture are worth naming before the checks are. **The client signs the conversation, not just the sentence.** The window it signs is the window it now treats as acknowledged, so the signature binds the context the sender had in front of them — which is what makes a report show what a message was a reply *to*. **The hop is deliberate.** `ServerGamePacketListenerImpl.handleChat` never calls the usual same-thread guard: the window and the character check run on the Netty thread, and only then does `ServerGamePacketListenerImpl.tryHandleChat` post the rest to the server. That posted task, and the `FutureChain` continuation that joins the text filter to it, drain with every other queued server task — so a slow filter service delays delivery by however many ticks it takes ([the server tick](../server/server-tick.md)). **Decoration is not sequenced after filtering.** The handler starts the filter future, decorates immediately and synchronously, and only then registers the continuation that joins the two. A decorator never sees filtered text. **Broadcast is per recipient, and gated in three places** — two before the message is built for that player, and `ServerPlayer.shouldFilterMessageTo` inside it. `PlayerList.broadcastChatMessage` logs the line — marked *Not Secure* by `PlayerList.verifyChatTrusted` if it has no signature or has expired — and then offers it to every player without testing anything. `ServerPlayer` drops it unless that player's setting is `ChatVisiblity.FULL`; `OutgoingChatMessage.Player` applies the per-recipient filter mask and skips a copy that was filtered away entirely, telling the *sender* so. A message whose sender is `Util.NIL_UUID` is a system message and leaves as an unsigned, unreportable `ClientboundDisguisedChatPacket` instead. ## Three ways to say no ```mermaid flowchart TD P["ServerboundChatPacket, on the Netty thread"] --> W{"last-seen window agrees"} W -- no --> X1["connection closed: chat_validation_failed"] W -- yes --> C{"every character allowed"} C -- no --> X2["connection closed: illegal_characters"] C -- yes --> H["queued on the Server thread"] H --> S{"SignedMessageChain.Decoder.unpack"} S -- "no signature, or key expired" --> M["message dropped, red line to the sender, the next one may still land"] S -- "out of order, or signature invalid" --> B["chain broken, every later message this session fails too"] S -- "accepted" --> OK["filter, decorate, broadcast"] ``` Those three endings are the whole vocabulary of failure here, and every check in the next section lands on exactly one of them. The **message** dies alone: it is dropped, the sender usually gets a red system line explaining why, and the next thing they send is judged on its own merits. The **chain** dies for the session: `SignedMessageChain` clears the link it was going to advance, and from then on no unpack can succeed — the *chain broken* error if the message is otherwise well-formed, and a missing-key or expired-key error before that if it is not. Only a new session key, announced with `ServerboundChatSessionUpdatePacket`, restores it. The **connection** dies immediately, and the player is back at the multiplayer list. ## Every check, and what it costs The first fifteen rows are the server treating the client as the adversary. The last three are the client treating the server as one — the same design mirrored, because a server can lie about who said what at least as easily as a client can. | the check | what it catches | what dies | |---|---|---| | `LastSeenMessagesValidator.applyOffset`, from a chat packet or a bare `ServerboundChatAckPacket` | a client advancing its window past messages the server has not sent it | **connection** | | `LastSeenMessagesValidator.applyUpdate`, the acknowledged bits | a bit set longer than twenty, one naming a slot the server does not hold, or one un-acknowledging a slot already acknowledged | **connection** | | `LastSeenMessages.Update.verifyChecksum` | the two sides holding different signatures in slots whose bits agree — a desync the crypto would otherwise report as a bad signature | **connection**, unless the client sent `LastSeenMessages.Update.IGNORE_CHECKSUM` | | `ServerGamePacketListenerImpl.isChatMessageIllegal`, over `StringUtil.isAllowedChatCharacter` | section signs and control characters — formatting injected into everyone else's chat | **connection** | | `ServerPlayer.getChatVisibility`, non-commands only | a player who turned chat off and sent a line anyway | **message**, with a red *chat.disabled.options* back to the sender | | `SignedMessageChain.Decoder.unpack`, no signature present | an unsigned message once a chat session exists — unconditionally, whatever `MinecraftServer.enforceSecureProfile` says, which governs only the decoder used before one does | **message** | | the same, `ProfilePublicKey.Data.hasExpired` | a session key past its expiry still being used to sign | **message** | | the same, a timestamp before the last accepted one | a replayed or reordered message from this sender | **chain** | | the same, `PlayerChatMessage.verify` | content, timestamp, salt or window that do not match the signature — a forgery, or a proxy editing text in flight | **chain** | | `ServerGamePacketListenerImpl.collectSignedArguments`, an unknown argument name | a client signing arguments of a command the server's own parse does not have | **chain**, broken explicitly | | the same, a signable argument with no signature | signatures stripped from *some* arguments of a signed command | **message** — the chain is left intact | | `ServerGamePacketListenerImpl.performUnsignedChatCommand` | a signable command sent down the plain command packet with its signatures removed | **message**, and only when `MinecraftServer.enforceSecureProfile` is on | | `ServerGamePacketListenerImpl.detectRateSpam`, a `TickThrottler` per player | flooding: each message costs 20 and one point decays per tick | **connection**, except for operators and the singleplayer host | | `ServerGamePacketListenerImpl.sendPlayerChatMessage`, via `LastSeenMessagesValidator.trackedMessagesCount` | a client that is sent signed messages and never acknowledges them | **connection**, past 4,096 pending | | `ServerGamePacketListenerImpl.handleChatSessionUpdate` | a key that expires *earlier* than the one it replaces, or one `RemoteChatSession.Data.validate` cannot trace to Mojang's services key | **connection** | | `ClientPacketListener.handlePlayerChat`, the global index | a server dropping, duplicating or reordering messages beneath the player, which would falsify any report drawn from the log | **connection** | | `MessageSignature.Packed.unpack` against the client's cache | a server naming a cached signature the client has never held | **connection** | | `SignedMessageValidator.KeyBased` — expired key, failed signature, or a link that is not `SignedMessageLink.isDescendantOf` the last | a server inventing lines in another player's name | **chain**, latched: the validator never returns to valid | Two rows want a sentence more. The checksum is the only check in the table a client may decline: `LastSeenMessages.Update.verifyChecksum` passes anything when the byte is zero, and a real checksum that computes to zero is bumped to one so it can never be mistaken for the opt-out — though the vanilla client never opts out, because `LastSeenMessagesTracker.generateAndApplyUpdate` always computes one. And *chain broken* is a latch on both sides: the server's `SignedMessageChain` and the receiving client's `SignedMessageValidator.KeyBased` both refuse everything afterwards, so one bad signature costs a sender their voice until a key rotation, not one line. ## Why losing the window is worse than losing the signature The asymmetry looks backwards until you notice what the window is *for*. The last-seen list is signed **in full** but sent as `MessageSignature.Packed` indices into a cache both sides maintain identically. If those caches ever diverge, the receiver reconstructs a different `SignedMessageBody`, and every signature after that fails for a reason no cryptographic error message can explain. There is no recovery from inside: the state is shared, and half of it is wrong. So the game ends the connection at the first sign of that divergence, and `LastSeenMessagesValidator` is written to be suspicious. It rejects an acknowledgement of a slot it does not hold, an *un*-acknowledgement of a slot it already acknowledged, a negative offset, an offset larger than the number of tracked messages outside the window it has actually sent, and a bit set wider than the window; a checksum mismatch on top of all that says *the client and server must have desynced* in as many words. A bad signature is the opposite kind of problem: local, provable and attributable. One sender is misbehaving, everyone else's conversation is intact, and the proportionate answer is to stop trusting that sender — not to end a session for every other player in the room. **Sixty-four** — the acknowledgement offset a client may accumulate before `ClientPacketListener.markMessageAsProcessed` sends a bare `ServerboundChatAckPacket` unprompted. That packet exists so that a player who only listens never reaches the server's 4,096 pending messages and gets dropped for saying nothing all evening. ## What the signature covers `PlayerChatMessage.updateSignature` feeds the signer a version constant, then the link — sender id, session id, index — then the body: the salt, the timestamp **in seconds**, the length of the content, the content bytes, the count of last-seen signatures and each one's raw bytes. Not fed to it: the decorated `Component`, the `FilterMask`, the `ChatType.Bound` that supplies the *someone said* wrapper, and the global index the receiving client checks. A server is free to change any of those. The signature is over what the player typed and what they had seen when they typed it, and nothing else. That gap is what `ChatTrustLevel` exists to expose, and it tests for it crudely on purpose. `ChatTrustLevel.evaluate` calls a message *modified* the moment the rendered string does not **contain** the signed string — the limb that catches a server rewriting what someone said. Only if that passes does it look at style, and only inside the unsigned copy — which vanilla does send, for any command message carrying a selector. A message with no signature at all, or one older than seven minutes, is *not secure* instead. The tag is normally all that happens; with `Options.onlyShowSecureChat` on, a not-secure message is discarded rather than drawn — that test runs first, and `Minecraft.isBlocked` and `Minecraft.isFriendOnlyRestricted` can swallow what survives it. The session key is signed one level up. `ProfilePublicKey.Data` carries an expiry, the public key and a signature over the profile id, the expiry **in milliseconds** and the encoded key, checked against Mojang's services key. The receiving client allows `ProfilePublicKey.EXPIRY_GRACE_PERIOD` — eight hours — that the signing chain on the server does not. ## Commands: one signature per argument `ClientPacketListener.sendCommand` parses the command locally and builds a `SignableCommand`. If nothing in it needs signing it sends `ServerboundChatCommandPacket`, which carries the string and nothing else. If something does, it sends `ServerboundChatCommandSignedPacket` with `ArgumentSignatures`: one signature per argument, each consuming its own chain index, all of them sharing one timestamp, salt and window. *Signable* means the argument type implements `SignedArgument`, and in 26.2 exactly one type does — `MessageArgument`, behind the message-shaped commands. Its `MessageArgument.Message.toComponent` is also the one place chat text has its selectors expanded — each `MessageArgument.Part` resolved by `EntitySelector.joinNames`, behind a permission — which is why `/say @a` names people and a chat line saying the same thing does not. The server re-parses its own copy and looks each signature up **by argument name**, which is where two rows of the table above come from: a name its parse does not have breaks the chain outright, while a signable argument the client left unsigned only fails the command. Both sides cap the shape of the packet at `ArgumentSignatures.MAX_ARGUMENT_COUNT` — eight — and `ArgumentSignatures.MAX_ARGUMENT_NAME_LENGTH`, sixteen. Commands run against their own `TickThrottler`, separate from chat's and with its own threshold. A command message that ends up with no signed argument is broadcast as a `ClientboundDisguisedChatPacket`: chat-type decorated, unsigned, unreportable. ## Questions players ask **What actually makes the "Not Secure" tag appear?** No signature, or a timestamp more than seven minutes old by the receiving client's clock. The server calls the same message stale after five, and logs it as *Not Secure* there too. Two machines whose clocks are a few minutes apart will flag perfectly honest messages, and the server says so in its log. **Why does a custom font not flag every line on my server?** Because the style test only looks inside the unsigned, decorated copy — and vanilla never sends one. The font check is dead on a vanilla server and live on a server that decorates. A player's own lines on an integrated server skip both tests and are secure by definition. **Can a server delete a message from my chat?** It has the packet for it: `ClientboundDeleteChatPacket` is registered and handled, and the handler will pull the line out of the player's own chat-delay queue if their *chatDelay* option means it has not been drawn yet. Nothing in the game constructs it. **Why can I report some lines and not others?** `LoggedChatMessage.canReport` needs a signature from the player being reported, and system messages, disguised command output and anything a broken chain swallowed carry none. What a report uploads is the *signed* material — index, session id, timestamp, salt, the last-seen signatures and the signed content — so it can be re-verified independently, with `ChatReportContextBuilder.collectAllContext` walking the last-seen links backwards for the conversation around it. **Where does my signing key live?** Nowhere, in a shipped client: `AccountProfileKeyPairManager` writes the key file only when `SharedConstants.IS_RUNNING_IN_IDE` and deletes it otherwise, so each launch re-fetches from the account service — and only if `ClientboundLoginPacket.onlineMode` said the server was in online mode. **Why can I not chat here even though nothing is wrong?** `ChatAbilities` and `ChatRestriction` are a client-side layer the server has no part in: game options, launcher policy and the account profile each strip permissions independently, and what survives decides whether this client will send messages, send commands, or accept player or system messages at all. `ChatVisiblity` is only the sliver of that the server is told about, and it has three values, not two — `ChatVisiblity.HIDDEN` still lets action-bar text through. **And if somebody's session key fails validation?** Which side notices decides the cost. On the server it closes the connection. On another client, `ClientPacketListener.initializeChatSession` merely calls `PlayerInfo.clearChatSession`, and that player's lines arrive unsigned — taken and tagged insecure by `SignedMessageValidator.ACCEPT_UNSIGNED`, or refused by `SignedMessageValidator.REJECT_ALL` where secure profiles are enforced. ## Where to look `ChatScreen.normalizeChatMessage` · `ClientPacketListener.sendChat` · `LastSeenMessagesTracker.generateAndApplyUpdate` · `ServerGamePacketListenerImpl.handleChat` · `ServerGamePacketListenerImpl.unpackAndApplyLastSeen` · `LastSeenMessagesValidator.applyUpdate` · `ServerGamePacketListenerImpl.tryHandleChat` · `SignedMessageChain.Decoder` · `PlayerChatMessage.updateSignature` · `PlayerList.broadcastChatMessage` · `OutgoingChatMessage.create` · `ServerGamePacketListenerImpl.sendPlayerChatMessage` · `ClientPacketListener.handlePlayerChat` · `MessageSignatureCache.push` · `SignedMessageValidator.KeyBased` · `ChatListener.handlePlayerChatMessage` · `ChatTrustLevel.evaluate` · `ClientPacketListener.markMessageAsProcessed` · `ChatReportContextBuilder.collectAllContext` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # X · The client > Verified against **Minecraft 26.2** · Part X · One thread, one loop, and seven systems that differ mainly in how often the loop gets round to them. Everything in this part that touches the game happens on one thread. Nothing in the client's simulation is driven by a scheduler or a timer callback — the two classes that own one, `PeriodicNotificationManager` and `RemoteFriendListUpdateHandler`, hop back to the game thread before they touch anything — and, despite the name printed in every stack trace, there is no render thread: the thread called *Render thread* is the main thread, and it is the same thread that ticks the world, applies packets, handles your keyboard, decides what a screen looks like and asks the GPU to draw it. A player recognises the part by its symptoms of that arrangement: the stutter where the world moves on without you, the block that appears and then disappears, the terrain filling in ahead of you as you fly, the sound that arrives a beat after the packet. ## The shape of the part Part X is a **hub and its spokes**, and the spokes are cadences rather than stages: with one exception, noted below, nothing here hands off to anything. `the-client-loop` is the hub because it is the one page that says *when* anything on the client runs, and every other page in the part answers the same question about itself: **when in that loop does this happen?** Read the labels on the arrows as cadences, not as an order. ```mermaid flowchart TD LOOP["The client loop — the hub"] LEVEL["The client level"] PRED["Prediction and acknowledgement"] INPUT["Input and keybinds"] OPT["Options"] GUI["The GUI stack — screens, the render tree, text, the HUD"] SND["Sound — the engine, and what makes a sound happen"] DBG["Debugging the running game"] LOOP -- "per tick, and light per frame" --> LEVEL LOOP -- "per action, in one synchronous window" --> PRED LOOP -- "per GLFW callback, keys before the tick" --> INPUT LOOP -- "per save, which a cycle button does on click" --> OPT LOOP -- "per frame, recorded then drawn" --> GUI LOOP -- "per event, then three more threads of its own" --> SND LOOP -- "per tick, and a packet only when the set changes" --> DBG ``` The one genuine pipeline inside the part is the GUI stack: a screen records itself into a tree, the text in it becomes glyphs, and the tree is then sorted and batched into draws. Those three are three stages of one journey and are watched in a different order from the one they run in — the tree before the text, because the text's stages are easier to follow once you know what they are recording into — with the HUD after them as the other thing that records into the same tree. Everything else in the part is independent of everything else in the part. ## Before you start [Part IX](../networking/README.md), and not optionally — this part is the same wire watched from the receiving end. Three pages here begin at a packet that has already arrived, and [the connection](../networking/the-connection.md) is what it took to get there. [Part I's anatomy](../anatomy/anatomy.md) for the two-loops figure, which is the premise of the whole part: the server's tick loop and the client's frame loop are different clocks, and almost every surprise in Part X is a consequence of one of them waiting on the other. [Authority](../entities/authority.md) from Part VI, because "what the client is allowed to decide" is the question `the-client-level` and `prediction-and-acks` are both answering, and neither re-derives the five predicates. Two smaller ones, each for one page. [Part V](../blocks/README.md) before [prediction and acknowledgement](prediction-and-acks.md): the ledger's six windows open around rather more than a block placed and a block broken, but those two are the ones a viewer needs to have seen, and Part V's landing page already rules that its pages are watched first. And [text components](../foundations/text-components.md) from Part II before [text and fonts](text-and-fonts.md), which starts from "you have a `Component`". ## Watch in this order 1. [The client loop](the-client-loop.md) — the hub, and the one page every other page in the part leans on. How much simulated time a frame owes, what it spends it on, and what happens to the time it cannot afford. Watch this before anything else in Parts X and XI. 2. [The client level](the-client-level.md) — the same `Level` class the server runs, with its authority removed. A comparison: what the client really simulates, and what it only pretends to. 3. [Prediction and acknowledgement](prediction-and-acks.md) — the block that appears and then disappears. One ledger, one counter, and a receipt that is not a verdict. 4. [Input and keybinds](input-and-keybinds.md) — everything between the operating system and a key being *down*, and the five places a press can be swallowed on the way. 5. [Options](options.md) — one flat file and nine fields the server ever hears about. A policy page: what saving does, and who is told. 6. [GUI and screens](gui-and-screens.md) — what a screen *is*: the manager, the lifecycle, the widget family, and the four routes by which a screen comes to exist. 7. [The GUI render tree](the-gui-render-tree.md) — the second stage of the same journey. Nothing in the 2D UI draws anything; it all appends to a tree that infers its own layering from bounding boxes. 8. [Text and fonts](text-and-fonts.md) — the third stage, and a pipeline of its own: six stages from a `Component` at one end to a quad with a glyph on it at the other. 9. [The HUD](hud.md) — the other thing that records into that tree, and the part's second policy page: what is drawn over the world, in what order, and under exactly which conditions. 10. [Sound: the engine](sound-engine.md) — the page in the part with the most threads in it: five take part, a block placed near you crosses four of them on its way to an OpenAL source, and one hop it cannot skip. 11. [What makes a sound happen](what-makes-a-sound.md) — the content model: three doors a sound comes through, only one of which names it. 12. [Debugging the running game](debugging-the-running-game.md) — the closer, and the part's one *pattern* lecture: one subscription mechanism, sixteen instances, all of them shipped and fifteen of them unreachable without a JVM flag. Two and three are a pair — the ledger lives on `ClientLevel` and is reached through four of its methods — and six to nine are the GUI stack, watched together. Ten and eleven are the two halves of sound and can be watched in either order; the engine first is the easier way round. ## Reference this part uses [Diagram lanes](../../reference/lanes.md) for the abbreviations these pages' figures use, and [the threads](../../reference/threads.md), which is where the sound engine's own thread sits among the rest of the game's. [HUD elements](../../reference/hud-elements.md) is the gate table [the HUD](hud.md) is built on, in record order. [Packets](../../reference/packets.md) for everything arriving from Part IX, and [the glossary](../../reference/glossary.md) for *partial tick*, *prediction ledger* and *extract*. Where the part stops: `Minecraft.renderFrame` from its *extract* zone onwards is Part XI, which begins where [the client loop](the-client-loop.md) ends — at [the frame](../rendering/the-frame.md), and the acquired surface. The handful of statements before that zone are still this part's, which is why the per-frame light pass is a Part X cadence even though it runs inside the render method. What the *server* chose to send is [what the client is told](../networking/what-the-client-is-told.md) in Part IX. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The client loop > Verified against **Minecraft 26.2** · Part X · one turn of `Minecraft.run`: how much simulated time a frame owes, what it spends it on, and what happens to the time it cannot afford. The client has one loop and no schedule. A tick is not a timer callback and not a thread — it is something the loop does on its way to a frame, as many times as the clock says it owes. The clock is asked once per iteration, it answers in whole ticks, and the loop then runs at most **ten** of them. A frame that earned fifteen runs ten and loses five: they are already gone from the residual, nothing will ever run them, and the world you are standing in has skipped forward without simulating the gap. The server drops ticks too, but only once it is more than the overload threshold plus twenty ticks behind — and it logs *Can't keep up!* when it does. The client does it on any frame that needs to, at a ceiling of ten, and says nothing. Everything on this page is one thread. The thread named `"Render thread"` is the main thread — `Main.main` renames it and `RenderSystem.initRenderThread` claims it — so `Minecraft.gameThread`, `BlockableEventLoop.isSameThread` and `RenderSystem.assertOnRenderThread` all agree about the same thread. There is no render thread, and there never was one in this version. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | the loop itself, and — being a `ReentrantBlockableEventLoop` — the main thread's task queue | Render thread | | `DeltaTracker.Timer` | how many whole ticks this frame owes, and what the leftover fraction is | Render thread | | `PacketProcessor` | where packets decoded on Netty threads wait to be applied | filled on Netty, drained here | | `TickRateManager` | the millisecond target the Timer divides by — a *server* object | read here, owned there | | `FramerateLimitTracker` | what the frame cap actually is, which is not always the option | Render thread | | `FramerateLimiter` | the park that enforces it | Render thread | | `Main` | the process: the config, the shutdown hook, the thread's name | JVM main = Render thread | ## One turn of the loop `Minecraft.run` spins until `Minecraft.running` goes false, and each iteration is `RenderSystem.pollEvents` followed by `Minecraft.runTick`. The figure is that iteration. It is drawn as a flowchart rather than a conversation because the fact worth having is a *decision* — the clamp, and what falls off the end of it. ```mermaid flowchart TD POLL["RenderSystem.pollEvents — GLFW callbacks run here, inline, on this thread"] PRE["Pre render: Window.shouldClose, then any pending resource reload"] ASK["DeltaTracker.Timer.advanceGameTime — how many whole ticks has the clock owed since last time?"] DRAIN["PacketProcessor.processQueuedPackets, then BlockableEventLoop.runAllTasks"] TEX["TextureManager.tick — once, and only if ticks are owed and the level is running normally"] CLAMP{"more than ten ticks owed?"} DROP["the excess is already out of the residual — nothing will ever run it"] TICK["Minecraft.tick, up to ten times"] PREFRAME["SoundManager.updateSource, then MouseHandler.handleAccumulatedMovement"] FRAME["Render: renderFrame — its frameLimiter zone parks for the cap, then fpsUpdate samples the counters"] POST["Post render: recompute Minecraft.pause, update the timer's pause and freeze"] POLL --> PRE --> ASK --> DRAIN --> TEX --> CLAMP CLAMP -- "yes" --> DROP --> TICK CLAMP -- "no" --> TICK TICK --> PREFRAME --> FRAME --> POST POST -- "next iteration of Minecraft.run" --> POLL ``` Read it as **owe, spend, draw, settle**. The clock says how much simulated time has passed; the loop spends it on packets, tasks and up to ten ticks; the frame draws whatever the world looks like afterwards; and only then does the loop notice whether the game is now paused — which is why the first frame of a pause is drawn unpaused. The quoted phrases are `Window.setErrorSection` calls, the crash report's breadcrumb, so a client that dies takes *Pre render*, *Render* or *Post render* to the report with it. What happens inside the frame is [the frame](../rendering/the-frame.md); this page stops at the profiler's *frame* zone. Note where the frame limiter sits: inside `Minecraft.renderFrame`, after the present, with only the *fpsUpdate* zone after it, and *before* the pause is recomputed. ## The ten, and the arithmetic behind it `DeltaTracker.Timer.advanceGameTime` takes the elapsed milliseconds, divides by whatever `DeltaTracker.Timer.targetMsptProvider` returns for `DeltaTracker.Timer.msPerTick`, adds the result to `DeltaTracker.Timer.deltaTickResidual`, takes the whole part out and returns it. The fraction that stays behind is the partial tick everything interpolates against. The clamp then happens in the loop, not in the Timer — so the ticks above ten are not deferred to the next frame, because they left the residual when they were counted. **Ten** — the ceiling, named by `Minecraft.MAX_TICKS_PER_UPDATE`, though the clamp in `Minecraft.runTick` is written as a literal and no reader of the constant survives the decompile. *javac* inlines a `static final int` at every use site, so a decompile can never tell a documented constant from a dead one; what it does show is that the number the loop obeys is the literal. The divisor is not the client's to choose. `DeltaTracker.Timer` gets its target from `DeltaTracker.Timer.targetMsptProvider`, which is `Minecraft.getTickTargetMillis`, which asks the level's `TickRateManager` for `TickRateManager.millisecondsPerTick` whenever it `TickRateManager.runsNormally`. **`/tick rate` is a server command that changes the arithmetic inside the client's frame loop.** `/tick freeze` is the same lever pulled the other way, and the loop reads it directly rather than through the Timer: `Minecraft.isLevelRunningNormally` asks the level's `TickRateManager` again, and that is what stops `TextureManager.tick` — which is why freezing the world freezes the water texture — and what gates `ClientLevel.animateTick` and `ParticleEngine.tick` inside the tick. The Timer is *told* the same answer at the end of the iteration, through `DeltaTracker.Timer.updateFrozenState`, so that the partial tick it hands out stops moving too. Alongside the game clock the Timer runs a second, unpausable one. `DeltaTracker.Timer.advanceRealTime` produces `DeltaTracker.getRealtimeDeltaTicks`, which is what a menu animates against while the world is stopped. The two constants `DeltaTracker.ZERO` and `DeltaTracker.ONE` — two instances of the one nested `DeltaTracker.DefaultValue` — exist so that code which needs a partial tick can be handed *no* interpolation or *complete* interpolation without a branch. ## What a tick is, in order `Minecraft.tick` is one long method and its order is a dependency order. It advances `Minecraft.clientTickCount`; then, when there is a level and the game is not paused, it ticks the `TickRateManager`. Then in sequence: the game mode; `Minecraft.pick` at a partial tick of one; `Tutorial.onLookAt` with the result; the GUI block (`TextInputManager`, then `Gui.tick`, with `Minecraft.missTime` pinned high while a screen is open); the keybind drain, **only** when there is neither an overlay nor a screen; then `GameRenderer.tick`, `ClientLevel.tickEntities` and `Level.tickBlockEntities`; then the music and sound managers, which sit *outside* the level check and run with no world at all; then the level block — the first-server toast, `Tutorial.tick`, and then `ClientLevel.tick` alone inside a crash-report handler; then `ClientLevel.animateTick` and `ParticleEngine.tick`, both additionally gated on the level running normally; then `ServerboundClientTickEndPacket`; and last of all `KeyboardHandler.tick`, where the F3+C crash countdown lives. With no level that whole middle collapses, but not into one branch: two separate *else* arms at two points in the method clear any post-effect and tick the pending connection, with the unconditional music and sound managers running between them. Two orderings in that list are load-bearing elsewhere in the book. `ServerboundClientTickEndPacket` goes out once per unpaused client tick that has a connection, and the server reads it to decide that a player who sent no movement this tick is standing still. And `Minecraft.pick` runs **once per tick and once per frame** — the tick's call at a partial tick of one, the frame's at the real one, and it is the frame's result the crosshair and the block outline use. A frame that runs three ticks calls it four times; a frame that runs none calls it once. ## Where work leaves this thread, and where it comes back Four queues and one re-entry that is not a queue. - **Packets** decoded on Netty threads are parked by `PacketProcessor.scheduleIfPossible` and drained in the *scheduledPacketProcessing* zone — once per frame, not once per tick. That single fact is behind most of what looks like network jitter; [the connection](../networking/the-connection.md) is the other side of it. - **Tasks** from other threads land in *scheduledExecutables* through `BlockableEventLoop.execute`. From *this* thread the same call usually runs inline instead of queueing — but not while a queued task is already running, because `ReentrantBlockableEventLoop.scheduleExecutables` returns true for the whole of `ReentrantBlockableEventLoop.doRunTask`, which is what stops a task from re-entering itself. GLFW callbacks, dispatched inside `RenderSystem.pollEvents`, are not inside one, so they execute *before* the tick that will observe them — see [input and keybinds](input-and-keybinds.md). - **Section meshing** goes to `Util.backgroundExecutor` and is collected by `SectionRenderDispatcher` (Part XI). - **GPU work** registered with `RenderSystem.queueFencedTask` is picked up by `RenderSystem.executePendingTasks`, which stops at the first unsignalled fence rather than waiting. It looks general and is not: the one thing in the tree that queues a fenced task is the OpenGL backend's asynchronous texture readback. - And `BlockableEventLoop.managedBlock` pumps tasks while the loop is *blocked* waiting for the integrated server — the mechanism [`server-tick`](../server/server-tick.md) owns. The profiler wraps all of it. `Minecraft.constructProfiler` picks per iteration between `InactiveProfiler`, the frame-profile `ContinuousProfiler` behind the F3 pie chart, the `MetricsRecorder` and a `SingleTickProfiler`, and `Minecraft.finishProfilers` closes it. `RenderSystem.pollEvents` is inside the profiler scope but outside `Minecraft.runTick`, so **input polling lands in no named zone** and shows up on the pie chart as unspecified time. That is not why `RenderSystem.isFrozenAtPollEvents` exists, though: its one caller is `ClientCommonPacketListenerImpl.handleKeepAlive`, which defers the keep-alive reply while the poll is blocked, so that dragging the window does not look to the server like a network stall. ## Pausing, which is two things and neither is the menu `Minecraft.pauseIfInactive`, called during the frame, pauses the game when the window has been unfocused for more than half a second and `Options.pauseOnLostFocus` is on. `Minecraft.pause` — the field — is recomputed at the very end of `Minecraft.runTick` as *singleplayer, and the GUI says we are pausing, and the world is not open to LAN*. `Gui.isPausing` asks the current screen and overlay, and `Screen.isPauseScreen` defaults to **true**: this is why the options screen stops a singleplayer world and a chest does not — `AbstractContainerScreen` overrides it to false. On the rising edge the loop calls `SoundManager.pauseAllExcept`, sparing music and UI sounds, and hands the new state to `DeltaTracker.Timer.updatePauseState`. ## The frame cap is usually the option, and sometimes is not `FramerateLimitTracker.getFramerateLimit` returns the option unchanged normally; caps it at thirty after a minute idle; replaces it with ten when the window is iconified or after ten minutes idle; and replaces it with **sixty** in a menu with no level — which can be *more* than the player asked for. The two idle cases apply only when `Options.inactivityFpsLimit` is set to the AFK behaviour, and the iconified test wins over both. `FramerateLimiter.limitDisplayFPS` is skipped entirely at or above 260 — the option's own maximum, i.e. "unlimited"; below it, it parks for most of the remainder, correcting for how much the JDK's park habitually overshoots, and busy-spins the last fraction. The profiler notices too, though it does not stop: `FramerateLimitTracker.isHeavilyThrottled` is the `ContinuousProfiler`'s *suppress warnings* predicate, so a throttled client still measures itself but stops complaining that its frames are slow. The numbers on the F3 screen are three different measurements and it is worth knowing which is which. `Minecraft.fps` is a static field sampled once a second. `Minecraft.frameTimeNs` is a CPU span that stops at the blit, before the present and before the limiter, and is read by nothing but telemetry. The graph uses wall-clock between frames, measured *after* the limiter, so it includes the sleep. ## Starting, and the three ways of stopping `Main.main` builds a `GameConfig` from the command line, installs a shutdown hook, renames the thread, calls `RenderSystem.initRenderThread` and constructs `Minecraft`; a `SilentInitException` out of that constructor exits quietly rather than crashing. `Minecraft.running` is set true inside that constructor, but five statements *after* the `Options` are read from disk — which is why every `OptionInstance.set` performed while loading *options.txt* silently skips its listener (see [options](options.md)). `Main.main` then calls `Minecraft.exitWorldAndClose`, and its last statement arms `ClientShutdownWatchdog.startShutdownWatchdog` over what follows. Stopping has three doors and one corridor. `Minecraft.stop` sets `Minecraft.running` false, and is what `Window.shouldClose` triggers at the top of `Minecraft.runTick`. `Minecraft.emergencySaveAndCrash` is where a `ReportedException` or any other throwable from the loop body ends up, by way of `Minecraft.emergencySave`, which releases the reserved memory block, halts the integrated server and shows the saving screen. And an out-of-memory error does not necessarily end anything: the first one makes `Minecraft.run` stop advancing game time altogether — GUI only, no ticks, no packets, no world — after an emergency save; a second one rethrows. The corridor is `Minecraft.exitWorldAndClose` and then `Minecraft.close`, which tears down in a fixed order — the time source first, outside the try, then the friends list, the timer query, telemetry, compliancies, the atlas and font managers, the game renderer, the shader manager, the level renderer, the sound manager, the two texture managers, resources, the Tracy capture, the narrator, FreeType, the executors, the surface and the renderer — and then, in a finally block, only the window, the monitor manager and GLFW's own termination. There is no *Minecraft.destroy*. > **For a 1.21-era reader.** Names to stop hunting for: > *Minecraft.getPartialTick*, *Minecraft.noRender*, *Minecraft.tell*, > *Minecraft.destroy*, *Minecraft.screen* and *Minecraft.setScreen* (both now > on `Gui`), *Timer* (now `DeltaTracker.Timer`), and *initGameThread* / > *isOnGameThread*, which do not exist because the second thread they > distinguished does not either. ## Where to look `Minecraft.run` and `Minecraft.runTick` — the loop is those two methods. `DeltaTracker.Timer.advanceGameTime` for the tick arithmetic and `Minecraft.getTickTargetMillis` for who sets its rate. `Minecraft.tick` for the ordered contents of a tick. `FramerateLimitTracker.getFramerateLimit` for the frame cap that is not the option. `Main.main` for how the process starts, and `Minecraft.close` for the order in which it comes apart. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The client level > Verified against **Minecraft 26.2** · Part X · the same `Level` class the server runs, with its authority removed: what the client really simulates, and what it only pretends to. Place a repeater on the client and it is *there* — drawn, collidable, part of the world. Ask the client whether that repeater has a tick scheduled and it will tell you, confidently, that it does not. `ClientLevel`'s two scheduled-tick lists are a `BlackholeTickAccess`: it accepts a schedule, drops it, reports *no* when asked whether a tick is pending, and counts zero. Shared block code that consults the level therefore gets a wrong answer rather than an error, and anything that reschedules itself looks inert until the server speaks. That is the shape of the whole class. `ClientLevel` is not a passive receiver and not an authority either ([authority](../entities/authority.md) is where the five predicates that word stands for are set out): it simulates hard — every block entity ticks regardless of distance, every local block change is relit locally, it keeps its own clock and free-runs between corrections — while inheriting a set of shared `Level` methods that have been quietly reduced to constants. Reading `ClientLevel` is largely a matter of noticing which overrides are empty. ## The cast | class | what it decides | thread | |---|---|---| | `ClientLevel` | what the client simulates, and what it answers when asked | Render thread | | `ClientChunkCache` | which chunks exist, in a fixed-size array indexed modulo the view diameter | Render thread | | `ClientLevel.ClientLevelData` | the client's own game time, difficulty, horizon height and void darkness | Render thread | | `LevelLightEngine` | the light the client computes for itself, unbudgeted | Render thread | | `LevelExtractor` | the main route from the level to the renderer — pushed *and* pulled | Render thread | | `ClientPacketListener` | the view radius and simulation distance the server announced | Render thread | | `TransientEntitySectionManager` | entity storage with no persistence, no chunk save, no index to disk | Render thread | | `Entity` | whether a position update is a snap or an interpolation | Render thread | ## Where the two levels differ The comparison is the page. Every row but the last is a method both sides inherit from the shared hierarchy — `Level` itself, or one of the interfaces above it — and that one side has hollowed out; the last row is a field. | shared method | on the server | on `ClientLevel` | |---|---|---| | `Level.shouldTickBlocksAt` | ticket range | inherited — unconditionally true | | `ScheduledTickAccess.scheduleTick` | real `LevelTicks` | both lists are `BlackholeTickAccess` | | `Level.explode` | the real thing | empty override — particles arrive by packet | | `LevelAccessor.gameEvent` | vibrations, sculk | empty override | | `LevelReader.getUncachedNoiseBiome` | generates | returns plains | | `LevelReader.hasChunk` | asks the source | unconditionally true | | `Level.setBlocksDirty` | empty | the renderer notification | | `Level.shouldTickDeath` | true | **stricter** — within the server's simulation distance | | entity storage | persistent: a disk store, known UUIDs, per-chunk load states | transient: the same lookup, none of the bookkeeping | Three of those deserve their own sentence. `LevelReader.hasChunk` returning true unconditionally means that particular question is useless on the client — though `Level.isLoaded` still works, because it goes through the chunk source instead. `Level.explode` doing nothing is why an explosion you can see is not a simulation: one `ClientboundExplodePacket` carries the sound, the particle and the knockback, and the handler plays all of it. And `Level.shouldTickDeath` is the only row where the *client* is the stricter of the two: it uses the server's announced simulation distance to decide whether a dying mob plays its death animation. **Two** — the number of things that read the server's announced simulation distance off `ClientLevel`. That one, and `LevelExtractor`'s render-stats string. (The client's own `Options.simulationDistance`, which is a different number, has its own readers.) The value only ever arrives from the server, on `ClientPacketListener.serverSimulationDistance`, beside `ClientPacketListener.serverChunkRadius`; both are seeded at login, updated by their own packets, and handed to each new `ClientLevel` at construction. ## What it does simulate: the two cadences **Per client tick**, from `Minecraft.tick`: `ClientLevel.tickEntities`, then `Level.tickBlockEntities`, then `ClientLevel.tick` — which does `Level.updateSkyBrightness` unconditionally and then, **only if the tick rate manager is running normally**, the world border, the clock, the weather *effects* and the breaking-progress sweep. After that, unconditionally again: the sky flash countdown, the End flash state, and the explosion tracker. `ClientLevel.animateTick` and `ParticleEngine.tick` follow, both gated on the game not being frozen. So "every block entity ticks, at any distance" is true of *distance* and false of `/tick freeze`: `Level.shouldTickBlocksAt` is unconditionally true here, but `Level.tickBlockEntities` still checks the tick rate manager, and the loop skips the whole tick while paused. **Per frame**, and only per frame: `ClientLevel.update`, which calls `ClientLevel.pollLightUpdates` and then runs the light engine. It is gated on the game being loaded, the level existing, and the frame being one that advances game time — so the blocking loops that draw a frame without ticking do no lighting either. The light budget is a cliff rather than a slope, and it budgets the wrong half of the work on purpose. Below `ClientLevel.LIGHT_UPDATE_QUEUE_SIZE_THRESHOLD` the frame runs a tenth of `ClientLevel.lightUpdateQueue`, floored at `ClientLevel.NORMAL_LIGHT_UPDATES_PER_FRAME`; at the threshold or above it runs the entire queue. Then `LevelLightEngine.runLightUpdates` drains the engine's own propagation queue **completely, every frame, with no budget at all**. The budget controls how fast the client accepts the *server's* light, not how fast it computes its own — and a chunk-load burst therefore produces one long frame rather than a hundred slightly late ones. ## A chunk arrives The grounding trace, and the one place the whole class is visible at once. ```mermaid sequenceDiagram participant CPL as ClientPacketListener participant CCC as ClientChunkCache participant CL as ClientLevel participant LLE as LevelLightEngine participant LX as LevelExtractor CPL->>CPL: handleLevelChunkWithLight — already hopped to the client thread CPL->>CCC: replaceWithPacketData — blocks now CCC->>CCC: inRange? out-of-range chunks are logged and thrown away CCC->>CL: unload(old) if the torus slot was occupied CCC->>CL: onChunkLoaded — four tint caches invalidated, entityStorage.startTicking CPL->>CL: queueLightUpdate(lambda) — light later Note over CL: any ticks this frame owes, which above 20 fps is usually none CL->>CL: tickEntities, then Level.tickBlockEntities — the new chunk's block entities tick at once Note over CL: still inside the same runTick, in renderFrame CL->>CL: update, then pollLightUpdates — the queued lambda finally runs CPL->>LLE: applyLightData, then enableChunkLight, whose last act is setSectionRangeDirty over a 3x3 of columns CL->>LLE: runLightUpdates (unbounded) CCC->>LX: onLightUpdate, then setSectionDirty — straight to the extractor, bypassing the level ``` Three things about the shape. **Blocks and light are separated in the handler**, so a chunk exists, ticks and can be walked on before it is lit. **The separation is not a wait**: both notes fall inside the one `Minecraft.runTick` that handled the packet, and above twenty frames a second the frame usually owes no tick at all, so the light is applied with nothing having ticked in between. And **the renderer is reached two ways** — the level pushes (`ClientLevel.sendBlockUpdated`, `ClientLevel.setBlocksDirty`, `ClientLevel.setSectionRangeDirty`), but the chunk cache and one packet handler call `LevelExtractor` directly, and the extractor also *pulls*: `ClientLevel.entitiesForRendering`, `ClientLevel.destructionProgress` and `ClientLevel.getGloballyRenderedBlockEntities` are read each frame and are mutated with no notification at all. Unloading is the same trace backwards: `ClientChunkCache.drop` clears the slot, `ClientLevel.unload` clears the chunk's block entities and stops ticking its entities, and a light removal is queued for a later frame. ## The chunk cache is a torus `ClientChunkCache` is not a map. `ClientChunkCache.Storage` is a flat `AtomicReferenceArray` whose side is the view diameter, indexed by the chunk coordinates *modulo* that diameter — so moving the origin evicts the ring behind you by overwriting it. The array is atomic, the two centre coordinates are declared *volatile*, and so is the reference to the `ClientChunkCache.Storage` itself, because the packet handlers are not the only readers. The section-compile workers are: a `RenderSectionRegion` resolves biome tint *live* rather than from its snapshot, so `RenderSectionRegion.getBlockTint` goes through `ClientLevel.getBlockTint` into the chunk cache from a background thread while the main thread is moving the origin. The `ThreadLocal` and the read-write lock inside `BlockTintCache` are the same contract said out loud. `ClientChunkCache.calculateStorageRange` makes the array a few rings wider than the view distance, `ClientChunkCache.Storage.inRange` and `ClientChunkCache.Storage.getIndex` decide where a chunk lands, and `ClientChunkCache.updateViewCenter` moves the origin by assigning two integers. Its verbs are `ClientChunkCache.replaceWithPacketData`, `ClientChunkCache.drop`, `ClientChunkCache.replaceBiomes`, `ClientChunkCache.updateViewRadius` and `ClientChunkCache.onLightUpdate`, and it keeps four delta sets — `ClientChunkCache.addedEmptySections`, `ClientChunkCache.removedEmptySections`, `ClientChunkCache.addedLoadedChunks` and `ClientChunkCache.removedLoadedChunks` — double-buffered by `ClientChunkCache.flipUpdateTrackingSets` so the renderer can ask what changed since last frame. ## Who interpolates, and who snaps An entity position arriving from the server does not simply become the entity's position. `Entity.moveOrInterpolateTo` asks `Entity.getInterpolation` for an `InterpolationHandler`; if there is one the new position is handed to it as a target, and if there is not the position, yaw and pitch are assigned directly. The base implementation returns **null**, so the default across the entity tree is to snap, and interpolation is opted into by exactly seven overrides. | supplies an `InterpolationHandler` | snaps | |---|---| | `LivingEntity` — so every mob and every remote player | `AbstractArrow` and the other projectiles | | `Display` | `PrimedTnt` | | `ExperienceOrb` | `ItemEntity` — a dropped item | | `Shulker` | `FallingBlockEntity` | | `FishingHook` | everything else that does not override | | `AbstractBoat` and `AbstractMinecart` | | That table is the reason a dropped item's movement looks different from a mob's over the same connection: nothing is smoothing it. The handler itself — its three-tick window, and the 64-block distance past which `ClientPacketListener` does not hand it the move at all and snaps instead — belongs to [movement and collision](../entities/movement-and-collision.md); what this page owns is *who has one*. `Entity.isInterpolating` is the question `ServerboundMoveVehiclePacket` and `PositionMoveRotation` both ask before deciding whether to publish the interpolation's target or the entity's current position. ## Questions players ask **Does the client model the speed of sound?** For a few sounds, yes — and not for the one you would expect. `ClientLevel.playLocalSound` takes a *distance delay* flag, and when it is set and the source is more than ten blocks away the sound is deferred by its distance over a fixed rate. Firework explosions set it, and so do a handful of level events — the trial spawner, the vault, a cobweb placed. Thunder does **not**: `LightningBolt` passes the flag as false, so the crack is instant and what makes it feel late is the lightning being drawn first. See [the sound engine](sound-engine.md). **Why do I hear my own footsteps instantly on a laggy server?** `ClientLevel.playSeededSound` plays a sound locally when the *excluded* player is the local one. The server tells everyone else and the client produces its own copy. What lags is what you hear of other people. **Who decides how hard it is raining?** The server, one hundredth at a time. Weather on the client is presentation only: `ClientLevel.tickWeatherEffects` spawns rain particles and picks rain sounds, while the rain and thunder *levels* are ramped on the server by ±0.01 a tick and broadcast on every tick they change — about a hundred packets across a five-second transition. What the client does not do is interpolate *within* a tick: `Level.setRainLevel` writes the old and new values to the same number, so the partial tick buys nothing and the level steps twenty times a second rather than smoothly. **Why does the clock in a screenshot disagree with the server's?** The client keeps its own. `ClientLevel.tickTime` increments `ClientLevel.ClientLevelData.gameTime` unconditionally every tick and hands the result to a `ClientClockManager` owned by `ClientPacketListener` and reached through `ClientLevel.clockManager`. `ClientLevel.setTimeFromServer` is the only correction, and its only caller is `ClientPacketListener.handleSetTime`. **Why does the crack overlay on someone else's block lag?** The breaking-progress sweep over `ClientLevel.destroyingBlocks` and `ClientLevel.destructionProgress` only runs on every twentieth tick. It is approximate by construction. **Why does the ambient particle load not scale with my machine?** `ClientLevel.animateTick` samples 667 positions at radius sixteen and another 667 at radius thirty-two, every tick, regardless. `ClientLevel.doAddParticle` culls by distance afterwards and can stochastically downgrade the particle setting further. ## What else it holds, and what it will not tell you `ClientLevel.tickingEntities` is an `EntityTickList`, fed by the callbacks `ClientLevel.entityStorage` — a `TransientEntitySectionManager` — invokes when a chunk starts or stops ticking. `ClientLevel.tintCaches` holds four `BlockTintCache`s — grass, foliage, dry foliage, water. `ClientLevel.globallyRenderedBlockEntities` is the set that draws from anywhere, populated by `ClientLevel.onBlockEntityAdded`. `ClientLevel.explosionTracker` is a `ClientExplosionTracker`, a per-tick budget of at most 512 block particles that empties itself every tick rather than deferring anything. `ClientLevel.blockStatePredictionHandler` is the ledger [prediction and acknowledgement](prediction-and-acks.md) owns, and `ClientLevel.levelExtractor` is the push half of the route to the renderer. The client runs a real light engine — block light always, sky light only where the dimension has it — and every client-side `Level.setBlock` relights, but only when `LightEngine.hasDifferentLightProperties` says so — which is emission or dampening differing, *or* either state using its shape for light occlusion, so a purely cosmetic change to a stair or a slab relights anyway. The test is in shared `LevelChunk` code, so the server applies it too. Its collision world, on the other hand, is one entity wide: `ClientLevel.getPushableEntities` returns at most the local player. And `ClientLevel` never notifies `LevelRenderer`. It has no reference to it, and not one per-block or per-section dirty method is left on `LevelRenderer` — they are all on `LevelExtractor` now. What `LevelRenderer` keeps is whole-world invalidation, `LevelRenderer.invalidateCompiledGeometry` and its neighbours, which the extractor calls. > **For a 1.21-era reader.** Gone: *ClientLevel.levelRenderer*, every dirty > method on *LevelRenderer* (now on `LevelExtractor`), > *ClientLevel.getStarBrightness* and *ClientLevel.effects* (both now > `EnvironmentAttribute` lookups — see [environment attributes and > timelines](../world/environment-attributes-and-timelines.md)), and > *ClientChunkCache.ChunkArray*. ## Where to look `ClientLevel.tick` and `ClientLevel.update` for the two cadences. `ClientChunkCache.Storage` for the torus, and `ClientChunkCache.replaceWithPacketData` for what a chunk packet actually does. `ClientLevel.pollLightUpdates` for the budget arithmetic. `ClientLevel.tickEntities` for the entity walk and `ClientLevel.EntityCallbacks` for what joins the ticking set. `Entity.moveOrInterpolateTo` for the snap-or-smooth fork. Then read the empty overrides — `ClientLevel.explode`, `ClientLevel.gameEvent`, `ClientLevel.getBlockTicks` — because they are the page in miniature. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Prediction and acknowledgement > Verified against **Minecraft 26.2** · Part X · a block placed against a wall the server will not allow: the client shows it, the server refuses it, and a numbered receipt decides when the lie ends. The client cannot wait a round trip to show you the block you just placed, so it places it locally and tells the server afterwards. Everybody who has described this system has described it as a rollback: the client guesses, the server judges, the ack says yes or no. That is not what happens. **`ClientboundBlockChangedAckPacket` is a receipt for a number, not a verdict on an action.** It is sent for actions the server refused exactly as it is sent for actions it allowed, and for an aborted dig it is sent carrying zero. What makes the system correct is an ordering rule instead: any correction the server intends travels *earlier in the stream* than the receipt for it, because the correction is sent from inside the handler while the receipt is only a number the connection flushes later. Part V's [block interaction](../blocks/block-interaction.md) and [block breaking](../blocks/block-breaking.md) are the two applications, and both carry the same four-sentence statement of that contract. This page is the machinery underneath: one ledger per level, one counter per connection, six windows in which a prediction can be opened, and four methods that between them decide whether the world moves. ## The cast | class | what it decides | thread | |---|---|---| | `MultiPlayerGameMode` | when a prediction window opens, and what goes in it | Render thread | | `BlockStatePredictionHandler` | the ledger: what was there before, under which sequence number | Render thread | | `ClientLevel` | the four writes — the prediction, the absorbed correction, the trigger, the settle | Render thread | | `PredictiveAction` | a one-method interface: given the sequence, produce the packet | Render thread | | `ServerGamePacketListenerImpl` | one integer per connection, and when it is emitted | Server thread | | `ServerPlayerGameMode` | the authoritative version of the same action | Server thread | ## Two state machines, running against each other The whole mechanism is one small state machine on each side, and neither knows the other's state. The client's runs per *position*; the server's runs per *connection*. ```mermaid stateDiagram-v2 state "Client — one position in the ledger" as CLIENT { [*] --> Absent Absent --> Retained : setBlock inside a window, filed under sequence n Retained --> Retained : setBlock again here, only the sequence is refreshed Retained --> Corrected : setServerVerifiedBlockState, the entry is overwritten and the world is untouched Retained --> [*] : endPredictionsUpTo(n), nothing overwrote it, so syncBlockState puts the old state back Corrected --> [*] : endPredictionsUpTo(n), syncBlockState writes the absorbed state, which is a no-op if it is already on screen } state "Server — one integer per connection" as SERVER { [*] --> Idle Idle --> Raised : ackBlockChangesUpTo(n), the maximum of current and incoming Raised --> Raised : another acked action in the same tick Raised --> Idle : emitted at the head of ServerGamePacketListenerImpl.tick, then back to minus one } ``` Read the two columns as running at different rates. The client's machine advances several times per tick, once per position touched. The server's advances on every packet that reaches a `ServerGamePacketListenerImpl.ackBlockChangesUpTo` call — the two use packets, and the three destroy actions of `ServerboundPlayerActionPacket` but not its other five — and *empties* once per connection tick, which is why five acked actions in one tick produce one receipt carrying the highest number, and why one ack can drive a dozen positions out of the ledger at once. Note what the diagram does not contain: any transition on which the server says *no*. There is none. Both of the client's exit transitions are driven by the same packet, and which of the two a position takes is decided entirely by whether a block update reached it first. ## The four writes Everything the ledger does happens through four methods on `ClientLevel`, and the difference between them is the whole mechanism. **`ClientLevel.setBlock`** — the ordinary write. While a prediction is open, and only if the write succeeded, it calls `BlockStatePredictionHandler.retainKnownServerState` with the state that was there *before*. If the position already has an entry, only the sequence is refreshed: the ledger keeps the **first** pre-change state it ever saw for that position, and the player position recorded with it. **`ClientLevel.setServerVerifiedBlockState`** — every inbound block update goes through here. If the position is in the ledger it overwrites the entry and the world is not touched, so the prediction stays on screen. If it is **not** in the ledger — the common case, for blocks the player did not touch — it writes the world immediately. The absorption is per position, not per packet. **`ClientLevel.handleBlockChangedAck`** — the trigger. The ledger's only entry point from the network: it hands the receipt's number straight to `BlockStatePredictionHandler.endPredictionsUpTo`, which removes every entry at or below it and passes each one's recorded state to the settle. **`ClientLevel.syncBlockState`** — the settle. Applies the recorded state only if it differs from what is there, with flags `Block.UPDATE_NEIGHBORS` plus `Block.UPDATE_CLIENTS` plus `Block.UPDATE_KNOWN_SHAPE`. That third flag suppresses the shape pass, and neighbour updates are inert on the client anyway — so the restore is a bare state write plus a remesh. **The cascade that produced the prediction does not re-run on the way back.** Reconciliation is correct only because every position the cascade touched got its own ledger entry on the way out. ## A placement the server refuses The state diagram says what the states are; this says what order the packets arrive in, which is the part correctness actually rests on. ```mermaid sequenceDiagram participant MPGM as MultiPlayerGameMode participant BSPH as BlockStatePredictionHandler participant CL as ClientLevel participant SGPL as ServerGamePacketListenerImpl participant SPGM as ServerPlayerGameMode MPGM->>BSPH: startPredicting — currentSequenceNr becomes n MPGM->>CL: performUseItemOn, then ItemStack.useOn, then setBlock CL->>BSPH: retainKnownServerState(pos, air, LocalPlayer) — the truth, filed under n MPGM->>SGPL: ServerboundUseItemOnPacket(hand, hit, n) MPGM->>BSPH: close — the window shuts and the block is on screen SGPL->>SGPL: hasClientLoaded? then ackBlockChangesUpTo(n) — the first statement SGPL->>SPGM: useItemOn — the place fails canPlace, so nothing changes SGPL->>CL: two ClientboundBlockUpdatePackets, sent whatever the outcome — the clicked block and the one past its face CL->>BSPH: updateKnownServerState — the entry is overwritten, the world is not Note over SGPL: the connection phase of the next tickChildren SGPL->>CL: ClientboundBlockChangedAckPacket(n) CL->>BSPH: endPredictionsUpTo(n), then syncBlockState — air goes back CL->>CL: Entity.absSnapTo on the LocalPlayer — only if the restored block now intersects it ``` The ack is recorded **before** the action is attempted, so by the time the refusal happens the receipt is already promised. The correction is not the ordinary chunk broadcast but a targeted pair of resends the handler makes unconditionally, which is what actually puts it ahead of the receipt: the resends go out inside the handler, while the ack is only a field assignment that `ServerGamePacketListenerImpl.tick` flushes later. The settle is what finally moves the world, and it is a no-op when the absorbed state is already what is on screen. And the snap only happens when the restored block turns out to be inside the player. Because the ack is buffered rather than sent, *where* a handler records it does not affect stream order — `ServerGamePacketListenerImpl.handleUseItemOn` and `ServerGamePacketListenerImpl.handleUseItem` record it as their first statement, `ServerGamePacketListenerImpl.handlePlayerAction` after running the break action, and in both cases the correction still reaches the client first. ## The six windows A window is a few microseconds long and entirely synchronous: on the client thread, inside one call from `Minecraft.tick`. The counter is raised, the local effect runs, the packet is built with the new sequence and sent, and the window closes — in that order, so the packet is constructed while the ledger is still recording. `MultiPlayerGameMode.startPrediction` is the private method all six go through, and `ClientLevel.getBlockStatePredictionHandler` is package-private, so nothing outside `client/multiplayer` can reach the ledger at all. | where | what it predicts locally | what it sends | |---|---|---| | `MultiPlayerGameMode.startDestroyBlock`, creative | the block removed at once | `ServerboundPlayerActionPacket` START | | `MultiPlayerGameMode.startDestroyBlock`, survival | `BlockBehaviour.attack`, then removal if the block breaks instantly | START | | `MultiPlayerGameMode.continueDestroyBlock`, creative | removal, every sixth tick | START | | `MultiPlayerGameMode.continueDestroyBlock`, at full progress | removal | STOP | | `MultiPlayerGameMode.useItemOn` | `MultiPlayerGameMode.performUseItemOn` — the block's hook, the empty-hand hook, `ItemStack.useOn` | `ServerboundUseItemOnPacket` | | `MultiPlayerGameMode.useItem` | `ItemStack.use`, including a transformed held item | `ServerboundUseItemPacket` | So the ledger covers rather more than "blocks the player placed or broke": it covers **every** `ClientLevel.setBlock` performed inside one of those windows. That includes the second half of a door, every position a shape cascade revisits, and — the only case of its own — `RedStoneOreBlock.attack`, whose lighting change is not side-gated and therefore files a ledger entry when you merely left-click redstone ore. ## What the ledger does not cover Half the value of this page is the list of things people assume it handles. `MultiPlayerGameMode`'s non-predicting verbs are the giveaway: `MultiPlayerGameMode.attack`, `MultiPlayerGameMode.interact`, `MultiPlayerGameMode.stopDestroyBlock`, `MultiPlayerGameMode.releaseUsingItem`, `MultiPlayerGameMode.piercingAttack` and every container verb open no window at all. - **Breaking progress.** `MultiPlayerGameMode.destroyProgress` and its companions are a plain parallel clock with no sequence and no reconciliation; the crack overlay is written straight into the client level. Of the progress clock itself nothing is predicted — only the removals at either end of it are. - **Item use.** `MultiPlayerGameMode.useItem` opens a window, but a consumed item, a started use and a cooldown are not block states and the ack does nothing for them. They are corrected by other means: the living-entity flags in synched data, `ClientboundCooldownPacket`, and the menu resend the server performs when the stack changed. - **Dropping.** `LocalPlayer.drop` predicts the removal from the selected slot and sends its action packet with sequence **zero** — a local mutation with no rollback path at all. - **Movement.** Rubber-banding is a different mechanism entirely: an id-matched teleport handshake with no sequence and no ledger, described in [input to movement](../player/input-to-movement.md). The two systems touch at one point in each direction — `ClientPacketListener.handleMovePlayer` calls `BlockStatePredictionHandler.onTeleport`, so a teleport disarms the ledger's position snap, and the settle calls `Entity.absSnapTo` on the `LocalPlayer` when the restored block is inside you. ## Questions players ask **Why does the block come back and then vanish again?** Your client finished its own progress clock first. It fires the STOP window and removes the block locally, but the server recomputes the progress itself, finds it under `0.7F`, and takes the delayed-destroy branch instead of breaking anything. The action is acknowledged all the same, so the client settles the entry, restores the stone, and the block reappears — until the server's own delayed destroy completes a few ticks later and broadcasts air. Releasing the mouse does *not* do this: that is `MultiPlayerGameMode.stopDestroyBlock`, which opens no window at all. **Why did that ack arrive with a zero in it?** The three-argument `ServerboundPlayerActionPacket` constructor defaults the sequence to zero, and aborting a dig uses it. The server dutifully sends `ClientboundBlockChangedAckPacket` carrying zero, which settles nothing — `BlockStatePredictionHandler.startPredicting` pre-increments, so the first real sequence is one and no genuine prediction is ever numbered zero. **Can a wrong guess get stuck on screen forever?** Yes. There is no timeout and no cap: nothing clears the ledger except a settle, and while the server considers the client not yet loaded, sequenced packets are dropped *before* the ack is recorded. Predictions accumulate and the client's guess stands. The only reset is a new `ClientLevel`. **Does one ack do one thing?** It does three. Every entry at or below the acknowledged sequence is removed and handed to the settle, and there the paths part: write nothing, because the recorded state is already on screen (the correct prediction); write the state; or write the state and snap the player. A single ack can produce all three across the map in one pass. And `BlockStatePredictionHandler.lastTeleportSequence` is compared against the *acknowledged* sequence rather than each entry's, and is never reset, so one teleport suppresses a whole batch of snaps. **Why can I not right-click while mining?** `Minecraft.startUseItem` is gated on `MultiPlayerGameMode.isDestroying`. Spectators are the odd case in the other direction and are asymmetric about it: `MultiPlayerGameMode.useItem` returns early, before the window opens, while `MultiPlayerGameMode.useItemOn` returns *inside* it — so the sequence is burned and the packet is sent anyway. One last asymmetry worth carrying away: the outbound write and the inbound restore use different flags. The predicted removal in `MultiPlayerGameMode.destroyBlock` writes with `Block.UPDATE_IMMEDIATE` in the mix; the restore writes with `Block.UPDATE_KNOWN_SHAPE`. One cascades, the other deliberately does not. ## Where to look `MultiPlayerGameMode.startPrediction` — the whole client side is that one private method and its six call sites. `BlockStatePredictionHandler` end to end; it is under a hundred lines and every one of them matters, including `BlockStatePredictionHandler.currentSequence`, `BlockStatePredictionHandler.isPredicting` and `BlockStatePredictionHandler.close`. `ClientLevel.setBlock`, `ClientLevel.setServerVerifiedBlockState`, `ClientLevel.handleBlockChangedAck` and `ClientLevel.syncBlockState` for the four writes. `ServerGamePacketListenerImpl.ackBlockChangesUpTo` and `ServerGamePacketListenerImpl.tick` for the receipt — the field and the method that raises it share a name. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Input and keybinds > Verified against **Minecraft 26.2** · Part X · holding sneak: a GLFW callback, five chances to be swallowed, and a key that stays down while you are not touching it. Turn on toggle sneak and hold the key. `ToggleKeyMapping.setDown` sees the press, flips the mapping to *down*, and then swallows the release entirely — so as far as the rest of the game is concerned you are still holding a key you let go of. Open your inventory and the mapping is released along with every other one; close it and the mapping *comes back on*, because the toggle remembered it was released by a screen rather than by you. The press and the release never involve the tick at all — they have already happened by the time the tick that observes them runs. Opening the inventory is the exception that shows where the seam is: that one is a *drain*, and the drain is inside the tick. That is the fact this page is built on. **GLFW callbacks are not queued.** They are dispatched from inside `RenderSystem.pollEvents`, which `Minecraft.run` calls immediately before `Minecraft.runTick`; the handlers wrap their bodies in `BlockableEventLoop.execute`, but on the game thread that call runs the task rather than queueing it. Any description of Minecraft input that says a key press is "queued onto the client thread" is describing a different game. What the movement keys *mean* once they are down belongs to [input to movement](../player/input-to-movement.md); this page stops at the mapping. ## The cast | class | what it decides | thread | |---|---|---| | `KeyboardHandler` | the key, character and pre-edit callbacks, and the gauntlet a press runs | Render thread | | `MouseHandler` | motion accumulation, the sensitivity curve, and who is allowed to turn the player | Render thread | | `KeyMapping` | whether a mapping is down, and how many clicks are owed | Render thread | | `ToggleKeyMapping` | the four mappings that can behave as toggles: sneak, sprint, use, attack | Render thread | | `InputConstants` | the key universe, and the string a binding is saved as | Render thread | | `InputQuirks` | four platform constants that visibly change behaviour | Render thread | | `KeyboardInput` | which of the seven movement mappings are down, once per tick | Render thread | | `Gui` | the housekeeping at both ends of a screen's life | Render thread | ## Holding sneak ```mermaid sequenceDiagram participant KH as KeyboardHandler participant MC as Minecraft participant KM as KeyMapping participant Gui as Gui participant KI as KeyboardInput participant LP as LocalPlayer participant MH as MouseHandler KH->>KH: keyPress — inside glfwPollEvents, on the game thread, not queued KH->>MC: handleGlobalKeyPress — fullscreen, screenshot, friends: handled here, so usually no click KH->>Gui: screen keyPressed — a screen that consumes it ends the story here KH->>KH: keyDebugModifier held? then handleDebugKeys instead KH->>KM: set(down) and click — only with no screen open KM->>KM: ToggleKeyMapping.setDown flips instead of following, and swallows the release Note over MC: next client tick LP->>KI: tick — from LocalPlayer.aiStep, reads isDown on seven mappings, not consumeClick KI->>KI: the Input record, then a normalised move vector Note over Gui: and when a screen opens Gui->>MH: releaseMouse Gui->>KM: releaseAll — every mapping goes up, and the toggle notes that a screen did it Note over Gui: and when that screen closes Gui->>KM: restoreToggleStatesOnScreenClosed — sneak comes back on Gui->>MH: grabMouse ``` **A key press has five chances to be swallowed before it counts** — the global-key check, an open screen, the debug modifier, the no-screen gate on recording, and the no-screen-no-overlay gate on draining. And the two ends of a screen's life are where the input system does its housekeeping: opening a screen releases every mapping, and closing one restores those toggles that asked to be restored — with default bindings, sneak and sprint, and only when there is a level. ## Two ways gameplay reads a mapping, and they behave differently `KeyMapping.isDown` is a boolean the movement code samples. `KeyboardInput.tick` reads seven of them — forward, back, left, right, jump, sneak, sprint — once per client tick, packs them into an `Input` record and derives a normalised move vector. Nothing is consumed; a key held for ten ticks reads down ten times. `KeyMapping.consumeClick` is a **drain, not an edge**. It decrements a counter that `KeyMapping.click` incremented, which means presses that happened faster than the tick rate are not lost — and that binding two *mappings* to one key code fires both, because `KeyMapping.click` increments every mapping registered under that key. `Minecraft.handleKeybinds` is the drain, and it runs from `Minecraft.tick` only when there is neither a screen nor an overlay. `KeyMapping.matches` and `KeyMapping.matchesMouse` are the third way, used where no counter is wanted: they test an event against the binding directly, which is what screens do, and what `KeyboardHandler.handleDebugKeys` does nineteen times over for the F3 combinations. `KeyMapping.same`, `KeyMapping.isDefault` and `KeyMapping.isUnbound` are what the binding screen asks. ## The bulk operations, and their single callers `KeyMapping` keeps two static registries of every mapping ever constructed — one by name, one by key — which is how a key code is turned back into the mappings that want it. Five static operations walk them. Four of the five are called from exactly one place each, and those four are the interesting ones — the fifth, `KeyMapping.resetMapping`, is the binding screen's own reset and has two callers. | operation | called from | why | |---|---|---| | `KeyMapping.releaseAll` | `Gui.setScreen` | a screen may swallow a release, and a stuck-held mapping is worse than a lost press | | `KeyMapping.restoreToggleStatesOnScreenClosed` | `Gui.setScreen` | put back the toggles that the release above turned off | | `KeyMapping.resetToggleKeys` | `LocalPlayer.respawn` | you should not wake up sneaking | | `KeyMapping.setAll` | `MouseHandler.grabMouse` | only where `InputQuirks.RESTORE_KEY_STATE_AFTER_MOUSE_GRAB` is set | The asymmetry between a swallowed press and a swallowed release is worth stating plainly, because it is the reason the first two rows exist. A press a screen swallows is harmless: the mapping was never set down. A *release* a screen swallows leaves the mapping down with nothing to clear it. ## The mouse: accumulate, apply, discard `MouseHandler.onMove`, `MouseHandler.onButton`, `MouseHandler.onScroll` and `MouseHandler.onDrop` are the callbacks; `MouseHandler.accumulatedDX` and `MouseHandler.accumulatedDY` are the pending motion; and `MouseHandler.handleAccumulatedMovement` applies it — from `Minecraft.runTick`, between the sound update and the frame, **once per frame rather than once per tick**. So a look is applied at frame rate and a step is applied at tick rate, on the same input device. Accumulated motion goes to whatever is in front of it and is then cleared unconditionally. With a screen open the delta goes to the screen's move and drag handlers, and not to the player — not because the two are exclusive in `MouseHandler`, which tests them separately, but because opening a screen released the mouse. With the window unfocused nothing accumulates in the first place; and the reset at the end runs either way, so motion is never banked. `MouseHandler.turnPlayer` holds the sensitivity curve, and it has an arithmetic surprise in it. The curve is a **cube** of the slider, and the ordinary and smooth-camera paths multiply the result by eight while the scoped path does not — so **aiming a spyglass is exactly eight times slower, by construction.** The scoped path additionally requires the smooth camera to be off, the camera to be first-person, and the player to be actually scoping. Minimum sensitivity is not zero either: the cubed term is taken of the slider scaled and offset, so the slowest setting still turns. `MouseHandler.grabMouse` and `MouseHandler.releaseMouse` are two directions of one edge with `Gui.setScreen`, guarded so the two cannot recurse — though only `MouseHandler.releaseMouse` has the single caller; `MouseHandler.grabMouse` has five, and also clears the screen. `MouseHandler.isMouseGrabbed` is the state, `MouseHandler.setIgnoreFirstMove` suppresses the jump after a resize, and `MouseHandler.simulateRightClick` is macOS-only and fires on a control-modified left click rather than on a long one, whatever its constant is called. Double-click is a threshold plus two identities: the two clicks must be within a quarter of a second, on the same button, **and** on the same screen instance. ## Questions players ask **I bound two things to one key and both happen.** They will. Conflict detection lives only in the binding screen and is purely cosmetic — nothing in the input path resolves a collision. It also refuses to flag one when *both* mappings are still at their defaults, which quietly exempts the pairs the game itself ships colliding. **Why does F3 toggle on release?** F3 is a key binding, and the debug modifier and the overlay toggle are the same key by default — so the overlay can only fire on the release, and only when no combination was used in between. Rebind either and that behaviour disappears. **Why can I not rebind that debug shortcut?** Twenty debug shortcuts are ordinary mappings in the debug-keys array. A second family is a raw switch on key codes in `KeyboardHandler.handleChunkDebugKeys`, gated on the game's debug flag and bindable to nothing. **Can a mod add a keybind category?** `KeyMapping.Category` is a registrable record, not an enum, so yes; registering a duplicate id throws. Ordering is plain registration order into one list — the eight built-ins come first only because the record's own static initialiser registers them first. Almost nothing on this page sends a packet, and the exceptions are worth naming because they are the shortcuts: `KeyboardHandler` sends `ServerboundChangeGameModePacket` for F3+N, and `Minecraft.handleKeybinds` sends the swap-offhand action straight out of the drain. Everything else a key press means reaches the server later and by an entirely different route. The two smaller supporting types are `ScrollWheelHandler`, the wheel's accumulator, and `InputType`, the four-valued "what did the player last use" that decides initial keyboard focus, narration timing and whether a focused widget shows its tooltip. > **For a 1.21-era reader.** Input events are **records** now. The > `(key, scancode, modifiers, action)` integer tuple is gone from every > screen signature, replaced by `KeyEvent`, `MouseButtonEvent`, > `CharacterEvent` and `PreeditEvent` in `client/input`, with shared helpers > on `InputWithModifiers` — so a widget asks an event whether it *is* a > confirmation or a paste rather than decoding modifiers itself. Also gone: > *Options.keyBindings* (now the key-mappings array), categories as > translation-key strings, and *MouseHandler.lastMouseEventTime*. ## Where to look `KeyboardHandler.keyPress` — the whole gauntlet is one method, read top to bottom. Then `ToggleKeyMapping` end to end, which is under sixty lines and explains this page's opening paragraph; `Minecraft.handleKeybinds` for the drain; `KeyboardInput.tick` for the other way a mapping is read; `Gui.setScreen` for the housekeeping at both ends of a screen; and `MouseHandler.turnPlayer` for the sensitivity curve and its three gates. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Options > Verified against **Minecraft 26.2** · Part X · the render-distance slider: a value that takes effect on a delay, rebuilds the world on a later frame, and reaches the server only when a screen closes — with no reply. There is no settings-changed hook in the Minecraft client. **Saving is the event system.** The server learns your new view distance because something called `Options.save`, and `Options.save` is the only caller of `Options.broadcastOptions` — which sounds like a tight, tidy rule until you notice that every cycle-option button calls `Options.save` on click. Change chat visibility and your `ClientInformation` is on the wire before you have left the screen; drag the render-distance slider and nothing is sent until the screen closes. This page is the policy: what a setting *is*, when a change takes effect, who finds out, and what the server does with the nine fields it is told. The bindings that also live on `Options` are [input and keybinds](input-and-keybinds.md). ## The cast | class | what it decides | thread | |---|---|---| | `Options` | the file, the fields, and when to save | Render thread | | `OptionInstance` | one setting: value, codec, caption, listener, and the widget | Render thread | | `OptionInstance.ValueSet` | the legal values — and, by its subtype, whether the widget saves on click | Render thread | | `ClientInformation` | the nine-field record the server is told, kept in `server/level` | crosses the wire | | `LevelExtractor` | notices, next frame, that the effective render distance changed | Render thread | | `ChunkMap` | clamps the request and re-tracks, silently | Server thread | | `IntegratedServer` | the singleplayer back door: reads the sliders directly, every tick | Server thread | ## What happens when a setting changes Four decisions, and none of them is "run the listener and be done". ```mermaid flowchart TD CHANGE["a widget changes a value"] KIND{"slider or cycle?"} ARM["slider that opted out of applying immediately: arm 600 ms, checked during the extract pass"] EARLY{"screen dismissed inside the window?"} SET["OptionInstance.set"] RUNNING{"is Minecraft.running true?"} SILENT["assign the field, skip the equality test and the listener — this is what loading does"] LISTEN["run the listener"] SAVE["Options.save — write options.txt"] BCAST["Options.broadcastOptions — build a ClientInformation"] SAME{"identical to the last one sent?"} SEND["ServerboundClientInformationPacket"] NOTHING["nothing is sent"] CHANGE --> KIND KIND -- "slider" --> ARM --> EARLY EARLY -- "yes, apply at once" --> SET EARLY -- "no, apply on a later frame" --> SET KIND -- "cycle: apply now" --> SET SET --> RUNNING RUNNING -- "no" --> SILENT RUNNING -- "yes" --> LISTEN LISTEN --> SAVE SAVE --> BCAST --> SAME SAME -- "yes" --> NOTHING SAME -- "no" --> SEND ``` The cycle branch reaches `Options.save` on every click; the slider branch reaches it when the screen closes. That single asymmetry is the whole behaviour difference between the two widget families, and it comes from the value set's subtype: `OptionInstance.SliderableValueSet` against `OptionInstance.CycleableValueSet`. ## The three ways a setting is stored `OptionInstance` is the modern shape — a value, a codec, an initial value, a caption, a listener, and an `OptionInstance.ValueSet` that decides both the legal values and the widget. The concrete sets are `OptionInstance.IntRange`, `OptionInstance.ClampingLazyMaxIntRange`, `OptionInstance.UnitDouble`, `OptionInstance.Enum`, `OptionInstance.LazyEnum`, `OptionInstance.AltEnum` and `OptionInstance.SliderableEnum`. **Plain fields** are the older shape, read and written by name in `Options.processOptions`: the language code, the resource-pack lists, `Options.tutorialStep`, `Options.smoothCamera`, `Options.advancedItemTooltips`, `Options.joinedFirstServer`, `Options.startedCleanly` and a dozen others. They have no listener and no widget machinery at all — and `Options.smoothCamera`, which a keybind toggles, is not persisted anywhere. **The key-mapping array** is the third, and is the input page's subject. `Options.processOptions` is worth naming twice: it is the file format and the field list in one method, which is why it is the first place to look for anything about *options.txt*. `Options.dumpOptionsForReport` and `Options.processDumpedOptions` are the smaller subset that goes into the profiling report. ## The delay, and why the world rebuilds anyway The render-distance slider's listener does exactly one thing: it marks the graphics preset custom. It does not invalidate a single chunk. It is also one of only three options in the game that defer their value at all — with simulation distance and biome blend, it is built to *not* apply immediately, which is what arms the 600 ms; every other slider applies on release. The world rebuilds because `LevelExtractor` notices, on the *next frame*, that `Options.getEffectiveRenderDistance` differs from the last value it saw, and calls its own full invalidation — tint caches cleared, the tracker rebuilt, all geometry dirty. The setting and the consequence are joined by a poll, not by a callback. Seven of the other quality options are the opposite way round: their listeners reach straight into the level extractor. Nine are not — their listeners do nothing but flip the preset back to custom, exactly as render distance's does. Elsewhere in the file the immediate listeners are real enough: the window, the sound device and the font manager all have one. `Options.graphicsPreset` and `Options.setGraphicsPresetToCustom` are the preset machinery — a preset writes a batch of settings at once, and almost every graphics listener flips the preset back to custom, which is how "Custom" appears without anyone selecting it. Some of those listeners are far more interesting than their options. GUI scale resizes whichever screen is open. Vsync invalidates the surface configuration. Fullscreen toggles the window and then writes the option back from what the window actually did. The unicode-font toggle throws away every glyph atlas. High contrast adds and removes a resource pack. ## Who is told, and what is not said `Options.buildPlayerInformation` assembles a `ClientInformation`: language, view distance, chat visibility, chat colours, skin model parts, main hand, text filtering, listing permission, particle status. **Nine fields, and simulation distance is not one of them** — the record has no place to put it. The packet is `ServerboundClientInformationPacket`, and it is a *common* packet rather than a play one: the first is sent during configuration, straight from `ClientHandshakePacketListenerImpl`, before the play phase exists. Every later one comes from `Options.broadcastOptions`. **There is no acknowledgement, and the absence is the point.** The one thing a client-information packet can provoke is a hat-visibility broadcast to the whole player list, and nothing in it tells you what happened to what you asked for. The only thing that ever sets `Options.serverRenderDistance` is the server announcing its *own* view distance — in the login packet, or by broadcasting `ClientboundSetChunkCacheRadiusPacket` when an operator changes it. Your request is clamped by `ChunkMap.getPlayerViewDistance` and used for chunk tracking, and you are never told what it was clamped to; the client clamps itself, with `Options.getEffectiveRenderDistance`. `ClientboundSetSimulationDistancePacket` is likewise an announcement, not a reply. Singleplayer short-circuits half of it. `IntegratedServer.tickServer` reads both the simulation and render sliders off the client's options every unpaused server tick and pushes them into the player list, so both drive the server directly without waiting for a packet. Render distance travels as client information anyway — it is field two of the record, sent over the memory connection exactly as over a socket — and simulation distance never does, in singleplayer or out of it, because that is the one number the client has no say in. ## Questions players ask **Why does my render-distance slider stop short?** Its maximum is computed once, from the JVM's maximum memory, and the option is a plain `OptionInstance.IntRange` built around that bound. It depends on your heap, not on your graphics card. (The genuinely lazy `OptionInstance.ClampingLazyMaxIntRange` is GUI scale's, and reads the window.) **Why did none of my settings' side effects run at startup?** Because `Minecraft.running` is still false. `OptionInstance.set` checks it and, when the game is not running, assigns the field and skips both the equality test and the listener. That is not a special path for loading — it silences *any* set performed before the loop starts, and loading happens in the `Options` constructor, which runs inside the `Minecraft` constructor five statements before `Minecraft.running` is set. **Which settings really need a restart?** Two, and they say so. The graphics backend and exclusive fullscreen are compared against snapshots taken at startup, and `Options.isRestartRequiredToApplyVideoSettings` is what the screen asks. Related: `Options.startedCleanly` is written false at startup and true once the game is up, so a crash during boot can drop the client back to a safe backend. **Why does the tutorial never come back?** Options are per installation, never per world — including `Options.tutorialStep`. Once any world drives the tutorial to the end, no later world shows the toasts again, and an unrecognised step name silently reads as *finished*. **I edited options.txt and broke it. Why did nothing complain?** Failure is quiet by design: a bad line is logged and skipped, a bad value for an `OptionInstance` is logged and dropped back to the initial value, and a bad number keeps the current one. Nothing about a corrupt *options.txt* stops the game starting. The file carries a version line and is run through the data fixer on load. > **For a 1.21-era reader.** *Options.mouseSensitivity* and the other bare > public fields are gone — most settings are now private with an accessor of > the same name, so `Options.fov` and `Options.guiScale` are calls. Two traps: > mouse sensitivity was *renamed* as well as encapsulated, to > `Options.sensitivity` (only *options.txt* still says *mouseSensitivity*); > and *Options.keyBindings* was renamed to `Options.keyMappings` but is still > a public field, not a call. ## Where to look `Options.processOptions` for the settings table. `OptionInstance.set` for the listener guard that explains the loading behaviour, and `OptionInstance.CycleableValueSet` for the button that saves on click. `Options.buildPlayerInformation` for the nine fields the server hears. `ChunkMap.getPlayerViewDistance` for what the server does with the one that matters, and `IntegratedServer.tickServer` for the singleplayer back door. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # GUI and screens > Verified against **Minecraft 26.2** · Part X · pressing E: a screen the server is not told about until you close it, opened onto a menu that was built when you spawned. Press E in survival and no packet is sent, no packet is received, and nothing on the server changes. `Player.inventoryMenu` has existed since the player object was constructed, it has **no `MenuType` at all**, and `MenuScreens` could therefore never build an `InventoryScreen` from a packet even if one arrived. The *opening* is entirely a client-side event. Press E again and the symmetry breaks: `LocalPlayer.closeContainer` sends `ServerboundContainerClosePacket`, and the server empties your 2×2 crafting grid on the way out. The screen the server is never told about is one the server is told about exactly once, at the end. The menu underneath it is not symmetric in the same way, and the qualification matters: `InventoryMenu` is constructed on both sides, and its crafting result is recomputed only on the server — so the client is rendering a result it did not compute, in a screen the server does not know is open. This page is what a screen *is*: the manager that holds one, the lifecycle it runs through, the widget and layout families it is built from, and the four routes by which one comes to exist. How its contents become pixels is [the GUI render tree](the-gui-render-tree.md); how a `Component` becomes glyphs is [text and fonts](text-and-fonts.md). ## The cast | class | what it decides | thread | |---|---|---| | `Gui` | which screen and which overlay exist, and what an absent screen means | Render thread | | `Screen` | one screen's lifecycle, children, focus and narration | Render thread | | `AbstractWidget` | the final outer shape of a widget, and one inner hook per subclass | Render thread | | `Layout` over `LayoutElement` | where widgets end up, re-arranged on most screens whenever the window changes | Render thread | | `AbstractContainerScreen` | a screen mirroring a server-side menu, and the slot geometry | Render thread | | `MenuScreens` | `MenuType` to screen class — the only registry of screens in the game | Render thread | | `Overlay` | suppresses the screen's record pass, its mouse and its typing — but not its key presses | Render thread | | `ScreenNarrationCollector` | what has already been said, so it is not said twice | Render thread | ## The objects, and what contains what ```mermaid flowchart TD Gui["Gui — the manager, once per game"] Screen["Screen — zero or one"] Overlay["Overlay — zero or one, and it wins"] Hud["Hud — reached as Gui.hud"] Toasts["ToastManager, ChatListener, SplashManager"] Children["Screen.children — GuiEventListener, gets input"] Rend["Screen.renderables — Renderable, gets recorded"] Narr["Screen.narratables — NarratableEntry, gets described"] Widget["AbstractWidget — usually in all three lists at once"] Layout["Layout over LayoutElement — arranges, then forgets"] ACS["AbstractContainerScreen — a Screen with a menu behind it"] Menu["AbstractContainerMenu — shared with the server"] Gui --> Screen Gui --> Overlay Gui --> Hud Gui --> Toasts Screen --> Children Screen --> Rend Screen --> Narr Children --> Widget Rend --> Widget Narr --> Widget Layout --> Widget Screen --> ACS ACS --> Menu ``` The three lists on `Screen` are the shape worth remembering: a widget added with `Screen.addRenderableWidget` joins all three, and the sibling add methods exist precisely so that something can be in one or two of them and not the rest. A `Tooltip` is in none of them — it is held by a `WidgetTooltipHolder` — and `MultiLineLabel` is an interface rather than a widget at all. ## `Gui`, which is not the HUD `Gui` owns `Gui.screen` and `Gui.overlay` — set through `Gui.setScreen` and `Gui.setOverlay` — plus `Gui.hud`, `Gui.toastManager`, `Gui.chatListener`, `Gui.splashManager` and the reference to the frame's render state. It has **three** cadences, not two: `Gui.tick` once per client tick, `Gui.update` once per frame — which advances toasts and fires delayed narration — and `Gui.extractRenderState` once per frame in the record pass. The rest of its surface is `Gui.isPausing`, `Gui.handleKeybinds`, `Gui.openChatScreen`, `Gui.canInterruptScreen`, `Gui.buildInitialScreens` and `Gui.setClientLevelTeardownInProgress`. Two of its behaviours are the sort of thing a reader assumes and gets wrong. **`Gui.setScreen` with a null screen does not mean "close the screen".** It means "decide what should be up instead". With no level it substitutes the title screen; with a dead player it substitutes the death screen, or respawns; otherwise it restores the chat screen if one was saved. During a level teardown it throws, rather than return you to a world that is being dismantled. **`Gui.isPausing` is what stops the integrated server**, and it asks the screen. `Screen.isPauseScreen` defaults to **true** and `AbstractContainerScreen` overrides it to false — which is the whole reason the options screen pauses a singleplayer world and a chest does not. An overlay pauses by default too. And an overlay does not stack on a screen: in the record pass it *replaces* it. Nothing draws both. `LoadingOverlay` is the only implementation of `Overlay` in the game. ## The lifecycle, and what is final `Screen.init` is **final**, and a resize goes through `Screen.resize` to `Screen.repositionElements`. The default `Screen.repositionElements` rebuilds every widget through `Screen.rebuildWidgets`, which does re-enter the overridable `Screen.init` hook — so on a plain screen everything really is rebuilt. Forty-one screens override `Screen.repositionElements` instead and keep their widgets, most of them just re-arranging their `Layout`. "Everything is rebuilt on resize" is true of a plain screen and false of most interesting ones. The rest of the lifecycle is `Screen.added`, `Screen.tick`, `Screen.removed` and `Screen.onClose`, and the record entry point is the final `Screen.extractRenderStateWithTooltipAndSubtitles`. The framework fixes the outer shape everywhere and hands the subclass one inner hook. `Screen.init`, `Screen.extractRenderStateWithTooltipAndSubtitles`, `AbstractWidget.extractRenderState`, `AbstractWidget.updateNarration`, `AbstractButton.extractWidgetRenderState` and `AbstractContainerScreen.tick` are all final; `AbstractWidget.extractWidgetRenderState` and `AbstractButton.extractContents` are the hooks they leave open. The widget family under them is `Button`, `EditBox`, `Checkbox`, `CycleButton`, `AbstractScrollArea` and the selection lists over it — `AbstractSelectionList`, `ObjectSelectionList`, `ContainerObjectSelectionList` and `OptionsList`. Layout is `Layout` over `LayoutElement`: `LinearLayout`, `GridLayout`, `FrameLayout`, `EqualSpacingLayout`, `HeaderAndFooterLayout` and `SpacerElement`, configured by `LayoutSettings` and resolved by `Layout.arrangeElements` and `Layout.visitWidgets`. Input arrives as the `client/input` records through `GuiEventListener` and `ContainerEventHandler`; focus is a `ComponentPath` moved by a `FocusNavigationEvent`, ordered by `TabOrderedElement.getTabOrderGroup`; and geometry is `ScreenRectangle`, `ScreenPosition`, `ScreenAxis` and `ScreenDirection`. One consequence of doing all this in a record pass: `AbstractWidget.extractRenderState` computes *hovered* as "inside my rectangle **and** inside the current scissor", so a widget scrolled out of a list does not light up. ## Pressing E ```mermaid sequenceDiagram participant KH as KeyboardHandler participant MC as Minecraft participant Gui as Gui participant InvS as InventoryScreen participant MPGM as MultiPlayerGameMode KH->>KH: keyPress — no screen is open, so the mapping records a click Note over MC: next client tick MC->>MC: handleKeybinds — only with no screen and no overlay MC->>MPGM: isServerControlledInventory? false for a player on foot MC->>MC: Tutorial.onOpenInventory MC->>Gui: setScreen(new InventoryScreen(player)) Gui->>Gui: MouseHandler.releaseMouse, then KeyMapping.releaseAll — both before init Gui->>InvS: removed on the old screen, then added, then Screen.init InvS->>InvS: init — creative? replace myself with CreativeModeInventoryScreen Note over Gui: next frame, record Gui->>InvS: extractRenderStateWithTooltipAndSubtitles InvS->>InvS: extractBackground — in-game UI, so a gradient, no blur, no panorama InvS->>InvS: extractContents, then labels, slots, the hovered highlight, the carried item ``` The busiest screen in the game is `AbstractContainerScreen`, and its record pass is worth following once: `AbstractContainerScreen.extractContents` draws the widget list, translates to the container origin, and runs `AbstractContainerScreen.extractLabels`, `AbstractContainerScreen.extractSlots` and the two slot-highlight passes; then `AbstractContainerScreen.extractCarriedItem`, then `AbstractContainerScreen.extractTooltip`. It holds `AbstractContainerScreen.menu`, `AbstractContainerScreen.leftPos`, `AbstractContainerScreen.topPos`, `AbstractContainerScreen.hoveredSlot` and the quick-craft state, and a click goes `AbstractContainerScreen.slotClicked` to `MultiPlayerGameMode.handleContainerInput` — see [containers and menus](../items/containers-and-menus.md). The final `AbstractContainerScreen.tick` closes the container when the player is dead or removed. The *client* notices first. ## Who opens a screen | route | examples | |---|---| | entirely client-side | title, pause, options, chat, advancements, social interactions, the survival and creative inventories | | `ClientboundOpenScreenPacket` | every menu with a `MenuType` — chests, furnaces, anvils, and a chest boat | | `ClientboundMountScreenOpenPacket` | a horse's or a nautilus's own inventory | | other packets | the book viewer, the sign editor, the death screen, the win screen, the demo popup, the level-loading screen, dialogs | Three entities implement `HasCustomInventoryScreen`, and they do not agree: two use the mount packet and one falls back to the ordinary menu packet. The screens you see *first* are a chain rather than a screen. `Gui.buildInitialScreens` composes accessibility onboarding, ban notices, a forced name change and a banned-skin notice ahead of the title screen or a quick-play launch. And `Minecraft.setScreenAndShow` sets a screen and then renders one frame on the spot — synchronously — which is how progress appears during blocking main-thread work such as a world load, a data fix or a save. Narration, finally, is mostly timed rather than immediate — mostly, because `Screen.init` narrates the new screen at once before arming anything. Thereafter `Screen.handleDelayedNarration` fires from `Gui.update` once two clocks have passed — one delay after a mouse move, a shorter one after a keyboard action, and a two-second suppression after a screen is built — and then picks a *single* widget to narrate, by tab-order group and priority. > **For a 1.21-era reader.** `Minecraft.screen` is gone: the current screen > belongs to `Gui`, which is now the screen-and-overlay manager rather than > the HUD — the HUD is `Hud`, reached as `Gui.hud`. Gone with it: > *Screen.render*, *renderBackground* and *renderDirtBackground*; > *AbstractContainerScreen.renderBg* / *renderLabels* / *renderSlot*; > *AbstractWidget.renderWidget*; *ClickType* (now `ContainerInput`); > *MultiPlayerGameMode.handleInventoryMouseClick* (now > `MultiPlayerGameMode.handleContainerInput`); and *Minecraft.setScreen*. > A screen no longer draws — it *records*. ## Where to look `Gui.setScreen` — the substitution tree and the input housekeeping at both ends of a screen's life. `Screen.init` and `Screen.resize` for the lifecycle, `Screen.extractRenderStateWithTooltipAndSubtitles` for the record pass, and `Gui.extractRenderState` for the frame's contributor order. `AbstractContainerScreen.extractContents` for the busiest screen in the game, and `MenuScreens` for the screen registry the menu types use — `DialogScreens` is the second one. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The GUI render tree > Verified against **Minecraft 26.2** · Part X · a chest full of the same item: how the 2D UI decides what is in front of what, without anybody ever saying so. Nothing in the client's 2D UI draws anything. Every call a screen, a widget or the HUD makes appends a *render state* to a tree; later in the same frame a second pass resolves that tree, sorts it, batches it and issues the draws. And the tree is not told what order to put things in. **Layering is inferred from bounding boxes**: a new element goes above the highest existing element whose box it *intersects*, and two elements that do not overlap can share a node and be reordered freely by the batching sort. There is no Z value, no layer index, and no declared order beyond the call order and two explicit barriers. That inference is what makes the batching possible, and the batching is why a chest full of identical stacks is cheap: each distinct item model is rendered into a dynamic atlas **once and reused for as long as it stays resident** — one 3D render ever, not one per frame, and not one per stack. ## The cast | class | what it decides | thread | |---|---|---| | `GuiGraphicsExtractor` | what a screen is handed — every drawing verb, and the scissor stack | Render thread | | `GuiRenderState` | the tree: strata, nodes, and where a new element belongs | Render thread | | `GuiRenderState.Node` | one layer: a list of elements and a separate list of glyphs | Render thread | | `GuiElementRenderState` | one recorded thing, and the bounds the layering algorithm reads | Render thread | | `GuiRenderer` | resolving, sorting, coalescing and issuing the draws | Render thread | | `GuiItemAtlas` | which item models are already rendered, and which age out | Render thread | | `GameRenderState` | who actually owns the tree — not `Gui` | Render thread | ## The tree, and where a new element lands ```mermaid flowchart TD GRS["GuiRenderState — a list of strata"] S1["stratum 0"] S2["stratum 1 — opened by nextStratum, a hard floor for the search"] N1["Node"] N2["Node — above"] N3["Node — above that"] EL["elements: BlitRenderState, TiledBlitRenderState, ColoredRectangleRenderState — the list the sort works on"] GL["glyphs: GlyphRenderState, in a second list the sort never touches"] OTH["and three more lists beside them: items, text, pictures in picture"] NEW["a new element arrives"] FAST{"does the previous element's box contain it?"} UP["up one node — no intersection test at all"] WALK["walk up from the current stratum to just above the highest box it touches"] NONE{"has bounds?"} DROP["silently discarded"] GRS --> S1 GRS --> S2 S2 --> N1 --> N2 --> N3 N2 --> EL N2 --> GL N2 --> OTH NEW --> NONE NONE -- "no" --> DROP NONE -- "yes" --> FAST FAST -- "yes" --> UP FAST -- "no" --> WALK ``` Three consequences fall straight out of that picture. **An element with no bounds is silently discarded.** Every *recording* add verb is conditional on the tree finding a node, and finding a node requires bounds. Glyph states deliberately have none — which is exactly why they are added through the layer-bypassing verb, `GuiRenderState.addGlyphToCurrentLayer`, which the draw pass calls rather than the record pass, and emitted after their node's geometry. **Glyphs are never sorted**, and that is the whole mechanism behind "text draws on top of its own background". **The search never descends below the current stratum**, which is what makes `GuiRenderState.nextStratum` a hard barrier rather than a hint. It is how the HUD keeps the hotbar block out of the crosshair's layering, and how chat stays clear of the scoreboard. **The fast path is the common case.** If the previous element's box *contains* the new one — a label inside a button, a sprite inside a slot — it goes straight up with no test. The recording verbs are `GuiRenderState.addGuiElement`, `GuiRenderState.addText`, `GuiRenderState.addItem`, `GuiRenderState.addPicturesInPictureState`, `GuiRenderState.addBlitToCurrentLayer` and `GuiRenderState.addGlyphToCurrentLayer`; the structural ones are `GuiRenderState.nextStratum` and `GuiRenderState.blurBeforeThisStratum`. The states themselves are `BlitRenderState`, `TiledBlitRenderState`, `ColoredRectangleRenderState`, `GuiTextRenderState`, `GlyphRenderState`, `GuiItemRenderState`, `PanoramaRenderState`, and the `PictureInPictureRenderState` family — `GuiEntityRenderState`, `GuiSkinRenderState`, `GuiBookModelRenderState`, `GuiBannerResultRenderState`, `GuiProfilerChartRenderState` and `OversizedItemRenderState`. ## The draw pass Two phases, one thread, one frame. It is a *data* split rather than a thread split: the record phase touches game state, the draw phase touches the recorded objects and the GPU. ```mermaid flowchart TD REC["Gui.extractRenderState — reset the tree, build a fresh extractor, call the contributors in order"] PREP["GuiRenderer.prepare"] PIP["pictures-in-picture: 3D content rendered to textures"] ITEM["items: models rendered into GuiItemAtlas, once each, then reused"] TEXT["text: prepared text expanded into per-glyph states"] SORT["sortElements — per node, by scissor, then pipeline, then texture"] MESH["addElementToMesh — a new Draw only when pipeline, scissor or texture changes"] DRAW["GuiRenderer.draw"] BEFORE["everything before the blur"] BLUR["clear depth, run the post effect"] AFTER["everything after"] END["GuiRenderer.endFrame — called by GameRenderer, ages the item atlas"] REC --> PREP --> PIP --> ITEM --> TEXT --> SORT --> MESH --> DRAW DRAW --> BEFORE --> BLUR --> AFTER --> END ``` One thing happens eagerly during recording that looks like it should not: adding text forces the text to be **prepared**, because the tree needs its bounds to place it. Only the expansion into per-glyph states waits for the draw pass — see [text and fonts](text-and-fonts.md). The three sort comparators, `GuiRenderer.ELEMENT_SORT_COMPARATOR`, `GuiRenderer.SCISSOR_COMPARATOR` and `GuiRenderer.TEXTURE_COMPARATOR`, are what turns a node's element list into as few `GuiRenderer.Draw`s as possible. Both the sort and the coalescing happen inside `GuiRenderer.prepare`; `GuiRenderer.draw` only replays the list they produced. ## Blur is a barrier, and it is fussy `GuiRenderState.blurBeforeThisStratum` splits the draw list in two. Asking for it twice in one frame **throws**. It is conditional on the menu-background blurriness option being at least one, and screens that declare themselves in-game UI — container screens, sign editors, book screens — take the transparent-background path and never request it. That is why the pause menu blurs the world and a chest does not. ## Questions a reader asks **Does the item atlas ever get expensive?** Whenever a slot is not already resident and current — a slot that has gone stale, or was never filled, is redrawn with no invalidation involved. Wholesale invalidation is the loud case: changing the GUI scale throws the atlas away, and an atlas that cannot grow logs that some items will be skipped. Animated models are the exception to residency: they are redrawn every frame. The aging that evicts a slot happens in `GuiRenderer.endFrame`, which `GameRenderer` calls — not `GuiRenderer.render`. **Does the extractor really have no side effects?** It has two, just not drawing ones. The scissor stack is real state, and the cursor shape requested during the record pass is applied to the window at the end of it. It also holds the deferred tooltip and the pre-edit overlay. **Can a 2D flag change how the world is drawn?** Yes. The HUD's hidden flag and a clear-colour override live on `GuiRenderState` and are read by `GameRenderer` — every site is `GameRenderer`'s, not `LevelRenderer`'s. The tree also belongs to `GameRenderState` rather than to `Gui`: the GUI holds a reference to it. Nor is this the only reach backwards: the HUD's boss bar reads world fog, the lightmap and the level render state as well. **What if the batching sort were wrong?** There are debug switches for exactly that. One promotes every element into its own layer and outlines it; another shuffles each node's element list and re-seeds the sort keys, to shake out accidental order dependence. The second one is how you would find out. > **For a 1.21-era reader.** There is no *GuiGraphics*. The class is > `GuiGraphicsExtractor`, and the name is the whole design — it extracts, it > does not paint. *LayeredDraw* is gone too: ordering is the literal call > order plus explicit barriers. *GuiGraphics.renderTooltip* is gone, and so > is `PoseStack` in 2D GUI code — the GUI transform is a 2D affine stack now, > though a real `PoseStack` still lives inside the item atlas, where actual > 3D models are drawn. ## Where to look `GuiRenderState.nextStratum` and the node-placement logic beside it — the layering rule is thirty lines and explains most of the UI's behaviour. `GuiGraphicsExtractor` for what a screen is actually handed. `GuiRenderer.prepare` for where items, text and picture-in-picture content are resolved, and `GuiRenderer.draw` for the batching rule and the blur split. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Text and fonts > Verified against **Minecraft 26.2** · Part X · a chat line in a language whose characters are not in the default font: six stages from a `Component` to a quad, one of which uploads a texture while pretending to measure. Ask the client how wide a piece of text is and it will stitch a glyph into a texture sheet and upload it to the GPU. `Font.width` resolves each codepoint to a **baked** glyph and reads its advance — so measuring a codepoint nobody has drawn yet is neither free nor read-only, and a layout pass over a never-before-seen script is a series of texture uploads wearing arithmetic's clothes. This page is the pipeline that ends in that glyph: flattening a component tree into styled runs, measuring and wrapping it, reordering it for bidirectional scripts, choosing a glyph for every codepoint from a chain of providers, baking it the first time anyone asks, and emitting the vertices. What a `Component` *is* — its contents kinds, its `Style`, how it is serialised — is Part II's [text components](../foundations/text-components.md), and how a chat message is signed is [chat and signing](../networking/chat-and-signing.md). This page starts from "you have one". ## The cast | class | what it decides | thread | |---|---|---| | `StringDecomposer` | where one styled run ends and the next begins, and what a section sign means | Render thread | | `StringSplitter` | width, line breaks and word boundaries, given a width provider | Render thread | | `FormattedBidiReorder` | visual order, by handing the flattened text to ICU | Render thread | | `FontManager` | which `GlyphSource` a `FontDescription` resolves to — three different ways | reload workers, then Render thread | | `FontSet` | the provider chain for one font id, its codepoint cache, and the fishy split | Render thread | | `GlyphStitcher` | where a bitmap lands in a `FontTexture`, and the upload | Render thread | | `Font` | the per-codepoint loop, and the `Font.PreparedText` everything else walks | Render thread | | `GuiRenderer` | expanding prepared text into one `GlyphRenderState` per glyph | Render thread | ## The six stages ```mermaid flowchart TD C["a Component"] RUNS["1 · flatten — Component.visit yields (style, string) runs in logical order, Style.applyTo down the sibling tree"] DEC["StringDecomposer walks them codepoint by codepoint and interprets legacy section-sign codes"] WRAP["2 · measure and wrap — StringSplitter cuts at a character offset, each surviving run keeping its own Style"] BIDI["3 · reorder — Language.getVisualOrder, ClientLanguage, FormattedBidiReorder, ICU"] RESOLVE["4 · resolve — FontManager answers a FontDescription with a GlyphSource, then the first provider that has the codepoint"] BAKE["5 · bake — first sight only: GlyphBitmap into a FontTexture, uploaded, producing a BakedGlyph"] EMIT["6 · emit — a TextRenderable per glyph, plus effect glyphs, into a Font.PreparedText"] GUI["GuiRenderer expands it into GlyphRenderState"] WORLD["SubmitNodeCollection.submitText and submitNameTag feed the world renderers"] C --> RUNS --> DEC --> WRAP --> BIDI --> RESOLVE --> BAKE --> EMIT WRAP -. "measuring a codepoint resolves and bakes it too" .-> RESOLVE EMIT --> GUI EMIT --> WORLD ``` The numbering is the order the stages *matter* in, not a pipeline anything walks straight through. Stages one to three run whenever the text changes, and four to six run inside `Font.prepareText`, which the GUI calls **during the record pass**, because [the render tree](the-gui-render-tree.md) needs the text's bounds before it can place it. But four and five are also reached from *two*: the width function `Font` hands its `StringSplitter` asks the glyph source for each codepoint, and that call resolves the provider and forces the bake. Measuring a string you never draw still uploads its glyphs. Only the expansion into per-glyph states waits for the draw pass. All of it is on the Render thread, glyph baking and GPU uploads included. The only work that leaves the thread is *loading*: `FontManager`'s prepare phase parses the font definitions, loads each provider, resolves references between them, and pre-warms every provider by asking it for every codepoint it claims, on the reload workers. The apply phase — closing the old font sets and building new ones — is back on the Render thread. ### 1 · Flatten `Component` and `FormattedText` provide the walk: `Component.visit` yields (style, string) runs in logical order, applying `Style.applyTo` down the sibling tree. `StringDecomposer` iterates those runs codepoint by codepoint and is where a legacy section-sign colour code is interpreted. `FormattedCharSequence` is the end product — a one-method interface that pushes (index, style, codepoint) triples at a `FormattedCharSink` — and `ComponentCollector` reassembles pieces back into a component. ### 2 · Measure and wrap `StringSplitter` owns width and line breaking: `StringSplitter.stringWidth`, `StringSplitter.splitLines`, `StringSplitter.headByWidth`, `StringSplitter.findLineBreak` and `StringSplitter.getWordPosition`. It works against a `StringSplitter.WidthProvider`, and on the real `Font` that provider is what bakes. `Font.width`, `Font.split`, `Font.splitIgnoringLanguage`, `Font.wordWrapHeight` and `Font.getSplitter` are the public face of it. **Wrapping preserves styles rather than ignoring them.** It cuts at a character offset, captures the style in force at the cut, and re-applies it to the continuation — which is why a colour code before a wrap point still colours the line after it. And `Font.split` reorders while `Font.splitIgnoringLanguage` does not — anything that will re-measure or re-wrap must use the second. ### 3 · Reorder `Language.getVisualOrder` is the entry point; on the client `ClientLanguage` implements it with `FormattedBidiReorder`, which builds a `SubStringSource` — the flattened plain text plus one `Style` per character — runs ICU's bidi algorithm over it, and re-emits each run through `SubStringSource.substring`. `MutableComponent.getVisualOrderText` caches the result against the identity of the current `Language`. `Font.bidirectionalShaping` is a separate, much smaller thing: it shapes a bare string, and beside `Font.prepareText`'s own use of it the sign editor is the only caller. ### 4 · Resolve `FontManager` is the reload listener and the resolver: `Font.Provider` asks it for a `GlyphSource` given a `FontDescription`, and it answers three different ways. A `FontDescription.Resource` resolves to a `FontSet` — the per-font-id object holding the provider list, the codepoint cache (`CodepointMap`), a `GlyphStitcher` and the by-width table used for obfuscation. A `FontDescription.AtlasSprite` or `FontDescription.PlayerSprite` resolves instead to a `SingleSpriteSource` from `AtlasGlyphProvider` or `PlayerGlyphProvider` — a one-glyph font that returns the same sprite for every codepoint, with no texture sheet of its own; `FontManager` still keeps a `FontSet` behind it as the fallback. Below that: `GlyphProvider` implementations chosen by `GlyphProviderType` — bitmap, TrueType, space, unihex, reference — declared in *font/* JSON as `GlyphProviderDefinition`s. ### 5 · Bake A provider returns an `UnbakedGlyph`, whose `GlyphBitmap` is handed to `GlyphStitcher.stitch`, placed into a `FontTexture` sheet and uploaded, producing a `BakedGlyph`. `BakedGlyph` is an interface; the sheet implementation is `BakedSheetGlyph`, and `EffectGlyph` covers the solid quads used for underlines and backgrounds. There is **one glyph atlas family per font id**, each with its own stitcher, and colour and greyscale glyphs never share a sheet. The atlas is discarded, never compacted: a resource reload throws it away — and so does toggling the force-unicode or Japanese-variants option, which rebuilds every font set with no reload at all. ### 6 · Emit What comes out is a `TextRenderable` per glyph inside a `Font.PreparedText`. In the GUI, `GuiGraphicsExtractor.text` and its siblings build a `GuiTextRenderState` that `GuiRenderer` later expands into `GlyphRenderState`s. In the world, `SubmitNodeCollection.submitText` and `SubmitNodeCollection.submitNameTag` feed `TextFeatureRenderer` and `NameTagFeatureRenderer`, and `GlyphRenderTypes.select` picks between the normal, see-through and polygon-offset render types by `Font.DisplayMode`. ## A chat line, through all six ```mermaid sequenceDiagram participant ChatC as ChatComponent participant CRU as ComponentRenderUtils participant SSpl as StringSplitter participant FBR as FormattedBidiReorder participant Font as Font participant FSet as FontSet participant GStit as GlyphStitcher participant GuiR as GuiRenderer ChatC->>CRU: wrapComponents — the chat width divided by the chat scale CRU->>SSpl: splitLines — translation happens here, on first visit SSpl->>SSpl: break at a char offset, each surviving run keeping its own Style CRU->>FBR: Language.getVisualOrder(line) FBR->>FBR: SubStringSource, ICU bidi, one substring per run Note over ChatC: next frame, record ChatC->>Font: prepareText — via GuiTextRenderState asking for its own bounds loop per codepoint Font->>FSet: getGlyph — the first provider that has it FSet->>GStit: first sight only: stitch into a FontTexture and upload Font->>Font: emit a TextRenderable, add underline or strikethrough, advance the pen end Note over GuiR: same frame, draw GuiR->>GuiR: walk the PreparedText with a Font.GlyphVisitor GuiR->>GuiR: one GlyphRenderState per glyph — the shadow pass, the bold copy and the italic shear are the glyph's own, inside BakedSheetGlyph.renderChar ``` Two moments beyond the baking are worth pausing on. **Translation is lazy and cached on the `TranslatableContents` itself**, so the first thing to visit a message is what translates it — usually a measure, sometimes a log line one statement earlier. And the continuation indent on a wrapped chat line is a **literal space codepoint** prepended by `ComponentRenderUtils` — the one place in the pipeline where a character is invented rather than derived. ## Questions a reader asks **Why is this glyph a hollow box?** Four different causes, one appearance. No provider has the codepoint; the font id is unknown, so the whole font set is the missing-font set; the glyph's advance is "fishy" and the caller asked for the filtered font; or the bitmap fit no sheet. **What is a "fishy" glyph?** An advance outside a sane range. Every font set stores two suppliers per codepoint because of it, and the game builds a second `Font` that filters the fishy ones out. That second font has exactly one use in the entire client: the chat input box — a font that cannot be made to draw a character three screens wide. **How is a block icon in a chat line drawn?** As a glyph. An object component emits a single object replacement character with a synthetic font description, which resolves to a one-glyph sprite font. A block icon or a player head is, mechanically, one character in a font with one character in it — and the plain-text walk of the same component yields a bracketed fallback string instead, which is what the narrator and `Component.getString` see. A data pack cannot do this: the codec behind `FontDescription` only encodes the resource kind, so **a style can never name a sprite font**. The sprite descriptions arise only from object contents. **Why does obfuscated text not shift the layout?** The glyph is swapped for a random one *of the same width*, from a table built once per font set. And because the swap happens inside `Font.prepareText`, which already runs once per frame, the animation costs nothing extra. **Is bold a font?** No. Bold draws the glyph twice with a small offset and thickens it; italic shears the top and bottom edges. Nothing in the font pipeline knows what a bold face is. Shadow is a colour rather than a boolean, and zero means none. Underlines, strikethroughs and text backgrounds always come from the **default** font whatever the style names, because the effect glyph is looked up separately. **How does hovering a link work?** `ActiveTextCollector` walks the very same `Font.PreparedText` that will be drawn, looking for the style under the cursor — which is why preparation can be asked to record *empty* areas, so that hovering the space inside a hover-event run still finds the style. Glyph areas deliberately extend to the full advance, so there are no dead gaps between characters. **Are the caches safe?** Mostly by being single-threaded rather than by being locked: they are meant to be touched only on the Render thread. It is not a clean rule — some are keyed on identity and some on equality, and the glyph layer does use *volatile* fields and a Guava cache. A component's cached visual order is invalidated by the `Language` object changing, and a translatable component's decomposition likewise. The one place that leaks: the sign block entity caches its rendered lines on a class the *server* also ships, and that cache does not notice a font reload. > **For a 1.21-era reader.** `Font` cannot draw. Every *drawInBatch* and > *drawString* is gone; `Font.prepareText` returns a `Font.PreparedText` that > somebody else walks later. `Style.getFont` no longer returns an identifier > — it returns a `FontDescription`, which may not name a font file at all. > Also gone: *Font.StringRenderOutput*; *RawGlyph* and *SheetGlyphInfo* (now > `UnbakedGlyph` and `GlyphBitmap`); *BakedGlyph* as a class; > *GlyphProviderBuilder* (now `GlyphProviderDefinition`); and > `FontSet.getGlyph` as public API. `StringSplitter`, `StringDecomposer`, > `FormattedCharSequence`, `SubStringSource`, `GlyphStitcher` and > `FontTexture` all survive under their old names. ## Where to look `Font.prepareText` — the per-codepoint loop is the centre of the page. `FontSet` for the provider chain and the fishy-advance split, `FontManager` for how a `FontDescription` becomes a `GlyphSource`, and `GlyphStitcher.stitch` for the moment a glyph acquires a texture. `StringSplitter.splitLines` for how a line break preserves styles, and `FormattedBidiReorder.reorder` for the heaviest of the four places ICU is used. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The HUD > Verified against **Minecraft 26.2** · Part X · press F1 and the interface goes away — except for the thing that can black out your whole screen. `Hud.extractRenderState` is a single ordered method wrapped in two hidden-gated blocks, and one element sits *between* them: the sleep fade. So F1 hides the hearts, the hotbar, the crosshair, chat and the tab list, and does not hide the black screen that comes over you when you get into bed. Four more elements are recorded by `Gui`, after the screen rather than with the rest of the HUD — the saving indicator, the debug overlay, the deferred subtitles and the toasts — and only the toasts are outside `Hud`'s own methods. Only the saving indicator ignores the flag. That is what this page is: not a tour of hearts and hotbars but a **policy**. What is drawn over the world, in what order, under exactly which conditions, and which of those conditions is a surprise. The full per-element table is [what the HUD draws, and when](../../reference/hud-elements.md) in Reference; the machinery underneath every one of them is [the GUI render tree](the-gui-render-tree.md). ## The cast | class | what it decides | thread | |---|---|---| | `Hud` | the order, the two hidden-gated blocks, and the health animation state | Render thread | | `Gui` | the four elements recorded *after* the screen | Render thread | | `ContextualBar` | which of four things occupies the one slot above the hotbar | Render thread | | `BossHealthOverlay` | the bars, and three questions the world renderer asks it | Render thread | | `ChatComponent` | the message list, its wrapped lines, and what is faded out | Render thread | | `DebugScreenEntries` | the F3 registry: what an entry is, and whether it is on | Render thread | | `DebugScreenEntryList` | the per-entry status, its presets, and its own save file | Render thread | | `GuiGraphicsExtractor` | everything the HUD records into | Render thread | ## The order, and the two blocks ```mermaid flowchart TD PUB["publish GuiRenderState.isHudHidden — before any check, so the flag is always current"] LLS{"is a LevelLoadingScreen up?"} STOP["record nothing"] H1{"hidden?"} A["camera overlays, then the crosshair, then a new stratum, then the hotbar block, effects, boss bars"] SLEEP["the sleep fade — ungated"] H2{"hidden?"} B["demo text, scoreboard sidebar, action bar, title, chat, tab list, subtitles"] B2["subtitles only, and only if an in-game-UI screen is up"] GUI["Gui continues: saving indicator, toasts, debug overlay, deferred subtitles"] PUB --> LLS LLS -- "yes" --> STOP LLS -- "no" --> H1 H1 -- "no" --> A --> SLEEP H1 -- "yes" --> SLEEP SLEEP --> H2 H2 -- "no" --> B --> GUI H2 -- "yes" --> B2 --> GUI ``` Two structural facts follow from the shape. The hidden flag is published *before* the loading-screen short-circuit, so the renderer's copy is correct even on a frame where the HUD records nothing. And toasts and the debug overlay are always **above** a screen, because `Gui` records them after it — while the deferred subtitles are called from a screen's *background* pass and therefore land under the screen's widgets. `Hud.tick` runs from `Gui.tick` once per client tick and takes a pause flag: the autosave indicator animates either way, everything else only when the game is not paused. ## The hidden flag travels two ways The interesting one is the smaller. `GuiRenderState.isHudHidden` is read by `GameRenderer` in three places, to suppress the held item, the three-dimensional crosshair and — the smallest of the three — the totem-pop animation. The block-in-eyes, water and fire overlays are drawn whatever F1 says. So a 2D flag does change how the *world* is drawn, in three narrow ways. But `Hud.isHidden` itself is read directly by six other places across the client, including two entity renderers that suppress name tags. ## Four states, one slot, and an asymmetric rule `Hud.contextualInfoBar` holds one of four states — nothing, experience, the locator, a jumpable vehicle — and `Hud.nextContextualInfoState` re-decides which every frame. It is a rule, not a state machine, and the rule is not symmetric. With waypoints present, a jumping vehicle or a *recently changed* experience total beats the locator; with no waypoints, a jumpable vehicle beats experience unconditionally — so **mounting a horse silently takes your XP bar away.** The level number is recorded separately from the bar, so it survives whichever bar wins. The three implementations are `ExperienceBar`, `LocatorBar` — backed by `ClientWaypointManager` and styled by `Hud.waypointStyles`, a `WaypointStyleManager` that is the HUD's own reload listener — and `JumpableVehicleBar`. `Hud.ContextualInfo` is the enum of the four states. ## The hearts, which are three numbers at once ```mermaid sequenceDiagram participant CPL as ClientPacketListener participant LP as LocalPlayer participant Hud as Hud participant GGE as GuiGraphicsExtractor CPL->>LP: handleSetHealth — hurtTo, which sets hurtTime and invulnerableTime Note over Hud: next frame Hud->>Hud: extractPlayerHealth — health fell while invulnerable Hud->>Hud: healthBlinkTime becomes tickCount plus 20 (a heal sets 10) Hud->>Hud: displayHealth catches up only once the second has elapsed Hud->>Hud: random.setSeed(tickCount times a constant) — the jitter is per tick, not per frame Hud->>Hud: extractHearts — one descending pass: container, absorption, ghost, truth Hud->>GGE: blitSprite per heart, chosen by HeartType.getSprite ``` **The shake is seeded from the tick counter**, so it jitters at 20 Hz and is identical across two frames of the same tick — and the same seeded stream drives the hunger jitter and the air-bubble wobble, which is why they shake together. **The blink is a square wave** with a three-tick half-period, running for twenty ticks after damage and ten after a heal, and what it draws is the *ghost*: the HUD keeps three health numbers at once — last frame's, a lagging display value that catches up about once a second, and the truth — and the blinking layer shows the one that is out of date on purpose. `Hud.HeartType` is six constants, one of which Mojang spells `Hud.HeartType.POISIONED`, each with eight sprites. One gate silences four elements at once: armour, hearts, food and air are all recorded inside `Hud.extractPlayerHealth`, which is gated on the game mode being able to hurt you — which is why creative has no armour bar either. Food and mount health share a slot, and the air bubbles shift up when either is drawn. And the HUD makes a sound: `Hud.playAirBubblePoppedSound` ramps its volume and pitch with how many bubbles are *gone*, so it climbs as you drown. ## Questions players ask **Why does the boss bar change the sky?** The overlay answers three questions — should the screen darken, should world fog be created, should the End music play — read from five places between the frame, the fog environment and the lightmap. The bar itself interpolates against wall-clock time inside `LerpingBossEvent`, so discrete packet progress becomes a smooth bar. **Why do subtitles appear under an open chest?** They are deferred past the screen, along with the tooltip and the pre-edit overlay the extractor holds. The deferral fires when there is no screen at all *or* the screen declares itself in-game UI — the common case, not the rare one — and the deferred call is made from a screen's background pass. **Are the chat HUD and the chat screen the same thing?** The same code in different modes. The HUD bails out entirely when the chat screen is focused. Message age is measured in HUD ticks rather than timestamps, and a message the server asked to delete keeps its original timestamp when it is replaced by a marker — so the marker fades on the original message's schedule. `ChatComponent` holds **four** collections, not two: every message and every wrapped *line* are separate lists with separate caps, and the deletion queue and the recent-input history are the other two. A fifth, the delay-option queue, lives on `ChatListener`. Signing belongs to [chat and signing](../networking/chat-and-signing.md); this page owns display only. **Is the pumpkin blur hardcoded?** No. The camera overlay list is data-driven: every equipment slot is asked whether its item declares a camera overlay. **Can I turn a debug line on without pressing F3?** Yes, and the game saves that you did. `DebugScreenEntries` holds every entry by `Identifier`, each a `DebugScreenEntry` writing lines through a `DebugScreenDisplayer`; `DebugScreenEntryList`, reachable as `Minecraft.debugEntries`, stores a `DebugScreenEntryStatus` per entry, ships `DebugScreenProfile` presets and persists to its own file with its own data-fixer type. An entry set to always-on renders with F3 never pressed. The screen that edits it is suppressed by `Gui`, not by the overlay. The charts are `FpsDebugChart`, `TpsDebugChart`, `PingDebugChart` and `BandwidthDebugChart` over `AbstractDebugChart` — plus `ProfilerPieChart`, which is not one of them. **Why is that F3 shortcut not rebindable?** Twenty are ordinary key mappings; a second family is a raw switch on key codes behind the game's debug flag, bindable to nothing. Several of the mappings toggle debug entries that print nothing at all and exist only to carry a flag the world renderer reads. **What counts as "HUD state"?** Whatever `Hud.onDisconnected` resets — tab list, boss bars, toasts, debug overlay, chat and titles, together. It is the only place that clears the HUD as a whole, and it is a better definition than any list of fields. > **For a 1.21-era reader.** `Gui` is not the HUD any more. The class that > drew the hotbar and hearts is `Hud`; the name `Gui` was reused for the > screen and overlay manager that used to be fields on `Minecraft`. The > canonical path is `Gui.hud`. Every *render\** method on `Gui` is now an *extract\** on `Hud`, and gone with them: *LayeredDraw*, *Minecraft.screen*, > *Minecraft.getToastManager*, *Minecraft.fpsString*, *Options.hideGui*, and > *DebugScreenOverlay.render* along with its two information-gathering > methods — the line content moved out into the entry registry. ## Where to look `Hud.extractRenderState` — the whole HUD is one ordered method, and the two hidden-gated blocks are visible at a glance. Then `Hud.extractPlayerHealth` for the most-loved fifty-seven lines in the client, `Hud.nextContextualInfoState` for the bar arbitration, `Gui.extractRenderState` for the four recorded after the screen, `DebugScreenEntries` for the F3 registry, and `ChatComponent` for the message list. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Sound: the engine > Verified against **Minecraft 26.2** · Part X · a block placed near you: from a packet on the game thread to an OpenAL source, across five threads and one hop the sound cannot skip. `SoundEngine.play` never starts a sound. It resolves the name, picks a variant, tells the subtitle overlay, computes the volume and asks for a channel — and then posts *attach buffer, play* as a task on another thread. Even when the decoded audio is already in the cache, **a sound always starts at least one hop after the packet that asked for it**, because the engine has no path that calls `Channel.play` itself. Preloading removes the decode from that latency; it does not remove the hop. This page is the machine: the five threads that take part, the borrowed OpenAL source, the buffer that arrives afterwards, and the arithmetic that decides how loud it is. What *decides that a sound should happen at all* — and the fact that most world sounds are not named on the wire — is [what makes a sound happen](what-makes-a-sound.md). OpenAL is touched **only** inside `com/mojang/blaze3d/audio`, plus `NativeLibrariesBootstrap`, which loads the native library. Nothing in `client/sounds` makes an AL call itself: it calls into that wrapper, and everything outside calls `SoundManager.play` and forgets. ## The cast | class | what it decides | thread | |---|---|---| | `SoundManager` | the loaded `sounds.json` map, and the public front door | Render thread | | `SoundEngine` | which instances are playing, how loud, and what to drop | Render thread | | `SoundInstance` | one playing-or-wanting-to-play sound: position, pitch, looping, attenuation | Render thread | | `SoundEngineExecutor` | a `BlockableEventLoop` around one daemon thread — every *per-source* AL call | Sound engine | | `ChannelAccess` | acquiring, configuring and releasing a channel, as tasks | posts to Sound engine | | `Library` | the OpenAL device, context, listener and channel limits | Render thread opens it | | `SoundBufferLibrary` | decoded `.ogg` data, cached per path | Download pool | | `AbstractDeviceTracker` | noticing that the default device changed | IO-Worker, plus OpenAL's own callback | ## Five threads, and one the game does not own The page is mostly about which thread does what, so it is worth having the list before the trace. | thread | its part in a sound | |---|---| | **Server** | decides a sound happens, computes who is in range, sends packets. Never audio. | | **Render** (the client game thread) | receives the packet, builds a `SoundInstance`, calls `SoundManager.play`. Also everything OpenAL that is *not* per-source: opening and closing the device and context, resetting the `Listener`, deleting buffers. | | **Sound engine** | every per-source AL call: channel acquisition, parameter setting and release through `ChannelAccess`, plus the listener transform, which `SoundEngine.updateSource` posts to the executor directly. | | **`Util.nonCriticalIoPool`** (the *Download-* threads) | reads and decodes `.ogg` files with `JOrbisAudioStream` into a `SoundBuffer`, inside `SoundBufferLibrary.getCompleteBuffer`. | | **`Util.ioPool`** (the *IO-Worker-* threads) | device enumeration — `AbstractDeviceTracker.tick` dispatches `DeviceList.query` there, so the periodic poll of the ALC device list does not stall a frame. A forced refresh still queries on the Render thread. | And one the game does not own: OpenAL Soft's own event-callback thread, which invokes the callback `CallbackDeviceTracker` installs to notice that the default device changed. The Render thread's two cadences: once per client tick `Minecraft.tick` calls `MusicManager.tick` and then `SoundManager.tick`, which walks the ticking sounds, updates positions and volumes, releases finished channels and drains the delayed queue; once per *frame* `Minecraft.runTick` calls `SoundManager.updateSource` with the camera. ## A block is placed near you ```mermaid sequenceDiagram participant SL as ServerLevel participant PL as PlayerList participant CPL as ClientPacketListener participant CL as ClientLevel participant SndE as SoundEngine participant SBL as SoundBufferLibrary participant ChanA as ChannelAccess participant Library as Library SL->>SL: BlockItem.place, then Level.playSound(player, pos, event, BLOCKS, volume, pitch) SL->>PL: broadcast to everyone in range except the placer, with a seed PL-->>CPL: ClientboundSoundPacket — a holder, a position in eighths of a block, a seed CPL->>CL: handleSoundEvent, then playSeededSound, after ensureRunningOnSameThread CL->>SndE: SoundManager.play(SimpleSoundInstance) — seeded, so every client picks the same variant SndE->>SndE: resolve, pick by weight, calculateVolume, tell every SoundEventListener, then drop a silent one SndE->>ChanA: createHandle(STATIC or STREAMING limit) — a task on the sound thread ChanA->>Library: acquireChannel — generate an OpenAL source, or null if the limit is reached SndE->>ChanA: ChannelHandle.execute — setPitch, setVolume, linearAttenuation, setSelfPosition SndE->>SBL: getCompleteBuffer(path) — decode off-thread, cached per path SBL-->>ChanA: thenAccept, then ChannelHandle.execute — attachStaticBuffer, play loop every client tick SndE->>ChanA: scheduleTick — pump streams, release channels OpenAL reports stopped end ``` The four beats worth narrating. **The name is resolved, in an order you can hear.** The instance's `Identifier` is looked up in the `SoundManager` registry for a `WeighedSoundEvents`, and `WeighedSoundEvents.getSound` rolls the weighted choice — following event-to-event redirects — to a concrete `Sound`. Then, in this order: the unknown-event and empty-sound cases return early; the volume is computed; every registered `SoundEventListener` is told; and only *then* is a zero-volume sound abandoned. So a sound whose category is muted still produces a **subtitle**, and a sound with no `sounds.json` entry does not. `SubtitleOverlay` is the only `SoundEventListener` in the game, and that ordering is what it is for. **A channel is borrowed, on the sound thread.** `ChannelAccess.createHandle` posts a task to the `SoundEngineExecutor`; on that thread `Library` generates a new OpenAL source, provided the static or streaming limit — chosen by `Sound.shouldStream` — has room. The Render thread *blocks* on that future, which is the shorter of the two places the game thread waits on the sound thread, and gets a handle or null. Null means the sound is silently dropped. **Parameters go first, data arrives later.** `ChannelAccess.ChannelHandle.execute` posts the pitch/volume/attenuation/position setup, while `SoundBufferLibrary.getCompleteBuffer` returns a future for the decoded buffer. When that completes, its continuation posts *attach buffer, play* to the sound thread. That second post is the hop this page opens with. `Sound.shouldPreload` and `SoundEngine.requestPreload` remove the decode from the latency, not the hop. **Streams are pumped by the tick.** Long sounds — music, records — are streamed: `Channel.attachBufferStream` queues `Channel.QUEUED_BUFFER_COUNT` buffers of `Channel.BUFFER_DURATION_SECONDS` each, four seconds in all, and `ChannelAccess.scheduleTick`, posted once per client tick from `SoundEngine.tick`, calls `Channel.updateStream` on each to refill. The same pass releases channels whose source reports stopped. ## The channel limits are counters, not pools `Library` asks the device how many mono sources it offers, falling back to `Library.DEFAULT_CHANNEL_COUNT` — thirty. The streaming limit is the square root of that, clamped between two and eight; the static limit is a *clamped* remainder, floored at eight and capped at 255, so it is not simply "the rest". Sources are generated on acquire and deleted on release: one OpenAL source per playing sound, and when a limit is reached new sounds are dropped rather than queued. **The game does not steal channels by priority.** Nor does muting free one. `SoundEngine.refreshCategoryVolume` pushes the new volume to every playing channel of that category and stops nothing, so a looping sound muted to zero holds its OpenAL source until it ends on its own. Muting suppresses *new* allocations; it does not reclaim old ones. The OpenAL source itself, though, goes back the moment the channel reports stopped: `ChannelAccess.scheduleTick` releases it with no lifetime gate at all. `SoundEngine.MIN_SOURCE_LIFETIME` holds something else for twenty ticks — the engine's *bookkeeping* entry for the instance, long after the source it named has been deleted. `SoundEngine`'s own state is `SoundEngine.instanceToChannel`, `SoundEngine.instanceBySource`, `SoundEngine.queuedSounds` (delayed), `SoundEngine.tickingSounds`, `SoundEngine.gainBySource` and `SoundEngine.soundBuffers`. `SoundEngine.play` returns a `SoundEngine.PlayResult` — started, started silently, or not started — and `SoundManager.play` passes it through; `MusicManager` is the only caller that reads it. ## Volume is three factors, and looping is three mechanisms `SoundEngine.calculateVolume` multiplies the instance's own volume, the options volume (`Options.getFinalSoundSourceVolume`, itself category times master) and the runtime gain in `SoundEngine.gainBySource`. The third exists so that `MusicManager` can fade a category without touching the player's slider — which is why the music slider and the music *fade* are two different numbers. A computed volume of zero is abandoned **unless it is music**: `SoundEngine.play` drops it only when the instance does not say `SoundInstance.canStartSilent` *and* the category is not `SoundSource.MUSIC`. Music always starts, silently if need be, which is how a track fades in from nothing. Looping happens three different ways. Static sounds loop in OpenAL, with `Channel.setLooping`. Streamed sounds loop by wrapping the decoder in a `LoopingAudioStream`, since the source only ever holds a few seconds. And a looping instance *with a delay* is looped manually — `SoundEngine.shouldLoopManually` — by re-queueing it into `SoundEngine.queuedSounds` when its channel stops. One more attenuation subtlety, because the obvious explanation is wrong. UI sounds do not attenuate because of their **attenuation**, not their relativity: `SimpleSoundInstance.forUI` sets both `SoundInstance.Attenuation.NONE` and the relative flag, and it is the former that makes the engine call `Channel.disableAttenuation`. A relative sound offset from the listener would still fall off. ## The instance family, and the decode stack `SoundInstance` lives in `client/resources/sounds` and carries event, source, volume, pitch, position, looping, relative and attenuation. `SimpleSoundInstance` is a one-shot at a point; `EntityBoundSoundInstance` follows an entity; the `TickableSoundInstance` subclasses — `AbstractTickableSoundInstance`, minecarts, elytra, bees, ambient loops — re-evaluate themselves every tick. Below the engine, `com/mojang/blaze3d/audio` is the OpenAL wrapper: `Library` (device, context, listener, channel limits), `Channel` (one source), `SoundBuffer` (one buffer), `Listener` (the ear, set from a `ListenerTransform`), `DeviceList`, and the device-tracker family (`AbstractDeviceTracker`, `CallbackDeviceTracker`) that notices headphones being unplugged. The decode stack is three interfaces deep: `AudioStream`, then `FiniteAudioStream`, then `FloatSampleSource`, with `ChunkedSampleByteBuf` assembling the samples and `JOrbisAudioStream` — JOrbis, a Java Vorbis decoder — as the one real implementation. `LoopingAudioStream` wraps any of them. ## Questions a reader asks **Why does sound cut out when I plug in headphones?** Because reload is destroy-and-rebuild, and it arrives from three different doors. The resource reload arrives as `SoundManager.apply`, which ends by reloading the engine; `SoundManager.reload` is the *options* path, taken when the audio device is changed; and `SoundEngine.tick` reloads itself when the device tracker reports the default device changed. All three tear the OpenAL context down in `Library.cleanup` and call `SoundEngine.loadLibrary` again. **Is the sound thread the mixer?** No. `SoundEngineExecutor` does nothing but run tasks; OpenAL, the native library, does the mixing on its own threads. The Java thread exists so that per-source AL calls are serialised, and almost all of them go through it — the exceptions are the bulk teardowns, `ChannelAccess.clear` and `Library.cleanup`, which release handles directly on the Render thread once the sound thread is already joined. Confusingly, the *device* is not the sound thread's either: opening and closing it, resetting the listener and deleting buffers all happen on the Render thread, and teardown happens deliberately **after** `SoundEngineExecutor.shutDown` has joined the sound thread — which makes `SoundEngine.stopAll` the longer of the two places the game thread blocks on it. **Why is there no sound for the first few frames of a world?** `SoundEngine.updateSource` posts a `ListenerTransform` — position, forward, up — from the `Camera` every frame, and it no-ops until `Camera.isInitialized`. The same is true again after `GameRenderer.resetData`. And the ear is always one frame stale: `Minecraft.runTick` posts the transform *before* it renders, and the camera is only advanced inside the frame, so the ear is where the eye was last frame. At 60 fps nobody hears it — but it is worth knowing before blaming OpenAL. **What does the game demand of an audio device?** Three things. `Library.init` refuses a device without the OpenAL distance-model and linear-distance extensions, or with an ALC older than 1.1. HRTF is enabled from `Options.directionalAudio`. **Does pausing stop everything?** No: `SoundManager.pauseAllExcept` leaves `SoundSource.MUSIC` and `SoundSource.UI` running, and `SoundEngine.tickMusicWhenPaused` is the pause-menu tick. ## Where to look `SoundEngine.play` — the whole resolution-and-dispatch order is one method, and the early returns in it are audible. `ChannelAccess` and `ChannelAccess.ChannelHandle` for how a per-source call becomes a task, and `SoundEngineExecutor` for the thread it becomes a task on. `SoundEngine.calculateVolume` for the three factors, `Library.init` for the limits and the device requirements, `SoundBufferLibrary.getCompleteBuffer` for the decode, and `SoundEngine.tick` for the once-a-tick sweep. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # What makes a sound happen > Verified against **Minecraft 26.2** · Part X · you break a block and you hear it: three doors a sound can come through, and only one of them says what the sound is. Watch someone place a block and the server sends `ClientboundSoundPacket`, naming the sound. Watch them break the same block and it sends nothing of the kind: `Block.spawnDestroyParticles` fires a **level event**, and `ClientboundLevelEventPacket` carries an int and a block-state id. The client decides for itself what that int means — for a break, `SoundType.getBreakSound` from the block's own `SoundType` — and plays the result locally. So the two halves of one interaction reach you by different mechanisms, and the second one is not a sound at all until your client makes it one. Then there is the third door, the one people are most surprised by. Both of those calls name the acting player as the *excluded* entity, so neither packet is sent to whoever did it — and they do not need to be, because the same shared code runs on that player's own client, where `ClientLevel.playSeededSound` plays a sound exactly when the excluded entity *is* the local player. Your own place and break are predicted, not delivered. The rule does not generalise, though: `Player.playServerSideSound`, which plays the six attack sounds, excludes nobody, so your own critical hit is one of the sounds that does travel the whole way out and back. This page is the content model and those three doors. The machine that turns any of them into an OpenAL source is [the sound engine](sound-engine.md). ## The cast | class | what it decides | thread | |---|---|---| | `SoundEvent` | a name and an optional fixed range — never a file | both sides | | `SoundEvents` | the static registry of every event the game defines | both sides | | `SoundSource` | which volume slider applies | both sides | | `SoundEventRegistration` | what one `sounds.json` entry says, and whether it replaces or appends | Render thread | | `WeighedSoundEvents` | the weighted list a name resolves to, and the redirects in it | Render thread | | `LevelEventHandler` | what an int from `ClientboundLevelEventPacket` means | Render thread | | `BiomeAmbientSoundsHandler` | the loop, the random additions and the cave mood | Render thread | | `MusicManager` | which track, how often, and the fade that is not the slider | Render thread | ## The three doors ```mermaid flowchart TD SERVER["something happens on the server"] NAMED["Level.playSound — the server names a SoundEvent"] EVENT["Level.levelEvent — the server sends an int and a block-state id"] CLIENTONLY["nothing is sent at all"] P1["ClientboundSoundPacket, ClientboundSoundEntityPacket, or the sound inside ClientboundExplodePacket"] P2["ClientboundLevelEventPacket"] LEH["LevelEventHandler decides what the int means, using this client's block data"] LOCAL["ClientLevel.playSeededSound — which plays only when the excluded entity is the local player, so the same call is the prediction path too"] AMB["BiomeAmbientSoundsHandler, MusicManager, the underwater and bubble-column handlers"] SM["SoundManager.play"] SERVER --> NAMED --> P1 --> LOCAL --> SM SERVER --> EVENT --> P2 --> LEH --> SM CLIENTONLY --> AMB --> SM ``` The distinction matters for anyone reasoning about the wire. | | a named sound | a level event | client-side ambience | |---|---|---|---| | what crosses | a `SoundEvent` holder, a position in eighths of a block, a seed | an int and a block-state id | nothing | | who chooses the sound | the server | **this client**, from its own resource pack and block data | this client | | can it name a sound in no registry | **yes** — `SoundEvent.STREAM_CODEC` sends either a registry id or an id plus a range | no | no | | examples | a block placed, `/playsound`, a mob's voice | a block broken, a dispenser, fire extinguished, a ghast warning, the wither spawn, the dragon's death | biome loops, cave mood, music, underwater, bubble columns | That third row is a genuine hole in the usual summary. Data packs cannot *register* sound events — `Registries.SOUND_EVENT` is a static registry — but a packet may carry an **inline** `SoundEvent`, so a server can name a sound that is in no registry at all. The two statements are both true and are usually run together into a false one. The other clientbound members of the family are `ClientboundSoundEntityPacket`, which follows a moving entity, `ClientboundStopSoundPacket`, which `/stopsound` sends, and `ClientboundExplodePacket`, which carries its own sound alongside everything else an explosion needs. There is **no serverbound sound packet** anywhere: the server infers what you did from other packets and tells everyone else about the sound. ## What a sound *is*, as data `SoundEvent` — in `net/minecraft/sounds`, and therefore shared — is a record of an `Identifier` and an optional fixed range. `SoundEvents` is the 2,000-line static registry of every one the game defines. **It is a name, not a file.** The file comes from `sounds.json`, one per namespace in every resource pack, which maps an event name to a `SoundEventRegistration`: a weighted list of `Sound` entries — a file, or a redirect to another event, per the `Sound.Type` enum — each with volume, pitch, weight, attenuation distance, and whether to *stream* rather than load whole. `SoundManager` owns the loaded form, a map of `Identifier` to `WeighedSoundEvents`, rebuilt on every resource reload. Packs merge rather than replace, unless told otherwise. `SoundEventRegistration` carries a replace flag; without it a higher pack's entries are **appended** to the lower pack's list, so a pack that adds one variant gets a mix rather than an override. A redirect entry multiplies volume and pitch through and ORs the streaming flag. And there are two kinds of silence. The identifier `SoundManager.INTENTIONALLY_EMPTY_SOUND_LOCATION` is short-circuited by name in `AbstractSoundInstance.resolve` before the registry is consulted at all, so anything asking for it is silenced with no log warning — as distinct from an event that simply does not resolve, which logs. A pack that empties an event's list gets the warning, not the silence. `SoundEngine.MISSING_SOUND` is the development counterpart, which makes a missing sound *audible*, and `SharedConstants.DEBUG_SUBTITLES` the one that makes every sound *visible*. `SoundSource` is the volume category and each one is an options slider: `SoundSource.MASTER`, `SoundSource.MUSIC`, `SoundSource.RECORDS`, `SoundSource.WEATHER`, `SoundSource.BLOCKS`, `SoundSource.HOSTILE`, `SoundSource.NEUTRAL`, `SoundSource.PLAYERS`, `SoundSource.AMBIENT`, `SoundSource.VOICE`, `SoundSource.UI`. Two of those are read wrongly from the options screen alone: `SoundSource.RECORDS` is the jukebox slider and `SoundSource.WEATHER` the rain one. Neither is `SoundSource.AMBIENT`. ## Who hears it `ServerLevel.playSeededSound` asks `SoundEvent.getRange` for the audible radius — a fixed range if the event declares one, otherwise sixteen blocks scaled up by volumes above one — and `PlayerList.broadcast` sends the packet to every player in that dimension within range, **skipping the excluded player**. The seed travels in the packet so that every client picks the same random variant and the same pitch, which is why a sound that is one of eight variants sounds the same to two players standing together. The position is quantised on the way: `ClientboundSoundPacket.LOCATION_ACCURACY` is eight, so the wire carries three ints in eighths of a block. And **sound has a speed**, for the few callers that ask for it. `ClientLevel.playLocalSound` takes a distance-delay flag, and when it is set and the source is more than ten blocks off the sound is deferred by its distance over a fixed rate, through `SoundManager.playDelayed` into `SoundEngine.queuedSounds`. Firework explosions and a handful of level events set it. Thunder, the sound everyone assumes is the reason it exists, does not: `LightningBolt` passes the flag as false and the crack is instant. `LocalPlayer.playSound` goes the other way and calls `ClientLevel.playLocalSound` directly, skipping even the exclusion check. ## Music and ambience are environment attributes This is the biggest 26.2 change in the system and the one a 1.21-era reader will get wrong. *BiomeSpecialEffects* no longer carries music, ambient loops, additions or mood — it is block tint only. Every one of those is now an `EnvironmentAttribute` (see [environment attributes and timelines](../world/environment-attributes-and-timelines.md)): `EnvironmentAttributes.BACKGROUND_MUSIC`, `EnvironmentAttributes.MUSIC_VOLUME`, `EnvironmentAttributes.AMBIENT_SOUNDS` and `EnvironmentAttributes.FIREFLY_BUSH_SOUNDS`, all syncable, all resolved through the same dimension-then-biome-then-timeline-then-weather layer stack as fog and sky colour. `Minecraft.getSituationalMusic` reads `EnvironmentAttributes.BACKGROUND_MUSIC` off the camera's attribute probe and asks `BackgroundMusic.select` for the creative or underwater variant; the End boss fight overrides it directly. `Minecraft.getMusicVolume` reads `EnvironmentAttributes.MUSIC_VOLUME` the same way, which is how a biome dims its own music without touching the slider. `BiomeAmbientSoundsHandler` reads `EnvironmentAttributes.AMBIENT_SOUNDS` from `Level.environmentAttributes` and drives three things from it: a cross-faded loop, random additions on a per-tick chance (`AmbientAdditionsSettings`), and the cave "mood" that accumulates in darkness (`AmbientMoodSettings`). `UnderwaterAmbientSoundHandler` and `BubbleColumnAmbientSoundHandler` are the two that remain plain client-side handlers with no attribute behind them. `MusicManager` owns the rest: a `MusicManager.MusicFrequency` setting that scales the gap between tracks, a fade implemented by driving `SoundManager.updateCategoryVolume` — which is why the music slider and the music fade are two different numbers — and the now-playing toast, shown or not depending on the `SoundEngine.PlayResult` the engine returned. The remaining callers are worth naming because they are the ones that are neither the world nor the wire: `PlaySoundCommand`, the ambient handlers in `client/resources/sounds` for loops that exist only on the client, and `SoundPreviewHandler`, which previews a representative sound per category while a volume slider is dragged outside a world. ## Where to look `LevelEventHandler` — the whole second door is one switch, and reading it is the fastest way to see how much of the game's audio is not named on the wire. `ServerLevel.playSeededSound` and `PlayerList.broadcast` for who is told; `ClientLevel.playSeededSound` for the local-player branch that closes the loop. `SoundEventRegistration` and `WeighedSoundEvents` for the pack model, and `BiomeAmbientSoundsHandler` for the three ambience mechanisms in one class. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Debugging the running game > Verified against **Minecraft 26.2** · Part X · a villager's brain drawn over its head: one subscription mechanism, sixteen instances, all of them in the jar you downloaded and fifteen of them unreachable without a JVM flag. Every one of these sixteen debug subscriptions is compiled into the shipped client and the shipped dedicated server. Every subscription is in the registry, every packet is in the protocol, every producer call site is there. Nothing is stripped. **The client simply never asks** — fifteen of the sixteen are behind a JVM system property read at startup, and the server has to agree besides. The idle cost is small but it is not nothing: the producers check for a subscriber before they work, and the server sweeps every online player's permissions once a tick whether or not anyone has asked for anything. That is the pattern the page is about: a registry of subscription kinds, a per-level engine that sleeps until somebody asks, a poll-and-diff sender, and about two dozen renderers that turn the results into floating text and boxes. It is only half a client system — the machinery ships on the dedicated server — but the client is the only thing that ever asks and the only thing that draws, and the trace ends in a renderer. ## The cast | class | what it decides | thread | |---|---|---| | `DebugSubscription` | what one kind of debug value is: a stream codec, and an expiry | both sides | | `DebugSubscriptions` | the sixteen kinds | both sides | | `DebugValueSource` | which objects can answer which subscriptions | Server thread | | `ServerDebugSubscribers` | who is subscribed, rebuilt every tick, and the permission rule | Server thread | | `LevelDebugSynchronizers` | one synchronizer per subscription per level, and the sleep flag | Server thread | | `TrackingDebugSynchronizer` | the engine: registration, the diff, and the tracking filter | Server thread | | `ClientDebugSubscriber` | what to ask for, and the maps holding what came back | Render thread | | `DebugRenderer` | the renderer list, rebuilt when the enabled entries change | Render thread | ## The idea A `DebugSubscription` is a registry object — held in `BuiltInRegistries.DEBUG_SUBSCRIPTION` under `Registries.DEBUG_SUBSCRIPTION` — carrying exactly two things: a nullable stream codec for its value type, and an expiry in ticks, which is `DebugSubscription.DOES_NOT_EXPIRE` for most. Its payload wrappers are the records `DebugSubscription.Update` — a subscription plus an *optional* value, so absence is expressible — and `DebugSubscription.Event`, a subscription plus a value. Both are serialised by dispatching on the registry id onto the subscription's own codec. That is the whole abstraction, and it replaces what used to be a fixed packet type per kind of information. `DebugValueSource` is the supply side, implemented by `Entity`, `Mob`, `Bee`, `Breeze`, `BlockEntity`, `BeehiveBlockEntity` and `LevelChunk`; `DebugValueSource.registerDebugValues` hands back one `DebugValueSource.ValueGetter` per subscription the object can answer. On the client, `ClientDebugSubscriber` keeps what came back, keyed by chunk position, block position or entity UUID, plus a list of expiring events, and `ClientDebugSubscriber.createDebugValueAccess` hands renderers a read-only `DebugValueAccess` view. `DebugRenderer` is a plain list of `DebugRenderer.SimpleDebugRenderer`s, rebuilt by `DebugRenderer.refreshRendererList` — and they do not draw either: they emit through `Gizmos`. ## The sixteen instances | subscription | carries | fed by | |---|---|---| | `DebugSubscriptions.BRAINS` | `DebugBrainDump` | `Mob.registerDebugValues` | | `DebugSubscriptions.GOAL_SELECTORS` | `DebugGoalInfo` | `Mob.registerDebugValues` | | `DebugSubscriptions.ENTITY_PATHS` | `DebugPathInfo` | the navigator's current `Path` | | `DebugSubscriptions.BEES` / `DebugSubscriptions.BEE_HIVES` | `DebugBeeInfo` / `DebugHiveInfo` | `Bee` and `BeehiveBlockEntity` | | `DebugSubscriptions.BREEZES` | `DebugBreezeInfo` | `Breeze` | | `DebugSubscriptions.POIS` | `DebugPoiInfo` | `TrackingDebugSynchronizer.PoiSynchronizer`, event-driven | | `DebugSubscriptions.VILLAGE_SECTIONS` | nothing but presence | `TrackingDebugSynchronizer.VillageSectionSynchronizer` | | `DebugSubscriptions.RAIDS` / `DebugSubscriptions.STRUCTURES` | positions / `DebugStructureInfo` | `LevelChunk.registerDebugValues` | | `DebugSubscriptions.GAME_EVENT_LISTENERS` | `DebugGameEventListenerInfo` | the listener registry | | `DebugSubscriptions.GAME_EVENTS` | `DebugGameEventInfo` | the dispatcher — an *event*, expiring after 60 ticks | | `DebugSubscriptions.NEIGHBOR_UPDATES` | a position | a listener installed on the neighbour updater — an event, 200 ticks | | `DebugSubscriptions.ENTITY_BLOCK_INTERSECTIONS` | `DebugEntityBlockIntersection` | `Entity`, pushed directly, 100 ticks | | `DebugSubscriptions.REDSTONE_WIRE_ORIENTATIONS` | an `Orientation` | the experimental wire evaluator, 200 ticks | | `DebugSubscriptions.DEDICATED_SERVER_TICK_TIME` | **no value at all** | see *the sample path* below | "Expires after *n* ticks" means two different things across those four expiring rows. For the two *event* kinds it is how long the event stays on screen; for the two *pushed-value* kinds it is a time-to-live on a stored value. Only the client purges, and only for subscriptions that declare an expiry at all. ## One instance traced: a villager's brain ```mermaid sequenceDiagram participant CDS as ClientDebugSubscriber participant CPL as ClientPacketListener participant SGPL as ServerGamePacketListenerImpl participant SDS as ServerDebugSubscribers participant LDS as LevelDebugSynchronizers participant TDSS as TrackingDebugSynchronizer.SourceSynchronizer participant BDR as BrainDebugRenderer CDS->>CDS: requestedSubscriptions — the JVM was started with the brain flag CDS->>SGPL: ServerboundDebugSubscriptionRequestPacket with BRAINS SGPL->>SGPL: ServerPlayer.requestDebugSubscriptions — stored, not yet honoured Note over SDS: end of the next server tick SDS->>SDS: tick — is this player op, or the owner of an IDE singleplayer world? Note over LDS: the tick after that LDS->>LDS: tick — subscribers exist, so wake up LDS->>TDSS: registerChunk and registerEntity for everything already tracked TDSS->>TDSS: Mob.registerDebugValues gives a ValueGetter for BRAINS loop every server tick TDSS->>TDSS: pollUpdate — takeBrainDump, compare with the last value sent TDSS->>CPL: ClientboundDebugEntityValuePacket — only if it differs end CPL->>CDS: updateEntity — stored under the villager's UUID Note over BDR: next frame BDR->>BDR: emitGizmos — reads through DebugValueAccess BDR->>BDR: Gizmos.billboardTextOverMob — appended, drawn later in the frame ``` The engine is the middle three steps, and it has three properties worth naming. **Nothing exists until somebody asks**: the level's synchronizers start asleep, and the first non-empty subscriber set wakes them and retroactively registers every ready chunk and every tracked entity. **Nothing is sent twice**: each value source keeps the last value it sent and compares. And **nothing reaches a player who cannot see it**: sending is filtered by subscription *and* by whether that player is tracking the chunk or entity. When the last subscriber goes away the whole thing is cleared. The three cadences: `ClientDebugSubscriber.tick` runs from `ClientPacketListener.tick`, once per client tick, and sends only when the wanted set differs from the last one sent. `ServerDebugSubscribers.tick` runs from `MinecraftServer.tickChildren` *after* the levels have ticked, while each `LevelDebugSynchronizers.tick` runs *inside* its level's tick — so **every level acts on the previous tick's subscriber snapshot**, a built-in one-tick lag. And `DebugRenderer.emitGizmos` runs inside `LevelExtractor.extract`, after entities, block entities, particles, sky and clouds, fetching one `DebugValueAccess` for the whole pass. ## The exceptions Every pattern page's real content. **Two gates, and the second is not a flag.** Fifteen of the sixteen kinds are behind `SharedConstants.DEBUG_ENABLED` *and* an individual flag, both read from JVM system properties at startup — the only subscription an F3 key can reach is the dedicated server's tick time, through the FPS charts. And the server still has to agree: `ServerPlayer.debugSubscriptions` returns nothing unless `ServerDebugSubscribers.hasRequiredPermissions` passes, which means op on the player list, or the owner of a singleplayer world run from an IDE. On a normal singleplayer world that means cheats must be on. **Producers check before they work.** Path finding only records its open and closed node sets when somebody wants paths; entities only collect block intersections when somebody wants them; the neighbour updater's debug listener is only installed while someone is subscribed. There are two gates of that name and the producers do not agree on which to use: path finding and block intersections ask `ServerDebugSubscribers.hasAnySubscriberFor`, the live map, while the neighbour updater asks `LevelDebugSynchronizers.hasAnySubscriberFor`, which reads the level's snapshot from the previous tick. The change detection, by contrast, is *record equality*: a brain dump is rebuilt every tick per villager and compared with the last one sent — so the saving is in bandwidth, not in server time. **About half the renderers do not use this system at all.** The chunk debug renderer reaches directly into `Minecraft.getSingleplayerServer` and shows nothing in multiplayer; the entity hitbox renderer reaches for it too, but only for its optional *server* hitbox — its ordinary client hitboxes are drawn for every visible entity, on any server, from an F3 entry rather than a flag. And a whole family of them — chunk borders, light, collision boxes, height maps, the section octree — are purely client-side views that need no server. **One flag combination under-delivers, and says so.** The POI renderer's ticket-holder rows are behind an explicit `SharedConstants.DEBUG_BRAIN` test, so running with the POI flag alone gives POI boxes with their own two labels and none of the brain ones. The bee flag avoids needing the test at all by explicitly also requesting goal selectors. **Subscriptions survive a dimension change and a death, but not a reconnect.** A dimension change keeps the same `ServerPlayer`, so the set is simply never touched; a respawn builds a new one and `ServerPlayer.restoreFrom` copies the requested set across. A fresh login starts empty, and the client re-sends on its next tick because `ClientDebugSubscriber` was cleared at login. ## The sample path, which shares only the subscriber map The performance charts are a separate and much simpler system. A `SampleLogger` takes a vector of longs; partial values are logged during a tick and a final call flushes the whole vector. There are two implementations and the difference is the whole story: `LocalSampleLogger` **is** the storage — a `SampleStorage` ring buffer the charts read directly — while `RemoteSampleLogger` stores nothing and broadcasts a `ClientboundDebugSamplePacket` if anyone is subscribed. So a dedicated server measures its tick with a remote logger and sends, while `IntegratedServer.getTickTimeLogger` hands back **the client's own** local logger and reports logging as unconditionally enabled — singleplayer TPS never touches the network. Client-side, `DebugScreenOverlay` owns four local loggers with four different feeders: the frame time from the loop, the tick time from either of the two paths above, the ping from `PingDebugMonitor` (which sends its own ping requests, and only while the network charts are shown), and the bandwidth from a `BandwidthDebugMonitor` that counts bytes on the Netty thread and is drained by `Connection.tick`. Six packets carry all of this: `ServerboundDebugSubscriptionRequestPacket` outbound, and `ClientboundDebugChunkValuePacket`, `ClientboundDebugBlockValuePacket`, `ClientboundDebugEntityValuePacket`, `ClientboundDebugEventPacket` and `ClientboundDebugSamplePacket` inbound — for a system that used to have one per subject. > **For a 1.21-era reader.** The fixed set of debug packets is gone. Instead > of one packet type per kind of debug information there is one > `DebugSubscription` registry and three generic value packets that dispatch > on the registry id. The debug *screen* is a different system again — the F3 > entry registry described in [the HUD](hud.md) — and it is not a light > touch: the F3 entries decide whether eleven of the twenty-five renderers > exist at all when the list is rebuilt, and the FPS charts gate the tick-time > subscription. ## Where to look `DebugSubscriptions` for the catalogue and `DebugSubscription` for how little a subscription is. `TrackingDebugSynchronizer` for the engine — the tracking diff, the back-fill and the equality check are all in that one class. `LevelDebugSynchronizers.tick` for the sleep flag, `ClientDebugSubscriber` for both ends of the client's half, and `DebugRenderer.refreshRendererList` for which renderers exist and why. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # XI · Rendering > Verified against **Minecraft 26.2** · Part XI · one thread, a hundred-odd times a second, turning a world nobody can see into a picture — and two layers of machinery underneath that never touch the world at all. A frame is one method call. Most of this part happens inside `Minecraft.renderFrame`, on the same thread that ticked the world a moment earlier, and it happens twice over: once to *copy* the live game into a pile of immutable value objects, and once to *draw* those objects with the live game held at arm's length. The exceptions are worth naming up front, because each is a page: particles are stepped from the *tick*, sections are meshed on a background pool, and the atlases are built by a resource reload. A player recognises the part by the seams in that arrangement — terrain filling in outward as you fly, a block you placed appearing before the server has agreed to it, a mob that pins in place under `/tick freeze` while you keep moving, a resource pack that makes the game stop for a second and come back looking different. It is also the largest thing on the client by a distance. Counting `client/renderer`, `client/model` and `com/mojang/blaze3d` together — one class per file, one line per line of decompiled source, the same way [the atlas](../../maps/README.md) counts everything else — the renderer is **1,179 classes and 87,000 lines**, against 420 classes and 53,000 for the whole of `net/minecraft/server`. This part does not cover all of it, and [what this book skips](../anatomy/what-this-book-skips.md) says which parts are declined and why. ## The shape of the part Part XI is **a substrate under a pipeline**. Two of its pages — the window and Blaze3D — are what the renderer stands on: neither has a trace through the world, and both are cited from the pages above rather than the other way round — Blaze3D by eight of the other ten, the window by two. The rest really is a pipeline, in the order things happen inside one frame. `the-frame` opens the part because it is the shortest way to see the whole shape at once, and because a reader who has watched one frame end to end has a reason to care what a `GpuDevice` is. ```mermaid flowchart TD FRAME["The frame — extract, then draw"] subgraph SUB["the substrate — no trace through the world, made once at startup"] direction LR WIN["The window"] B3D["Blaze3D"] end subgraph PIPE["the pipeline — the order to watch them in, and what each page needs from the last"] direction TB VIS["Visibility and the frame graph"] MESH["Section meshing"] MOD["Models and atlases"] ENT["Entity rendering"] BEN["Block-entity rendering"] SKY["Lightmap, fog and sky"] PART["Particles"] POST["Post-processing"] VIS -- "the sections it decided to draw" --> MESH MESH -- "and where a section's quads came from" --> MOD MOD -- "the same atlases, a different pipeline" --> ENT ENT -- "and the things that are neither terrain nor entity" --> BEN BEN -- "and every draw needs a colour" --> SKY SKY -- "plus the quads that are not geometry" --> PART PART -- "then the finished picture, read back" --> POST end SUB --> FRAME FRAME --> PIPE ``` Read the substrate arrow as *depends on*, not as *happens before*: the window and the device are made once at startup and never again. The pipeline arrows are not frame order either — inside a frame the sky pass is declared before the main one, and the lightmap is built before the world is drawn at all. They are the order to watch the pages in, each labelled with what the next one needs from the one before. The last arrow is the one the figure flatters: **two** of the six post chains append their passes to the very frame graph the visibility page describes, and the other four build a graph of their own and throw it away. They are one machine because they are one loader, one schema and one pass class — not because they all end up in one graph. ## Before you start [The client loop](../client/the-client-loop.md) from Part X, and not optionally: it is the page that says *when* a frame happens and how many ticks ran before it, and this part begins exactly where that page's *frame* zone opens. It is one of the two longest pages in Part X, so budget for it. [The resource system](../foundations/resource-system.md) from Part II before [models and atlases](models-and-atlases.md), which is a reload listener and leans on the barrier semantics taught there rather than restating them. [Environment attributes and timelines](../world/environment-attributes-and-timelines.md) from Part IV before [lightmap, fog and sky](lightmap-fog-and-sky.md). Part IV owns that system; Part XI is its client-side consumer and deliberately does not re-teach it. [The client level](../client/the-client-level.md), for what the thing being drawn actually is — a `Level` with its authority removed — and for the two ways `LevelExtractor` is reached, pushed and pulled. ## Watch in this order 1. [The frame](the-frame.md) — one method, two halves, and a wall between them. Nine profiler zones, six clocks disagreeing on purpose, and a failed surface acquisition that costs you the picture but not the work. 2. [The window](the-window.md) — the substrate nothing else admits to needing. A retry loop that creates a window and a graphics backend together, six operating-system callbacks of which the game hears two, and `NativeImage`, the seam between a file and a texture. 3. [Blaze3D](blaze3d.md) — the game's own graphics API, and the part's vocabulary page. Four validating façades over two real backends, one of which is Vulkan and is the larger of the two. 4. [Visibility and the frame graph](visibility-and-the-frame-graph.md) — what the frame decides to draw, and in what order. A reachability walk whose asymmetry — uncompiled sections stop it, empty ones do not — is why terrain reveals itself outward. 5. [Section meshing](section-meshing.md) — where the triangles came from. A block is placed, a halo of positions goes dirty, a worker compiles a snapshot, and the swap happens frames later and all at once. 6. [Models and atlases](models-and-atlases.md) — the reload pipeline behind every quad. Thirteen atlases stitched in parallel, one barrier, and a quad whose chunk layer is read out of its sprite's pixels. 7. [Entity rendering](entity-rendering.md) — everything in the world that is not terrain, in four stages, none of which is called *render*. The zombie is animated at least twice per frame. 8. [Block-entity rendering](block-entity-rendering.md) — the same four stages with three differences that show. A chest's block model is empty, a block entity is culled by its section rather than by the frustum, and the chest in your hand is drawn by a different renderer at a different partial tick. 9. [Lightmap, fog and sky](lightmap-fog-and-sky.md) — what colour all of it is. One question asked five times over, by renderers that mostly no longer know what time it is. 10. [Particles](particles.md) — the part's policy page: three distance rules enforced in three places and three readers of one setting who disagree about what its values mean, with a break puff that answers to almost none of them. 11. [Post-processing](post-processing.md) — the closer. Six JSON-declared shader chains, which is how the pause-menu blur and the creeper spectator shader turn out to be the same machine — and a resource pack can rewrite all six and add none. Four and five are a pair — they were one page until pass 3, and they are still one journey seen from its two ends — and so are seven and eight, the second of which is written as the differences from the first. One to three can be watched in order or in the order one, three, two; the window is the page a viewer is most likely to skip and least likely to regret. ## Reference this part uses [Submit phases and feature renderers](../../reference/submit-phases.md) is the catalogue behind [entity rendering](entity-rendering.md) and [block-entity rendering](block-entity-rendering.md): the fifteen phases a submitted feature can land in, in declaration order, and the thirteen renderers that write the vertices. [Diagram lanes](../../reference/lanes.md) for the abbreviations these figures use, and [the threads](../../reference/threads.md) for the two that matter here — the one the whole part runs on, and the background pool that meshes sections. [Naming drift](../../reference/naming-drift.md) is worth having open for this part in particular: the client was rewritten around extract-then-render, so almost nothing at the top of the render stack kept the name a 1.21-era reader knows it by. [The glossary](../../reference/glossary.md) for *extract*, *render state*, *frame graph*, *partial tick*, *atlas*, *special model renderer* and *built-in block model*. Where the part stops: what draws *over* the world rather than in it — screens, the HUD, the render tree they record into and the text inside them — is Part X, from [the GUI render tree](../client/the-gui-render-tree.md) onward. What the server chose to tell this client in the first place is Part IX. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The frame > Verified against **Minecraft 26.2** · Part XI · one frame: from acquiring a surface texture to handing it back, and the wall in the middle that the drawing half is not allowed to look past. The number in the top-left says 143 fps, and each of those 143 is one call to `Minecraft.renderFrame` — one method with two halves. The first walks the live game and copies everything drawable into a `GameRenderState`; the second draws that state and nothing else. Before either half runs, the frame asks [the window](the-window.md) for somewhere to put the picture, and the surprising thing is what happens when that request fails. It does not skip the frame. It skips the *picture*. The world still renders in full into `GameRenderer.mainRenderTarget`, the GUI still goes on top, the framerate limiter still parks; only the blit and the present are guarded on a surface having been acquired, and both are quietly skipped. A minimized window is the same story with no attempt made at all — a client drawing complete frames that nobody will ever see. All of this is one thread — the same one that ticked the world a moment earlier. How many ticks ran before the frame, and what paced it afterwards, is [the client loop](../client/the-client-loop.md); this page starts where that page's *frame* zone opens. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | the zones, the acquire guard, and whether this frame advances game time at all | Render thread | | `GameRenderer` | the frame's owner of everything drawn — the snapshot target, the main render target, the camera, the lightmaps | Render thread | | `GameRenderState` | what the drawing half is allowed to read, and therefore where the wall is | written in *extract*, read in *render* | | `Camera` | where the eye is, how wide the view is, and how wide the *cull* frustum is — which is not the same number | Render thread, but ticked from `GameRenderer.tick` | | `LevelExtractor` | which of the live world becomes drawable state, at one partial tick **per entity** | Render thread | | `LevelRenderer` | the world half — handed a `CameraRenderState`, never a `Camera` | Render thread | | `GuiRenderer` | the GUI half, with its own `StagedVertexBuffer` and its own `FeatureRenderDispatcher` | Render thread | | `GpuSurface` | whether there is anywhere to put the picture: acquire, blit, present | Render thread | ## Nine zones, which are the frame's table of contents Naming the profiler zones in order is the shortest honest description of what a frame is: *update window* · *update* · *extract* · *gpuAsync* · *render* — which pushes *world* and *gui*, and inside the world *matrices*, *fog*, *level*, *hand*, *screenEffects* — then *present* · *swapBuffers* · *frameLimiter* · *fpsUpdate*. One of those names is a lie, and the section on presentation below says which. ```mermaid sequenceDiagram participant MC as Minecraft participant GpuS as GpuSurface participant Camera as Camera participant GR as GameRenderer participant LX as LevelExtractor participant LR as LevelRenderer participant GuiR as GuiRenderer MC->>GpuS: update window — reconfigure if needed, then acquireNextTexture Note over MC,GpuS: vsync lives here, as a GpuSurface.PresentMode MC->>MC: update — advanceRealTime, the timer query, pauseIfInactive, Gui.update MC->>MC: ClientLevel.update — the client's own light engine, ticking frames only GR->>Camera: Camera.update — align, fov, cull frustum, perspective MC->>MC: Minecraft.pick writes Minecraft.hitResult GR->>LX: extract — window, options, lightmap, camera, the level, the GUI Note over GR,LX: the wall. Everything after this reads GameRenderState MC->>MC: gpuAsync — RenderSystem.executePendingTasks drains signalled fences GR->>GR: render — resize if needed, clear, the lightmap, then the world GR->>LR: LevelRenderer.render with a CameraRenderState, no live game object GR->>GR: the held item under a second projection, then the screen effects GR->>GuiR: render, then endFrame MC->>GpuS: present — blitFromTexture from GameRenderer.mainRenderTarget MC->>GpuS: swapBuffers — CommandEncoder.submit, then GpuSurface.present Note over MC: frameLimiter, then fpsUpdate ``` Read it as **acquire, snapshot, draw, present** — and note that only the first and last of those four touch the surface. ## Acquire, and the frame that carries on without one The *update window* zone reconfigures the surface if it needs it and then calls `GpuSurface.acquireNextTexture`. Vsync is not a swap interval any more: it is a `GpuSurface.PresentMode` baked into the surface configuration, so toggling it in the options forces a reconfigure — `Minecraft.invalidateSurfaceConfiguration` — rather than setting a flag. When the acquisition throws, the surface is marked invalid, a reconfigure is scheduled, and the frame goes on as if nothing had happened but for a line in the log. Exactly two later statements re-test whether a surface is actually held, and both are in `Minecraft.renderFrame` itself: the blit and the present are each guarded on `GpuSurface.isAcquired`, with nothing on the other branch. The tolerance is the caller's, not the surface's — `GpuSurface.blitFromTexture` and `GpuSurface.present` both throw if you reach them without one. Everything between those two guards — the extract, the world, the GUI — is paid in full. There is one guard on the whole method, and it is about re-entry rather than failure: if the surface is *already* acquired when `Minecraft.renderFrame` is called, the call is a silent no-op. ## Update and extract: six clocks in one frame The *update* zone advances the real-time clock, reads `Minecraft.timerQuery` — the GPU-side stopwatch behind the F3 utilisation figure — runs `Minecraft.pauseIfInactive` and updates the GUI. On ticking frames `ClientLevel.update` runs the client's own light engine. Then `GameRenderer.update` calls `Camera.update`, and `Minecraft.renderFrame` follows it with `Minecraft.pick` — a private method of `Minecraft`, not the renderer's — which writes `Minecraft.hitResult` for the crosshair and the block outline to find later. `Camera` is split three ways across the client, and two of the three are here. `Camera.tick` — driven from `GameRenderer.tick`, not from the frame — smooths the eye height and the field-of-view modifier and advances the camera's `EnvironmentAttributeProbe`. `Camera.update` does the frame's work: `Camera.alignWithEntity`, `Camera.calculateFov`, `Camera.prepareCullFrustum`, `Camera.setupPerspective`. `Camera.extractRenderState` then copies the result across the wall. *Extract* opens by snapshotting the framerate limit into `GameRenderState.framerateLimit` and goes on to copy the window, the options, the lightmap, the camera and the level. It is here that the frame's oddest number appears: there is no such thing as *the* partial tick of a frame. There are six, they disagree on purpose, and one of them is not a partial tick at all. | who is interpolated | which value | what it ignores | |---|---|---| | the world | `DeltaTracker.getGameTimeDeltaPartialTick` | nothing — frozen time is honoured | | the camera and the held item | `Camera.getCameraEntityPartialTicks` | freezing, unless the *camera entity itself* is frozen, which a player never is | | the lightmap | a literal one | everything — it is extracted fully advanced | | screens and overlays | `DeltaTracker.getGameTimeDeltaTicks` | the fraction — this is the whole delta since the last frame, not a position inside a tick | | the autosave indicator and the title-screen panorama | `DeltaTracker.getRealtimeDeltaTicks` | the game clock, and anything past seven ticks, where it clamps to a half | | each entity, separately | its own frozen-honouring value, asked for by `LevelExtractor` | the world's single answer | The last row is the one you can see. Under `/tick freeze` a mob pins at the end of its last tick while players go on interpolating — in the same frame, from the same extract. The exception is worth knowing, because it is the one a player rides: `TickRateManager.isEntityFrozen` excludes anything with a player aboard, so the horse under you keeps moving smoothly while the horse beside you is a statue. ## The wall, and the one level at which it is real `LevelRenderer.render` is handed state and reads no live *game* object: no `ClientLevel`, no `Minecraft`. That is the wall, and one level down it holds. It still reaches back into live *renderer* objects for the main target and the shader manager, but no live game object is among them. At the top it leaks, and the leak is sharper than the naming suggests. `GameRenderer.render` reads whether the game has finished loading, whether a level exists, and the world's game time, every frame including GUI-only ones. Inside the world half, `GameRenderer.shouldRenderBlockOutline` reads the **live camera entity during rendering**, and in adventure or spectator mode goes further: for a player who may not build it reads `Minecraft.hitResult`, looks a `BlockState` up in the level and asks the game mode what it is, all mid-draw. The interesting fact is not that a wall exists but that it is drawn one level below where *extract then render* implies it is. A resize is handled inside the render half rather than before it: `GameRenderer.render` opens by comparing `GameRenderState.windowRenderState` against the main target's size and resizing the renderer inline when they differ. The snapshot is what the frame believes the window size to be, even if the window has changed since. The buffers the halves share are far smaller than a 1.21-era reader expects. `RenderBuffers` holds `RenderBuffers.fixedBufferPack`, the section-meshing scratch `RenderBuffers.sectionBufferPool` — capped by processor count and again by a memory budget — and a single shared `RenderBuffers.stagedVertexBuffer` released by `RenderBuffers.endFrame`, with the GUI keeping a second staged buffer of its own inside `GuiRenderer`. Geometry submission moved to `SubmitNodeCollector` and `SubmitNodeStorage`, drawn either by the passes of the frame graph in [visibility and the frame graph](visibility-and-the-frame-graph.md) or by `FeatureRenderDispatcher.renderAllFeatures` — which, despite the name, is **not** how the level is drawn. It has four call sites: the held item, the screen effects, the GUI's item atlas and picture-in-picture. The last two are why the GUI needs submit storage at all. The world's submitted features are prepared into the frame graph and drawn by its passes. ## Present, swapBuffers, and which of the two names lies **Nothing is presented in the zone called *present*.** That zone does the blit from `GameRenderer.mainRenderTarget` to the acquired surface texture. The submit and the actual `GpuSurface.present` happen in the *swapBuffers* zone after it. Only one of the two names lies: *swapBuffers* is honest, since on the OpenGL backend `GpuSurface.present` is a single call to GLFW's buffer swap. The last two zones are bookkeeping. *frameLimiter* spends the limit that *extract* snapshotted, parking only below a threshold, so the top slider position never parks at all; `FramerateLimitTracker` is what may have overridden the player's option before the snapshot was taken, when the window is iconified, after a spell of idleness, or in a menu with no level. Then *fpsUpdate*, and the frame is over. ## Questions players ask **Why does lowering a spyglass never reveal a hole in the world?** Because the cull frustum is deliberately wider than the camera. `Camera.createProjectionMatrixForCulling` builds its matrix from the larger of the current and the *configured* field of view; `Camera.prepareCullFrustum` turns that into `Camera.cullFrustum`, and `Camera.extractRenderState` copies it across the wall into `CameraRenderState.cullFrustum`. Sprinting and flying raise the live FOV above the option, so for them the maximum does nothing. It bites when something **narrows** the view — a spyglass, a drawn bow, the dying-camera effect — where culling against the configured FOV keeps the geometry a narrowed view would have thrown away. **Why is the HUD not shaded by the light the player is standing in?** Because it is lit by a one-pixel white texture. `GameRenderer.lightmap` hands out `GameRenderer.uiLightmap` for as long as `GameRenderer.useUiLightmap` is set, which is exactly the GUI block; `GameRenderer.levelLightmap` always returns the real one. **Why does the world go strange when spectating a creeper?** The post-effect chain is chosen by what you are spectating, not by an option: `GameRenderer.checkEntityPostEffect` switches on the camera entity's type and sets `GameRenderer.postEffectId` from it, and F4 (`GameRenderer.togglePostEffect`) flips it off and on. What the chain then does to the picture is [post-processing](post-processing.md). **Does minimizing the window save the client any work?** A great deal, but not where you would look for it. Every zone still runs: the acquire is not attempted and the blit and the present are skipped, which is three calls. The saving is the limiter. `FramerateLimitTracker.getThrottleReason` tests iconification *first*, ahead of idleness and the menu, and answers with a limit of ten — so *frameLimiter* parks the thread for most of every hundred milliseconds and the client draws its unseen frames about ten times a second instead of at the player's setting. On top of that, losing focus for half a second pauses a singleplayer world outright through `Minecraft.pauseIfInactive`, and then there is no world left to draw. **Where does the main menu's panorama come from?** The game, on the same two halves. `Minecraft.grabPanoramixScreenshot` runs `GameRenderer.update`, `GameRenderer.extract` and `GameRenderer.renderLevel` six times at 4096×4096, with a fixed delta of one and the camera in panoramic mode — which is what `CameraRenderState.isPanoramicMode` exists for. A second, silent screenshot path inside the world half writes the world icon, singleplayer only, and only once enough sections have actually been rendered. **When does the client draw a frame that runs no ticks?** Whenever `Minecraft.renderFrame` is passed false for the flag that says this frame advances game time. Three call sites do: the two loops that wait for the integrated server to start and to stop, and `Minecraft.setScreenAndShow`, which forces a single frame so that a screen appears during blocking work. Such a frame has no ticks, no client lighting and no world render at all — it is GUI only, and [GUI and screens](../client/gui-and-screens.md) is the half that survives. > **For a 1.21-era reader.** The rendering model is now **extract then > render**: `GameRenderer.extract` copies the live game into a > `GameRenderState` and `LevelRenderer.render` is handed a > `CameraRenderState` rather than a `Camera`, because by the time it runs the > camera is allowed to have moved on. Names to stop hunting for: > *Minecraft.getMainRenderTarget* (now `GameRenderer.mainRenderTarget`), > *Camera.setup* (now `Camera.update` plus `Camera.extractRenderState`), > *GameRenderer.getProjectionMatrix* and *resetProjectionMatrix*, > *Window.updateDisplay*, *LightTexture* (now `Lightmap`), and > *MultiBufferSource* with every buffer source that used to hang off > `RenderBuffers` — none of them exist. Most `Camera` accessors lost their > *get* prefix (`Camera.position`, `Camera.entity`, `Camera.rotation`, > `Camera.forwardVector`), though `Camera.getCullFrustum`, `Camera.getFov` > and `Camera.getCameraEntityPartialTicks` kept theirs. ## Where to look `Minecraft.renderFrame` — the frame is one method, and its profiler zones are its table of contents. Then `GameRenderer.extract` and `GameRenderer.render` for the wall between live objects and drawing, `GameRenderer.tick` for the per-tick half nobody expects a renderer to have, `Camera.update` for how the view is decided, and `GpuSurface.present` — in the zone called *swapBuffers* — for where a frame ends. The GPU abstraction underneath all of it is [blaze3d](blaze3d.md). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The window > Verified against **Minecraft 26.2** · Part XI · before the first frame: the game asks the operating system for a window, and finds out which graphics backend it has by which window survives. The process is a second old. There is no world, no renderer, no resource pack and nothing to draw. `GLX._initGlfw` has just brought GLFW up, `NativeLibrariesBootstrap` has probed for the GL and Vulkan loaders and `MonitorManager` has enumerated the monitors and the video modes each of them offers. Now `Minecraft` wants a window — and it cannot ask for one without already having decided how the pixels will be drawn. **The window and the graphics backend are created together and neither can go first**: an OpenGL window and a Vulkan window are made from different GLFW hints, so a Vulkan attempt that gets a window and then fails at the device does not get to keep the window. It is thrown away with the attempt, and the next candidate starts from a fresh one. [The frame](the-frame.md) is the lecture you watch first, and it opens on a surface that has already been acquired. This page is what acquired it. [Input and keybinds](../client/input-and-keybinds.md) opens on a callback that has already fired, and [blaze3d](blaze3d.md) on a `GpuDevice` that already exists. All three of them start here. ## The cast | class | what it decides | thread | |---|---|---| | `Minecraft` | which backends to try, in which order, and when to give up | Render thread | | `Window` | the GLFW handle, the three sizes, and every fullscreen transition | Render thread | | `GpuBackend` | the window hints, and whether a failed window is the backend's fault | Render thread | | `MonitorManager` | which monitor the window is considered to be on | Render thread | | `Monitor` | which `VideoMode` an exclusive fullscreen switch takes | Render thread | | `WindowEventHandler` | which of the six operating-system callbacks reach the game | Render thread | | `FramerateLimitTracker` | what an iconified, idle or menu-bound window is allowed to cost | Render thread | | `NativeImage` | the CPU-side pixels between a file and a texture | native memory, closed by its owner | All of it lives in *com/mojang/blaze3d/platform*, and none of it exists on the server: `server-classes.txt` has no entry under *com/mojang/blaze3d* at all. ## Trying backends until one of them makes a window The startup path is a retry loop, and it is drawn as a flowchart rather than a conversation because the shape *is* the fact: the loop encloses the window and the device together. A backend that cannot make a window and a backend that cannot make a device fail identically, and both hand the next candidate a clean slate. ```mermaid flowchart TD GLX["GLX._initGlfw brings GLFW up, NativeLibrariesBootstrap probes the loaders"] MonM["MonitorManager enumerates the monitors and their VideoModes"] MC["Minecraft takes the next candidate from PreferredGraphicsApi.getBackendsToTry"] GB["GpuBackend.setWindowHints — OpenGL and Vulkan want different ones"] Window["a new Window: create the GLFW window, then register the six callbacks"] Q1{"did a window appear?"} ERRS["GpuBackend.handleWindowCreationErrors reads what GLFW complained about"] DEV["GpuBackend.createDevice against the window handle, with the shader source and the debug options"] Q2{"did a device come back?"} KILL["close the window — its hints are wrong for the next candidate"] LEFT{"any candidate left?"} BOX["MessageBox.error, and the game never starts"] RS["RenderSystem.initRenderer with the device that survived"] DONE["setIcon, setTitle, setDefaultErrorCallback"] GLX --> MonM --> MC --> GB --> Window --> Q1 Q1 -- "no" --> ERRS --> LEFT Q1 -- "yes" --> DEV --> Q2 Q2 -- "no" --> KILL --> LEFT Q2 -- "yes" --> RS --> DONE LEFT -- "yes" --> MC LEFT -- "no" --> BOX ``` What the window is asked for is a `DisplayData`: a size, an optional fullscreen size and a fullscreen flag, with `DisplayData.withSize` and `DisplayData.withFullscreen` for the transitions that change them later. What comes back, if anything comes back, is a `Window` holding a `Window.handle` and a `Window.backend` — and never a `GpuDevice`. The window knows which backend made it and nothing about what that backend went on to build. Below GLFW and STB, reached through LWJGL, the window itself calls almost nothing else in the game — the exceptions are the three classes that have to report a failure upward, which reach for `Minecraft`, `CrashReport` and the server's watchdog. Above it, `Minecraft` drives startup and the two per-frame calls below, `KeyboardHandler` and `MouseHandler` take the input callbacks and the clipboard, `VideoSettingsScreen` drives the fullscreen and video-mode controls, and `Screenshot` and `TextureManager` want `NativeImage`. What the player's saved choices reach is `Options`: an override width and height, the fullscreen flag and video-mode string, exclusive fullscreen, the GUI scale, and the graphics-API preference that ordered the loop above. The window's *position* is not among them — it is a field the move callback keeps and nobody saves. ## Six callbacks are the entire surface, and the game hears two Once the window exists, `Window`'s constructor registers six GLFW callbacks, and they are almost the whole of what the operating system can say to it — a seventh, the close callback, is added later by `Minecraft` and is the subject of the last section. `WindowEventHandler` — a three-method interface that `Minecraft` implements — is the whole of what a window is allowed to say back to the game, and the window only ever reaches for two of those three methods. ```mermaid flowchart LR OS["the operating system, through GLFW"] FB["framebuffer size changed"] CE["cursor entered the window"] SZ["window resized"] PS["window moved"] FC["focus gained or lost"] IC["iconified or restored"] WEH["WindowEventHandler, implemented by Minecraft"] W["a field on the Window, for whoever asks later"] OS --> FB --> WEH OS --> CE --> WEH OS --> SZ --> W OS --> PS --> W OS --> FC --> W OS --> IC --> W ``` `WindowEventHandler.framebufferSizeChanged` and `WindowEventHandler.cursorEntered` are the two. A window resize, a window move, a focus change and an iconify all end in a field — `Window.getX`, `Window.getY`, `Window.isFocused` and `Window.isIconified` are what anyone asks instead, whenever they get round to it — so four of the six events the operating system reports are things the game is never *told*, only things it can look up. `Window.isMinimized` is the one that reads like a fifth and is not: it is set by the framebuffer callback, which fires with a zero-by-zero size when the window goes away, and cleared by the same callback when a real size comes back. The third method on the interface is the odd one. `WindowEventHandler.resizeGui` is never called by `Window` at all: its callers are `Minecraft` and `Options`, which is to say the game calling itself when the GUI scale option changes. And `WindowEventHandler.framebufferSizeChanged` is not only a callback — `Window.updateFullscreenIfChanged` and `Window.changeFullscreenVideoMode` both raise it directly, which is how F11 and a video-mode switch reach the renderer by the same route a dragged window corner does. Notice what is *not* among the six: keys, characters, mouse buttons, cursor motion and scrolling. Every input callback is registered somewhere else entirely, by `KeyboardHandler` and `MouseHandler` — see [input and keybinds](../client/input-and-keybinds.md). ## Three sizes, and every misplaced GUI element is a confusion between them | the size | how it is asked for | what it is | |---|---|---| | framebuffer | `Window.getWidth`, `Window.getHeight` | the pixels the renderer actually targets | | screen | `Window.getScreenWidth`, `Window.getScreenHeight` | the window as the operating system reports it, which under DPI scaling is not the framebuffer | | GUI-scaled | `Window.getGuiScaledWidth`, `Window.getGuiScaledHeight` | the framebuffer divided by an integer scale | The integer scale is the part with a policy in it, and the two methods run the other way round from their names. `Window.calculateScale` is handed what the option asked for as a *ceiling* and decides what is actually possible, counting upward while the framebuffer still divides by the two constants `Window.BASE_WIDTH` and `Window.BASE_HEIGHT`, then rounding *up* to an even number when the font needs unicode. `Window.setGuiScale` takes that answer and stores it, computing the two scaled sizes from it. A high-DPI display is what makes the first two rows diverge, and a GUI element that lands in the wrong place is nearly always code that read one of the three and meant another. ## What the window does per frame, which is almost nothing Two calls, both inside `Minecraft.renderFrame`, both in the *update window* profiler zone: `Window.updateFullscreenIfChanged` at the very top of it, and the surface reconfigure-and-acquire immediately after. Everything else the window does is a callback firing. `Window.updateFullscreenIfChanged` is where F11 lands. `Window.toggleFullScreen` and `Window.setWindowed` flip the state, `Window.isFullscreen` reports it, and `Window.changeFullscreenVideoMode` with `Window.getPreferredFullscreenVideoMode` and `Window.setPreferredFullscreenVideoMode` negotiate what exclusive fullscreen turns into. Dragging the window to the other monitor is the same machinery approached from the other end: `MonitorManager.findBestMonitor` decides which monitor a window is on *by overlap*, and `Monitor.getPreferredVidMode` looks for the saved preference among the modes this monitor actually offers, taking the monitor's current mode when there is no exact match — it never approximates. A `Monitor` is a record — a name, a handle, its list of `VideoMode`s, the current one and its position — and `Window.getRefreshRate` is the number that comes out of the mode. The one thing on this page that runs continuously is `FramerateLimitTracker`, which watches iconification and idle time — not focus — and overrides the frame limit: ten frames a second for an iconified or long-idle window, thirty for a short idle, sixty in a menu with no level. [The frame](the-frame.md) is where that limit gets spent. ## `NativeImage`, the seam between a file and a texture `NativeImage` is a `NativeImage.Format`, a width, a height and a pointer into native memory. It is where every image in the game briefly is, and it is not only textures. `NativeImage.read` is an STB decode from a stream, a byte array or an NIO buffer — that is the PNG path. `NativeImage.copyFromFont` receives a rasterised FreeType glyph. An atlas is assembled into one with `NativeImage.copyRect`, `NativeImage.resizeSubRectTo` and `NativeImage.fillRect`, and `NativeImage.mappedCopy`, `NativeImage.getPixel` and `NativeImage.setPixel` are the rest of the vocabulary. A downloaded skin sits in one while it is being validated, and a screenshot arrives in one read back off the GPU on its way to `NativeImage.writeToFile`. `NativeImage.computeTransparency` is the method that [models and atlases](models-and-atlases.md) leans on to decide which chunk layer a quad belongs to — a rendering decision made by looking at the pixels of a file. Because the memory is native, ownership is explicit: `NativeImage.close` frees it, and `NativeImage.untrack` exists for the cases where something else has taken the pointer over. ## Questions players ask **Why does the game keep drawing while it is minimised?** Because nothing stops it. `Window.isMinimized` suppresses the surface acquisition and the frame then runs to completion regardless — see [the frame](the-frame.md). The work that is actually saved is saved by `FramerateLimitTracker` dropping the limit to ten, not by the frame being skipped. **Why does a graphics crash report name the thing the game was doing?** Because the error callback is swapped three times over the game's life: a boot-crash handler while starting, `Window.setDefaultErrorCallback` once running, and a null on close. `Window.setErrorSection` tags whatever GLFW complains about with what the game was busy with when it complained, so a driver's error message arrives attached to a phase rather than floating free. **Why does the mouse cursor stop changing shape sometimes?** Because you turned it off, or never turned it on. `Window.setAllowCursorChanges` is driven by a player option on the mouse-settings screen and nothing else, and with it clear every request is answered with the default arrow. The other half of the problem is the platform's: `CursorType.createStandardCursor` takes a fallback for the shapes a given system does not provide. **Why does the game sometimes leave a crash report behind after I close it?** Because a shutdown that hangs is reported from outside. `ClientShutdownWatchdog` starts a daemon thread that sleeps fifteen seconds and, if the shutdown has not claimed the counter by then, builds the crash report itself from the main thread's stack. It is armed twice with different powers: the window-close callback arms it to *report only*, while the one `Minecraft.run` has already returned from arms it to report and then take the process down. **What is the rest of the package?** The corners the story above does not pass through: `ClipboardManager` and `TextInputManager` for copy, paste and IME text (both reached from `KeyboardHandler`), `CursorType` and `CursorTypes` for the cursor shapes, `IconSet` for what `Window.setIcon` picks between, `MacosUtil`, `DebugMemoryUntracker`, the rest of `GLX` (`GLX._getCpuInfo`, `GLX._getLWJGLVersion`, `GLX.getGlfwPlatform`) — and `InputConstants`, the key and mouse-button vocabulary that every `KeyMapping` is written in. > **For a 1.21-era reader.** The headline is that the window no longer > presents anything: *Window.updateDisplay* and *Window.setVsync* are gone, > presentation is [blaze3d](blaze3d.md)'s `GpuSurface` protocol and vsync is a > `GpuSurface.PresentMode`. Also gone: *Window.setupGuiState*, and > *ScreenManager*, which never existed here — monitor handling has always been > `MonitorManager`. And the constructor now takes a `GpuBackend`, because the > window cannot be made without knowing which API is going to draw into it. ## Where to look `Minecraft`'s constructor for the candidate loop and what happens when it runs out of candidates. `Window`'s constructor for the order in which a window and a backend come into being, then `Window.updateFullscreenIfChanged` for the only thing the window does per frame. `MonitorManager.findBestMonitor` and `Monitor.getPreferredVidMode` for the fullscreen negotiation. `NativeImage.read` and `NativeImage.computeTransparency` for the image type the rest of Part XI is built on. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Blaze3D > Verified against **Minecraft 26.2** · Part XI · one draw call, from a declared pipeline to a triangle — and the two backends that can serve it. Open Video Settings, change **Graphics API**, restart, and the game comes back looking exactly as it did. Nothing in the *net.minecraft* packages noticed, because nothing in them talks to a driver: they talk to `GpuDevice`, `CommandEncoder` and `RenderPass`, and a backend under `com/mojang/blaze3d/opengl` or `com/mojang/blaze3d/vulkan` turns that into real calls. What made the swap possible is that the state machine left the game. Blend mode, depth test, cull and polygon mode are *fields of a `RenderPipeline`*, declared once and applied when the pipeline is bound, and `RenderSystem` — the class that used to be the state machine — contains no GL call at all. It did not vanish, though. It moved behind the backend boundary, where `GlStateManager` still shadows every toggle and still elides the redundant ones. This page is the vocabulary of that boundary; the window the device is created against is [the window](the-window.md), the frame [the frame](the-frame.md). ## The cast | class | what it decides | thread | |---|---|---| | `RenderSystem` | the static holder: the device, the render thread, the frame's shared uniforms and buffers | Render thread | | `GpuBackend` | which API is alive — window hints, creation errors, and the device itself | Render thread | | `GpuDevice` | what exists, and what the hardware will allow | Render thread | | `CommandEncoder` | whether a pass may open, and with which attachments | Render thread | | `RenderPass` | that the pipeline matches the attachments before a draw is allowed | Render thread | | `GpuSurface` | how a finished frame reaches the screen, and what vsync means | Render thread | | `RenderPipeline` | how to rasterise: shaders, blend, depth, cull, topology — declared, never called | declaration only, read on the Render thread | | `BufferBuilder` | vertex data — on the render thread, except the one instance chunk meshing runs | Render thread, and the meshing workers | ## Four objects the game only touches through a façade Four concrete, validating classes sit in `blaze3d/systems`, each over one thin per-backend interface: the game holds the left column below, never the right. ```mermaid flowchart TB GB["GpuBackend — GLFW window hints, window-creation errors, and it creates the device. The one the game names and never wraps"] subgraph F["what the game holds: the facade in blaze3d/systems, concrete and validating"] GD["GpuDevice"] CE["CommandEncoder"] RP["RenderPass"] GS["GpuSurface"] end subgraph I["one thin interface behind each"] GDB["GpuDeviceBackend"] CEB["CommandEncoderBackend"] RPB["RenderPassBackend"] GSB["GpuSurfaceBackend"] end GB -- "creates the device, and the device creates the rest" --> GD GD --> GDB CE --> CEB RP --> RPB GS --> GSB I --> OGL["com/mojang/blaze3d/opengl — GlStateManager shadows every toggle"] I --> VK["com/mojang/blaze3d/vulkan — swapchain, SPIR-V, reflection"] ``` | the game holds | the backend implements | |---|---| | `GpuDevice` | `GpuDeviceBackend` | | `CommandEncoder` | `CommandEncoderBackend` | | `RenderPass` | `RenderPassBackend` | | `GpuSurface` | `GpuSurfaceBackend` | `GpuBackend` is the entry point and the exception: no façade, because it is what exists before a device does. Everything else the device creates, and it answers for the hardware too — as one record graph, not a pile of getters. `GpuDevice.getDeviceInfo` returns a `DeviceInfo` of `DeviceInfo.name`, `DeviceInfo.backendName`, `DeviceInfo.isZZeroToOne`, a `DeviceFeatures` of seven booleans, a `HintsAndWorkarounds`, a `DeviceType` and a `DeviceLimits` whose `DeviceLimits.maxMemoryAllocationSize` caps the window size. ### Who checks what The façade owns the **API-contract** checks and the backend owns the resource-state ones. `CommandEncoder.createRenderPass` validates the attachment count against `DeviceLimits.maxColorAttachments`, each attachment's `GpuTexture.USAGE_RENDER_ATTACHMENT` bit, that the attachments are all one size, that a render area was supplied and fits, and that no pass is already open. `RenderPass.setPipeline` checks the pipeline's `ColorTargetState` list against the pass's attachments in both count and `GpuFormat`. The `GpuBufferSlice` overload of `RenderPass.setUniform` checks the offset against `DeviceLimits.minUniformOffsetAlignment` — the plain `GpuBuffer` overload checks nothing. Underneath, the backend throws on its own account: `GlBuffer` for a buffer mapped without persistent-mapping support, unreadably, unwritably or over two gigabytes, and `GlDevice` `GpuOutOfMemoryException` on a failed allocation. And it is the backends, not the façade, that keep the development-only checks: the only validation in this whole tree gated on running from an IDE is in `GlRenderPass` and `VulkanRenderPass`. Everything the façade asserts, it asserts in a shipped game. The thread assertions are not where a reader expects them either. `RenderSystem.assertOnRenderThread` is called from eleven classes, eight of those sites inside `RenderSystem` itself: it guards `RenderSystem`'s own mutable statics and the GL- and GLFW-facing classes, while `GpuDevice`, `CommandEncoder` and `RenderPass` assert nothing at all. And `GpuDevice.createCommandEncoder` does not create an encoder — it allocates a fresh façade over the one long-lived encoder the backend owns, so the *is a pass open* guard is per-façade and the game calls it fresh at every use site. ### How tight the boundary is **OpenGL is imported by exactly fourteen files** — thirteen in `com/mojang/blaze3d/opengl`, the fourteenth the native-library bootstrap — and nothing else in the game references LWJGL's OpenGL bindings. Vulkan leaks upward in two places, not one: `RenderPass` imports two Vulkan indirect-command structs to use their size when validating an indirect buffer, and the loader probe imports Vulkan too, while `BackendCreationException` in `blaze3d/systems` carries seven Vulkan-named failure reasons. The two exemptions are one, granted twice. Graphics is not all of it either, and neither is the render thread: `com/mojang/blaze3d/audio` is the OpenAL wrapper and runs on the sound engine's own thread — see [the sound engine](../client/sound-engine.md). ## Vulkan is not a stub **7,477 lines against 5,627** — the Vulkan backend against the OpenGL one, forty classes against twenty-eight. It is the larger of the two trees: a real swapchain, the same GLSL compiled to SPIR-V and reflected to build bind-group layouts, five required device extensions, nine required features, and vendor-specific GPU crash breadcrumbs in *vulkan/checkpoints* that OpenGL has no answer to. `VulkanBackend.checkBackendAvailable` says why it is unavailable, though only the default preference consults it. The backends differ in **all seven** `DeviceFeatures` flags, and only one difference is symmetric: Vulkan hardcodes five flags true that OpenGL derives from extensions, and the mirrored pair is the two direct multi-draw flavours, where OpenGL has the separate one and never the interleaved one and Vulkan the interleaved one only if the driver offers *VK_EXT_multi_draw* — so a Vulkan device can support neither. Not every draw consults those flags: the six multi-draw and indirect entry points gate unconditionally, `RenderPass.draw` and `RenderPass.drawIndexed` only when a non-zero first instance is asked for, and `RenderPass.drawMultipleIndexed` — the batched chunk path — not at all. ## A pipeline is a record, not a sequence of calls `RenderPipeline` is declarative and effectively immutable: a `RenderPipeline.getLocation` identity, two shader `Identifier`s, a `ShaderDefines`, a list of `BindGroupLayout`, up to eight `ColorTargetState`, an optional `DepthStencilState`, vertex bindings, a `PolygonMode`, a cull flag and a `PrimitiveTopology`. `RenderPipeline.Builder` assembles one, and composition is the static `RenderPipeline.builder` taking `RenderPipeline.Snippet`s, which `RenderPipeline.Builder.buildSnippet` produces rather than consumes. Blending is a named `BlendFunction` (`BlendFunction.TRANSLUCENT`, `BlendFunction.ADDITIVE`…) rather than a pair of loose factors, and `RenderPipeline.Builder.build` refuses a pipeline whose colour targets do not all share one blend function, or that binds more than sixteen vertex attributes. Depth is reversed-Z throughout: `DepthStencilState.DEFAULT` compares greater-or-equal and `RenderSystem.DEFAULT_DEPTH_CLEAR_VALUE` is zero. The catalogue lives on the game side: `RenderPipelines` registers the static pipelines — `RenderPipelines.GUI`, `RenderPipelines.LIGHTMAP`, `RenderPipelines.SKY` and dozens more, eighty-seven in all — from a shallow tree of snippets and the shared uniform-name sets in `BindGroupLayouts`, and `RenderPipelines.getStaticPipelines` is the list `ShaderManager` walks to precompile them. ## What a pipeline does not say A `RenderPipeline` says how to rasterise. It does not say which textures to bind or which target to draw into — and that is where a 1.21 reader's composed stack of *RenderStateShard*s went. The answer is *client/renderer/rendertype*: `RenderType` wraps a `RenderPipeline` with an `OutputTarget`, a `TextureTransform`, a `LayeringTransform`, an outline variant and the batching predicates `RenderType.canConsolidateConsecutiveGeometry` and `RenderType.sortOnUpload`. `RenderTypes` is the static catalogue, `RenderSetup` builds the entries, and `RenderType.prepare` resolves one into a `PreparedRenderType` — pipeline, texture bindings, uniform slice — at draw time. ## Buffers, uniforms, and the ring that resets every frame The resource vocabulary is small: `GpuBuffer` and `GpuBufferSlice` with usage bits (`GpuBuffer.USAGE_VERTEX`, `GpuBuffer.USAGE_UNIFORM`, `GpuBuffer.USAGE_MAP_WRITE`…), `GpuTexture` and `GpuTextureView` with theirs, `GpuSampler`, `GpuFence`, `GpuFormat`, `IndexType`, `PrimitiveTopology`. Two of those are where old habits break. Sampler state left the texture — `GpuTexture` has no filter or wrap setters, filtering is an immutable `GpuSampler` bound per draw, and `SamplerCache` eagerly creates all thirty-two combinations at startup and throws if either enum ever gains a constant. And there are three shared index buffers, not one: `RenderSystem.getSequentialBuffer` switches between a quad buffer, a line buffer with different winding, and a one-to-one buffer. Per-draw uniform data does not come from per-draw uniform calls; it is carved out of ring buffers. `DynamicUniforms` and `DynamicUniformStorage` hand out `GpuBufferSlice`s of a `MappableRingBuffer` reset once a frame, while `GlobalSettingsUniform` and `ProjectionMatrixBuffer` hold one buffer apiece and rewrite it in place, which is all a frame-wide value needs. Per-frame scratch comes from `TransientMemory` — one interface, two large implementations over the shared `TransientBlockAllocator`. Blocks are packed by hand with `Std140Builder`, sized by `Std140SizeCalculator`. Vertex data is described by `VertexFormat` and `VertexFormatElement` (a plain record of name, offset and `GpuFormat`) with the standard layouts in `DefaultVertexFormat`, and built with `ByteBufferBuilder` and `BufferBuilder` into a `MeshData`. Most `BufferBuilder`s are on the render thread like everything else here; the exception is the one that matters most for throughput, because chunk meshing runs `BufferBuilder` on worker threads and stages the result through `StagedVertexBuffer` and `UberGpuBuffer` into a `StagingBuffer`, which is why `SectionRenderDispatcher` has a spin-wait guarded by `RenderSystem.isOnRenderThread`. Render targets are `RenderTarget`, `TextureTarget` and `MainTarget`, the transient ones allocated through `GraphicsResourceAllocator` — `CrossFrameResourcePool` implements it — and declared in the `FrameGraphBuilder` of [visibility and the frame graph](visibility-and-the-frame-graph.md). ## Shaders, and the reflection that checks them `ShaderManager` loads shader sources and hands them to a backend through `ShaderSource`. `ShaderManager` resolves the *moj_import* directives at load time, before a backend sees anything; the `ShaderDefines` are injected later and inside each backend, by the same shared `GlslPreprocessor` at the moment a program is compiled. The Vulkan side goes further than compiling: `GlslCompiler` runs the GLSL through shaderc to SPIR-V and `IntermediaryShaderModule` *reflects* the result with spirv-cross, enumerating `SpvUniformBuffer`s and `SpvSampler`s — which is what lets a declared `BindGroupLayout` be checked against what the shader declares. It is all data on disk, alongside the chains in [post-processing](post-processing.md). ## One draw Every drawing class comes through this one shape: `LevelRenderer`, `GuiRenderer`, `FeatureRenderDispatcher`, `Lightmap`, `TextureAtlas`. ```mermaid sequenceDiagram participant Game as the game's own code participant GD as GpuDevice participant CE as CommandEncoder participant RP as RenderPass participant GlCE as GlCommandEncoder participant GpuS as GpuSurface Game->>GD: createCommandEncoder — a fresh facade over the one real encoder Game->>CE: createRenderPass with a RenderPassDescriptor CE->>CE: validate attachments, sizes, usage bits, render area, no pass open CE->>GlCE: bind an FBO from the cache, viewport, scissor, clear CE-->>Game: RenderPass, an AutoCloseable Game->>RP: setPipeline — formats must match the attachments Game->>RP: bindDefaultUniforms — Projection, Fog, Globals, Lighting Game->>RP: setVertexBuffer, setIndexBuffer, bindTexture Game->>RP: drawIndexed RP->>GlCE: look up or compile the program, apply pipeline state, bind VAO GlCE->>GlCE: glDrawElementsInstancedBaseVertex Game->>RP: close — debug groups must balance RP->>CE: submitRenderPass Note over GpuS: the surface, at the two ends of the frame Game->>GpuS: acquireNextTexture at the top of renderFrame, then blitFromTexture of the main target and present at the bottom ``` Everything above the `GlCommandEncoder` lane is validation or declaration. Below it, one call is not one call: pass setup alone binds a framebuffer, sets viewport and scissor and clears, and a single `RenderPass.drawIndexed` applies depth, cull, blend, polygon mode and colour mask, binds a program, walks the uniform and sampler bindings, binds a vertex array and finally draws. The point is not that a draw is cheap. It is that *the game* never sees any of it. The pipeline compiles lazily on its first `RenderPass.setPipeline` and is cached by identity on the device, but no frame in a running game pays for it: `ShaderManager` precompiles the static catalogue into that cache on every resource reload, leaving the lazy path for pipelines outside it. Run the trace on Vulkan and the game code is unchanged — dynamic rendering replaces the framebuffer bind, push descriptors the uniform binding, and the swapchain lives in `VulkanGpuSurface`. ## How a frame reaches the screen Presentation is a four-step protocol, not a swap: `GpuSurface.configure`, then `GpuSurface.acquireNextTexture`, then `GpuSurface.blitFromTexture`, then `GpuSurface.present`. Vsync is not a toggle in that sequence but a `GpuSurface.PresentMode` in the configuration: OpenGL offers a fixed pair of modes, Vulkan whatever the driver enumerates, mailbox and relaxed FIFO included. ## Questions players ask **Why did that draw produce nothing, and say nothing?** Because the deep validation is a development-environment feature: the *missing uniform*, *invalid shader program* and buffer-usage checks are gated on the in-IDE flag, and in a shipped game the same conditions make the draw return without a word. **Why is the game on OpenGL when I asked for Vulkan?** Because the backend is chosen in `Minecraft`, not in Blaze3D. `PreferredGraphicsApi.getBackendsToTry` returns an ordered *pair*, each candidate tried in turn, so every setting has the other API as its fallback and the default is OpenGL-first. A previous unclean shutdown downgrades twice: a Vulkan preference to the default, the default to OpenGL. **Why does the game care which GPU I have, when it can ask the driver?** Because the capability record is sniffed as well as queried. `GlHeuristics` reads the renderer and vendor strings to guess the device type, flags GL-over-D3D12 — assumed on Windows-on-ARM whatever the string says — and flags AMD for anisotropy problems, both of which change how the game uploads and filters. The backend also probes the reported maximum texture size rather than trusting it, halving a proxy allocation until the driver accepts one. **What stops the CPU running a hundred frames ahead of the GPU?** A two-deep submit fence, not the present: `GlCommandEncoder` rotates its transient memory and a small fence ring on submit, and that is the pacing. Results that must come *back* use `GpuFence` — a callback registered with `RenderSystem.queueFencedTask`, run by `RenderSystem.executePendingTasks` once a frame in the *gpuAsync* zone, stopping at the first fence that has not signalled. Its one registration site is `GlCommandEncoder`'s texture readback, and Vulkan routes the same callbacks through its own destruction queue. > **For a 1.21-era reader.** Nearly every name you would reach for in this > corner of the codebase has gone. `PoseStack` did *not* move, and is still here. | you are looking for | it is now | |---|---| | *RenderSystem.setShader* and every state toggle on it | fields of a `RenderPipeline` | | *ShaderInstance* | the pipeline's two shader `Identifier`s, compiled by `ShaderManager` | | *RenderStateShard* | `RenderType` over a `RenderPipeline` | | *VertexBuffer*, *Tesselator*, *BufferUploader* | `BufferBuilder` into a `MeshData`, then a `GpuBuffer` | | *VertexFormat.Mode*, *VertexFormat.IndexType*, *TextureFormat* | `PrimitiveTopology`, a top-level `IndexType`, `GpuFormat` | | *GpuDevice.getDeviceName* and its siblings | the `DeviceInfo` record | | *Window.updateDisplay*, *setVsync* | `GpuSurface.present` and a `GpuSurface.PresentMode` | ## Where to look `RenderSystem` for what the game holds, then `GpuDevice`, `CommandEncoder` and `RenderPass` for the façade and its checks. `RenderPipeline.Builder` and `RenderPipelines` for how a draw is declared, `RenderTypes` for how one is dressed. `GlCommandEncoder` for an OpenGL draw, `VulkanCommandEncoder` for the other answer, `GpuSurface` for where a frame ends. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Visibility and the frame graph > Verified against **Minecraft 26.2** · Part XI · you fly forward in creative and the world opens out in front of you, one section at a time. You fly forward in creative and the world arrives. It does not resolve as a wall of fog lifting all at once — it grows *outward* from where you already are, section by section, at exactly the rate the mesher can keep up with. That order is not the download order and it is not the mesher's queue order. It falls out of an asymmetry inside the walk that decides what is visible at all: a section that has not been meshed yet is **opaque** to the walk and stops it dead, a section known to be empty is **transparent** and lets it straight through, and a section that meshed to *nothing* is neither — it keeps a real compiled mesh that happens to have no draws in it. Terrain reveals itself outward because the walk can only reach as far as the meshes that already exist. [The frame](the-frame.md) ends where this page begins — but not quite at one method. The first stage runs on the *extract* side of the wall: `LevelExtractor.applyFrustum` trims the reached sections to the visible list before `GameRenderer.render` is called at all. The other four are `LevelRenderer.render`, which gathers what was submitted, declares the passes of a frame, draws the terrain, schedules translucency work for a later frame and re-runs the walk on its way out — in that order, and the order is the page. ## The cast | class | what it decides | thread | |---|---|---| | `LevelRenderer` | which sections are visible, which passes the frame declares, and how terrain is finally drawn | Render thread | | `SectionOcclusionGraph` | which sections the walk can reach from the camera at all | full walk on `Util.backgroundExecutor`, partial walk here | | `SectionOcclusionGraph.GraphState` | the published result of a walk — swapped whole on a rebuild, extended in place by a partial walk | rebuilt by a worker, extended and read here | | `LevelExtractor` | whether the frustum is re-applied this frame, and so whether the visible list is rebuilt or reused | Render thread | | `Frustum` | which of the reached sections survive into `LevelRenderer.visibleSections` | Render thread | | `FrameGraphBuilder` | which passes exist, what each reads and writes, and what order they execute in | Render thread | | `LevelTargetBundle` | the named render targets the passes hand between them | Render thread | | `ChunkSectionsToRender` | one bucket per buffer set, so sections that share buffers share a binding | Render thread | Only one of those reads the world, and it is the one on the extract side: `LevelExtractor` holds the `ClientLevel` and is the class that carries it across the wall. `LevelRenderer` does not — its single `ClientLevel` reference is a *parameter*, to `LevelRenderer.invalidateCompiledGeometry`, which is the reload path and not the frame path. Everything the drawing half knows about the world came across in the snapshot; what the renderer still owns is geometry, targets and order. ## Five stages, and the first one decides the other four ```mermaid flowchart TD S1["1. what is visible, in extract — SectionOcclusionGraph reaches sections outward from the camera, then LevelExtractor.applyFrustum keeps the ones the Frustum admits and caches them as LevelRenderer.visibleSections"] S2["2. what is submitted — LevelRenderer.submitFeatures gathers entities and block entities, and FeatureRenderDispatcher.prepareFrame groups them, all before a pass exists"] S3["3. what passes exist — FrameGraphBuilder.addPass declares each pass with its reads and writes, then FrameGraphBuilder.execute orders and runs them"] S4["4. how terrain is drawn — LevelRenderer.prepareChunkRenders buckets the visible sections and ChunkSectionsToRender multi-draws each bucket, inside the main pass"] S5["5. what is re-sorted — a rolling budget of translucent sections is scheduled, for a mesh that arrives a frame or more later"] S6["SectionOcclusionGraph.update — the walk is re-run at the end of render, so the next frame reads a newer graph"] S1 --> S2 --> S3 --> S4 --> S5 --> S6 S6 -. "next frame" .-> S1 ``` Read it as **reach, gather, declare, draw, defer**, and note that stage one has already happened when `LevelRenderer.render` is entered — it is the last thing *extract* does. Stages one and five are bookkeeping that decides what the drawing stages will have to do, and both are budgeted rather than complete. ## The walk that decides what exists, and the frustum that only trims it Visibility here is *reachability*, not a frustum test. `SectionOcclusionGraph` starts at the camera's own section and walks outward one neighbour at a time, and — with smart cull on, which is the default — it may step from a section into a neighbour only if the two faces involved can see each other through that section's geometry. That per-section answer is a `VisibilitySet`, computed by `VisGraph` when the section was meshed — so the question *can you see through this section* is decided once at compile time and then read for free thousands of times a frame. A wall of stone does not hide the world behind it because a frustum test rejected it. It hides it because the walk cannot get past. The reached sections live in an `Octree` inside `SectionOcclusionGraph.GraphState`, alongside the queue of sections whose neighbours still need visiting. A *rebuilt* state is never edited into the old one: the whole of it is published through an `AtomicReference`, because `SectionOcclusionGraph.scheduleFullUpdate` runs the complete rebuild on `Util.backgroundExecutor` and the client thread has to keep reading the old graph until the new one is ready. Everything else happens on the client thread, including `SectionOcclusionGraph.runPartialUpdate`, which does edit the published state in place — it walks outward again from the sections that `SectionOcclusionGraph.schedulePropagationFrom` flagged, typically because a new mesh landed for them and their neighbours are worth trying again — which is a real walk, not a drain, and it is where the outward reveal actually advances. `SectionOcclusionGraph.update`, at the very end of `LevelRenderer.render`, is what runs both. The frustum arrives after all of this, and only trims. `SectionOcclusionGraph.addSectionsInFrustum` visits the octree, keeps what a `Frustum` admits, and fills `LevelRenderer.visibleSections` plus the small short-radius subset `LevelRenderer.nearbyVisibleSections`. Looking a section up by position goes through `LevelRenderer.viewArea`. **Beyond sixty blocks the walk gets harder** — and *sixty blocks* and *three sections* are one number written twice. `SectionOcclusionGraph.MINIMUM_ADVANCED_CULLING_DISTANCE` is sixty, and `SectionOcclusionGraph.MINIMUM_ADVANCED_CULLING_SECTION_DISTANCE` is that same distance converted to section coordinates, which comes out at three; the test that uses it compares section coordinates on each axis separately. Out past it, smart cull adds a ray march *back toward the camera* from the neighbour being considered, and rejects that neighbour if any section along the line has not itself been reached by this walk. Inside it, nothing marches — nearby geometry is cheap enough not to argue about. ### Which is why the reveal is outward The three states a section can be in are the whole trick. An **uncompiled** section is opaque: the walk stops there and everything behind it stays unreached. An **empty** section is transparent: the walk passes through without needing a mesh at all. A section that **compiled to nothing** is neither — it holds a real compiled mesh with no draws in it and answers from its own `VisibilitySet` like any other section. So as meshes land, the frontier of the walk moves outward one shell at a time, and each newly meshed section re-arms its neighbours through `SectionOcclusionGraph.schedulePropagationFrom`. Streaming is a separate handshake laid over the same walk. A section whose chunk has not arrived yet is treated as neither opaque nor transparent — it is *parked*, filed against the chunk it is waiting for, and resumed when `ClientChunkCache` reports that chunk loaded. The walk then continues from where it stopped rather than starting over. The gate this stage controls is not only what gets drawn. **Only visible sections are re-meshed**, so a block you place behind you costs nothing until the walk reaches that section again; how a section becomes triangles once it has been chosen — the dirty flags, the snapshot a worker reads, the compiler, the three chunk layers and the buffer arenas they upload into — is [section meshing](section-meshing.md). ### The visible list is a cache, not a per-frame computation `LevelExtractor.applyFrustum` does not run every frame, and **two different clocks** decide when it does. They are easy to run together and they are not the same thing. The **walk** is thrown away and redone when the camera crosses an eight-block cell on any axis, when the field of view changes, or when the smart-cull toggle changes. That is `SectionOcclusionGraph.invalidateIfNeeded`, and what it schedules is the full off-thread rebuild. The **frustum step** asks a different question — the walk's result may still be good while the set of it you can see is not — and it re-runs when either of two things happens: the graph raises its own frustum-update flag, which a completed full walk always does and a partial walk does whenever it added a section inside the offset frustum; or the camera's pitch or yaw crosses a two-degree step. Turning your head therefore re-applies the frustum without disturbing the walk at all, and between these events `LevelRenderer.visibleSections` is simply the list from last time. Stand still and stare, and the frame's terrain cost does not move. ## Everything is submitted before there is anywhere to put it Entities and block entities do not draw themselves inside a pass. They are gathered *first*, before a single pass has been declared. `LevelRenderer.submitFeatures` runs `LevelRenderer.submitEntities` and `LevelRenderer.submitBlockEntities` into `LevelRenderer.submitNodeStorage`, and `FeatureRenderDispatcher.prepareFrame` groups everything submitted into a `FeatureRenderDispatcher.PreparedFrame`. The passes declared in the next stage capture that prepared frame and call into it; they never walk an entity list themselves. What a submission contains, and how an entity produces one, is [entity rendering](entity-rendering.md). The ordering matters for a reason that only shows up in the next stage: the frame graph needs to know, *while it is being declared*, whether anything in this frame wants an outline. It can know that because the submission has already happened. ## Declaring the passes, and why none of them is ever culled `LevelRenderer.render` builds a graph and then executes it. Building it means declaring resources and passes. `FrameGraphBuilder.importExternal` brings in the targets that already exist outside the frame — the main render target and the entity-outline target — and `FrameGraphBuilder.createInternal` declares five that exist only for the duration of this frame. Each pass comes from `FrameGraphBuilder.addPass`, states its dependencies with `FramePass.reads` and `FramePass.readsAndWrites`, and states its body with `FramePass.executes`. `LevelTargetBundle` is where the handles live under names — `LevelTargetBundle.main`, `.translucent`, `.itemEntity`, `.particles`, `.weather`, `.clouds`, `.entityOutline` — and `LevelRenderer.targets` is the bundle the frame threads through every declaration. ```mermaid flowchart TD CLEAR["clear — wipes colour and depth on the main target"] SKY["sky — LevelRenderer.addSkyPass"] MAIN["main — LevelRenderer.addMainPass"] OUT["the entity outline post chain — added only when something submitted an outline"] CLOUDS["clouds — LevelRenderer.addCloudsPass, added only in a frame that has clouds"] WEATHER["weather — LevelRenderer.addWeatherPass, which also draws the world border"] TRANS["the transparency post chain"] TOP["always on top — LevelRenderer.addAlwaysOnTopPass, which clears depth first"] CLEAR --> SKY --> MAIN --> OUT --> CLOUDS --> WEATHER --> TRANS --> TOP subgraph INSIDE ["inside the main pass, in order"] direction TB T1["opaque terrain — the OPAQUE draw group"] T2["FeatureRenderDispatcher.PreparedFrame.executeSolid"] T3["depth copied out of the main target into the translucent, item entity and particle targets"] T4["PreparedFrame.executeTranslucent, then PreparedFrame.executeOutline"] T5["translucent terrain — the TRANSLUCENT draw group"] T6["PreparedFrame.executeTranslucentAfterTerrain"] T1 --> T2 --> T3 --> T4 --> T5 --> T6 end MAIN -.-> INSIDE ``` The two post chains in that figure are declared here and explained in [post-processing](post-processing.md). The outline chain is why the entity-outline target is imported at all, and the transparency chain is the only reason the five internal targets are ever created. **The graph culls passes, and culls none of these.** `FrameGraphBuilder.execute` keeps only the passes that transitively feed an imported external resource and drops the rest before it orders anything. Note the plural: it seeds from *every* imported resource, not from the main target alone, which is what saves the entity-outline chain — none of its four passes ever writes to main, and all four survive because the glow target is imported too. With that seeding, no pass `LevelRenderer` declares is ever dropped in a stock game. The clouds pass is absent from a clouds-off frame for a different and much cheaper reason: `LevelRenderer.render` never *adds* it. The declaration is the branch; the culling machinery is insurance against a declaration that has become pointless, not the mechanism the game uses to turn features off. ## One bucket per buffer set, and what bucketing actually buys `LevelRenderer.prepareChunkRenders` runs before the main pass is declared, and its output is what that pass will execute. It walks `LevelRenderer.visibleSections` and, for each layer a section has geometry in, computes a hash of the buffers that geometry lives in and files the draw under that hash. Sections sharing buffers land in the same bucket, and `ChunkSectionsToRender` then issues one `RenderPass.drawMultipleIndexed` per bucket, with each section's transform arriving as a slice of a uniform buffer rather than as a per-draw state change. **The saving is not the draw calls.** Both backends still loop and issue one GPU draw per section — what the bucket removes is the buffer rebinding and the per-draw state change between them, which is the expensive part on this side of the driver. The two groups the main pass renders are `ChunkSectionLayerGroup.OPAQUE` and `ChunkSectionLayerGroup.TRANSLUCENT`; the layers underneath them belong to [section meshing](section-meshing.md). The translucent layer inverts the rule, and it inverts it in the direction nobody guesses. Its grouping hash omits the buffer contribution entirely, so the hash never changes — **every** translucent section files into one bucket instead of being spread across several. That is exactly what preserves the visit order inside it, and the visit order is what the draw list is then reversed against, so the far sections blend before the near ones. Correct blending is bought here by refusing to *distinguish* the buffers, not by refusing to share them. **Directional shading is per dimension, and it is not data.** How bright a face is by direction comes from a `CardinalLighting` record, and there are exactly two of them: `CardinalLighting.DEFAULT` and `CardinalLighting.NETHER`, both hard-coded. `DimensionType` carries the choice between them and nothing else — a datapack picks, it does not supply numbers. One ordering here catches everyone out. **Terrain is drawn before the sections queued this frame are compiled**: `LevelRenderer.compileSections` runs *after* `FrameGraphBuilder.execute`, so even the option that forces a synchronous rebuild only guarantees the mesh exists by the end of frame *N* — it appears in frame *N+1*. Again, [section meshing](section-meshing.md). ## Translucency, re-sorted on a budget it never finishes Translucent quads inside a section have to be sorted back to front from where you are standing, and where you are standing changes constantly. Re-sorting every visible section every frame is not affordable, so the client re-sorts a slice of them each frame and lets the rest be slightly stale. Two groups are considered. Everything in `LevelRenderer.nearbyVisibleSections` — the short-radius set the frustum step filled alongside the main list — and then a round-robin slice of `LevelRenderer.visibleSections`, an eighth of it or fifteen sections, whichever is larger, walked from `LevelRenderer.translucencyResortIterationIndex` so that successive frames continue where the last one stopped. Being considered is not being re-sorted. A section is scheduled if its `TranslucencyPointOfView` actually changed, **or** if the camera's block position moved since `LevelRenderer.lastTranslucentSortBlockPos` and the section is either axis-aligned from the camera or one of the nearby ones. It is then skipped anyway if a re-sort is already scheduled for it, or if it has no translucent geometry at all. So standing still costs nothing, walking costs a bounded amount, and a fast enough sideways move can leave a distant pane of glass sorted for a viewpoint you have already left. > **For a 1.21-era reader.** *LevelRenderer.renderLevel* does not exist — the > method is `LevelRenderer.render`, and it is handed render state rather than > a level. *LevelRenderer.renderChunkLayer* is gone, because a layer is no > longer drawn by a method looping over chunks: it is a bucketed multi-draw > built by `LevelRenderer.prepareChunkRenders` and issued by > `ChunkSectionsToRender`. *LevelRenderer.setupRender* is gone too, its work > split between `SectionOcclusionGraph` and `LevelExtractor.applyFrustum`. And > every dirty method that used to hang off `LevelRenderer` moved to > `LevelExtractor`, the world-facing half of the old class. Four names survive > unchanged and mean what they always did: `ViewArea`, `VisGraph`, `Octree` > and `Frustum`. ## Where to look `LevelRenderer.render` — the stages of this page are that one method, top to bottom. `SectionOcclusionGraph.update` for the walk, and `SectionOcclusionGraph.runPartialUpdate` for the only part of it on the client thread. `LevelExtractor.applyFrustum` for why the visible list is usually a cache. `FrameGraphBuilder.execute` for how declared passes are ordered and which are dropped. `LevelRenderer.prepareChunkRenders` and `ChunkSectionsToRender` for how terrain finally reaches the GPU, with [blaze3d](blaze3d.md) underneath it. The models and the atlas the terrain is textured from are [models and atlases](models-and-atlases.md); the block changes that make sections dirty arrive as the packets in [what the client is told](../networking/what-the-client-is-told.md) and become dirty sections in [the client level](../client/the-client-level.md). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Section meshing > Verified against **Minecraft 26.2** · Part XI · a block is placed, and the section it lives in is re-meshed, uploaded and drawn. You right-click a block into place and it is simply *there* — no shimmer, no gap, no frame in which the wall you just built has a hole in it. Behind that instant there is a worker thread rebuilding a cube of world sixteen blocks on a side from scratch, a snapshot of twenty-seven sections taken so that it may, a scratch buffer it had to queue for, and a swap that waits until every last byte has landed on the GPU. All of which is why the *other* half of the story is the surprising one: place a block behind you and none of it happens at all. Only sections the frame is already drawing are swept for dirtiness, so the flag on a section at your back is set and then simply waits — for a second, for an hour, for as long as you keep your back turned. ## The cast | class | what it decides | thread | |---|---|---| | `LevelExtractor` | which block changes dirty which sections, and which dirty sections are worth compiling this frame | Render thread | | `SectionUpdateTracker` | where dirtiness lives, and whether a never-compiled section is allowed to compile yet | Render thread | | `RotatingSectionStorage` | which slot in a fixed ring a section's flag belongs to, and when that slot is re-homed | Render thread | | `RenderRegionCache` | the snapshot a mesher is allowed to read, and how much of it is shared | Render thread | | `SectionRenderDispatcher` | queue order, scratch buffers, the GPU arenas, and the moment a new mesh becomes the drawn one | Render thread, workers | | `SectionTaskDynamicQueue` | which section a free worker takes next | any | | `SectionCompiler` | the block-by-block walk that turns a snapshot into vertices | `Util.backgroundExecutor` | | `BlockModelLighter` | smooth lighting and ambient occlusion, per quad, as the walk goes | `Util.backgroundExecutor` | ## The whole trip, in one figure ```mermaid sequenceDiagram participant MPGM as MultiPlayerGameMode participant CL as ClientLevel participant LX as LevelExtractor participant SUT as SectionUpdateTracker participant LR as LevelRenderer participant SRD as SectionRenderDispatcher participant Worker as Worker participant SectC as SectionCompiler MPGM->>CL: useItemOn under a prediction, then BlockItem.placeBlock calls setBlock CL->>LX: setBlockDirty, but only if ModelManager.requiresRender CL->>LX: blockChanged, player-changed or not, read off the update flags LX->>SUT: dirty over a 3x3x3 block halo, one section or up to eight on a boundary Note over LX,SUT: the same frame's extract pass, which runs after the tick that handled the click LX->>LR: walk the visible sections, and only those LX->>LX: RenderRegionCache builds a 27-section snapshot LX->>SUT: the flag is cleared as the work is taken Note over LR,SRD: same frame, after this frame's terrain has already been drawn LR->>SRD: compileAsync, or compileSync under PrioritizeChunkUpdates SRD->>Worker: taken nearest-first, and only if a buffer pack is free Worker->>SectC: compile every block in the section into at most three layers SectC-->>Worker: layers, block entities, visibility, sort state Worker->>SRD: append to the staging buffer, spin-waiting if it is full Note over LR,SRD: the end of a later frame LR->>SRD: uploadTerrainBuffersToGpu, whose callback swaps the mesh in ``` Read it in three beats: a change makes a flag, a frame turns some flags into work, and a much later frame publishes the result. The middle beat is the one that leaves the client thread, and it does not always — a synchronous rebuild compiles inline where it stands, and an empty mesh is published by the worker that found it empty. ## A click, and the flag it leaves behind The first arrow is not what it looks like. `MultiPlayerGameMode` owns the prediction sequence — the client places the block itself and remembers what it assumed — but the `Level.setBlock` that actually changes the world happens down inside `BlockItem.placeBlock`. From the renderer's side it makes no difference whether the change came from your own hand or from the server; [what the client is told](../networking/what-the-client-is-told.md) is the other door into the same call, by way of `ClientLevel.sendBlockUpdated`. Dirtiness is a small API on `LevelExtractor`, and the calls differ mostly in how much of the world they condemn. | call | what it marks | |---|---| | `LevelExtractor.blockChanged` | one changed block, with the player-changed bit read off the update flags | | `LevelExtractor.setBlockDirty` | one changed block, and only if `ModelManager.requiresRender` says a model cares | | `LevelExtractor.setBlocksDirty` | a box of block positions | | `LevelExtractor.setSectionDirty` | one section, by section coordinate | | `LevelExtractor.setSectionDirtyWithNeighbors` | that section and the ones touching it | | `LevelExtractor.setSectionRangeDirty` | a range of sections | | `LevelExtractor.allChanged` | everything at once | **27** — the size of the halo a single block change marks, in *block positions*, not sections. This is the number to keep straight. A 3×3×3 neighbourhood of blocks maps to exactly **one** section for any block that is not on a section boundary, and to at most eight when it is — a corner block touching seven neighbours plus its own. Only the mesher's *read* region, much later on, is genuinely twenty-seven sections. There *is* a gate on one of the two doors — `ModelManager.requiresRender` guards `LevelExtractor.setBlockDirty`, so a state change no model reacts to marks nothing through that route — but it is not the route a placed block takes. `Level.setBlock` goes through both, and the second, `LevelExtractor.blockChanged`, marks the halo whatever the models say. ### The flag belongs to a slot, not to a section `SectionUpdateTracker` holds a `SectionUpdateTracker.SectionDirtyState` per section — with `SectionUpdateTracker.SectionDirtyState.isDirty` and `SectionUpdateTracker.SectionDirtyState.isDirtyFromPlayer`, the second being what the *prioritise chunk updates* setting keys off further down this page — and the tracker is built on `RotatingSectionStorage`, a fixed ring of slots that `RotatingSectionStorage.repositionCenter` re-homes as the camera moves. A dirty flag therefore belongs to a *slot*, not to a section: walk far enough away and the slot is re-homed and the flag is gone. Nothing is lost by that, because a newly homed slot starts dirty. ## The sweep that only looks at what you can see Once a frame, the extract pass walks the sections the renderer considers visible and collects the dirty ones. That is the whole mechanism behind the hook: a section that is not in the visible set is never even asked whether it is dirty, so its flag sits there, indefinitely, and the section rebuilds the instant it comes back into view. Which sections count as visible, and the reachability walk that decides it, belong to [visibility and the frame graph](visibility-and-the-frame-graph.md). One extra gate applies, and only to sections that have never been compiled before. `SectionUpdateTracker.hasAllNeighbors` requires the eight surrounding chunk columns — horizontally only, never the section's own column — to be loaded and lit before a first compile is allowed. A mesher decides whether a block's face is worth drawing by looking at the block on the other side of it, so a section built without its neighbours would be a section built against nothing. A *re*compile skips that check entirely: a section that has a mesh already may always build a new one. ## What a mesher is allowed to read A compile runs for an unbounded time on a worker while the client thread keeps applying block updates, so it cannot be allowed near the live world. `RenderRegionCache` builds it a `RenderSectionRegion` instead: a 3×3×3 grid of `SectionCopy`, each holding a genuine *copy* of one section's `PalettedContainer` together with an immutable map of that chunk's block entities. Inside the compile, a block state read is a read of that copy, and it will answer the same way from the first quad to the last however much the world has moved on. The cache is what stops this being ruinous. Twenty-seven copies per dirty section would mean 27*n* copies for *n* sections, and neighbouring dirty sections share almost all of their neighbourhood — so the regions built in one extract share their `SectionCopy` instances, and the real cost is far closer to *n*. What is *not* copied is as informative as what is. Tints and light are read live, through the region's references to `ClientLevel` and the light engine — so for those two the mesher is looking at the world as it is when it asks, not at the world as it was when the snapshot was taken. ## The queue, the packs, and why more cores do not always help `SectionRenderDispatcher` takes the sections the extract collected and either queues them (`SectionRenderDispatcher.RenderSection.compileAsync`) or, under the *prioritise chunk updates* setting, compiles them on the spot (`SectionRenderDispatcher.RenderSection.compileSync`). `SectionTaskDynamicQueue` decides the order and it is nearest-first, with one guard: a recompile only beats a first-time compile while a small quota lasts, and only if it is also nearer. With no first-time compile queued at all, a recompile wins outright. So terrain you have never seen is never starved by a stream of rebuilds to terrain you have. The throttle is not the thread count. Each task-runner must acquire a `SectionBufferBuilderPack` — the scratch vertex buffers a compile writes into — from `SectionBufferBuilderPool` before it can do anything, and the pool is sized to the processor count *or* to a share of the heap, whichever is smaller, degrading further if it hits an out-of-memory error while allocating. A worker that cannot get a pack puts its task back on the queue and gives up its turn. That requeue is a null check, and it is worth knowing how wide it is: the catch that implements it covers the whole compile, so *any* null-pointer failure inside the mesher quietly requeues the section rather than reporting it. A section that fails this way forever will re-mesh forever, silently. The ceiling on how many meshes exist at once is therefore the pool **plus one**: the synchronous path uses `RenderBuffers.fixedBufferPack`, which is not in the pool at all. And because the pool is usually larger than the background pool, the constraint you actually hit is normally the thread count after all. ## What the compiler makes `SectionCompiler.compile` walks every block in the section, asks the models for its quads and sorts them into layers. Its product is `SectionCompiler.Results`, four things at once: `SectionCompiler.Results.renderedLayers` (the geometry, per layer), the `SectionCompiler.Results.blockEntities` it found on the way, a `SectionCompiler.Results.visibilitySet` for the reachability walk on the other page, and `SectionCompiler.Results.transparencyState`, the sort state for the translucent layer. Those become the section's `CompiledSectionMesh`. Lighting is not a separate stage: `BlockModelLighter` computes smooth lighting and ambient occlusion as the walk goes, with a thread-local cache bracketed around each compile. **Three** — the chunk section layers, and there are only three: `ChunkSectionLayer.SOLID`, `ChunkSectionLayer.CUTOUT` and `ChunkSectionLayer.TRANSLUCENT`. A quad's layer is normally decided at bake time and simply carried into the compile — see [models and atlases](models-and-atlases.md) for the `BlockStateModelSet` the compiler reads and the reload that invalidates every mesh in the world. But the mesher can overrule it in two places. Every leaf quad is redirected to `ChunkSectionLayer.SOLID` when the *cutout leaves* option is off, so that setting is baked into the mesh rather than applied at draw time. And fluids never consult a baked quad at all: their layer comes from the `FluidModel`, through `FluidRenderer`. ## Onto the GPU, and a swap that is late on purpose The worker does not touch the GPU. It appends its vertices to a `StagingBuffer` under a lock, spinning if the buffer is full — a real back-pressure point, where a worker waits on the client thread rather than the buffer growing to fit it. The client thread drains it in `SectionRenderDispatcher.uploadTerrainBuffersToGpu`, and each completed upload fires the callback that publishes the new mesh. The result of a compile therefore arrives back on the client thread, always, with exactly one exception: a section that compiled to nothing at all is published directly on the worker, because there is nothing to upload. The destination is not one buffer per layer but an *arena* per layer. Each `ChunkSectionLayer` has an `UberGpuBuffer` pair owning a growing list of fixed-size heaps — 128 MiB for vertices, 32 MiB for indices — and each heap is a real GPU buffer sub-allocated by a `TlsfAllocator`, freed again when it empties. Sections are tenants in a shared allocation, not owners of buffers; [blaze3d](blaze3d.md) is where those buffers come from. And now the second fact this page exists to place: **the swap is atomic and late.** `SectionRenderDispatcher.RenderSection.sectionMesh` keeps pointing at the *old* mesh until every layer's vertex and index buffer has reported uploaded. There is no frame in which a rebuilt section is missing, no flicker and no hole — the price being that the section you can see is, for a few frames, deliberately out of date. `SectionRenderDispatcher.RenderSection.reset` is the other end of that lifecycle, and `SectionRenderDispatcher.RenderSection.getVisibility` is not part of it at all despite the name — it is the fade the next section explains, an alpha that climbs from nothing to one over the upload's fade duration. `SectionRenderDispatcher.RenderSection.resortTransparency` is the cheap path that reorders an existing translucent mesh without recompiling anything; [visibility and the frame graph](visibility-and-the-frame-graph.md) owns the budget that decides when it runs. ## Questions players ask **Why does distant terrain fade in, but a block I place never does?** Because the fade is deliberately restricted to the case it was written for. `SectionRenderDispatcher.RenderSection.setFadeDuration` is non-zero only for distant sections that were not previously empty, and the fade clock starts at a section's *first* upload — so a recompile of terrain you have been staring at, which is what placing a block is, has no fade left to spend. **I turned on "prioritise chunk updates" and it still costs me a frame. Why?** Because of where compiling sits in the frame. Terrain is drawn *before* the sections queued this frame are compiled, so the strongest promise the setting can make is that the mesh exists by the end of frame *N*. It appears in frame *N+1*. The setting buys you the compile, not the draw — and it buys it by doing the work on the client thread, which is why it can also cost you the frame outright. **What happens when the buffer pool runs out?** Nothing visible, which is the design. The worker that cannot acquire a `SectionBufferBuilderPack` puts its section back on `SectionTaskDynamicQueue` — the *task* is requeued, not the flag, which was cleared when the work was taken — so the only symptom is terrain arriving more slowly. The pool is also allowed to shrink itself: if it hits an out-of-memory error while allocating, it comes back smaller and the game keeps going with fewer concurrent meshes. > **For a 1.21-era reader.** The whole dirty API moved. Every > *setBlockDirty*-shaped method that used to live on `LevelRenderer` is now on > `LevelExtractor`, in *client/renderer/extract*, and the flags themselves > live in `SectionUpdateTracker`. Below that: *ChunkRenderDispatcher* and > *RenderChunk* are now `SectionRenderDispatcher` and its nested > `SectionRenderDispatcher.RenderSection`, *CompiledChunk* is > `CompiledSectionMesh`, *RenderChunkRegion* is `RenderSectionRegion`, and > *LiquidBlockRenderer* is `FluidRenderer`. *RenderType.chunkBufferLayers* and > its five chunk render types are three `ChunkSectionLayer`s. > *BlockRenderDispatcher* and *BlockAndTintGetter.getShade* are gone. ## Where to look `LevelExtractor.blockChanged` and `LevelExtractor.setBlockDirty` for where a change becomes a flag, and `SectionUpdateTracker` for where the flag lives. `SectionUpdateTracker.hasAllNeighbors` for the gate on a first compile. `RenderRegionCache` and `SectionCopy` for what a mesher may read. `SectionRenderDispatcher.RenderSection.compileAsync` and `SectionTaskDynamicQueue` for what gets built and in what order. `SectionCompiler.compile` for the walk itself, and `BlockModelLighter` for where ambient occlusion comes from. `SectionRenderDispatcher.uploadTerrainBuffersToGpu` for the upload and the swap. Then [visibility and the frame graph](visibility-and-the-frame-graph.md) for who decided the section was visible in the first place, and [the client level](../client/the-client-level.md) for the world all of this is reading. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Models and atlases > Verified against **Minecraft 26.2** · Part XI · a resource pack changes one texture, and every stone block in the world redraws. You drop a pack into the folder, move it above the default, and the screen goes to the loading overlay for a second. The pack replaced exactly one file, a single stone texture, and when the overlay clears every block you can see has been thrown away and rebuilt — not just the stone. The models for stone were byte-identical before and after, and were still re-listed, re-parsed, re-resolved and re-baked, because the sprite they point at is now a different object at different coordinates in a different image. **Nothing here is incremental. The unit of change is the model layer.** Every block face, every item sprite and every particle texture starts as JSON and a PNG in a pack and ends as four vertices pointing at a rectangle of a stitched atlas. The stages below are that journey, and they happen inside a resource reload — [the resource system](../foundations/resource-system.md) owns `ReloadableResourceManager` and the barrier this rides on — and at no other time. ## The cast | class | what it decides | thread | |---|---|---| | `ModelManager` | the reload's spine, and what the finished lookup sets contain | workers, then the client thread at apply | | `AtlasManager` | which atlases exist, and it publishes their stitches for others to await | client thread for the handshake, workers for the work | | `SpriteLoader` | how one atlas is decoded, packed and mipmapped | workers | | `ModelDiscovery` | which file an `Identifier` means, and what its parent chain resolved to | one worker task | | `ModelBakery` | which unbaked model each `BlockState` and each item file bakes to | workers, in batches | | `FaceBakery` | a quad's UVs, its rotated cull face, and its chunk layer | workers | | `TextureManager` | who owns each `AbstractTexture`, and when an animation advances | client thread | | `ItemModelResolver` | which `ItemModel` a given `ItemStack` draws with | client thread, every frame | ## The shape of the work: eighteen fans, one barrier This is the clearest fan-out-and-barrier in the client. Eighteen independent pieces of work start on worker threads at once — thirteen atlas stitches and the five roots `ModelManager.reload` opens, three of them directory listings that are each themselves a fan of one task per file — and they converge exactly once. ```mermaid flowchart TD RL["a reload starts: F3+T, a pack change, or the game booting"] HS["AtlasManager.prepareSharedState on the client thread, before any task runs"] S1["blocks and items atlases: one task per sprite to decode and read metadata, then one stitch each, then mipmaps"] S2["the other eleven atlases, the same fan, nothing awaits them until upload"] L1["listing of models/, one task per file"] L2["listing of blockstates/, one task per file"] L3["listing of items/, one task per file"] L4["EntityModelSet.vanilla and BuiltInBlockModels.createBlockModels"] RES["ModelDiscovery interns and resolves, ModelGroupCollector groups the states"] BAKE["ModelBakery.bakeModels in batches: one bake per BlockState, one per item file"] BAR["the barrier"] UP["client thread: TextureAtlas.upload, then ModelManager.apply"] INV["LevelExtractor.allChanged raises a flag the next frame reads"] RL --> HS HS --> S1 & S2 & L1 & L2 & L3 & L4 L1 & L2 & L3 --> RES S1 & RES --> BAKE BAKE & S2 & L4 --> BAR BAR --> UP --> INV ``` Read it as **spread, converge, upload, invalidate**: above the barrier, worker threads in any order; below it, the client thread in exactly one. ## Thirteen atlases and three listings, all at once **In:** every enabled pack. **Out:** thirteen stitched images, three parsed maps. `AtlasManager` owns `AtlasManager.KNOWN_ATLASES` — thirteen of them, named in `AtlasIds` — and each is a definition file in *atlases/*, not a folder scan. `SpriteSourceList` runs that file's five kinds of source in order — `SingleFile`, `DirectoryLister`, `SourceFilter`, `Unstitcher` and `PalettedPermutations`, registered in `SpriteSources` — and a later source overwrites an earlier one by id, which is how a pack replaces a vanilla texture blind. `SpriteLoader.loadAndStitch` decodes and reads metadata one task per sprite, hands the results to `Stitcher` — which sorts by height and grows by powers of two — and returns a `SpriteLoader.Preparations`, after which `MipmapGenerator` builds the mip chain under whatever `MipmapStrategy` the texture's own metadata asks for. Two properties of that packing surprise people. The mip level is clamped to the smallest sprite's power of two, with a warning, so **one undersized texture degrades mipmapping for every sprite in the atlas** — and only the block atlas asks for mipmaps, the other twelve stitch flat. And sprite padding derives from the mip level *and* the anisotropic filtering setting, with the UVs computed inside the padded box, so anisotropy changes the layout and every UV in the game. The mipmap slider is blunter still: it writes the new level onto `AtlasManager` and schedules a texture reload, so every sprite in the game is decoded and stitched again. Meanwhile `BlockStateModelLoader` parses *blockstates/* into a `BlockStateModel.UnbakedRoot` per `BlockState`, through `BlockStateModelDispatcher` and `VariantSelector`, and `ClientItemInfoLoader` parses *items/* into `ClientItem`. ### The handshake that lets two listeners share a future Reload listeners are not supposed to reach into each other, and this pair has to: baking cannot resolve a texture slot without knowing where the sprite landed. `PreparableReloadListener.prepareSharedState` runs for *every* listener on the client thread before *any* reload task starts, and `AtlasManager` uses its turn to publish thirteen pending stitches under `AtlasManager.PENDING_STITCH`, of which `ModelManager` awaits the two it bakes against — the blocks atlas and the items atlas. The *ordering* was always guaranteed anyway — the reload chains each listener's barrier onto the previous one, and `AtlasManager` is registered before `ModelManager` — so what the handshake buys is not order but the end of the reaching-in. ## Interning the models, and dropping the ones that loop **In:** the raw *models/* map. **Out:** a `ResolvedModel` per `Identifier`. `ModelDiscovery` is a single worker task and the narrow waist of the pipeline. Every `Identifier` becomes one `ModelDiscovery.ModelWrapper`, caching its resolved texture slots and its baked geometry per `ModelState`, and `ModelDiscovery.resolve` returns the map of `ResolvedModel`, whose helpers walk the parent chain for geometry, slots, ambient occlusion and transforms. A model whose parents never reach a root is logged and excluded, and a model nothing references is parsed and never baked. In parallel, `ModelGroupCollector` gives each block state a visual-equality group — the fact that lets `ModelManager.requiresRender` later say *that change is invisible*. The JSON model itself is `CuboidModel`, made of `CuboidModelElement`, `CuboidFace` and `CuboidRotation`, with `UnbakedGeometry` and `UnbakedCuboidGeometry` between a resolved model and its quads, and `Material`, `SpriteId`, `TextureSlots` and `MaterialBaker` are the indirection from a model's *slot* reference to a real sprite. ## One bake per block state, and why that is affordable **In:** resolved models and the stitched sprites. **Out:** a `ModelBakery.BakingResult`. `ModelBakery.bakeModels` bakes once per *block state* and once per item model file, sharing one baker whose caches are concurrent maps, and the bakes are batched rather than scheduled individually, so this is tens of tasks and not tens of thousands. `ModelBakery.MissingModels` — a block part, a block, an item and a fluid — is baked first of all, so there is always something to substitute. Two further bakes follow: the `BlockModel` display layer, including the hard-coded `BuiltInBlockModels`, and the `FluidStateModelSet`. Dedup is what makes per-state baking cheap. Every state sharing an unbaked variant gets the *same* baked object, geometry is cached per `ModelState`, and vertex positions and material infos are interned. Multipart is the exception that proves it: each state gets its own thin `MultiPartModel` over a shared `MultiPartModel.SharedBakedState`, so every fence, wall, pane and redstone-wire state really is a distinct object. The output is a `QuadCollection` of `BakedQuad` — a ten-component record of four positions, four packed UVs, a `Direction` recomputed from the baked vertices, and a `BakedQuad.MaterialInfo` holding the sprite, the `ChunkSectionLayer`, the item `RenderType`, the tint index, shade and light emission. Three decisions land here and not later. Cull faces are rotated at bake time — a rotated variant's north-culled quad is filed under the rotated direction, in `UnbakedCuboidGeometry`, with `FaceBakery` doing the UV half of the rotation and the uvlock — so the mesher never thinks about it. Tint is *not* resolved: the quad carries an index, and the colour arrives later from `BlockColors` or an `ItemTintSource`. And `QuadCollection` carries translucent and animated flags, OR-ed up through every model wrapper, so the mesher and `ItemStackRenderState` can decide whether they need sorting or re-uploading without reading a quad. ### A quad's chunk layer is read out of the sprite's pixels The most surprising decision in the pipeline is the one nobody has to make. `FaceBakery.bakeQuad` does not take the render layer from the block or from any per-face declaration: it asks `SpriteContents` what transparency actually exists inside *that quad's UV rectangle*, and picks solid, cutout or translucent from the answer. **One pixel** — enough alpha inside a quad's UV rectangle to move that face out of the solid layer (`FaceBakery`, asking `SpriteContents`). A pack author who softens one edge of a texture has changed which chunk layer that face draws in, and so when it is sorted and how it blends with everything behind it. There is no warning, and only one way out: a `Material` may set `Material.forceTranslucent`, from a *force_translucent* key beside the sprite name, which skips the scan and takes the translucent layer whatever the pixels say. A hundred and ten of the shipped models use it — the stained glass, mostly — and it is the exception that shows the rule, because it exists for textures whose alpha the scan would read correctly and whose author wants them sorted anyway. ## A dozen ways to fail soft, and the two that crash Missing and malformed input is a warning and a substitution at **twelve** separate layers. An unparseable model file, an unparseable blockstate file and a single bad variant selector are each swallowed locally, and the broadest of the twelve is the last: a block state with no entry at all still gets the missing model rather than an exception. In between sit missing parents, cycles, bakes that throw, sprite ids that belong to no atlas, unbound and unresolvable slot chains, and a block model caught reaching outside the block atlas. The startup sweep that would catch the last case, `Minecraft.selfTest`, **only runs in a development environment**, and there it throws rather than warns. Two places have no soft path. If `Stitcher` cannot grow an atlas within the device's maximum texture size it raises `StitcherException`, which becomes a crash report listing every sprite, and mip generation is the second hard crash site. A pack with too many textures does not degrade — it crashes. ## The barrier, and how a sprite reaches the GPU **In:** everything above. **Out:** live lookup sets and live GPU textures. Only now does the client thread do anything. `TextureAtlas.upload` builds the new texture and closes the old sprites, and `ModelManager.apply` assigns the new lookup sets in one go. Underneath sits `TextureManager`, a `PreparableReloadListener` owning every `AbstractTexture` by `Identifier` — each atlas included — plus the `TickableTexture`s it advances, and it registers the checkerboard `MissingTextureAtlasSprite` at construction. The GPU side is [blaze3d](blaze3d.md)'s. How a sprite's pixels reach the atlas depends on whether it moves. A static sprite goes into a throwaway scratch texture, is blitted into every mip level by a render pass, and the scratch texture is closed. An animated sprite instead keeps one *persistent* texture per unique frame for the life of the atlas and is redrawn only when it has something new to show, through a second pipeline entirely if the animation interpolates — `SpriteContents.AnimationState` drives it and `TextureAtlas.cycleAnimationFrames` steps it. ## The flag that rebuilds the world **In:** a successful reload. **Out:** every visible section re-meshed, a frame later. Nothing rebuilds during the reload. `LevelExtractor.allChanged` raises a flag, and on the *next frame's* extract `LevelRenderer.invalidateCompiledGeometry` builds a new `SectionCompiler` from the new model sets and queues every section for re-meshing — see [section meshing](section-meshing.md). `SectionCompiler` is the biggest consumer of `BlockStateModelSet.get` but far from the only one: the block-breaking overlay, moving blocks, the in-wall screen effect, the nether-portal sprite on the loading screen, `TerrainParticle` and `BlockMarker` all read it too, with the HUD, `LevelExtractor` and `FeatureRenderDispatcher` covering the rest. The path from a set to triangles is `BlockStateModel.collectParts` then `BlockStateModelPart.getQuads`, over `SingleVariant`, `WeightedVariants` or `MultiPartModel` with `SimpleModelWrapper` as the usual concrete part. None of it crosses the network: the server has no idea what a model is. ## How an item picks its model An item never asks for a model by name. Every `ItemStack` carries `DataComponents.ITEM_MODEL` — an `Identifier`, nothing else — and that id is the whole decision: a stack whose component says *diamond sword* renders as a diamond sword whatever item it actually is. `ItemModelResolver` — held by `Minecraft`, called by the entity, block entity and GUI renderers as they extract a frame — reads the component, asks `ModelManager.getItemModel` for the baked model and `ModelManager.getItemProperties` for the flags that travel with it, then calls `ItemModel.update`, which appends one or more layers to an `ItemStackRenderState`. Both lookups fall back rather than fail: an unknown id quietly yields the missing item model and the default `ClientItem.Properties`, whose two visible flags are whether the item plays the hand-swap animation and whether it may overflow its slot in the GUI. What each id maps to came from *items/*, parsed into `ClientItem` by `ClientItemInfoLoader` and baked alongside the block models. `ItemModels` registers **eight** kinds of unbaked item model and only one of them draws anything by itself — the rest select, compose, dispatch on a range or delegate — while `SpecialModelRenderers` covers the thirteen shapes no cuboid model can express, from chests and banners to shields, heads, tridents and decorated pots ([block-entity rendering](block-entity-rendering.md) is where those thirteen get their geometry). `ItemModelGenerator` is the odd one out and by far the most-used model in the game: it extrudes a flat sprite into geometry by tracing its alpha channel, which is what *item/generated* means. One rule is enforced here and nowhere else. A block model may only use the block atlas, and a cuboid *item* model must draw every quad from a single atlas — items or blocks, not both. A model that mixes them throws, the exception is caught upstream, and the item falls back to the missing model. Where the stack itself comes from is [items and stacks](../items/items-and-stacks.md). ## Questions players ask **Why does lag slow the water down but the pause menu not stop it?** Because animations do not advance once per tick. `TextureManager.tick` sits outside the client's catch-up tick loop, so a laggy client advances every animation by one frame however many ticks it just owed. Pausing does not touch it — `DeltaTracker` keeps handing out ticks with the menu open — and the one thing that does stop the water is `/tick freeze`, because the guard on that call is `Minecraft.isLevelRunningNormally` and nothing else. **Why does an item frame sometimes look different from the block in it?** Because blocks have two model layers with different jobs. `BlockStateModel` is the quad source for the mesher and for particles, while `BlockModel` — a different interface in a different package, reached through `ModelManager.getBlockModelSet` — is the *display* model used by item frames, block entities and ten entity renderers. Tints and a transform belong to its usual implementation, `BlockStateModelWrapper`, not to the interface. **Can I look at the atlas?** Yes. A debug keybind writes every atlas to disk, each with a text listing of every sprite's position and size beside it; a shared-constant flag makes the game dump one at upload time as well. **Why do some block updates cost nothing to draw?** Because two states that look identical share a group id from `ModelGroupCollector`, and `ModelManager.requiresRender` returns false for a change between them, unless the fluid state differs. > **For a 1.21-era reader.** This subsystem was renamed and re-packaged > wholesale: > > | you will hunt for | it is now | > |---|---| > | *BakedModel* | split into `BlockStateModel` and `ItemModel` | > | *ModelResourceLocation* | gone — block models are keyed by `BlockState` directly | > | *BlockModelShaper* | `BlockStateModelSet` | > | *WeightedBakedModel* | `WeightedVariants` | > | *BlockElement*, *BlockElementFace* | `CuboidModelElement`, `CuboidFace` | > | *BlockModelDefinition* | `BlockStateModelDispatcher` | > | *AtlasSet* | `AtlasManager` | > | *ItemModelShaper*, *BlockRenderDispatcher*, *ItemRenderer*, *ItemColors*, *SpriteTicker* | gone with no successor of that name | > > Two traps. `BlockModel` still exists and now means something entirely > different — the display model above, not the JSON one. And > `TextureAtlas.LOCATION_BLOCKS` and its siblings are deprecated but far > from dead: they are still the *texture* ids the atlas-membership checks > compare against, while `AtlasIds` names the *definition* files. ## Where to look `ModelManager.reload` for the shape of the pipeline, then `AtlasManager.prepareSharedState` for the handshake that makes it parallel. `ModelDiscovery` for resolution, `ModelBakery.bakeModels` for baking, `SpriteLoader.loadAndStitch` and `Stitcher` for the atlas, `FaceBakery.bakeQuad` for where a face's layer is decided, and `ItemModelResolver` and `BlockStateModelSet.get` for the two ways out. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Entity rendering > Verified against **Minecraft 26.2** · Part XI · a zombie is drawn: extract, submit, prepare, execute. A zombie shuffles out of the dark towards you, you hit it, and for a moment it flashes red. Between that zombie and those pixels stand four stages, each handing the next a value object rather than a shared one: the live mob is read into a fresh render state, the state is described as things that *ought* to be drawn, the descriptions are sorted and batched, and only then does anything write a vertex. Which is why `EntityRenderer` has no *render* method — nothing on this page draws anything — and why the zombie is posed **at least twice** in the frame you are looking at, three times if it is glowing, an arrangement that is only sound because `Model.setupAnim` resets every part to its baked pose before it starts. All four stages are one thread, inside one `Minecraft.renderFrame`. The split is a discipline, not a threading boundary: the first stage touches live entities, the other three only snapshots. Nothing here reads the network — everything drawn is a redrawing of what the server already told the client ([what the client is told](../networking/what-the-client-is-told.md), [synched entity data](../entities/synched-entity-data.md)). ## The cast | class | what it decides | thread | |---|---|---| | `LevelExtractor` | which entities are visible at all, and when their states are built | Render thread | | `EntityRenderDispatcher` | which `EntityRenderer` an entity gets, and the hand's own lighting | Render thread | | `EntityRenderer` | what goes into the render state, and what gets submitted — it never draws | Render thread | | `EntityRenderState` | one entity's whole frame, copied by value, holding no `Entity` and no `Level` | a value object | | `RenderLayer` | the extras — armour, held items, eyes, capes — each submitting at its own order | Render thread | | `SubmitNodeStorage` | the submit nodes, bucketed by a global draw order | Render thread | | `FeatureRenderDispatcher` | the grouping, the batching, and where the vertices are finally written | Render thread | | `ModelFeatureRenderer` | the one feature renderer that walks a `ModelPart` tree | Render thread | ## Four stages, and what each hands the next ```mermaid flowchart TD E["Extract: LevelExtractor walks the visible entities and fills one fresh render state per entity"] S["Submit: LevelRenderer walks the states, and each renderer describes what should be drawn"] P["Prepare: FeatureRenderDispatcher sorts every submit, groups it, and builds the vertices"] X["Execute: the frame graph's passes issue the draws the prepare already built"] E -- "value objects, no Entity, no Level" --> S S -- "submit nodes in SubmitNodeStorage, bucketed by order" --> P P -- "one PreparedFrame of batched draws" --> X ``` The worked instance is one zombie going through all four. ```mermaid sequenceDiagram participant LX as LevelExtractor participant ERD as EntityRenderDispatcher participant ZR as ZombieRenderer participant ZS as ZombieRenderState participant LR as LevelRenderer participant SNS as SubmitNodeStorage participant FRD as FeatureRenderDispatcher participant ZM as ZombieModel LX->>LX: isEntityVisible — frustum via ERD, then: is its section compiled and visible? LX->>ERD: extractEntity(zombie, its own partial tick) ERD->>ZR: createRenderState — a fresh object, every entity, every frame ZR->>ZS: extractRenderState down the whole chain Note over ZS: position lerp, walk animation, equipment,
hasRedOverlay from hurtTime or deathTime,
lightCoords from getPackedLightCoords ZR->>ZS: finalizeRenderState — sample the blocks under it for shadow pieces LR->>ERD: submit(state, camera, relative position, PoseStack, collector) ERD->>ZR: LivingEntityRenderer.submit ZR->>SNS: submitModel — the PoseStack pose is COPIED, not held ZR->>ZM: setupAnim — so the layers can read posed parts ZR->>SNS: each RenderLayer submits at its own order ZR->>SNS: submitLeash and submitNameTag, from the renderer ERD->>SNS: submitFlame if burning, submitShadow if it has pieces LR->>FRD: prepareFrame — group by feature type, batch by RenderType FRD->>ZM: setupAnim again, then walk ModelPart and write vertices FRD->>FRD: executeSolid, then executeTranslucent, then executeOutline ``` ## Extract: the live entity becomes a snapshot **In:** `ClientLevel.entitiesForRendering`, and a frustum. **Out:** one freshly allocated render state per surviving entity. **Decided:** visibility, and everything the other three stages will ever know. `LevelExtractor.extractVisibleEntities` walks the level's entity list, drops what `LevelExtractor.isEntityVisible` rejects, and hands each survivor to `EntityRenderDispatcher.extractEntity`. That call finds the renderer for the entity's `EntityType` through `EntityRenderDispatcher.getRenderer`, allocates a state with `EntityRenderer.createRenderState`, fills it by running `EntityRenderer.extractRenderState` down the whole inheritance chain, and then lets `EntityRenderer.finalizeRenderState` reach into the world one last time to sample the shadow. Visibility is **two tests in two places**. The frustum test is `EntityRenderer.shouldRender`, on the renderer; the "is the section this entity stands in actually compiled and visible" test belongs to `LevelExtractor`, and block entities must additionally clear a separate section threshold to count at all. Several things escape the frustum entirely: anything indirectly carrying the local player, the three renderers that declare themselves unculled, a `Display` that sets its own no-culling flag, and — the ones nobody expects — an entity on the other end of a visible leash, an end crystal with a beam target and a guardian firing one, each of which is drawn because something *else* in view is attached to it. Light is not read at draw time. It comes from `EntityRenderer.getPackedLightCoords` during extract — the dispatcher has a method of the same name, but its one caller is the first-person hand — and that method returns full brightness for a burning entity. Shadows are sampled here too, and never past sixteen blocks: the renderer walks the blocks under the entity, computes an alpha for each, and stores the shapes and alphas in the state, so that the feature renderer three stages later only has to turn them into quads. The strength falls to nothing at sixteen blocks, so a distant mob has no shadow at any settings, and an invisible entity skips the sampling entirely. ### The ladder of render states
*(figure: tree-EntityRenderState.svg — a generated SVG, not reproduced here)*
Every render state in the game, by depth. Click to enlarge.
The tree is a ladder, each rung adding what the rung below could not assume. `EntityRenderState` is the floor: position, `EntityRenderState.ageInTicks`, `EntityRenderState.lightCoords`, `EntityRenderState.outlineColor`, `EntityRenderState.nameTag`, `EntityRenderState.leashStates` and `EntityRenderState.shadowPieces` — as true of a dropped item as of a wither. `LivingEntityRenderState` adds rotations, `LivingEntityRenderState.walkAnimationPos`, `LivingEntityRenderState.deathTime`, `LivingEntityRenderState.isBaby` and `LivingEntityRenderState.hasRedOverlay`. `ArmedEntityRenderState` adds hands, `HumanoidRenderState` a pose and equipment, `UndeadRenderState` what the undead share, `ZombieRenderState` two flags of its own. The player sits off the ladder in `AvatarRenderState`. Nothing in any of them holds an `Entity` or a `Level` — verified across every class in the tree. The one member that looks live, an `AnimationState` on the eleven states that carry one, is a single-int tick counter copied by value, not a handle back into the world. ### The red flash is not a colour It starts as `LivingEntity.hurtTime` — or `LivingEntity.deathTime` — and becomes the boolean `LivingEntityRenderState.hasRedOverlay` here, at extract. At submit, `LivingEntityRenderer.getOverlayCoords` packs that boolean into an `OverlayTexture` coordinate alongside a separate white-flash axis, the one the creeper's fuse uses — and, besides the creeper, only a primed TNT minecart and the sulfur cube's inner layer. The wither is not on that list: it flashes by swapping to a second texture. That packed integer rides through the submit node untouched and lands, at execute, as a **per-vertex attribute**. Nothing along the way is ever tinted red. ## Submit: describing a draw without making one **In:** the render states, the camera, and a shared `PoseStack`. **Out:** submit nodes in `SubmitNodeStorage`, bucketed by order. **Decided:** what should be drawn, and where in the world's draw order. `LevelRenderer.submitEntities` walks the states and calls `EntityRenderDispatcher.submit` for each, after `EntityRenderDispatcher.prepare` has set the camera for the frame. `LivingEntityRenderer.submit` then describes the body, poses the model, and lets every `RenderLayer` describe its own extra through `RenderLayer.submit` — in that order, because the pose is only needed by the layers. The description API is `SubmitNodeCollector` and its ordered form: `OrderedSubmitNodeCollector.submitModel`, `.submitItem`, `.submitText`, `.submitNameTag`, `.submitShadow`, `.submitFlame`, `.submitLeash`, `.submitBlockModel` and `.submitCustomGeometry`, plus `SubmitNodeCollector.order` to choose a bucket. `SubmitNodeStorage` keeps one `SubmitNodeCollection` per order, and each collection files what it is given into one of fifteen named phases — `SubmitNodeCollection.solid`, `.translucentModels`, `.breakingOverlay`, `.outline` and eleven more, all catalogued in [submit phases and feature renderers](../../reference/submit-phases.md). **`SubmitNodeCollector.order` is a global key, not a per-entity one.** Order one means *after every entity's order-zero body in the whole world*, not "after this entity's body". That is how the eyes layer, the armour layers and the enchantment glint stack correctly across a crowd instead of interleaving one mob's helmet with another's head. Armour claims consecutive orders as it goes, so a dyed, enchanted, trimmed helmet occupies four — leather is the only dyeable helmet and its equipment definition has two layers of its own — and exactly one layer in the game asks for a **negative** order, to get underneath everything. The pose stack is transient, and half of it is dropped. A submit *copies* the current pose; nothing downstream ever sees the stack. Models, items and block models copy the full pose, while shadows, name tags, text and leashes copy only the 4×4, so no normal matrix crosses for those. A leaked push is fatal, but the check happens at the end of the *submit phase*, on a local stack, not at the end of the frame. Avatars differ in one small way: the crouch offset is removed *before* the shadow is submitted for a player and *after* it for everything else, which stops a sneaking player's shadow sinking into the ground. ### The renderers and models being described Renderers and models are **shared and mutable**; render states are not. One `ZombieRenderer` serves every zombie in the world — though it holds an adult model, a baby model and two baked armour sets — and safety comes entirely from the fresh per-entity state and from replaying the animation at draw time. The chain is `EntityRenderer` → `LivingEntityRenderer` → `MobRenderer` → `AgeableMobRenderer` → `HumanoidMobRenderer` → `AbstractZombieRenderer` → `ZombieRenderer`. `AgeableMobRenderer` is deprecated and is not the base of every humanoid: the enderman and the giant extend `MobRenderer` directly, the armour stand and the avatar extend `LivingEntityRenderer`, all four while using humanoid models. Players are served by `AvatarRenderer`, keyed by **skin model rather than entity type** — the type map has no entry for the player or the mannequin, and the dispatcher keeps two avatar maps, wide and slim, falling back to wide. Geometry is `Model` → `EntityModel` → `HumanoidModel`, built out of `ModelPart`s. A model is baked once from a `LayerDefinition` / `MeshDefinition` / `PartDefinition` tree named by a `ModelLayerLocation` in `ModelLayers` and held in an `EntityModelSet`; `LayerDefinitions` is the single static table that builds every one of them, out of the `CubeListBuilder` / `CubeDefinition` / `CubeDeformation` / `PartPose` vocabulary. Posing is `Model.setupAnim`, hand-written or driven by an `AnimationDefinition` of `Keyframe`s through `KeyframeAnimation`, whose channels interpolate linearly or along a Catmull–Rom spline. Extras hang off `RenderLayer` — `HumanoidArmorLayer`, `ItemInHandLayer`, `CustomHeadLayer`, `WingsLayer`, `EyesLayer`, `CapeLayer` and forty-odd more. Armour and trims funnel through `EquipmentLayerRenderer`, dressed by `EquipmentAssetManager` and `EquipmentClientInfo` out of the resource packs, and a renderer holds a whole `ArmorModelSet` per body size rather than one armour model. Player skins come by another road — `SkinManager`, `SkinTextureDownloader`, `PlayerSkinRenderCache`, with `DefaultPlayerSkin` as the fallback — and held or worn items through `ItemModelResolver` and `ItemStackRenderState`, [models and atlases](models-and-atlases.md)'s business. ## Prepare: sorting, batching, and the vertices **In:** a whole frame of submit nodes. **Out:** a `FeatureRenderDispatcher.PreparedFrame` whose vertices already exist. **Decided:** how few draws this can be. `FeatureRenderDispatcher.prepareFrame` drains every phase of every order bucket, groups the nodes, and lets the thirteen feature renderers build geometry — `ModelFeatureRenderer` being where a `ModelPart` tree finally becomes vertices. **Only two of the thirteen kinds of submit can be batched at all** — a model and a piece of custom geometry — and everything else keeps the order it was submitted in and merges only with its immediate neighbour. Where the zombies of the world do collapse into one draw, that is the group finding they all want the same `RenderType`. A translucent phase marks its group strictly ordered, and that switches off exactly one of the group's two merges: consecutive submits of one render type still share a draw, and what stops is the fold into a *non-adjacent* earlier draw — which is the merge that would move geometry ahead of everything between, and so undo the depth sort the phase exists for. ### Why the zombie is animated more than once Once during **submit** — but only because it has layers, and `ItemInHandLayer`, `CustomHeadLayer` and half a dozen others need posed `ModelPart`s to hang things off. Once again, per model submission, at **prepare** time, because the submit node carried the model and the state but not a pose for every part. A glowing zombie is animated three times, since the outline is a second submission of the same model into `SubmitNodeCollection.outline`. A fourth pass exists in the machinery — the crumbling overlay — but no entity ever reaches it: the only non-null `ModelFeatureRenderer.CrumblingOverlay` in the game is built in `LevelExtractor`'s *block-entity* loop, so it is a chest being mined that gets a fourth pose, never a mob. That the repeats are sound at all is only because `Model.setupAnim` **resets every part to its baked pose first**, so each call is idempotent from a known base — not because the model is stateless, which it emphatically is not. ## Execute: the frame graph pulls the trigger **In:** the prepared frame. **Out:** draws. **Decided:** almost nothing — the ordering was fixed two stages ago. `FeatureRenderDispatcher.PreparedFrame` exposes five drains. `.executeSolid`, `.executeTranslucent`, `.executeOutline` and `.executeTranslucentAfterTerrain` are called from the frame graph's main pass, while `.executeAlwaysOnTop` runs in a later pass of its own that clears depth first. Where those passes sit relative to terrain, sky and post-processing is [visibility and the frame graph](visibility-and-the-frame-graph.md)'s subject; the draws go out through [blaze3d](blaze3d.md). ## Three things shaped like this pipeline, that are not it **Block entities** take the same four stages under a different visibility policy, a different partial tick and an empty block model — [block-entity rendering](block-entity-rendering.md) is the whole of the difference. **The first-person hand** is a second pipeline entirely: `ItemInHandRenderer` submits into its own `SubmitNodeStorage` and is drawn by `FeatureRenderDispatcher.renderAllFeatures`, outside the frame graph — see [the frame](the-frame.md). **Hitboxes** left the renderer. F3+B is a debug-entry toggle read by a separate debug renderer that emits gizmo primitives, suppressed under reduced debug info, and the old hitbox render state record is dead code with exactly two references, both inside its own file. Name tags borrow in the same way: the submit node carries only a `Component`, and every glyph in it is resolved by [text and fonts](../client/text-and-fonts.md), not here. > **For a 1.21-era reader.** `EntityRenderer` has no *render* method, and > neither does anything else on this page: the pair is > `EntityRenderer.extractRenderState`, which reads the live entity, and > `EntityRenderer.submit`, which describes what should be drawn without > touching a vertex. *MultiBufferSource* does not exist anywhere in the game, > nor any other buffer source. The rest of the names to stop hunting for: > *PlayerRenderer* (now `AvatarRenderer`, for players and mannequins alike), > every *render* method on `EntityRenderer` and `RenderLayer` (now *submit*), > *EntityRenderDispatcher.renderHitbox* and *renderLeash*, > *LivingEntityRenderer.getBob*, *MobRenderer.prepareMobModel*, > *RenderType.entityCutoutNoCull* (the polarity flipped — the culled variant > is now the one that says so), *ItemBlockRenderTypes*, and *ElytraLayer* > (now `WingsLayer`). `RenderLayerParent` survives in name only: it is now a > single-method interface, and the texture lookup that used to live on it is > gone. `PoseStack` and `ModelPart` are unchanged. ## Where to look `EntityRenderDispatcher.extractEntity` and `EntityRenderer.extractRenderState` for the first stage, `LivingEntityRenderer.submit` for the second — the clearest single method in the part. Then `SubmitNodeCollection` for the phase list and `ModelFeatureRenderer` for where vertices are written. `ModelLayers` and `LayerDefinitions` for how a model is described, and [submit phases and feature renderers](../../reference/submit-phases.md) for both catalogues. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Block-entity rendering > Verified against **Minecraft 26.2** · Part XI · a chest on the ground and a chest in your hand, drawn in the same frame by two renderers that share a model and nothing else. You place a chest, step back, and hold a second one up in front of your face. Both are chests, both are lit, both open the same lid on the same hinge — and almost nothing about how they got onto the screen is shared. The one on the ground is a *block entity*: the terrain mesh at its position contains no geometry at all, and everything you can see of it was extracted from the live world by `ChestRenderer` a few microseconds ago. The one in your hand is an *item*: it has no block entity, no render state and no extract stage, and it is drawn by a class in a different package that exists only because an item cannot be a block entity. The seam shows if you type */tick freeze*. The chest on the ground is nailed to its last tick; the chest in your hand keeps swaying with your view bob, because the two are drawn at **different partial ticks** and only one of them respects the freeze. This page is the sibling of [entity rendering](entity-rendering.md), and does not re-teach it. Extract, submit, prepare, execute; render states that hold no live object; `SubmitNodeCollector` and the fifteen phases behind it — all of that is that page's, and all of it is true here. What follows is only the differences, and they are larger than the shared machinery suggests. ## The cast | class | what it decides | thread | |---|---|---| | `LevelExtractor` | which block entities are candidates at all — the two lists it walks | Render thread | | `BlockEntityRenderDispatcher` | the renderer for a type, and the two gates every extraction passes | Render thread | | `BlockEntityRenderer` | the geometry, and its own answers to *how far* and *off screen* | Render thread | | `BlockEntityRenderState` | one block entity's whole frame: position, block state, type, light, break progress | a value object | | `ClientLevel` | membership of the globally-rendered set, decided once when the block entity is added | Render thread | | `SpecialModelRenderer` | the thirteen shapes an item model cannot express, drawn without a block entity | Render thread | | `SpecialModelWrapper` | how an item model reaches one — the item road into `renderer/special` | Render thread | | `BuiltInBlockModels` | how a *block state* reaches one, in a model table terrain never reads | a worker thread, on every resource reload | ## Three roads to the same collector ```mermaid flowchart TD BE["A chest placed in the world"] IT["A chest in your hand, on a shelf, or on the ground as an item"] BD["A chest carried by something that is not a chest — a block display, a minecart, an enderman"] SEC["LevelExtractor walks the visible sections, then the globally-rendered set"] BERD["BlockEntityRenderDispatcher — one shared ChestRenderer, one fresh ChestRenderState"] IMR["ItemModelResolver reads the item-model component and opens a layer"] SMW["SpecialModelWrapper puts a ChestSpecialRenderer in that layer"] BMR["BlockModelResolver reads the built-in block-model table"] SBM["SpecialBlockModelWrapper puts a ChestSpecialRenderer in that state"] COL["SubmitNodeCollector — the same phases, the same feature renderers, the same vertices"] BE --> SEC SEC --> BERD BERD --> COL IT --> IMR IMR --> SMW SMW --> COL BD --> BMR BMR --> SBM SBM --> COL ``` Two of the three roads end in `renderer/special`, and that is the package's whole reason to exist: a chest that is not a block entity still has to look like a chest. Only the left-hand road has a visibility policy of its own, a state class of its own, or an extract stage that reads the live world. | | entity | block entity | special model | |---|---|---|---| | what is walked | the level's renderable entities | the visible sections' meshes, then a global set | nothing — it is reached from a model | | the visibility test | a frustum, plus a size-scaled distance | the *section* is visible, then a per-renderer radius | whatever drew the thing holding it | | the stages | extract, finalize, submit | extract, submit | resolve and submit, in one call | | the state | an `EntityRenderState` subclass | a `BlockEntityRenderState` subclass | a layer of an `ItemStackRenderState` | | the partial tick | one computed per entity | one for every block entity in the world | the camera entity's | | where the pose comes from | the dispatcher, from the state's position | `LevelRenderer`, translated to the block | the item transform for the display context | | how many | one renderer per entity type | 24 renderer classes, 26 of the 49 types | 13 renderers under 13 ids | ## The chest, both halves, one frame ```mermaid sequenceDiagram participant LX as LevelExtractor participant BERD as BlockEntityRenderDispatcher participant ChestR as ChestRenderer participant LR as LevelRenderer participant IIHR as ItemInHandRenderer participant IMR as ItemModelResolver participant CSR as ChestSpecialRenderer Note over LX,CSR: the extract half — one partial tick for every block entity in the world LX->>LX: walk visibleSections, skip a section under 0.3 of its fade LX->>BERD: tryExtractRenderState with the not-global flag BERD->>BERD: the flag must equal shouldRenderOffScreen, then shouldRender within 64 blocks BERD->>ChestR: createRenderState, then extractRenderState ChestR->>ChestR: combine with the neighbour half — lid openness and the brighter of two lights Note over LX,CSR: the draw half — the world first, the hand afterwards, in two storages LR->>BERD: submit, with the pose already translated to the block BERD->>ChestR: submit — one model, one sprite, no world access IIHR->>IMR: resolve the held stack at the camera entity partial tick IMR->>CSR: the item model has no quads, only a special renderer CSR->>CSR: submit the same ChestModel with its openness fixed ``` The two halves of the figure are not two stages of one pipeline. The world's block entities go through `LevelRenderer.submitFeatures` into the frame graph. The held chest is submitted, prepared and drawn in `GameRenderer.renderItemInHand`, into `GameRenderer.handAndScreenSubmitNodeStorage` — a second storage, drained by `FeatureRenderDispatcher.renderAllFeatures` after the whole world is already on the screen. They never share a submit node. ## The chest's block model is empty, and there are two tables of them Open *blockstates/chest.json* and it names one model for every state. Open that model and it declares a particle texture and no elements. So when `SectionCompiler` walks the section and tesselates every block whose render shape is *MODEL*, the chest contributes exactly zero quads to the terrain mesh — and, in the same pass, adds itself to the section's list of block entities. Everything you see of a placed chest is drawn one stage later, by a renderer, from a snapshot. That is what a block entity renderer is *for*: the shapes that a cuboid model cannot express, and the state that a block state cannot hold. Only 26 of the 49 entries in `BlockEntityTypes` have a renderer registered in `BlockEntityRenderers`, and the other 23 — furnaces, hoppers, barrels, beehives — are drawn entirely by their block models, like any other block. Having a block entity does not make a block interesting to look at. There are **two** baked block-model tables, and confusing them is easy. `ModelManager.getBlockStateModelSet` is the one built from the resource packs, and it is the one `SectionCompiler` reads: a chest is empty in it. `ModelManager.getBlockModelSet` is built on top of that one and merged with `BuiltInBlockModels.createBlockModels`, which attaches a `SpecialBlockModelWrapper` to every state of every chest, banner, skull, shulker box, conduit, decorated pot, bell, enchanting table and end portal in the game. Nothing in terrain ever reads that table — that is the whole of the separation, and it is membership rather than behaviour. `BlockModelResolver` is its one reader, and its callers are all entity renderers: item frames, block displays, minecart contents, the block an enderman is carrying. When one of them draws a chest it gets the quads **and** the special renderer, both, because that road draws whatever it finds; terrain simply never asks this table, and reads `BlockStateModelSet` instead, where the chest's entry is empty. ## Culling by section, not by frustum A block entity is never frustum-tested. `LevelExtractor.extractVisibleBlockEntities` starts from `LevelRenderer.visibleSections` — the reachability walk that [visibility and the frame graph](visibility-and-the-frame-graph.md) describes — and takes each section's compiled list of block entities whole. Culling has already happened, one section at a time. Two extra gates then apply, and both are stricter than they look. The first is the section's own fade-in: a freshly uploaded section reports a visibility ramping from zero to one over a duration the *chunk section fade-in time* option sets, and `LevelExtractor` skips its block entities until that number reaches **0.3**. Terrain fades in from the first frame, and the chests inside it appear about a third of the way through — furniture arriving after the room. `LevelRenderer.compileSections` zeroes the duration for a section within about twenty-eight blocks of the camera or one that was empty before, so the gate only bites on distant terrain, which is exactly where you would blame the draw distance for it. The second is a distance test with the same name as the entity one and only half of its behaviour. `EntityRenderer.shouldRender` does a size-scaled distance test *and* a frustum intersection. `BlockEntityRenderer.shouldRender` keeps the distance half alone: a camera position, and whether the block's centre is within `BlockEntityRenderer.getViewDistance`. **Sixty-four blocks** — the default, taken by nineteen of the twenty-four renderer classes, and it does not scale with your render distance the way `Entity.shouldRender` does. | renderer | how far | why | |---|---|---| | the other nineteen | 64 | the interface default | | `PistonHeadRenderer` | 68 | a moving block starts outside the block it is drawn from | | `BlockEntityWithBoundingBoxRenderer` | 96 | the structure block's outline is a build tool | | `TheEndGatewayRenderer` | 256 | the beam is the thing you are looking for | | `BeaconRenderer` | the render distance in blocks | and measured **horizontally only** | | `TestInstanceRenderer` | the larger of its two delegates | it wraps a beacon and a bounding box | The beacon is the interesting row twice over. Its `BlockEntityRenderer.shouldRender` flattens both positions onto the horizontal plane before comparing, so altitude never costs you the beam — you can be at build height above a beacon at bedrock and still see it. And its extraction scales the beam's radius by the horizontal distance divided by 96, floored at one, so **the beam gets visibly wider the further away you stand**, which is why a distant beacon does not thin into nothing. Raising a spyglass resets the scale to one, because `BeaconRenderer` checks whether the local player is scoping. The topmost beam segment is drawn to `BeaconRenderer.MAX_RENDER_Y`, 2048 blocks above the block. ### Off screen means off *this* list `BlockEntityRenderer.shouldRenderOffScreen` is not an extra permission — it is a switch between two mutually exclusive lists, and it is enforced twice. `ClientLevel.onBlockEntityAdded` puts a block entity into `ClientLevel.getGloballyRenderedBlockEntities` only if its renderer says yes, and `BlockEntityRenderDispatcher.tryExtractRenderState` throws the extraction away unless the flag it was called with **equals** the renderer's answer. The second check is what stops double-drawing: a beacon inside a visible section is in that section's list *and* in the global set, and the equality test is the only thing that picks one. Exactly three renderers say yes — `BeaconRenderer`, `BlockEntityWithBoundingBoxRenderer` and `TestInstanceRenderer`, which says yes because either of its two delegates does. ## What a block entity's snapshot carries `blockentity/state` holds 26 classes: the base and twenty-five subclasses. The base is five fields wide — the position, the block state, the type, packed light sampled from the level, and the crumbling overlay — and it is filled by one static method, `BlockEntityRenderState.extractBase`, that every renderer calls before adding its own. There is no `EntityRenderer.finalizeRenderState` counterpart here: `BlockEntityRenderer` declares one extraction method, not two, so nothing reaches back into the world after the snapshot is taken. Twenty-five subclasses, twenty-six classes: `BedRenderState` is reachable from nothing in the game. There is no bed block entity in `BlockEntityTypes` and no bed renderer, and a corpus-wide search for the name finds only its own file. It is the only orphan in the package. Five states carry another pipeline's snapshot inside them, which is where the machines actually touch — and **two** of the five carry an *entity* state, not an item one. `SpawnerRenderState.displayEntity` is a whole `EntityRenderState`, extracted through `EntityRenderDispatcher` from a display entity the spawner creates client-side — a mob that `LevelExtractor` never sees, never frustum-tests, and whose light the spawner overwrites with the block's. `VaultRenderState.displayItem` reads like the item cases and is not one: it is an `ItemClusterRenderState`, which extends `EntityRenderState`, and the vault submits it through `ItemEntityRenderer` — the same renderer that draws a dropped item lying on the ground. The three genuine item carriers are `ShelfRenderState.items`, an array of three `ItemStackRenderState` that reaches `ChestSpecialRenderer` from inside a block-entity render state, and `CampfireRenderState.items` and `BrushableBlockRenderState.itemState` for what is cooking and what is buried. One live handle survives into a snapshot, and it is not unique to this side: `MovingBlockRenderState` is a one-block fake world that holds the level's `MovingBlockRenderState.lightEngine` and `MovingBlockRenderState.cardinalLighting` by reference, so a moving block is lit at *prepare* time rather than at extract. `PistonHeadRenderState` carries up to two of them, and the entity side's `FallingBlockRenderState` carries one. Signs are the other partial exception. `SignRenderState` stores the two `SignText` objects rather than laid-out glyphs, and `AbstractSignRenderer` calls `Font.split` during **submit** — the line wrapping of a sign happens a stage later than everything else in the frame. ## One partial tick for the whole world This is the difference a player can see. `LevelExtractor` gives every entity its own partial tick, asking `TickRateManager.isEntityFrozen` per entity, so a mob exempt from a freeze keeps interpolating while its neighbours stop. Block entities get no such question: they all receive the single `DeltaTracker.getGameTimeDeltaPartialTick` value that `GameRenderer.extract` computed for the world, with the frozen-game flag honoured — which returns exactly 1.0 while the game is frozen. Every block entity in the world is therefore pinned to its last completed tick, with no per-block exemption anywhere in the path. The held chest is a third answer again. `GameRenderer` draws it with `Camera.getCameraEntityPartialTicks`, which asks the frozen check about the *camera entity* and, when it is not frozen, ignores the frozen game entirely — and `TickRateManager.isEntityFrozen` never freezes a `Player`. So under */tick freeze* the item in your hand is redrawn from a live partial tick while every chest lid in the world is stopped dead. The same split shows up in the Christmas textures, which the game implements three times. `ChestRenderer` reads `SpecialDates.isExtendedChristmas` **once, in its constructor**, and its constructor runs only when `BlockEntityRenderDispatcher.onResourceManagerReload` rebuilds every renderer — so a placed chest that was ordinary at 23:59 on the 23rd stays ordinary until the next resource reload. The item model in *items/chest.json* selects on the *minecraft:local_time* property, whose `LocalTime` implementation re-checks the clock at most once a second. And the built-in block model wraps its two chests in a `ConditionalBlockModel` whose `IsXmas` property calls the same static method live, every time the model is resolved. Two of the three notice midnight almost at once. The one you are standing in front of does not. ## Where `renderer/special` borrows its geometry `SpecialModelRenderers.bootstrap` registers thirteen renderers under thirteen ids, dispatched by a codec, and the package holds exactly thirteen renderer classes to match. Nine of them implement `NoDataSpecialModelRenderer` and read nothing at all from the stack; the other four — banner, decorated pot, player head and shield — pull one component out of it through `SpecialModelRenderer.extractArgument`, which is the closest thing this road has to an extract stage. Eleven of the thirteen reach into `client/renderer/blockentity` for their geometry. Three hold an instance of the block-entity renderer outright (`BannerSpecialRenderer`, `DecoratedPotSpecialRenderer`, `ShulkerBoxSpecialRenderer`); the rest call a static submit or name a static texture or model layer on one — `SkullBlockRenderer.submitSkull`, `AbstractEndPortalRenderer.submitSpecial`, `BannerRenderer.submitPatterns`, `ChestRenderer.LAYERS`. Only `TridentSpecialRenderer` and `CopperGolemStatueSpecialRenderer` stand alone. The chest in your hand really is the same `ChestModel`, baked from the same `ModelLayerLocation`, posed at a fixed openness instead of an interpolated one. ### How an empty item model turns into a chest Reaching a special renderer from an item is one indirection: `SpecialModelWrapper` is an `ItemModel` like any other, and its baked form puts the renderer into a layer of the `ItemStackRenderState` through `ItemStackRenderState.LayerRenderState.setupSpecialModel`. A layer either has quads or has a special renderer, never both, and the layer's submit picks whichever it has. That is the entire mechanism by which an empty item model turns into a chest. > **For a 1.21-era reader.** `BlockEntityRenderer` has no *render* method > either: the pair is `BlockEntityRenderer.extractRenderState` and > `BlockEntityRenderer.submit`, and `blockentity/state` is a package that did > not exist. Three names to stop hunting for: *getRenderBoundingBox*, absent > from the corpus entirely, because visibility is the section's business now > plus the radius in `BlockEntityRenderer.getViewDistance`, *BedRenderer*, and > every *renderItem* on a block-entity class — an item held by a block entity > now goes through `ItemModelResolver` into an `ItemStackRenderState` like any > other. `BlockEntityRenderer.shouldRenderOffScreen` survives with its meaning > narrowed to *which of two lists*, `BlockEntityRenderers` still registers by > `BlockEntityType`, and the fifteen submit phases these renderers land in are > shared with entities. ## Where to look `BlockEntityRenderDispatcher.tryExtractRenderState` first — it is twenty-one lines and it contains both visibility gates. Then `LevelExtractor.extractVisibleBlockEntities` for the two lists it is called from, and `ChestRenderer` as the clearest renderer in the package, since it is the one with a counterpart in `renderer/special` to compare against. For the other road, `SpecialModelWrapper` and `ItemStackRenderState.LayerRenderState`, then `BuiltInBlockModels` for the block-state road nobody expects to exist. [Submit phases and feature renderers](../../reference/submit-phases.md) is the catalogue everything here submits into. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Lightmap, fog and sky > Verified against **Minecraft 26.2** · Part XI · the sun goes down: every colour on screen, traced back to one keyframe curve. Stand on a hill and watch the light go. The sky over the taiga slides from blue towards black, the murk closes in until the far trees dissolve, stars come up, the moon takes whatever shape it is owed tonight, and if a storm arrives the scene goes grey and streaked. Five renderers make those colours — `Lightmap` decides how bright, `FogRenderer` how far, `SkyRenderer` and `CloudRenderer` what is up there, `WeatherEffectRenderer` what is coming down — and between them they ask one question and nothing else: *what is this attribute worth, here, now?* The surprise is who they ask. **Most of them no longer know what time it is.** They ask a probe for a named value at a position and a partial tick, and the day/night curve behind it is keyframes in a data pack. Two still read the raw world clock — the clouds, because they drift, and the rain, whose texture scrolls — but the weather asks nobody for a colour: it seeds each column of rain from that column's own *coordinates*, and touches no attribute and no probe at all. ## The cast | class | what it decides | thread | |---|---|---| | `EnvironmentAttributeProbe` | what any attribute is worth at the camera, this frame | Render thread | | `LightmapRenderStateExtractor` | the lightmap's ten uniforms, and whether to redraw at all | Render thread | | `Lightmap` | how bright, as the 16×16 texture every terrain vertex samples | Render thread | | `FogRenderer` | how far you can see, in what colour, and in which medium | Render thread | | `SkyRenderer` | what hangs above the horizon — and which of two skies it is | Render thread | | `CloudRenderer` | the cloud cells, and the face list built from them | `CloudRenderer.prepare` bakes on a worker, the rest on Render | | `WeatherEffectRenderer` | which columns get rain, which get snow, and how hard | Render thread | | `LevelRenderer` | which of those become frame-graph passes at all | Render thread | ## What a renderer has to know about an attribute, and no more An **environment attribute** is a named, typed quantity — a colour, a distance, an angle, a moon phase — that the world answers for a position and an instant. `EnvironmentAttributeSystem` assembles that answer by running a short stack of layers over the attribute's own default: the dimension, the biome, one layer per timeline the dimension runs, and weather where a dimension can have it. That machinery belongs to [environment attributes and timelines](../world/environment-attributes-and-timelines.md); this page assumes it and names only what it consumes. Three consequences shape everything below. **The client resolves the same stack from the same data** — it is never sent a resolved colour — and what it adds is `EnvironmentAttributeProbe`, on the camera. `EnvironmentAttributeProbe.tick` re-samples the biome neighbourhood once per client tick and rolls each probed value's new answer down into last tick's; `EnvironmentAttributeProbe.getValue` fetches the fresh one lazily, during the frame, and interpolates between the two by partial tick; and any attribute nobody asked for during a tick is evicted. Every renderer here that asks for an attribute at all goes through the probe and never through the system — and one of the five asks for none. **Whether a value smooths or steps is declared on the attribute**, not chosen by the renderer — which is why the sky colour slides and `EnvironmentAttributes.MOON_PHASE` snaps, and why a renderer that wants a different curve must ask for a different attribute. **`ClientLevel` adds two layers of its own** on top of the four, and both are the **lightning** flash: one lerps `EnvironmentAttributes.SKY_COLOR` a fifth of the way towards a pale blue-white, the other pins `EnvironmentAttributes.SKY_LIGHT_FACTOR` to one while the flash lasts. Neither has anything to do with the End's sky flash, which never enters the stack at all. ### What the dimension type and the biome still carry Some of the old per-dimension and per-biome data survived the migration unchanged. `DimensionType.skybox` is a three-valued `DimensionType.Skybox` — `DimensionType.Skybox.NONE`, `DimensionType.Skybox.OVERWORLD`, `DimensionType.Skybox.END` — and it is a *branch*, not a colour. `BiomeSpecialEffects` still exists, hollowed out to `BiomeSpecialEffects.waterColor`, `BiomeSpecialEffects.grassColorOverride`, `BiomeSpecialEffects.grassColorModifier` and the foliage colours: every fog and sky colour left it for `Biome.getAttributes`. None of it crosses the network as pixels — the inputs arrive as registry sync during configuration, attribute maps filtered through `EnvironmentAttributeMap.NETWORK_CODEC`, then world time and weather during play ([protocol phases](../networking/protocol-phases.md)) — so a data pack retints a dimension without touching the client. ## The five askers | renderer | what it asks for | when it asks | what it produces | |---|---|---|---| | `Lightmap`, through `LightmapRenderStateExtractor` | how bright block light and sky light should read, and in what tint | once per tick, at a partial tick of exactly one | ten std140 uniforms and one 16×16 texture | | `FogRenderer` | the colour of the murk and the six distances it lives between | once per frame, inside the camera extract | one `FogData`, uploaded as one UBO slice | | `SkyRenderer` | where the sun, moon and stars are, and how bright | once per frame | a `SkyRenderState` for the sky pass | | `CloudRenderer` | what colour the clouds are and how high they sit | once per frame, read for it by `LevelExtractor` | a compressed face list, rebaked only when it must be | | `WeatherEffectRenderer` | nothing, until it is raining | once per frame, and only then | a list of `WeatherEffectRenderer.ColumnInstance` | ## The trace: the sun goes down ```mermaid sequenceDiagram participant Time as Timelines participant EAS as EnvironmentAttributeSystem participant EAP as EnvironmentAttributeProbe participant LRSE as LightmapRenderStateExtractor participant FR as FogRenderer participant SR as SkyRenderer participant LR as LevelRenderer participant LM as Lightmap Note over Time,EAS: per client tick Time->>EAS: the keyframe tracks for this world time — SUN_ANGLE, SKY_COLOR, SKY_LIGHT_FACTOR EAP->>EAP: tick — Gaussian biome blend, last becomes new, unread attributes evicted, driven from Camera.tick LRSE->>LRSE: tick — flicker walk, then needsUpdate is raised EAS->>EAS: invalidateTickCache, the last statement of ClientLevel.tick — marks the non-positional values stale, recomputing none of them Note over EAP,SR: per frame, extract LRSE->>EAP: getValue(SKY_LIGHT_FACTOR, BLOCK_LIGHT_TINT, AMBIENT_LIGHT_COLOR) LRSE-->>LM: LightmapRenderState — ten std140 values, plus the flag FR->>EAP: getValue(FOG_COLOR, SUNRISE_SUNSET_COLOR, SKY_FOG_END_DISTANCE) FR-->>LR: FogData — one colour and six distances, in one UBO SR->>EAP: getValue(SUN_ANGLE, MOON_ANGLE, STAR_BRIGHTNESS, MOON_PHASE) SR-->>LR: SkyRenderState Note over LM,LR: per frame, render LM->>LM: render — one three-vertex draw into a 16×16 texture LR->>LR: addSkyPass — disc, sunrise fan, sun, moon, stars, dark disc LR->>LR: addMainPass — terrain samples the lightmap LR->>LR: addCloudsPass, then addWeatherPass ``` The middle band's order is a dependency order. `GameRenderer.extract` runs `LightmapRenderStateExtractor.extract`, then `GameRenderer.extractCamera` — where `FogRenderer.setupFog` stashes its `FogData` on `CameraRenderState.fogData` — then `LevelExtractor.extract`, which drives `WeatherEffectRenderer.extractRenderState` and `SkyRenderer.extractRenderState`. Then `GameRenderer.renderLevel` uploads the fog with `FogRenderer.updateBuffer`, takes one slice with `FogRenderer.getBuffer`, and `LevelRenderer.render` declares the passes. `SkyRenderer.renderSunriseAndSunset` is the clearest instance of the pattern. The sunrise fan's colour *is* `EnvironmentAttributes.SUNRISE_SUNSET_COLOR`, an ARGB keyframe track, and its visibility is that colour's own alpha channel — which the renderer also scales the fan's depth by. The fade is a property of the data, not of the geometry, so a data pack restyles the sunset without a line of client code changing. ## How bright: one draw per tick, and no partial ticks at all `Lightmap` is a 16×16 `GpuTexture` plus a `MappableRingBuffer` of uniforms. `Lightmap.render` writes those uniforms and issues **one three-vertex draw** with `RenderPipelines.LIGHTMAP`: the brightness curve lives in the shader and the whole texture is a by-product of it. In 1.21 this was a `NativeImage` filled pixel by pixel in Java and re-uploaded every frame. What it draws from is `LightmapRenderState`: ten values in std140 order — six floats from `LightmapRenderState.skyFactor` and `LightmapRenderState.blockFactor` to `LightmapRenderState.brightness`, then four colours, `LightmapRenderState.blockLightTint` and `LightmapRenderState.skyLightColor` from `EnvironmentAttributes.BLOCK_LIGHT_TINT` and `EnvironmentAttributes.SKY_LIGHT_COLOR`, the other two from `EnvironmentAttributes.AMBIENT_LIGHT_COLOR` and `EnvironmentAttributes.NIGHT_VISION_COLOR` — and an eleventh field, `LightmapRenderState.needsUpdate`, which is not a uniform at all but the flag that decides whether the draw happens. `LightmapRenderStateExtractor.tick` runs the torch-flicker random walk in `LightmapRenderStateExtractor.blockLightFlicker` and raises its own copy of the flag; `LightmapRenderStateExtractor.extract` copies it across, clears it, and reads the probe alongside `Options.gamma`, `Options.darknessEffectScale`, the conduit-power water vision and `LightmapRenderStateExtractor.calculateDarknessScale`. **So the lightmap is recomputed once per tick and not once per frame** — and, deliberately, `GameRenderer.extract` hands the extractor a partial tick of exactly one while `FogRenderer.setupFog` and `SkyRenderer.extractRenderState` get the real one. Sky and fog interpolate mid-tick. World lighting steps. Three leftovers. `Lightmap.getBrightness` survives but no longer feeds the lightmap: it is a CPU-side duplicate of the shader's curve, kept for `Hud`, `EntityRenderer`'s shadow sampling and `ScreenEffectRenderer` alone. The packing statics moved out of the texture into `LightCoordsUtil`, from where a packed value reaches a vertex through `VertexConsumer.setLight`. And `UiLightmap` is the 1×1 white `DynamicTexture` handed out while `GameRenderer.useUiLightmap` is set. ### Two curves that look like one `EnvironmentAttributes.SKY_LIGHT_FACTOR` is a *visual* attribute, spatially interpolated, and the lightmap reads it; `EnvironmentAttributes.SKY_LIGHT_LEVEL` is a *gameplay* attribute, not positional, and `Level.updateSkyBrightness` turns it into `Level.skyDarken` for mob spawning. `Timelines.OVERWORLD_DAY` keyframes both, at slightly different times and to different night values — so they look like one number, and a data pack can pull them apart. ## How far: one block for the whole frame, filled by a priority walk `FogRenderer`'s output is a mutable `FogData`: `FogData.color` plus six distances — a start and an end each for the medium and the horizon, then `FogData.skyEnd` and `FogData.cloudEnd`. In open air those are `EnvironmentAttributes.FOG_COLOR`, `EnvironmentAttributes.FOG_START_DISTANCE`, `EnvironmentAttributes.FOG_END_DISTANCE`, `EnvironmentAttributes.SKY_FOG_END_DISTANCE` and `EnvironmentAttributes.CLOUD_FOG_END_DISTANCE`, and underwater they are `EnvironmentAttributes.WATER_FOG_COLOR`, `EnvironmentAttributes.WATER_FOG_START_DISTANCE` and `EnvironmentAttributes.WATER_FOG_END_DISTANCE` instead. It owns one ring buffer, `FogRenderer.regularBuffer`, and beside it a second buffer of its own, `FogRenderer.emptyBuffer`, filled with *infinitely far* for when fog is off. **There is one fog UBO for the whole frame, not one per pass.** `LevelRenderer.render` takes a single slice and hands the same one to the sky, main, weather and always-on-top passes, and does not hand it to the clouds pass — which reads a cloud fog end out of the same buffer anyway, because the binding is sticky and the shader simply keeps reading what was last bound. The sky and cloud fog ends are separate fields *inside that one block*, which the shaders choose between, so what a player sees as per-element fog is a shader decision and not a binding. ### The list that decides the colour The colour and the darkening come from different places. `FogRenderer.FOG_ENVIRONMENTS` is an ordered list and the order *is* the priority: `LavaFogEnvironment`, `PowderedSnowFogEnvironment`, `BlindnessFogEnvironment`, `DarknessFogEnvironment`, `WaterFogEnvironment`, and `AtmosphericFogEnvironment` **last**, which is what makes it the guaranteed fallback. `FogRenderer.computeFogColor` makes **one** pass down that list carrying two independent latches — it takes the colour from the first environment whose `FogEnvironment.providesColor` is true and the darkness from the first whose `FogEnvironment.modifiesDarkness` is, which need not be the same one — whereas `FogEnvironment.setupFog` stops at the first applicable one, and it and `FogEnvironment.isApplicable` are the class's only abstract methods. `MobEffectFogEnvironment` declares `FogEnvironment.providesColor` false on purpose: blindness and darkness may darken somebody else's colour, never supply one, and the atmospheric environment sits last precisely so somebody always does. Which medium the camera is in is a `FogType` (`FogType.WATER`, `FogType.LAVA`, `FogType.POWDER_SNOW`, `FogType.ATMOSPHERIC`, `FogType.NONE`), and *NONE* maps to the atmospheric environment. Rain fog is the only stateful one: `AtmosphericFogEnvironment.rainFogMultiplier` is an exponential follower, so the murk lags a storm starting rather than snapping to it, and `AtmosphericFogEnvironment.updateRainFogState` thickens it even in a biome with no precipitation, at half strength. ## What is up there: two skies, and a texture that is never bound `SkyRenderer` builds every buffer it will ever need in its constructor, from `SkyRenderer.buildStars` to `SkyRenderer.buildMoonPhases` against the `AtlasIds.CELESTIALS` atlas, and per frame fills a `SkyRenderState` running from `SkyRenderState.skybox` through `EnvironmentAttributes.SUN_ANGLE`, `EnvironmentAttributes.MOON_ANGLE`, `EnvironmentAttributes.STAR_ANGLE` and `EnvironmentAttributes.STAR_BRIGHTNESS` to `SkyRenderState.endFlashIntensity`. **The stars are the same in every world.** `SkyRenderer.buildStars` seeds a fixed constant and rejects samples outside a shell, so `SkyRenderer.STAR_COUNT` is an attempt count rather than a star count, and they are rebuilt only when a resource reload takes the whole renderer down: `LevelExtractor.onResourceManagerReload` sets `LevelExtractor.shouldResetSkyRenderer` and `LevelRenderer.addSkyPass` closes and reconstructs the entire `SkyRenderer`, stars, moon phases and all. The moon phase, likewise, is no longer arithmetic on the day count — it is `EnvironmentAttributes.MOON_PHASE` driven by `Timelines.MOON`, whose period is `MoonPhase.COUNT` days, and the renderer picks a sub-quad of an eight-quad buffer by `MoonPhase.index`. **The End takes a different branch entirely.** With `DimensionType.Skybox.END`, `SkyRenderer.extractRenderState` fills only the End-flash fields: the sun angle, the moon phase, the sky colour and the dark disc are never sampled. And `EndFlashState` is not the dragon fight — it is a free-running flash on a six-hundred-tick cycle, seeded per interval for its offset, duration and angles, advanced by `EndFlashState.tick` in any dimension whose skybox is the End's. The sky is also skipped five ways, four of them in one method: `LevelRenderer.addSkyPass` bails in lava, in powder snow, when `CameraRenderState` reports that a mob effect blocks the sky — which is blindness and darkness folded into one boolean before the method is entered — and when `DimensionType.Skybox` is *NONE*, which is the Nether. The fifth is outside it: `GameRenderer.renderLevel` suppresses the sky when a boss bar wants world fog, with `AtmosphericFogEnvironment.setupFog` clamping the fog hard in that case. ### The clouds, which get no fog and no texture The clouds are the first of the two exceptions: their colour and height are `EnvironmentAttributes.CLOUD_COLOR` and `EnvironmentAttributes.CLOUD_HEIGHT`, but their *drift* is raw world time. **And the cloud texture is never bound as a texture.** `CloudRenderer.prepare` does the whole job on a worker — reading the image and baking it into `CloudRenderer.TextureData` through `CloudRenderer.packCellData`, one 64-bit word per pixel with the colour in the high bits and four neighbour-emptiness flags in the low four — and `CloudRenderer.apply` is two statements on the client thread that install the result and raise the rebuild flag. `CloudRenderer.buildMesh` walks cells of `CloudRenderer.CELL_SIZE_IN_BLOCKS`, writing three bytes per face through `CloudRenderer.encodeFace` — a compressed *face list*, expanded to quads in the shader, with `CloudRenderer.RelativeCameraPos` and `CloudStatus` deciding which faces exist. It is rebuilt on a reload, when the camera crosses a cell boundary or changes side, or when the `CloudStatus` changes — and a data pack setting the cloud colour to zero alpha removes the pass entirely. ## What is coming down: rebuilt every frame, and seeded from the clock `WeatherEffectRenderer` is the second exception: its columns are placed by world position, but the streaks are seeded from raw world time. It holds one `WeatherEffectRenderer.vertexBuffer` and the precomputed tangent tables `WeatherEffectRenderer.columnSizeX` and `WeatherEffectRenderer.columnSizeZ`, and its per-frame product is a list of `WeatherEffectRenderer.ColumnInstance` records inside a `WeatherRenderState`. `WeatherEffectRenderer.extractRenderState` returns immediately when the rain level is zero, so a clear sky costs nothing. Otherwise it loops every column in a square of radius `Options.weatherRadius`, querying the heightmap and the precipitation at each — every frame, on the CPU. The vertex buffer is rebuilt in `WeatherEffectRenderer.render` rather than in extract, rain and snow are two indexed draws sharing it, and the world border rides in the same pass. Particles and sound are somebody else's job: `ClientLevel.tickWeatherEffects` spawns those per tick within the same radius, next to `ClientLevel.animateTick`, which scatters `EnvironmentAttributes.AMBIENT_PARTICLES`. ## What is not an attribute The migration was not total, which is why *everything is an attribute now* needs a qualifier. `DimensionType.ambientLight` and `DimensionType.cardinalLightType` are plain record fields, read directly — and the first of the two no longer reaches the lightmap at all: its two readers are `Lightmap.getBrightness`, the CPU-side duplicate this page has already said the shader does not use, and one deprecated method on `LevelReader`. Block tint never moved at all: grass, foliage and water are still `BiomeColors` reading `BiomeSpecialEffects` through the four `ColorResolver`s, with no probe and no layer stack in it. And the clouds still read the world clock, because a value sampled at the camera and lerped by partial tick is the wrong shape for a drift — while the weather reads neither clock nor attribute, seeding each column from its own coordinates. > **For a 1.21-era reader.** Nearly every per-dimension, per-biome, > per-time-of-day visual constant is an environment attribute now, so the > names to stop hunting for are: *LightTexture* (now `Lightmap` plus > `LightCoordsUtil`), *DimensionSpecialEffects* and all three subclasses (now > `DimensionType.skybox` plus attributes), *FogParameters* (now `FogData`), > *RenderSystem.setShaderFogColor* and its siblings (now one > `RenderSystem.setShaderFog` taking a uniform slice), > *LevelRenderer.renderSky* / *renderClouds* / *renderSnowAndRain* (now the > `LevelRenderer.addSkyPass` family of frame-graph passes, declared as > [visibility and the frame graph](visibility-and-the-frame-graph.md) > describes), and *Level.getSkyColor*, *ClientLevel.getStarBrightness* and > *ClientLevel.effects*, all attributes now. The draws went the way of > everything in [blaze3d](blaze3d.md), from `RenderPipelines.LIGHTMAP` and > `RenderPipelines.SKY` to `RenderPipelines.WEATHER_DEPTH_WRITE`. ## Where to look `LightmapRenderStateExtractor.extract` first, then `Lightmap.render` for what it feeds. `EnvironmentAttributeProbe.getValue` for the question every renderer here asks, and [environment attributes and timelines](../world/environment-attributes-and-timelines.md) for how it is answered. `FogRenderer.computeFogColor` for the priority walk. `SkyRenderer.extractRenderState` and `LevelRenderer.addSkyPass` for the sky and its two branches, `CloudRenderer.buildMesh` and `WeatherEffectRenderer.extractRenderState` for the meshes rebuilt inside the frame, and `BiomeColors` for the colour system that did not move. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Particles > Verified against **Minecraft 26.2** · Part XI · A player breaks a block, and the puff of block texture appears on every screen within sixty-four blocks. The block goes. On the breaker's own machine the puff is already there, predicted, before the server has heard about the swing; on every other machine within sixty-four blocks it arrives a moment later as a level event and lands as the same sixty-four textured quads, built by the same method from the same shape. Two entirely different routes, one visual result — and the interesting thing is what neither route does. Neither asks how far away you are. Neither asks whether the packet was worth sending. And neither asks your particle setting, which three pieces of the client read three different ways, while the server is told what you chose and never once acts on it. **A particle is not something the game decides to show you. It is something that survives a series of gates that disagree about what they are gating.** ## The cast | class | what it decides | thread | |---|---|---| | `ServerLevel` | which players are told about a particle at all — on dimension and distance, nothing else | Server | | `ParticleType` | the type's identity in the registry, and `ParticleType.getOverrideLimiter`, the "ignore the limits" flag baked into it | either | | `ClientLevel` | the gated entry point, `ClientLevel.doAddParticle` — and the ungated ones beside it | Client | | `ParticleResources` | which provider a type gets, registered once at construction, and which `SpriteSet` — rebound on every reload | load off-thread, bind on Client | | `ParticleEngine` | the groups, the one-tick admission queue, the emitters, the per-type counts | Client | | `ParticleGroup` | whether there is room: the per-render-type cap and the probabilistic reservoir | Client | | `ClientExplosionTracker` | how many explosion particles happen this tick, and where — the client's own budgeted generator | Client | | `SingleQuadParticle.Layer` | which of three atlases a quad reads, and which of two pipelines draws it | Client | Everything below the second row runs on the client thread, and the only off-thread work in the system is the load half of `ParticleResources.reload` — the bind that rebuilds each `SpriteSet` comes back to the client thread. Several things cross the network and none of them is a particle. Three carry the bulk of it: a `ClientboundLevelParticlesPacket` is an explicit request carrying count, spread, speed and two override flags, a `ClientboundLevelEventPacket` is an event the client interprets, and a `ClientboundExplodePacket` is a description the client expands itself. ## Does the particle happen at all? Both routes start in the same place. `Block.spawnDestroyParticles` raises level event `LevelEvent.PARTICLES_DESTROY_BLOCK` with the breaker as the source, and the two branches diverge only because of *who* that source is relative to whoever is watching. ```mermaid sequenceDiagram participant MPGM as MultiPlayerGameMode participant Block as Block participant SL as ServerLevel participant PL as PlayerList participant CPL as ClientPacketListener participant CL as ClientLevel participant PE as ParticleEngine Note over MPGM,PE: the breaker's own client, predicting MPGM->>Block: playerWillDestroy, then spawnDestroyParticles Block->>CL: levelEvent, PARTICLES_DESTROY_BLOCK, the breaker as source CL->>CL: LevelEventHandler, the sound, then addDestroyBlockEffect CL->>PE: add, one TerrainParticle per quarter-block cell of the shape Note over SL,PE: everybody else, within 64 blocks Block->>SL: the same levelEvent, on the server's copy of the block SL->>PL: broadcast within 64 blocks, skipping the source PL->>CPL: ClientboundLevelEventPacket CPL->>CL: levelEvent, then the same addDestroyBlockEffect CL->>PE: add, the identical particles ``` **Neither route is gated.** Both end in `ClientLevel.addDestroyBlockEffect`, which calls `ParticleEngine.add` directly and never passes through `ClientLevel.addParticle` — so neither the distance check nor the particle setting applies to either of them. The branches differ in who dispatches the event, not in what the client then does with it. And the breaker is not always a player. `LevelEvent.PARTICLES_DESTROY_BLOCK` has fifteen call sites and only three pass a source at all — a bed, a tall plant, and `Block.playerWillDestroy` itself. A fox faceplanting into snow, a rabbit eating a carrot down one age, a sheep eating grass, a zombie breaking a door, a suspicious block that fell and shattered, and `Level.destroyBlock` itself all raise it with a null source — which means the server broadcasts it to *everybody*, including whoever caused it. **Sixty-four** — quads in a full cube's puff, because `ClientLevel.addDestroyBlockEffect` walks every box of `BlockBehaviour.BlockStateBase.getShape` on a fixed quarter-block grid with a minimum of two cells per axis. That is the *outline* shape, not the collision shape, and the difference is visible: a torch has no collision shape at all and still gives twelve particles, where reading the collision shape would give none. Each particle shows a different randomly-offset quarter-crop of the block's sprite, which is why the puff does not look tiled. A block may opt out of the whole thing — `BlockBehaviour.BlockStateBase.shouldSpawnTerrainParticles` gates both the destroy and the crack effects — and `TerrainParticle` additionally refuses air and `Blocks.MOVING_PISTON`. ### Three neighbours that look like the same thing The *crack* particles that fly off while you are still mining come from `ClientLevel.addBreakingBlockEffect`, called once per client **tick** from `Minecraft.continueAttack` by way of `Minecraft.handleKeybinds`, and never networked at all — your neighbour's screen shows their own crack particles, computed locally, not yours. The `/particle` command arrives as `ClientboundLevelParticlesPacket` and goes through `ClientLevel.addParticle` once per requested count with Gaussian spread, unless the requested count is zero, which is a second mode entirely: one particle whose velocity is the offset vector scaled by the speed, which is how a *directed* particle is spawned. And the ambient scatter is a fixed cost paid every tick regardless of what is there — `ClientLevel.animateTick` samples 1,334 random block positions, 667 within sixteen blocks and 667 within thirty-two, most of which do nothing. Riding on that one loop are the drip particles (`ClientLevel.trySpawnDripParticles`), the biome's `AmbientParticle` list named by `EnvironmentAttributes.AMBIENT_PARTICLES` ([environment attributes and timelines](../world/environment-attributes-and-timelines.md)), and the barrier and light **block markers** — in creative, holding one of those two items makes every matching block in range emit a marker particle, which is the only reason you can see them. ## Who is allowed to see it? There are three distance rules, they are enforced by three different pieces of code, and two of them happen to be the same number. | the gate | measured from | the distance | what an override does to it | |---|---|---|---| | the server choosing whom to send a `ClientboundLevelParticlesPacket` to | the receiving player | 32 blocks | *widens* it, to 512 — but from the caller's own boolean, in practice `/particle … force`, and never from the particle type | | the client deciding whether to build the particle at all, in `ClientLevel.doAddParticle` | the **camera** | 32 blocks | skips the check entirely | | the server broadcasting a level event — the break puff's second route | the receiving player | 64 blocks | not consulted: a level event carries no particle type | | `ClientLevel.addDestroyBlockEffect`, and everything else that hands `ParticleEngine.add` a finished particle | — | none | nothing to override | The two thirty-twos are independent, not one check written twice. A particle that clears the server's test can still be dropped by the client's, because the client measures from where you are *looking* rather than from where your feet are, and a packet that took a tick to arrive is measured against a camera that has since moved. And the two overrides are not one flag either: the client's comes off the particle type, through `ParticleType.getOverrideLimiter`, whose three readers are all client-side, while the server's is a boolean the caller passes in. One deletes a check outright; the other multiplies a different check by sixteen. The 64-block radius is the third rule, and nothing overrides it in either direction. Once a level event lands the client asks no further questions, which is why a break puff at the edge of view is unconditional where the same particle requested by `/particle` would never have been sent. ## Does the setting apply? Three pieces of the client read the same particle setting, and none of them agrees with the others about what its values mean. | who reads it | what it does | |---|---| | `ClientLevel.doAddParticle` | *decreased* is rewritten to *minimal* about a third of the time, and *minimal* drops everything — except that the always-show flag rescues a *minimal* setting one time in ten, and the rescue lands on *decreased*, which is then re-rolled | | `ClientExplosionTracker` | anything below *All* is off. The pending explosions are cleared unused, and there is no decreased tier for explosion block particles at all | | `ClientLevel.tickWeatherEffects` | on *decreased*, halves its column count — rain thins rather than stopping; on *minimal* it breaks out before adding anything, and there it does stop | Only the first of those is the gate everything is nominally supposed to go through, and **four call sites outside the particle package bypass it entirely** by handing a constructed particle straight to `ParticleEngine.add`: the break puff, the crack effect, the firework starter and the item-pickup streak. A fifth is inside the system, a firework spark spawning more sparks. The same puff arriving as a particle *packet* would be distance-culled and might be diced away by the setting. Arriving as a level event, it is unconditional. The last piece reads like a bug and is not. **The server knows your particle setting and never uses it.** It arrives in the client information and is stored on the player, and the broadcast filters on dimension and distance and nothing else — so turning particles down saves your GPU and costs the server exactly nothing. ## Is there room for it? Explosions are the one source that budgets itself before it asks anyone else, and they are not a particle packet at all. `ServerLevel.explode` sends a `ClientboundExplodePacket` carrying a radius, a block count and a `WeightedList` of `ExplosionParticleInfo`, and `ClientPacketListener.handleExplosion` hands that to `ClientLevel.trackExplosionEffects` and the `ClientExplosionTracker`. Each tick the tracker totals the block counts of every explosion it is holding, caps the result at `ClientExplosionTracker.MAX_PARTICLES_PER_TICK`, and draws that many weighted samples: a random direction, a cube-root-distributed radius so the samples fill the volume evenly, rejected outright if the block there is not air. Each survivor picks an `ExplosionParticleInfo` from the weighted list for its type, its positional scaling and its speed multiplier. Then the whole list is cleared, spent or not. Everything else meets the engine's own two limits. ```mermaid flowchart TD A["a constructed particle reaches ParticleEngine.add"] --> B{"does Particle.getParticleLimit name a ParticleLimit"} B -- "no limit, the overwhelming majority" --> D B -- "SPORE_BLOSSOM, already at its count" --> X["dropped"] B -- "SPORE_BLOSSOM, under its count" --> Q["queued in particlesToAdd until the next ParticleEngine.tick"] Q --> D{"then ParticleGroup.add, for the particle's ParticleRenderType"} D -- "at ParticleGroup.MAX_PARTICLES" --> X D -- "past ParticleGroup.RESERVOIR_START" --> E["kept with probability equal to the square of the fraction of RESERVOIR_SIZE still free"] D -- "below RESERVOIR_START" --> K["kept"] E --> K E --> X K --> T["ticked from the next tick onward"] ``` The cap is per render type, not global, and the last quarter of it is probabilistic: past `ParticleGroup.RESERVOIR_START` the acceptance probability falls as the square of the free fraction, so the last few hundred slots are very hard to fill and a particle storm degrades gradually rather than hitting a wall. Since almost everything is a `ParticleRenderType.SINGLE_QUADS` particle, that one group's budget is effectively the whole budget; the other three groups have their own. The per-type machinery beside it is the strangest thing in the system. `ParticleLimit` is a full accounting apparatus — a key carried by the particle, a count map in `ParticleEngine.trackedParticleCounts`, a decrement when a group refuses a particle the limit had already accepted — and it has **exactly one instance**, `ParticleLimit.SPORE_BLOSSOM`. The whole mechanism exists to hold down one kind of falling petal, and `ParticleEngine.hasSpaceInParticleLimit` is the only thing that ever reads the map. The number on the debug screen is a different count: `ParticleEngine.countParticles` walks the live render-type groups, and `DebugEntryParticleRenderStats` is its only consumer. ## When does it move, and when is it drawn? Admission is deferred by up to a tick. `ParticleEngine.add` puts the particle in `ParticleEngine.particlesToAdd`, and `ParticleEngine.tick` — which runs from `Minecraft.tick`, right after the ambient scatter, and only while the level is running normally — does three things in a fixed order: tick every existing group, then tick the emitters, then drain the queue into groups. Because the drain is *last*, a particle never moves on the tick that admits it, and a particle created during rendering is invisible until a tick has run. The emitters are the exception to almost everything. A `TrackingEmitter` is a `NoRenderParticle` bolted to a moving entity, spending its short life calling `ClientLevel.addParticle` on that entity's behalf — crits, the enchanted-hit sparkles and the totem burst are all emitters. It lives in `ParticleEngine.trackingEmitters` rather than in a `ParticleGroup`, so it is never counted, never culled and never extracted. It is only ticked. So is a `NoRenderParticleGroup`: `ParticleEngine.tick` iterates the whole group map, but `ParticleEngine.extract` iterates `ParticleEngine.RENDER_ORDER`, which lists three of the four render types, so a no-render group ticks its contents forever and is never asked for a render state. Which is exactly what a no-render particle is for. Everything visible happens at extract time, once per frame, from `LevelExtractor`. That is where the particle's previous and current positions are lerped by the partial tick and made camera-relative before being packed — interpolation is not a property of the particle, it is a property of the extract. It is also where the culling happens, and the cull is a point test: the particle's centre, not its quad, against a `Frustum` whose origin has been slid a few blocks *behind* the camera so that particles just past the near plane survive. Three of the four groups take a `Frustum` and ignore it. Only `QuadParticleGroup` culls. What survives is packed into `ParticlesRenderState`, one `ParticleGroupRenderState` per group, with `QuadParticleRenderState` writing twelve floats and two integers per particle into a per-layer `QuadParticleRenderState.Storage` — a growable struct-of-arrays, reset and reused each frame rather than reallocated — and `QuadParticleFeatureRenderer` turning that into draws through [Blaze3D](blaze3d.md). The layer decides which atlas is bound, and the particle system draws from three of them, not one: | the sprite lives on | opaque | translucent | |---|---|---| | the particle atlas | `SingleQuadParticle.Layer.OPAQUE` | `SingleQuadParticle.Layer.TRANSLUCENT` | | the block atlas | `SingleQuadParticle.Layer.OPAQUE_TERRAIN` | `SingleQuadParticle.Layer.TRANSLUCENT_TERRAIN` | | the item atlas | `SingleQuadParticle.Layer.OPAQUE_ITEMS` | `SingleQuadParticle.Layer.TRANSLUCENT_ITEMS` | The six resolve to two pipelines, `RenderPipelines.OPAQUE_PARTICLE` and `RenderPipelines.TRANSLUCENT_PARTICLE`. `SingleQuadParticle.Layer.bySprite` picks a row and a column by reading whether the stitched sprite actually contains translucent texels and which atlas the sprite lives on — and only three particle classes ever ask it: `TerrainParticle`, `BlockMarker` and `BreakingItemParticle`. Every other quad particle hard-codes opaque or translucent on the particle atlas, so the four terrain and item layers exist solely for block- and item-textured particles ([models and atlases](models-and-atlases.md)). The last surprise is in the submission. **A quad particle group is submitted twice per frame** — once into the solid bucket and once into the after-terrain bucket, the same render state object entered twice, with the feature renderer filtering each entry by whether the layer is translucent. Opaque particles therefore draw before terrain-translucent geometry and translucent ones after, and the per-particle packing still happens only once. The dedicated particle render target exists only under the transparency post chain, and even then only translucent particles use it ([visibility and the frame graph](visibility-and-the-frame-graph.md)). One particle escapes this system entirely: `ItemPickupParticle` carries an `EntityRenderState` and is submitted through `EntityRenderDispatcher`, so the item flying into your inventory is [a rendered entity](entity-rendering.md) wearing a particle's lifetime. Two events empty the engine wholesale, and both have to. `ParticleEngine.clearParticles` runs on a resource reload, because every live particle holds a sprite reference into an atlas that no longer exists, and `ParticleEngine.setLevel` clears the particles and the emitters both. Particles are crash-report sites by design, though none of the reports is raised on `ParticleEngine`: they come from `ParticleGroup.tickParticle`, from `QuadParticleGroup.extractRenderState`, and from `ClientLevel.doAddParticle` for a provider that throws while constructing. The one malformed particle that does *not* crash is the one arriving over the network — `ClientPacketListener.handleParticleEvent` logs it and drops it. And `Particle.move` skips the collision sweep above a fixed speed, so a particle thrown hard enough stops colliding with the world altogether, while one that has been stopped by a collision once stays flagged as stopped. > **For a 1.21-era reader.** `ParticleEngine` no longer owns providers, > sprites, reloading or rendering — those became `ParticleResources` and the > extract-plus-feature-renderer pipeline, and every provider and sprite-set > member moved off the engine. *TextureSheetParticle* merged into > `SingleQuadParticle`; the sheet-based `ParticleRenderType` constants became > `SingleQuadParticle.Layer`, leaving `ParticleRenderType` a record with four > values; *Particle.getRenderType* is `Particle.getGroup`; > *Particle.getLightColor* is `Particle.getLightCoords`; *Particle.render* > and *ParticleEngine.render* are an extract method plus > `QuadParticleFeatureRenderer`; *ParticleEngine.destroy* and *crack* are on > `ClientLevel`. And the name `ParticleGroup` was reused for something > completely different: it is the per-render-type bucket now, and the limiter > record it used to be is `ParticleLimit`. ## Where to look `Block.spawnDestroyParticles` · `ClientLevel.addDestroyBlockEffect` · `ClientLevel.addBreakingBlockEffect` · `ClientLevel.doAddParticle`, the gate everything else bypasses · `ClientLevel.animateTick` · `ClientExplosionTracker.tick` · `ParticleEngine.add` · `ParticleEngine.tick` · `ParticleEngine.extract` · `ParticleGroup.MAX_PARTICLES` · `ParticleLimit` · `ParticleResources.registerProviders` for the catalogue · `SingleQuadParticle.Layer.bySprite` · `QuadParticleFeatureRenderer` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Post-processing > Verified against **Minecraft 26.2** · Part XI · you press Escape and the world goes soft behind the menu — and the machine that softened it is the one that turns everything green when you spectate a creeper. The blur behind the pause menu is not a GUI effect. It is a *post-processing chain*: a file called *blur.json*, sitting in the jar beside *creeper.json*, in the same format, loaded by the same loader, compiled into the same kind of object and run by the same three classes. Six such files ship, covering the pause menu, the three things it is unpleasant to spectate, the glow around a spectral-arrowed mob and the option that sorts water against particles. A resource pack can rewrite every one of them. What it cannot do is add a seventh, because every chain this game will ever load is named by a constant in Java, and there are only six of those. ## The cast | class | what it decides | thread | |---|---|---| | `PostChainConfig` | what a chain is as data: its own targets, and its passes in order | parsed on a worker | | `ShaderManager` | which chains exist, when they are compiled, and when they are thrown away | prepare on a worker, everything else on the render thread | | `PostChain` | which targets a pass may name, and where a pass's output lives | Render thread | | `PostPass` | one draw: a pipeline, its inputs as samplers, its uniforms as buffers | Render thread | | `UniformValue` | the seven types a JSON-declared uniform may have, and how each is packed | Render thread | | `LevelTargetBundle` | the seven names the level's targets answer to, and which set a chain may ask for | Render thread | | `LevelRenderer` | the two chains that become passes in the world's own frame graph | Render thread | | `GameRenderer` | the four chains that get a frame graph of their own, built and thrown away on the spot | Render thread | ## From a file on disk to a pass in a graph ```mermaid flowchart TD DISK["a JSON file under post_effect, plus the GLSL programs it names"] PREP["ShaderManager.prepare — every chain parsed into a PostChainConfig, off the render thread"] LOAD["first request: PostChain.load builds one PostPass per declared pass and precompiles each pipeline"] CACHE["cached by id, and only by id, until the next resource reload"] ADD["PostChain.addToFrame — external targets fetched from a bundle, internal ones declared in the graph"] PASS["PostPass.addToFrame — one FramePass per pass, reading its inputs and read-writing its output"] EXEC["FrameGraphBuilder.execute — each body binds a pipeline, binds the samplers, draws three vertices"] OUT["the last pass lands on an imported target, which is what the rest of the frame goes on to use"] DISK --> PREP --> LOAD --> CACHE --> ADD --> PASS --> EXEC --> OUT ``` Read it as **parse, compile, declare, draw**, and note that the halves live in different phases of the client's life: the parse belongs to a resource reload, and everything from *addToFrame* onward happens inside a frame, every frame, for as long as the effect is on. ## What a chain declares, and the two kinds of name in it A `PostChainConfig` is two things: a map of *targets* it wants for itself, and a list of *passes* in the order they run. That is the entire schema. Each pass names a vertex program and a fragment program by `Identifier`, an output target, a list of inputs and a map of uniform blocks. An input is one of exactly two shapes: `PostChainConfig.TargetInput` names another target and may ask for its depth attachment rather than its colour, and `PostChainConfig.TextureInput` names a PNG under *textures/effect* with its dimensions. Both carry a *sampler name*, and two inputs on one pass sharing one is rejected by the codec while the file is being parsed — so the chain never reaches the config map at all, rather than failing later when something asks for it — and that name is the contract with the GLSL — `PostChain` appends *Sampler* to it when it builds the pass's `BindGroupLayout`, so an input called *In* is the shader's *InSampler*. The distinction that matters is between the two kinds of target name. A name in the chain's own *targets* map is **internal**: it belongs to the chain, is created fresh inside the frame graph at screen size unless the chain overrides that, and is gone when the graph finishes. Any other name is **external** and must be supplied by whoever runs the chain. `PostChainConfig.Pass.referencedTargets` collects both kinds, `PostChain.load` subtracts the internal ones, and what remains must be a subset of the allowed set the caller passed in — `LevelTargetBundle.MAIN_TARGETS`, `LevelTargetBundle.OUTLINE_TARGETS` or `LevelTargetBundle.SORTING_TARGETS`, one, two or six names. A chain naming a target its caller did not offer does not load at all. An internal target may also be declared *persistent*, in which case `PostChain` allocates it once, keeps it in `PostChain.persistentTargets` and imports it rather than creating it, so a pass can read what it wrote last frame. None of the six asks for one. ## Loaded off-thread, compiled inside a frame `ShaderManager` is a `SimplePreparableReloadListener`, so its two halves run in two places. `ShaderManager.prepare` runs on the reload's worker executor: it reads every GLSL source under *shaders*, resolves each source's *moj_import* directives through `GlslPreprocessor`, and parses every JSON under *post_effect* with `PostChainConfig.CODEC` into an immutable map — a malformed chain is logged and simply absent from it. `ShaderManager.apply` then runs on the render thread, clears the device's pipeline cache, precompiles every statically registered pipeline and, only if all of them succeeded, swaps in a new `ShaderManager.CompilationCache` and closes the old. Post chains are not in that precompiled set. They are built lazily, the first time somebody asks: `ShaderManager.getPostChain` consults the cache, and on a miss `PostChain.load` walks the config, builds a `RenderPipeline` per pass from `RenderPipelines.POST_PROCESSING_SNIPPET`, names it *chain id* slash *pass index*, and precompiles it there and then. **The first frame you spectate a creeper compiles two shader programs in the middle of itself.** A failure throws `ShaderManager.CompilationException`, which `ShaderManager.getPostChain` logs, caches as a permanent absence so the next frame does not try again, and reports to `Minecraft.triggerResourcePackRecovery` — the path that disables a resource pack that broke the game. Closing the old cache, meanwhile, closes every `PostChain` in it, destroying its persistent targets and freeing each `PostPass`'s uniform buffers: a reload does not rebuild the chains, it forgets them. ## A pass is three vertices, and its uniforms are written once, at load `PostPass.addToFrame` adds one `FrameGraphBuilder.addPass` named after its pipeline's location. Every target input becomes a `FramePass.reads`, the output a `FramePass.readsAndWrites`, and the body goes in through `FramePass.executes` — nothing is drawn while the graph is built. When the body eventually runs, it sets an orthographic projection — `ShaderManager` keeps one `Projection` and one `ProjectionMatrixBuffer` and lends them to every chain — writes the output size and each input's size into a `MappableRingBuffer` as the *SamplerInfo* block, then opens a `RenderPass`, binds pipeline, default uniforms, custom uniform blocks and inputs, and draws. **Three** — vertices in every post-processing draw, in all twenty-six passes the six chains declare (`PostPass.addToFrame`). There is no quad and no vertex buffer: the shared vertex program builds one oversized triangle out of the vertex index alone, and the fragment shader sees the whole screen. The uniforms are stranger than they look. A `UniformValue` has seven types — int, ivec3, float, vec2, vec3, vec4 and a 4×4 matrix — and a block is a list of them under a name. `PostPass`'s constructor sizes the block with `UniformValue.addSize`, packs it with `UniformValue.writeTo` and uploads it to a `GpuBuffer` **once**, at load, never to be written again. The per-entry *name* in the JSON is read by no codec — only the type and the value are, and members match the GLSL block positionally. Only the block's own name, the key in the uniforms map, has to match anything. That is why the blur's radius is not one of them. *blur.json* declares a radius of zero, and *box_blur* treats zero as "ask elsewhere": it falls back to a member of the *Globals* block, which `GlobalSettingsUniform.update` rewrites every frame from `OptionsRenderState.menuBackgroundBlurriness` and `RenderSystem.bindDefaultUniforms` binds to every post pass. **Anything a chain needs to vary per frame cannot be a chain uniform.** It has to come in through the global block, whose seven members are fixed in Java. ## The six chains | chain | who declares it | what it reads | what a player sees | |---|---|---|---| | *blur* | `GameRenderer.processBlurEffect`, called from inside `GuiRenderer.draw` | the main target and one internal target, six passes alternating between them | the world going soft behind a pause or options screen | | *creeper* | `GameRenderer.render`, when the camera entity is a `Creeper` | the main target, and one internal target it bounces through | luminance collapsed into the green channel, then posterised and mosaicked | | *spider* | `GameRenderer.render`, when it is a `Spider` | the main target and four internal targets | the view repeated through several skewed, blurred, red-tinted lobes | | *invert* | `GameRenderer.render`, when it is an `EnderMan` | the main target, and one internal target it bounces through | colours inverted, four fifths of the way | | *entity_outline* | `LevelRenderer.render`, when anything submitted an outline this frame | the entity-outline target — and never the main one | the coloured halo around a glowing mob | | *transparency* | `LevelRenderer.render`, when improved transparency is on | **six** of the caller's targets, colour **and** depth, and just one internal target of its own | water, particles, clouds and rain layered in the right order | Only one of those is a screen effect. *blur* runs over whatever is currently on the main target, world and GUI alike, because `GuiRenderer.draw` splits the GUI in two and runs it in the gap: everything up to the blur boundary is drawn, the depth buffer is cleared, the chain runs, and the rest of the GUI is drawn crisp on top. `Screen.extractBlurredBackground` sets that boundary through `GuiGraphicsExtractor.blurBeforeThisStratum` when `Options.getMenuBackgroundBlurriness` is at least one, which is why the slider at zero costs nothing and why the menu's darkening tint is not blurred. Three are world effects: *creeper*, *spider* and *invert* run at the end of `GameRenderer.render`'s world block, after the level and before any GUI, so they warp the world and leave the HUD alone. The last two are neither. *entity_outline* never touches the main target — it reads and writes an offscreen glow buffer that something else composites — and *transparency* is not a filter over a picture at all but the step that *makes* the picture, merging six separately rendered layers by depth. `GameRenderState.useShaderTransparency` gates it on `OptionsRenderState.improvedTransparency` and on not being in panoramic mode, and when it is off `LevelRenderer` never creates those five targets, so everything draws into the main one and sorts by luck. ## The outline chain, end to end Take the one whose whole life is visible. A mob is glowing, so something submits it to `SubmitNodeCollection.outline`, and `FeatureRenderDispatcher.PreparedFrame.executeOutline` draws it — flat, in the glow colour — into the entity-outline target during the main pass. That target is a `TextureTarget` `LevelRenderer` owns across frames. ```mermaid sequenceDiagram participant LR as LevelRenderer participant ShadM as ShaderManager participant PChain as PostChain participant PPass as PostPass participant FGB as FrameGraphBuilder participant GR as GameRenderer LR->>FGB: importExternal — the entity outline target, which the main pass has just drawn into LR->>ShadM: getPostChain for entity_outline, allowing main and entity_outline ShadM-->>LR: the cached chain, or four freshly compiled pipelines Note over LR,ShadM: the lookup runs every level frame — only addToFrame is skipped when hasAnyOutline is false LR->>PChain: addToFrame with the screen size and the level's target bundle PChain->>FGB: createInternal — the chain's own swap target, at screen size PChain->>PPass: addToFrame, four times, in declared order PPass->>FGB: addPass, reads the input, reads and writes the output Note over PPass,FGB: sobel to swap, blur across, blur down, blit back PChain-->>LR: the bundle's outline handle replaced with the last one written FGB->>FGB: execute — bodies run in dependency order, three vertices each GR->>LR: doEntityOutline, after the whole graph has finished Note over GR,LR: blitAndBlendToTexture composites the glow onto the main target ``` The first pass is an edge detector and what it detects edges in is **alpha**, not colour: the target is transparent everywhere nothing was submitted, so the boundary of the silhouette is the boundary of the halo. The next two blur that outline across and then down, and the last blits it back where it started — the chain ends on the same external target its first pass read, and the internal *swap* target is what makes that legal, since no pass ever reads and writes one buffer at once. Then it stops, and the compositing is somebody else's job. `LevelRenderer.doEntityOutline` runs after `GameRenderer.renderLevel` returns, outside the graph entirely, and blends the glow onto the main target with a pipeline of its own. This is the one chain whose result is invisible until a separate blit puts it on screen. ## Two doors into the GPU, and one of them is deprecated `PostChain` has two entry points, and which one a chain goes through is the whole difference between the two halves of this page. `PostChain.addToFrame` takes a `FrameGraphBuilder` somebody else already started and appends to it. That is what `LevelRenderer` does with the outline and transparency chains: they are not a second rendering path but more passes in [the graph it was building anyway](visibility-and-the-frame-graph.md), ordered by their declared reads and writes alongside the sky, the terrain, the clouds and the weather. They survive that graph's culling for the ordinary reason — their last pass writes a target imported from outside — and they appear in the profiler under their pipeline names, because the level's graph is executed with an inspector that pushes a zone per pass. `PostChain.process` is the other door, and it is marked deprecated. It builds a `FrameGraphBuilder` of its own, imports one target as *main*, adds the chain, executes it and throws it away. Both its callers are in `GameRenderer`: the camera-entity effect at the end of the world block, and `GameRenderer.processBlurEffect` in the middle of the GUI. Neither passes an inspector, so **the blur and the spectator shaders never get a slice of the F3 pie chart to themselves**: their cost is folded into whichever enclosing zone they ran under, *render → world* for the spectator effects and *render → gui → draw* for the blur, and no name in the chart tells you a post chain is what you are looking at. The graphs are throwaway but the memory is not: both doors take internal targets from the one `CrossFrameResourcePool`, which holds a released target for three frames in case something asks again for that size and format. ## Questions players ask **Can a resource pack add a post effect?** It can add the *file*, the game will parse it, and nothing will ever run it. `ShaderManager.getPostChain` is called with six ids: three constants in `LevelRenderer` and `GameRenderer`, three built inside `GameRenderer.checkEntityPostEffect` from the camera entity's class. No registry, no data-driven selection, no command. What a pack *can* do is replace any of the six, with as many passes as it likes, running fragment programs it also ships under *shaders/post* — a real and underused amount of rope. **Why does the creeper effect vanish when I press F5?** Because third person clears it, and the perspective key does it directly. `GameRenderer.checkEntityPostEffect` switches on the camera entity's class, sets `GameRenderer.postEffectId` for a creeper, a spider or an enderman, and clears it for anything else — including for no entity at all. The perspective key calls it straight out of `Minecraft.handleKeybinds`; `Minecraft.setCameraEntity` is the other door into the same method, for when what you are spectating changes rather than how. F4 (`Options.keyToggleSpectatorShaderEffects`) is a separate switch, flipping `GameRenderer.effectActive` without forgetting which chain was chosen, and the F3 screen names the survivor through `DebugEntryPostEffect`. **What happens to a chain when the window is resized?** Nothing. A `PostChain` has no size of its own: the dimensions arrive as arguments to `PostChain.addToFrame` every frame, and internal targets are described fresh from them each time. `GameRenderer.resize` clears the resource pool so the old targets are not handed back, and `LevelRenderer.resize` resizes the entity-outline target it owns. The compiled pipelines never mention a resolution, so they are untouched. > **For a 1.21-era reader.** *ShaderInstance*, *EffectInstance*, *Effect* and > *AbstractUniform* are gone, and `Uniform` survives only as an OpenGL-backend > detail no post chain ever names: a post pass's uniforms are > `UniformValue` records packed into a `GpuBuffer` with `Std140Builder`, and > everything else is bound by `RenderSystem.bindDefaultUniforms`. > *PostChain.process* still exists, but it is deprecated and both its callers > build a throwaway frame graph — `PostChain.addToFrame` is the real one. > *PostChain.resize*, *PostChain.getTempTarget* and `PostChain`'s whole > bookkeeping of named render targets are gone, because the frame graph > allocates them now. And *Fabulous* is no longer a mode anything reads: it > survives as one of four `GraphicsPreset` values, but a preset only *writes* > the individual options and is then forgotten, so what actually gates the > transparency chain is `Options.improvedTransparency` — which > `GraphicsPreset.FABULOUS` sets true everywhere except macOS. ## Where to look `PostChainConfig` first — the record *is* the file format. Then `ShaderManager.prepare` and `ShaderManager.getPostChain` for where a chain comes from and how long it lives, `PostChain.load` for the validation that decides whether it loads at all, and `PostChain.addToFrame` for the only thing a chain does. `PostPass` is one pass and one draw. For the callers, `LevelRenderer.render` declares two chains into the world's frame graph, `GameRenderer.render` and `GameRenderer.processBlurEffect` run the other four through the deprecated door, and `LevelTargetBundle` names what any may ask. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # XII · World generation > Verified against **Minecraft 26.2** · Part XII · a world made out of one number per point, decided before anything about it exists, and reproducible from a seed and a data pack alone. Everything in this part is determined by two things: the world seed, and the data packs enabled when the world is opened — `WorldLoader.load` re-reads the worldgen registries out of the current packs every time, so only the seed and the dimension list are actually saved. No entity, no player and no tick has any say in it. Give the same seed and the same packs to two copies of the game and they will agree, block for block, forever — which is the property speedrunners, seed-hunting sites and structure finders all depend on. It holds not because nothing here reads the world — the decoration step reads block states, heights and the carving mask through `PlacementContext`, and the surface pass reads its neighbours' biomes — but because **everything it reads is itself a function of that seed and those packs**. There is one deliberate exception, and it is the only place generation reads something the current seed did not produce: [the boundary with chunks an older version generated](blending.md). What a player recognises the part by is the seam: the flat shelf of ground under a village that was not there before, the cave that is flooded the moment you break into it, the desert that becomes a jungle along a ragged line, the tree that grows up through another tree. Counting `world/level/levelgen` and `world/level/biome` together — one class per file, one line per line of decompiled source, the way [the atlas](../../maps/README.md) counts everything else — that is **451 classes and 45,700 lines**. This part does not cover all of it; [what this book skips](../anatomy/what-this-book-skips.md) says which parts are declined and why. ## The shape of the part Part XII is **a substrate, a pipeline, and a wing** — and the wing runs first while being taught last. Part IV owns the conveyor that runs the statuses ([the chunk generation pipeline](../world/chunk-generation-pipeline.md)); this part is the cargo of seven of them. ```mermaid flowchart TB CW["10 · Creating a world — where the seed and the packs were chosen, before any of this"] CW -.-> DF DF["1 · Density functions — the substrate, and the only page that is not a chunk step"] DF --> SS subgraph SS["STRUCTURE_STARTS, STRUCTURE_REFERENCES — first in the game, last in the lectures"] direction LR S6["7 · Structure placement"] --> S7["8 · Jigsaw and templates"] S6 --> S8["9 · Hand-built structures"] end SS --> BI subgraph BI["BIOMES"] L2["2 · Biomes"] end BI --> NO subgraph NO["NOISE, SURFACE, CARVERS"] L3["3 · Terrain"] --- L3b["4 · Blending — the one place generation reads an older version's work"] end NO --> FE subgraph FE["FEATURES"] direction LR L4["5 · Features and placement"] --> L5["6 · Trees"] end ``` Read the solid chain as the order the game runs, and the numbers as the order to watch. They disagree on purpose — and the dashed arrow disagrees most, since the world was created before a single chunk was, and its page is told tenth. A structure is *decided* two statuses before the biomes and the terrain it will stand in exist — it asks the generator directly for the numbers it needs rather than reading a chunk — and it writes its blocks three statuses after the terrain is cut, inside the decoration step. Putting the three structure pages last keeps that whole arc in one place, at the cost of one forward topic: three of the six pages before them reach for the beardifier — two by link, one by name — before the page that owns it. The substrate arrow means *is made of*, not *happens before*. Four consumers, spread over three of the nine pages below it, take the density graph — the biome sampler is six of its functions, the aquifer is four more, ore veins are three, and the beardifier is a node the chunk splices in. The decoration and structure packages never mention `DensityFunction` at all; they reach the substrate only through the beardifier and the heights the generator hands them. The tenth page is the part's origin, told last. The object every other page reads — the seed and the map of dimensions, each a generator built out of noise settings, biome sources and placed features — is a tree of everything the first nine explain, so the page that says where it came from is a closer with nine satisfied references rather than an opener with nine forward ones. ## Before you start [The chunk generation pipeline](../world/chunk-generation-pipeline.md) from Part IV, and not optionally. It is the only page that says *when* any of this runs, what the twelve chunk statuses are, how the dependency pyramid keeps neighbours out of each other's way, and which thread each step is on. Eight of the ten pages here name a status, and two of them open on one. [Chunk anatomy](../world/chunk-anatomy.md), for what is being written into — sections, the two paletted containers, and the heightmaps the terrain steps maintain by hand. [Environment attributes and timelines](../world/environment-attributes-and-timelines.md), also from Part IV, for lecture two: `Biome` has been hollowed out, and the sky, the fog, the music and a dozen gameplay switches now reach the player through a stack of modifier layers in which the biome is one layer rather than the owner. [Codecs, NBT and JSON](../foundations/codecs-nbt-json.md) and [identifiers and registries](../foundations/identifiers-and-registries.md) from Part II, because worldgen is the most thoroughly data-driven system in the game and this part assumes the dynamic-registry model rather than re-teaching it. [The data-driven type pattern](../foundations/data-driven-types.md) lists fifty-six instances of the pattern, and twenty-six of them are owned by a page in this part. ## Watch in this order 1. [Density functions](density-functions.md) — the substrate, and the abstract one. Three forms of one graph, two rewrites, and six caches that cache nothing until something else installs them. 2. [Biomes](biomes.md) — a nearest-neighbour search in seven dimensions, one of which is not sampled from the world at all, and the two biome borders the game keeps a couple of blocks apart. 3. [Terrain](terrain.md) — noise, surface and carvers. Seven hundred and sixty-eight cells filled from their corners, and a cave whose water was decided before the cave was. 4. [Blending at the old-chunk border](blending.md) — the one place world generation reads the world. Sixteen columns an old chunk re-measures out of its own blocks, and a seam where the terrain splines are switched off entirely. 5. [Features and placement](features-and-placement.md) — decoration as a stream of positions folded through filters, in an order the whole dimension agreed on before any chunk existed. 6. [Trees](trees.md) — one algorithm with five slots in it, and the clearance scan that runs after the crown has been sized. 7. [Structure placement](structure-placement.md) — the part's policy page. A lottery that never looks at the world, an absence stored as a hole, and a command that generates chunks to answer a question. 8. [Jigsaw and templates](jigsaw-and-templates.md) — how a village assembles itself, and how any piece becomes blocks. A growth limit that works by taking the right pool away. 9. [Hand-built structures](hand-built-structures.md) — the older assembler, which is still most of the code. Four families of piece grammar, and the one structure that throws itself away and starts again. 10. [Creating a world](creating-a-world.md) — where the seed and the data packs came from. A screen that is a running data-pack load with widgets on it, settings carried across a reload by being serialised to JSON, and a Cancel button that does not undo. Two comes before three: `ChunkStatus.BIOMES` is the parent of `ChunkStatus.NOISE`, it is where the terrain's own workspace is built, and the surface pass reads the biome. Four needs both, and reaches one status forward: the last of its five consumers is a border tick run at `ChunkStatus.FEATURES`. Seven comes before eight and nine, which are alternatives to each other rather than a sequence. Ten can be watched first by a viewer who wants the origin before the machinery, at the cost of nine forward references. ## Reference this part uses [Density-function nodes](../../reference/density-function-nodes.md) is the catalogue behind lecture one: all thirty-four node types in registration order, what each takes, and what the per-chunk rewrite turns it into. [Registries](../../reference/registries.md) for the fourteen *worldgen/* registries a data pack writes into, and [the data-driven type pattern](../foundations/data-driven-types.md) for why some of them are frozen at startup and some reload with the world. [Diagram lanes](../../reference/lanes.md) for the abbreviations these figures use. [Naming drift](../../reference/naming-drift.md) has twelve rows for this part, all of them re-derived and none of them changed since pass 2. [Level data and rules](../../reference/level-data-and-rules.md) for which file the seed, the dimensions and the rules each end up in, which lecture nine links to rather than restates. And [the glossary](../../reference/glossary.md) for *density function*, *aquifer*, *beardifier*, *NoiseChunk*, *blending data*, *old chunk*, *PlacedFeature*, *jigsaw*, *world preset* and *world gen settings*. Where the part stops: what happens to a chunk *after* `ChunkStatus.FEATURES` — lighting, spawning, promotion to a live chunk, and being saved or sent — is Part IV. What the client is told about any of it is Part IX, and the answer is the finished chunk with none of the machinery: block states and the biome palette in one buffer, the heightmaps, and the block entities. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Density functions > Verified against **Minecraft 26.2** · Part XII · One number out of one point: how a JSON file becomes "stone or air", and why the graph you can read in the registry is never the graph that runs. Open the overworld's *depth* function in a data pack and you can read the shape of the world out of it in eleven lines: a gradient down the Y axis, added to *overworld/offset* — which is fifteen hundred lines of continents and erosion wrapped in things called *flat_cache* and *cache_2d*. Both files are honest and readable. And the object it parses into is never sampled by anything: it is rewritten once per dimension, and again per chunk, and only the third form ever computes a number for a block. **The caches named in that file cache nothing.** They are requests, and something else grants them. Terrain in 26.2 is a scalar field: a function from a block position to a *double*, where the convention is that zero is the surface and **positive means solid**. `DensityFunction` is the interface, `DensityFunctions` is the library of thirty-four node types you build one out of, and a data pack assembles them as JSON. This page is the three forms that one graph takes and the machinery that moves between them; the node types themselves are [the node catalogue](../../reference/density-function-nodes.md), and the terrain steps that sample the result are [terrain](terrain.md). Nothing here touches a block, and nothing here differs between two worlds built from the same seed and the same packs. This is the layer that *the seed* actually means. ## The cast | class | what it owns | its clock | |---|---|---| | `DensityFunction` | one method that matters — `DensityFunction.compute`, taking a position and returning a double — plus `DensityFunction.fillArray` for the batch form and the two static bounds | — | | `DensityFunctions` | the node library, and the codecs that dispatch a JSON *type* to one of them | data-pack load | | `DensityFunction.NoiseHolder` | the seeding seam: a noise-parameters holder plus a `NormalNoise` that is **null as parsed** | filled once per dimension | | `DensityFunctions.Marker` | a cache *request*, wrapping one function and computing nothing itself | replaced once per chunk | | `NoiseRouter` | the fifteen functions a generator asks for, as one record; `NoiseRouter.mapAll` rebuilds all fifteen at once | — | | `NoiseRouterData` | vanilla's graph, written in Java and *emitted* as the JSON that ships | build time | | `RandomState` | the per-dimension instantiation: the seeded router, the climate sampler, the noise memo, the `SurfaceSystem` | once per level | | `NoiseChunk` | the per-chunk instantiation, and simultaneously the sample position *and* the loop driver — it implements `DensityFunction.FunctionContext` and `DensityFunction.ContextProvider` both | once per chunk | Underneath all of it is the *synth* package: `NormalNoise` (two `PerlinNoise` stacks summed and normalised), `PerlinNoise` (octaves of `ImprovedNoise`), `ImprovedNoise` (one octave of 3-D Perlin over a permutation table), `BlendedNoise` (the pre-1.18 terrain noise, itself a `DensityFunction.SimpleFunction`), plus `SimplexNoise` and `PerlinSimplexNoise`. `Noises` holds the sixty-three keys for the parameter sets and `Noises.instantiate` builds one from a positional factory. ## Three forms of one graph ```mermaid flowchart TB subgraph AA["as parsed — shared by every world, unseeded and cacheless"] direction TB A1["Ap2, add"] --> A2["YClampedGradient"] A1 --> A3["HolderHolder — a pointer at another registry entry"] A3 --> A4["Marker, flat cache — delegates, caches nothing"] A4 --> A5["NoiseHolder — noise is null, answers 0.0"] end subgraph BB["as seeded — RandomState.router, one per dimension"] direction TB B1["Ap2, add"] --> B2["YClampedGradient"] B1 --> B3["HolderHolder — still a pointer"] B3 --> B4["Marker — still delegating"] B4 --> B5["NoiseHolder — a real NormalNoise"] end subgraph CC["as wrapped — one per chunk, and the only form that runs"] direction TB C1["Ap2, add"] --> C2["YClampedGradient"] C1 --> C3["the pointed-at graph itself"] C3 --> C4["NoiseChunk.FlatCache — a real array, filled"] C4 --> C5["NoiseHolder — the same NormalNoise"] end AA -- "RandomState.create — one visitor over the whole router" --> BB BB -- "NoiseChunk.forChunk — a second visitor" --> CC ``` Both arrows are `DensityFunction.mapAll`, which is the only interesting operation in this system: it applies a `DensityFunction.Visitor` bottom-up over a whole graph, rebuilding each node's children through `DensityFunction.mapChildren`. A visitor has two channels — `DensityFunction.Visitor.apply` for nodes and `DensityFunction.Visitor.visitNoise` for noise leaves — and everything below is one or other channel doing its job. ## Parse: one file, one graph Every file under a pack's *worldgen/density_function* directory becomes one registry entry through `DensityFunctions.DIRECT_CODEC`, which is an *either*: a bare number in the JSON is silently a `DensityFunctions.Constant`, and anything else dispatches on its type id through `BuiltInRegistries.DENSITY_FUNCTION_TYPE`. Every *child* slot instead uses `DensityFunction.CODEC`, a `RegistryFileCodec`, so a string id, an inline object and a bare number are interchangeable everywhere a function is expected. A string becomes a `DensityFunctions.HolderHolder` — a live pointer at another entry, which is how a graph references a graph. Two things happen during construction that a reader of the JSON cannot see. Constructors **fold**: `DensityFunctions.TwoArgumentSimpleFunction.create` collapses an *add* or a *mul* with one constant argument into a `DensityFunctions.MulOrAdd`, so a node type in the file is not necessarily the class in memory. And the **bounds propagate**: `DensityFunction.minValue` and `DensityFunction.maxValue` are computed as each node is built and pushed upward, which makes them a static analysis of the data pack — one that talks back, because building a *min* or a *max* over two ranges that cannot possibly overlap logs a warning naming both arguments. `DensityFunctions.HolderHolder` is the one node that cannot be written back out: it is not registered, and asking it for its codec throws. It exists only in memory, and re-serialising a graph goes through `DensityFunction.CODEC`, which recognises it and emits the id string it came from. ## Seed: once per dimension `RandomState.create` forks the seed into named positional factories — `RandomState.aquiferRandom`, `RandomState.oreRandom`, and whatever else asks through `RandomState.getOrCreateRandomFactory` — and then runs `NoiseRouter.mapAll` with a wiring visitor over all fifteen router fields. The visitor fills each `DensityFunction.NoiseHolder` with a real `NormalNoise` from `RandomState.getOrCreateNoise`, rebuilds `BlendedNoise` with a new random source, and replaces the end-islands node with a reseeded one. Everything else it passes through untouched: the markers and the pointers survive this rewrite intact. Two details in there matter later. The **two nether climate noises are special-cased** into a legacy construction over a `LegacyRandomSource` and therefore skip the memo entirely. And the visitor keeps its own memo of what it has already rewritten, so a subgraph referenced from five router fields is rewritten **once and stays one object** — which is precisely what makes the per-chunk caching in the next step pay, because five router fields that share a subgraph will share its cache. Then a *second*, different visitor runs, and it strips machinery rather than installing it: it unwraps every `DensityFunctions.HolderHolder` to its value and every `DensityFunctions.Marker` to its wrapped function, over the six climate functions only, to build `RandomState.sampler`. That is the `Climate.Sampler` [biomes](biomes.md) reads — a copy of the climate half of the graph with no caches and no indirection in it at all. > **For a 1.21-era reader.** Three of the six climate functions have two > names. `NoiseRouter` calls them *vegetation*, *ridges* and *continents*; > `Climate.Sampler` calls the same three *humidity*, *weirdness* and > *continentalness*. Neither vocabulary is wrong and both ship. ## Wrap: once per chunk `NoiseChunk.forChunk` builds the workspace and runs `NoiseRouter.mapAll` a second time, and `NoiseChunk.wrapNew` is the switch that matters. A `DensityFunctions.Marker` becomes the real cache its type names. A `DensityFunctions.HolderHolder` is resolved to its value once instead of on every sample. And three singletons are swapped **by object identity**: `DensityFunctions.BlendAlpha` and `DensityFunctions.BlendOffset` become two flat caches the `NoiseChunk` constructor has *already filled*, before any router mapping ran, and `DensityFunctions.BeardifierMarker` becomes this chunk's `Beardifier`. If the level's [`Blender`](blending.md) is empty, the blend nodes survive as the constants they are and a *blend_density* marker is replaced by its own child, erasing the node. Afterwards `NoiseChunk` adds the beardifier marker to the router's final density itself, wraps the sum in one more cache-all-in-cell, and maps *that* — which is why `NoiseChunk.fullNoiseDensity` is not any node the data pack wrote ([terrain](terrain.md) walks the cells that sample it). The rewrite is reversible, for the caches: all six implement `DensityFunctions.MarkerOrMarked`, so they still report their original marker type and would serialise back to the id they came from. The blend nodes are not reversible — `DensityFunctions.BlendAlpha` comes back wrapped in a flat cache it did not start inside. ## The six caches, and the three a single point may use This is the payoff of the whole arrangement, and the split inside it is not the one the names suggest. `NoiseChunk.NoiseInterpolator`, `NoiseChunk.CacheAllInCell` and `NoiseChunk.CacheOnce` each begin by checking that the sampling context *is* the `NoiseChunk` itself, and delegate to the wrapped function when it is not. They are meaningful only inside the cell loop — the interpolator throws outright if sampled while the chunk is not interpolating, and the other two key on a cell index or on an interpolation counter, neither of which means anything outside it. `NoiseChunk.FlatCache` and `NoiseChunk.Cache2D` do the opposite: they key on **position alone** and will happily answer a `DensityFunction.SinglePointContext`. `NoiseChunk.Cache2D` could not do otherwise — it is the one nested class here that is *static*, so it holds no reference to the chunk to compare against. And that is exactly what makes `NoiseChunk.cachedClimateSampler` and `NoiseChunk.preliminarySurfaceLevel` cheap: both sample the wrapped graph with single-point contexts, and both hit the two-dimensional caches every time. **A single-point sample is not a cache bypass — it is a bypass of the three-dimensional caches only.** The resolutions are worth saying once. The *interpolated* marker sits on the expensive three-dimensional terms and is evaluated at cell corners. *flat_cache* is **not** exact per column: it fills its array by sampling at the quart corner with y = 0, so one value serves a four-by-four block group. Only *cache_2d* is genuinely per column, and vanilla is not consistent about where it puts one: of the twenty-four in the shipped files, eleven sit inside a flat cache and thirteen do not — and no *noise_settings* file contains a *flat_cache* at all. ## Questions players ask **Does editing a *cache_once* in a data pack do anything?** Yes, but not what it says. It is a request that `NoiseChunk.wrapNew` install a cache in that slot; the node itself computes nothing and delegates. Worldgen performance lives in a switch statement, not in the data. **Is the readable graph ever actually sampled?** Once, and you can watch it happen. `NoiseBasedChunkGenerator.addDebugScreenInfo` samples `RandomState.router` with single-point contexts to fill the F3 noise readout — the one production path that runs the graph with every marker a no-op, and the reason the once-rewritten form has to stay safe to sample from anywhere. **Why do two identical-looking subgraphs end up sharing one cache?** Because both visitors key their memo on the node *itself*, and the nodes are records, so two separately-parsed but structurally identical subgraphs are merged into one object. `DensityFunctions.Spline` makes this explicit: it has a hand-written equality that compares only the spline and ignores the derived sampler beside it, so two identical splines from two different files become one node with one cache. **Are the bounds trustworthy?** Mostly, with exceptions worth knowing, and they run in both directions. `DensityFunctions.Marker` passes its child's bounds through *except* when its type is *blend_density*, where it reports infinities — the one place a marker is not transparent. The two blend leaves under-report before the wrap and not after: `DensityFunctions.BlendAlpha` and `DensityFunctions.BlendOffset` parse as the constants 1 and 0, and become `NoiseChunk.BlendAlpha` and `NoiseChunk.BlendOffset` with a range of zero to one and an infinite one — so a fold decided above a blend node was decided on the wrong range. `DensityFunctions.HolderHolder` reports infinities while its holder is unbound, which is what lets forward references parse at all. And `DensityFunction.NoiseHolder` answers a maximum of 2.0 while its noise is still null, where every one of the sixty-three shipped noise definitions comes out between 2.57 and 7.32 once seeded — so the freshly parsed graph reports noise bounds that are too **narrow**, and seeding widens them. **Is there anything in here that does not work?** Three things. `DensityFunctions.TransformerWithContext` is the shape a position-dependent transform would take and has no implementation in 26.2. `Density` writes down the three conventions this whole system rests on — surface at zero, and the two values a node reaches for when it wants to end an argument — as constants that **nothing anywhere reads**; the routers spell the same numbers as literals. And `DensityFunctions.shift`, the three-dimensional domain warp, has no callers and appears in no shipped file: vanilla uses only the two two-dimensional warps, and those two read the *same* noise parameters with their axes swapped, behind a registry id that is called *offset* rather than *shift*. **Which of these nodes reads the world?** Two, and both do it the same way — by harvesting what they need from neighbouring chunks at construction and never touching a chunk afterwards. The three blend nodes reach `BlendingData` ([blending at the old-chunk border](blending.md)); the *beardifier* marker becomes a `Beardifier`, whose `Beardifier.forStructuresInChunk` reads the structure references out of chunks at `ChunkStatus.STRUCTURE_REFERENCES` ([structure placement](structure-placement.md)). Everything else — every noise, spline, selector and cache in the catalogue — reads no blocks, no chunks and no level, which is why the whole system can run on a worldgen worker with nothing loaded. ## Where to look `DensityFunction.compute` · `DensityFunction.mapAll` · `DensityFunction.Visitor` · `DensityFunction.NoiseHolder` · `DensityFunctions.DIRECT_CODEC` · `DensityFunctions.Marker` · `DensityFunctions.MarkerOrMarked` · `DensityFunctions.HolderHolder` · `DensityFunctions.TwoArgumentSimpleFunction` · `NoiseRouter` · `NoiseRouterData.overworld` · `RandomState.create` · `RandomState.getOrCreateNoise` · `NoiseChunk.forChunk` · `NoiseChunk.wrapNew` · `NoiseChunk.NoiseInterpolator` · `NoiseChunk.Cache2D` · `NoiseChunk.cachedClimateSampler` · `Climate.Sampler` · `NormalNoise.create` · `ImprovedNoise.noise` · `Noises.instantiate` · `Density` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Biomes > Verified against **Minecraft 26.2** · Part XII · A point in the world gets a biome: six numbers quantised to integers, a nearest-neighbour search in seven dimensions, and the two different answers the game keeps for the same block. Walk out of a desert into a jungle and watch the ground. The grass changes colour at one line. The fog and the sky change at a *different* line, a couple of blocks away. Neither is a bug and neither is a rendering artefact: the game genuinely stores one biome per four-by-four-by-four volume and then answers "which biome is this block in?" **two different ways**, one jittered and one not, and different systems ask different questions. The surprise is which side each thing is on — grass colour, mob spawning and whether water freezes are all on the *jittered* side, and the sky is not. A biome is a label in the chunk at quarter resolution plus a bundle of consequences hanging off that label. In 26.2 the bundle has been hollowed out: `Biome` itself holds five things, and most of what a player would call "the biome" now lives in the environment-attribute stack, where the biome is one layer among several rather than the owner ([environment attributes and timelines](../world/environment-attributes-and-timelines.md)). ## The cast | class | what it decides | when | |---|---|---| | `BiomeSource` | which biome a quart cell gets. Four implementations, and `BiomeSource.possibleBiomes` is the memoised pre-filter everything else leans on | `ChunkStatus.BIOMES`, on a worldgen worker | | `Climate.Sampler` | the six climate numbers at a point. Filling a chunk uses `NoiseChunk.cachedClimateSampler`, the chunk-wrapped copy; `RandomState.sampler` is the flattened, cacheless one everything outside a chunk asks ([density functions](density-functions.md)) | per quart cell | | `Climate.ParameterList` | the search space: one `Climate.ParameterPoint` per biome, indexed by a `Climate.RTree` | built once per world | | `OverworldBiomeBuilder` | the overworld's parameter table, in Java — temperature, humidity, erosion and continentalness bands over six tables of biome keys | build time | | `LevelChunkSection` | where the answer lives: a second `PalettedContainer` keyed by biome holder, two bits per axis | written once, saved, shipped | | `BiomeManager` | the jitter — which biome this *block* gets, as opposed to which cell it is in | every gameplay read | | `Biome` | five things: climate settings, an `EnvironmentAttributeMap`, `BiomeSpecialEffects`, generation settings and mob settings | — | | `EnvironmentAttributeMap` | the biome's contribution to sky, fog, music and the gameplay switches — as *modifiers*, not values. Twenty gameplay attributes exist; the sixty-six vanilla biome files touch three of them between them, and fifty-one touch none | per attribute, per read | ## The trace: a chunk's biomes ```mermaid sequenceDiagram participant CST as ChunkStatusTasks participant NBC as NoiseBasedChunkGenerator participant CA as ChunkAccess participant LCS as LevelChunkSection participant MNBS as MultiNoiseBiomeSource participant ClimS as Climate.Sampler participant CPList as Climate.ParameterList participant CRT as Climate.RTree CST->>NBC: createBiomes — ChunkStatus.BIOMES, which NOISE and SURFACE both require NBC->>NBC: fork to init_biomes, wrap the resolver in Blender and BelowZeroRetrogen NBC->>CA: fillBiomesFromNoise, with the chunk's cached climate sampler CA->>LCS: fillBiomesFromNoise — rebuild the container, 64 cells per section loop per quart cell LCS->>MNBS: getNoiseBiome — quart coordinates and the sampler MNBS->>ClimS: sample — six functions, each multiplied by 10,000 and truncated ClimS-->>MNBS: a Climate.TargetPoint of six longs MNBS->>CPList: findValue — the parameter list owns the search CPList->>CRT: findValueIndex — nearest neighbour over seven dimensions CRT-->>LCS: a biome holder, into the palette end Note over LCS: saved under "biomes", shipped inside the chunk payload ``` The two wrappers in the second arrow only do anything beside chunks an older version generated — [blending at the old-chunk border](blending.md) is where they are explained. **Biomes are decided before terrain, and not for it.** `ChunkStatus.BIOMES` precedes `ChunkStatus.NOISE`, and the two do not depend on each other at all: the noise fill never reads a biome. What makes a jungle and its terrain agree is that both were computed from the *same* noise router — `RandomState` builds the climate sampler out of the depth, continents, erosion and ridges functions, the very ones that shape the land. Neither was consulted about the other. The biome does not touch a block until `ChunkStatus.SURFACE` ([terrain](terrain.md)). Only the noise generator does the above. `FlatLevelSource` and `DebugLevelSource` inherit the base implementation and use the level's uncached sampler directly. The End is a `NoiseBasedChunkGenerator` like the overworld and the nether — just with `TheEndBiomeSource` in front of it, which does not do a climate search at all: it thresholds a single erosion sample outside a fixed central radius. ## The search, and the axis that is not sampled `Climate.quantizeCoord` multiplies each of the six climate values by ten thousand and truncates, so the entire search is integer arithmetic. The target is a `Climate.TargetPoint` of six longs; each biome declares a `Climate.ParameterPoint` of six `Climate.Parameter` intervals; and `Climate.ParameterList.findValue` walks a `Climate.RTree` — six children per node — minimising the sum of squared distances from the target to each interval. Except the count is seven, not six. `Climate.PARAMETER_COUNT` is **7** and `Climate.RTree.create` refuses a point that does not supply seven, because each `Climate.ParameterPoint` carries a scalar *offset* alongside its six intervals, and `Climate.TargetPoint.toParameterArray` appends a literal zero as the seventh coordinate of every query. So the seventh term of the metric is always that biome's offset squared: a fixed penalty added to its score. It is a "make this biome harder to win" dial, not anything sampled from the world. The tree also remembers. `Climate.RTree` keeps the winning leaf in a `ThreadLocal` and seeds the next search with it as the initial candidate. Adjacent quart cells almost always resolve to the same biome, so the walk usually prunes immediately — which is what makes filling sixty-four cells a section cheap. The tree is therefore stateful per thread, though never incorrect: the remembered leaf is only a starting bound. One thing the parameter table does *not* have is a separate underground system. `OverworldBiomeBuilder.addUndergroundBiomes` and `OverworldBiomeBuilder.addBottomBiome` place dripstone caves, lush caves, sulfur caves and the deep dark into the same seven-dimensional table by their *depth* band. A cave biome is an ordinary entry that happens to win only below the surface. ## The two borders The label goes into the section's biome palette — two bits per axis, so **sixty-four biome cells per section** — is written to NBT under *biomes* ([chunk storage](../world/chunk-storage.md)) and is shipped to the client inside the chunk payload. From then on it is *stored*, not computed, which is why `/fillbiome` can exist at all and why `ClientboundChunksBiomesPacket` exists to tell the client about it. And then two different readers ask for it two different ways. | | the jittered read | the exact read | |---|---|---| | entry point | `LevelReader.getBiome` → `BiomeManager.getBiome` | `BiomeManager.getNoiseBiomeAtPosition`, and on the client `BiomeManager.getNoiseBiomeAtQuart` | | what it does | offsets by two, takes the eight surrounding quart corners, and picks the one minimising `BiomeManager.getFiddledDistance` — a seeded hash worth up to ±0.45 of a cell per axis | floors to the quart cell and reads the palette | | who uses it | freezing and precipitation, mob spawning, commands — **and block tint**: grass, foliage and water colour, through `ClientLevel.calculateBlockTint` | the environment-attribute stack, and nothing else | | what it looks like | the ragged border | the straight one | **Block tint is on the jittered side**, which is the half of this that surprises people: grass colour follows exactly the same ragged line as whether snow falls. What softens the colour boundary in game is not the biome lookup but a box blur on top of it — `ClientLevel.calculateBlockTint` averages the result over the columns named by the *biome blend radius* option and caches that in a `BlockTintCache`. Fog and sky are the ones on the other border. The client's exact read is the more expensive of the two, and unconditionally so: `EnvironmentAttributeProbe.tick` runs a `GaussianSampler` over the neighbourhood **every tick**, accumulating whole `EnvironmentAttributeMap`s into a `SpatialAttributeInterpolator`. Whether an attribute is actually interpolated is tested later, when the layer is applied, and one that fails the test falls back to a single unfuzzed lookup. The server never interpolates at all — it passes no interpolator. ## What a biome still owns Five things, and none of them stops being read once the chunk is generated — `NaturalSpawner` asks the mob settings every tick and a bone-mealed grass block asks the generation settings. `Biome.climateSettings` is precipitation, a base temperature, a `Biome.TemperatureModifier` and downfall. Temperature is the interesting one: `Biome.getHeightAdjustedTemperature` samples noise per block high above sea level — which is why snow lines are ragged rather than flat — and `Biome` keeps a fixed-size per-thread cache in front of it that evicts rather than grows. Most of the public surface is the questions rather than the number: `Biome.warmEnoughToRain`, `Biome.coldEnoughToSnow`, `Biome.shouldFreeze`, `Biome.shouldSnow`. `Biome.getBaseTemperature` is the raw, uncached, unadjusted escape hatch. `BiomeSpecialEffects` is, in 26.2, **only block tint** — five fields, all of them colours or a grass-colour modifier, and only the water colour is mandatory. Fog, sky, clouds, ambient sound, music and particles have all left it for the attribute stack ([lightmap, fog and sky](../rendering/lightmap-fog-and-sky.md)). And when the four optional ones are silent, the tint does not come from the biome at all: grass and foliage colour are a lookup into the colormap images by temperature and downfall, through `GrassColor`, `FoliageColor` and `DryFoliageColor`. "The biome's grass colour" is usually just the two climate numbers that index a texture. `Biome.getAttributes` returns the `EnvironmentAttributeMap`, whose entries are `AttributeModifier`s rather than values — so a biome may *override* the layer below it or merely *modify* it, which is how a swamp thickens water fog without naming a distance. One restriction lands here specifically: `EnvironmentAttributeMap.CODEC_ONLY_POSITIONAL` means a biome may not set a non-positional attribute at all. `BiomeGenerationSettings` is a set of carvers and one set of placed features *per decoration step*, read by [features and placement](features-and-placement.md); `BiomeGenerationSettings.getBoneMealFeatures` is its only reader outside worldgen, and its one caller is `GrassBlock`. `MobSpawnSettings` is the weighted spawn lists `NaturalSpawner` reads ([entity lifecycle](../entities/entity-lifecycle.md)) — and its entry constructor silently rewrites any miscellaneous-category entity type to pig. ## Questions players ask **Why do I always spawn near the origin?** Because the *chunk* is chosen by a climate search. `Climate.SpawnFinder` and `Climate.findSpawnPosition` look for the point whose climate best matches the noise settings' spawn target, in two spiral passes out to a maximum radius of 2,048 blocks, with depth pinned to zero and the fitness deliberately biased toward the origin so that a tie lands near 0,0. `MinecraftServer` then keeps only that answer's chunk and does a terrain search inside it: an eleven-by-eleven chunk spiral of `PlayerSpawnFinder.getSpawnPosInChunk`, over a first guess taken from `ChunkGenerator.getSpawnHeight` or, failing that, the *WORLD_SURFACE* heightmap. A dimension whose settings name no spawn target skips the climate half and starts that spiral at the origin chunk. **Why does `/locate biome` find biomes in chunks I have never visited?** Because it asks the generator, not the world. `BiomeSource.findClosestBiome3d` spirals through `BiomeSource.getNoiseBiome` with the live sampler and never reads a palette — so it finds biomes in ungenerated chunks and will **never** find one placed by `/fillbiome`. It pre-filters against `BiomeSource.possibleBiomes`, so asking for an impossible biome fails instantly rather than after a spiral out to six thousand four hundred blocks. **Can a data pack change the overworld's biome layout?** Not the vanilla preset. `OverworldBiomeBuilder` is hardcoded, and the data-pack element that would carry a parameter list serialises to nothing but a preset name — there are two presets. A pack can supply its own parameter list for a multi-noise source; it cannot edit the one that ships. **What does the client actually have?** A hollow `Biome`. `Biome.NETWORK_CODEC` sends the climate settings, the *syncable* attributes and the effects, and substitutes empty generation and mob settings — features, carvers and spawn lists never cross the wire. And when a client asks for a biome in a chunk it does not have, it gets plains. **Does the biome decide which mobs spawn?** Not on its own, and not first. `ChunkGenerator.getMobsAt` consults `Structure.spawnOverrides` *before* `Biome.getMobSettings` ([structure placement](structure-placement.md)), and nether fortresses are special-cased earlier still, inside `NaturalSpawner`. **Why does adding one biome change the whole dimension?** Because `EnvironmentAttributeSystem` builds one positional layer per attribute that *any* biome in the registry mentions, at level construction. One new biome naming one new attribute adds a layer every position in the dimension then carries. ## Where to look `Biome` · `Biome.getAttributes` · `BiomeSpecialEffects` · `BiomeSource.getNoiseBiome` · `BiomeSource.possibleBiomes` · `MultiNoiseBiomeSource` · `TheEndBiomeSource` · `Climate.Sampler.sample` · `Climate.quantizeCoord` · `Climate.ParameterList.findValue` · `Climate.RTree.search` · `Climate.PARAMETER_COUNT` · `Climate.findSpawnPosition` · `OverworldBiomeBuilder.addBiomes` · `OverworldBiomeBuilder.addUndergroundBiomes` · `LevelChunkSection.fillBiomesFromNoise` · `BiomeManager.getBiome` · `BiomeManager.getFiddledDistance` · `BiomeManager.getNoiseBiomeAtPosition` · `BiomeGenerationSettings` · `MobSpawnSettings` · `BiomeColors` · `EnvironmentAttributeProbe` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Terrain > Verified against **Minecraft 26.2** · Part XII · One chunk's rock: seven hundred and sixty-eight cells filled from their corners, a water table decided before the caves are cut, and the cave that fills with water because of it. You dig into a cave and it is flooded. The water is not a fluid that flowed in and settled; nothing flowed anywhere. Before the cave existed, while the chunk was still solid stone, something decided that a point at that depth in that column belongs to water rather than to air — and when the carver came through and asked what to put in the hole it was digging, that answer was still on file. **A carver does not choose the block it carves.** It chooses the *shape*; the `Aquifer` chooses the material, and it chose it for the stone as well. This page is the three chunk statuses that turn a scalar field into blocks — `ChunkStatus.NOISE`, `ChunkStatus.SURFACE` and `ChunkStatus.CARVERS`, plus the workspace that `ChunkStatus.BIOMES` quietly builds before any of them. The conveyor that runs the statuses, the dependency pyramid and the threading are [the chunk generation pipeline](../world/chunk-generation-pipeline.md) in Part IV; this is the cargo. The scalar field itself is [density functions](density-functions.md), and the labels that steer the surface pass are [biomes](biomes.md). ## The cast | class | what it decides | when | |---|---|---| | `ChunkGenerator` | the API the statuses call — `ChunkGenerator.fillFromNoise`, `ChunkGenerator.buildSurface`, `ChunkGenerator.applyCarvers`. `ChunkGenerators.bootstrap` registers exactly three implementations | worldgen executor | | `NoiseGeneratorSettings` | the whole per-dimension recipe: the `NoiseSettings` cell dimensions, the default block and fluid, the `NoiseRouter`, the surface rules, the sea level, the aquifer and ore-vein switches | data, loaded with the world | | `NoiseChunk` | the per-chunk workspace — the wrapped router, the cell interpolators, the `Aquifer`, the filler chain | built at `ChunkStatus.BIOMES`, dies with the chunk | | `MaterialRuleList` | which filler answers first: the aquifer, then the ore veins | inside the cell loop | | `Aquifer` | what liquid, if any, belongs at a point — and therefore what a carver may leave behind | noise *and* carvers | | `OreVeinifier` | copper or iron, from the sign of one router function | inside the cell loop | | `SurfaceSystem` | the column re-skin: grass over dirt over stone, sand, the badlands bands | `ChunkStatus.SURFACE`, one instance per level | | `WorldCarver` | the shape of caves and canyons, and nothing about their contents | `ChunkStatus.CARVERS` | Everything here runs on the worldgen executor, one task at a time per dimension. Two of the steps fan out further. `ChunkGenerator.createBiomes` forks to the background pool as *init_biomes* for **every** generator — the base implementation does it, so `FlatLevelSource` and `DebugLevelSource` fork too — and `NoiseBasedChunkGenerator` overrides it only to use the chunk's cached sampler. The second fork, `NoiseBasedChunkGenerator.fillFromNoise` as *wgen_fill_noise*, really is the noise generator's alone. `RandomState` — the per-level seed root — is built once in `ChunkMap` and shared by every generating chunk, and it owns the `SurfaceSystem`, which is therefore per **level**, not per chunk. ## Four statuses, and what each hands on ```mermaid flowchart LR BIO["BIOMES"] -- "the NoiseChunk, with its caches and its Aquifer" --> NOI["NOISE"] NOI -- "solid rock, two worldgen heightmaps" --> SUR["SURFACE"] SUR -- "a skin, and a preliminary surface level" --> CAR["CARVERS"] CAR -- "holes, and a CarvingMask" --> FEA["FEATURES"] ``` The odd arrow is the first one. **The workspace is born one status before the terrain needs it**, because the biome sampler wants the chunk's caches too ([biomes](biomes.md) reads the climate functions through `NoiseChunk.cachedClimateSampler`). So `NoiseChunk.forChunk` runs at `ChunkStatus.BIOMES`, wrapping the seeded router into chunk-local caches and constructing the `Aquifer` and the `NoiseChunk.BlockStateFiller` chain, before a single block exists. That one instance then serves all three terrain steps, which is exactly what makes the aquifer's answers agree between filling and carving. It is heavily mutated on the way — and `NoiseChunk.stopInterpolation`, at the end of the noise fill, *disarms* it: from the surface step onward, sampling an interpolator with the `NoiseChunk` itself as the context throws. A caller that passes any other context is quietly served by the wrapped function instead, which is what every post-fill caller does. What survives for reuse is the aquifer's grid cache and the preliminary surface level. It is never cleared and it is not pinned to a thread; the three steps run as separate tasks on whichever worker takes them, and what serialises them is the chunk-status future chain rather than thread affinity. ## Filling the noise: six loops, one number at the bottom The overworld's `NoiseSettings` asks for a horizontal noise size of one and a vertical size of two, and `NoiseSettings.getCellWidth` and `NoiseSettings.getCellHeight` turn those into blocks by multiplying by four. So the unit of overworld terrain is a cell **four blocks wide, four deep and eight tall**, and a chunk is four by four by forty-eight of them. **768** — cells in one overworld chunk, each holding 128 blocks (`NoiseBasedChunkGenerator.fillFromNoise`). `NoiseChunk` evaluates the *interpolated* density terms at cell **corners** only. Everything inside a cell is three linear interpolations away from the eight corners around it, and the walk that does this is six loops deep: ```mermaid flowchart TB subgraph CX["for each of the 4 cell columns in X: advanceCellX fills the next corner slice, swapSlices drops the old one"] subgraph CZ["for each of the 4 cell rows in Z"] subgraph CY["for each of the 48 cells in Y, downward: selectCellYZ loads its eight corner values"] subgraph BY["for each of the 8 block layers in the cell, downward: updateForY"] subgraph BX["for each of the 4 blocks across in X: updateForX"] BZ["for each of the 4 blocks across in Z: updateForZ, then getInterpolatedState — one block decided"] end end end end end ``` Read the nesting as the cost model. The two outer levels are where the sampling happens — a slice of corner values is filled per cell column and dropped one column later — and everything below `NoiseChunk.selectCellYZ` is arithmetic on eight numbers. Counting the slices, one *interpolated* term is sampled five times five times forty-nine per chunk: **1,225** — corner samples per interpolated density term, per chunk (`NoiseChunk.fillSlice`, five slices of five by forty-nine). The Y direction runs **downward** at both nesting levels, which matters because the two worldgen heightmaps are updated as blocks are written and the first non-air block seen from the top is the answer. Two things about that lattice are worth stating plainly, because "Minecraft terrain is a lattice" is true twice over at two different resolutions. Only the terms explicitly marked *interpolated* come from the eight-corner lerp, and resolving every reference in the overworld router finds **eight** of them: one round the whole final-density subtree, four inside the noodle-cave graph, and three across the two vein terms — *vein_gap* is not one of them, and neither is the aquifer's barrier, which the `Aquifer` samples per block. The final density is then wrapped in a *cache_all_in_cell*, filled for every block in the cell. Meanwhile the 2-D shaping terms, continentalness and erosion and ridges and the splines, sit behind *flat_cache*, which samples once per **four-by-four block column group** at y = 0 and reuses that for all sixteen columns. Only *cache_2d* is exact per column ([density functions](density-functions.md) owns the caches). The write at the bottom of the loop does not go through `ChunkAccess.setBlockState`. The fill calls the section setter directly with the threading check disabled and updates `Heightmap.Types.OCEAN_FLOOR_WG` and `Heightmap.Types.WORLD_SURFACE_WG` by hand, and it skips air entirely — a chunk starts empty, so only non-air is ever written. The acquire/release pair around the whole fill is a concurrent-access assertion, not a lock. ## The two fillers: what the number becomes `NoiseChunk.getInterpolatedState` does not read the density and compare it to zero. It runs the `MaterialRuleList` — a chain of `NoiseChunk.BlockStateFiller`s — and takes the first non-null answer. There are two of them, in this order. **The aquifer.** `Aquifer.computeSubstance` receives the final density and decides, from its own barrier and fluid-level noises sampled on a coarse grid, whether this point is stone, air, or a fluid. `Aquifer.FluidPicker` and `Aquifer.FluidStatus` are the global fallback underneath its local water tables — the sea, and the lava. `Aquifer.NoiseBasedAquifer` is the real implementation; a dimension with aquifers switched off gets a trivial one. **The ore veins.** `OreVeinifier` is the second filler, active only when the settings enable it, and it is why copper and iron veins are *terrain rather than decoration*: they exist before the surface pass and before the carvers, and no feature places them. Which of the two `OreVeinifier.VeinType`s you get is the **sign** of one router function, `NoiseRouter.veinToggle` — there is no separate "which ore" noise. If both fillers return nothing, the block becomes the settings' default block. And `Aquifer` marks the positions where it placed fluid for post-processing, so the settled water table you can see in a cross-section becomes real fluid ticks the moment the chunk is promoted — which is why "nothing flowed in" is a true statement about worldgen and not about the chunk's first live tick ([scheduled ticks](../world/scheduled-ticks.md)). ## The surface pass, and the two places it breaks its own rule `SurfaceSystem.buildSurface` compiles the **dimension's** `NoiseGeneratorSettings.surfaceRule` tree once for the whole chunk — one rule tree per dimension, which then branches on biome inside itself, then walks each of the 256 columns downward from the worldgen surface heightmap, tracking depth below stone and water height in a `SurfaceRules.Context` that carries its own caches. Every write is gated on the existing block still being the settings' **default block**, which is what makes ore veins and aquifer water immune to being turned into grass. Two things sit outside the rule system entirely, and neither obeys that gate. `SurfaceSystem.erodedBadlandsExtension` runs *before* the column walk and fills air with the default block to raise the terracotta pillars. `SurfaceSystem.frozenOceanExtension` runs *after* it and writes snow and packed ice over air **and over water**, ungated. Both are selected by biome rather than by rule, and `SurfaceSystem` owns the noises they need along with the ones for the badlands bands and the icebergs. ## Carving, and who chooses the block `ChunkGenerator.applyCarvers` does not carve the chunk it was given from the chunk it was given. It loops over a **17×17 neighbourhood of source chunks**, asks each configured carver of that source chunk's biome whether a cave or a canyon *starts* there, and carves whatever does into the centre chunk. That reach costs the dependency pyramid nothing: the neighbours are read only as memo holders for `ChunkAccess.carverBiome`, and the biome itself is recomputed from the biome source. The reach the carvers need is already paid for: six of the generation steps ask for `ChunkStatus.STRUCTURE_STARTS` eight chunks out, written as a bare literal — the constant `ChunkStatus.MAX_STRUCTURE_DISTANCE` that holds the same eight is read by nothing — and the accumulated pyramid the ticket system sizes itself against is wider still ([the chunk generation pipeline](../world/chunk-generation-pipeline.md)). Three carvers are registered — `WorldCarver.CAVE`, `WorldCarver.NETHER_CAVE` and `WorldCarver.CANYON` — each paired with a `CarverConfiguration` as a `ConfiguredWorldCarver`, reading the world through a `CarvingContext` and recording what they touched in a `CarvingMask`, the per-chunk bit set. And then the hook. `WorldCarver.getCarveState` returns lava below the configured lava level, and otherwise asks `Aquifer.computeSubstance` with a density of **zero** what belongs at this point. `Aquifer.FluidStatus.at` answers plain air above the local water table and the fluid below it — never null; the null is `Aquifer.computeSubstance`'s own, and it means *do not carve here at all*. So the water in a flooded cave was decided by the same object that decided the water in the stone around it, and a dry cave is the carver writing air one block at a time because the aquifer told it to. What a cave may eat through is itself a data-pack decision: `WorldCarver.canReplaceBlock` tests the configuration's *replaceable* `HolderSet`, which is also what stops a carver from hollowing out an ore vein it was not told about. If a grass or mycelium block was passed on the way down, the dirt below is re-skinned through `SurfaceSystem.topMaterial`. `NetherWorldCarver` is the exception that proves the rule. It overrides `WorldCarver.carveBlock`, never consults the aquifer at all, and writes lava below thirty-one blocks above the dimension's minimum and cave air above. ## Questions players ask **Why is there always lava at the same depth, in every world?** Because the sea level moves with the noise settings and the lava level below it does not — it is a constant in the generator, not a data-pack field. **Do the caves change if I switch the dimension to the modern random source?** Not their seeding. `NoiseBasedChunkGenerator.applyCarvers` hardcodes a `LegacyRandomSource` whatever the settings say. The settings' `NoiseGeneratorSettings.useLegacyRandomSource` reaches further than the level's root random in one other way, though: it re-seeds `BlendedNoise`, and it changes the *Y* at which the surface pass samples a column's biome. The legacy nether biome noises are not part of that — `RandomState`'s wiring visitor builds those two from a `LegacyRandomSource` whatever the setting says. **Where does the flat shelf under a village come from?** From this page's density field, not from any block edit. `NoiseChunk`'s constructor adds `DensityFunctions.BeardifierMarker` to the router's final density *itself* and then swaps that exact instance for the chunk's real `Beardifier` while wrapping — so every noise dimension is beardified whether or not its router JSON ever mentions a beardifier, and "structures flatten terrain" is implemented as an object comparison inside a visitor ([structure placement](structure-placement.md)). **What height does a structure think the ground is at, then?** The one *before* it changes it. `NoiseBasedChunkGenerator.iterateNoiseColumn`, behind `ChunkGenerator.getBaseHeight` and `ChunkGenerator.getBaseColumn`, builds a throwaway one-cell `NoiseChunk` with an empty [`Blender`](blending.md) and the bare beardifier marker, samples a column, and discards it. **What happens at the boundary with chunks generated by an older version?** `Blender` reads `BlendingData` harvested from the neighbours and enters the density graph as three nodes — [blending at the old-chunk border](blending.md) is the page for it, and for `BelowZeroRetrogen`, the world-deepening path that rides the same hooks: it wraps the biome resolver, patches bedrock after the noise step, and gates the spawn step. **Is any of this really running in a superflat world?** Almost none of it. `FlatLevelSource` and `DebugLevelSource` implement surface, carvers and spawning as no-ops, and `DebugLevelSource` writes its state grid at the decoration step rather than the noise step. Development builds can switch off much more: `SharedConstants` carries flags that disable the surface pass, the carvers, the aquifers, the ore veins and fluid generation outright, plus visualisation modes that make the aquifer and the ore veins write marker blocks instead of real ones. **Why does a half-generated chunk have the wrong heightmaps in it?** Because which two are live is a property of the *status*, not of the step: `ChunkStatus` registers every status up to and including `ChunkStatus.SURFACE` with the worldgen pair and `ChunkStatus.CARVERS` onward with the four final ones. A chunk saved mid-generation really does persist its worldgen heightmaps, and they stop being written from the carvers status on ([chunk anatomy](../world/chunk-anatomy.md)). ## Where to look `ChunkGenerator` · `ChunkGenerators.bootstrap` · `NoiseBasedChunkGenerator.fillFromNoise` · `NoiseGeneratorSettings` · `NoiseSettings.getCellWidth` · `NoiseChunk.forChunk` · `NoiseChunk.fillSlice` · `NoiseChunk.getInterpolatedState` · `NoiseChunk.stopInterpolation` · `MaterialRuleList` · `Aquifer.computeSubstance` · `Aquifer.NoiseBasedAquifer` · `OreVeinifier.create` · `SurfaceSystem.buildSurface` · `SurfaceRules.RuleSource` · `SurfaceRules.Context` · `NoiseBasedChunkGenerator.applyCarvers` · `WorldCarver.getCarveState` · `WorldCarver.canReplaceBlock` · `CarvingContext` · `CarvingMask` · `Beardifier` · `Blender` · `BelowZeroRetrogen` · `Heightmap.Types` · `RandomState.create` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Blending at the old-chunk border > Verified against **Minecraft 26.2** · Part XII · a chunk generated beside one an older version left behind, and the five places its generation is bent toward that neighbour. You are walking east through a world you have had for years. Behind you the ground was decided by a version that is not this one; ahead of you it has not been decided at all. Put the debug screen up and the chunk-generation entry reads *Blending: Old* while you stand on the old side, and stops reading it once you cross. For roughly a hundred blocks either side of that line the game is generating chunks that are not free to be whatever the seed says — and directly against the seam they are hardly generated at all. The three splines that shape overworld terrain are swapped out for a ground height the game read off the old chunk's blocks a moment earlier, the constant ten, and zero. This is the deliberate exception [the part's premise](README.md) names. Everywhere else in world generation, a chunk is a function of the seed and the data packs. Here it is a function of the seed, the data packs, **and the blocks measured by up to a hundred and ninety-three chunks around it** — which the game does not remember and has to go and measure, one column at a time. ## The flag is a nullable field, and looking for it costs a disk read A chunk is old if `ChunkAccess.blendingData` is non-null. That is the whole test: `ChunkAccess.isOldNoiseGeneration` returns exactly whether that field is set, and the field is *final* — it arrives through the constructor from `SerializableChunkData`, which reads a *blending_data* compound out of the chunk's NBT, and nothing sets it afterwards. Which saves carry that key is `util/datafix`'s business, which [this book skips](../anatomy/what-this-book-skips.md); by the time world generation sees a chunk the key is either there or it is not. > **For a 1.21-era reader.** The class that used to read and write the chunk > NBT is now `SerializableChunkData`, a record with a *blending_data* > component and its own parse and write halves. The old *ChunkSerializer* > name is gone. The awkward part is that a chunk being generated cannot ask its neighbours whether they are old, because most of them do not exist yet. So `Blender.of` — the factory every noise-generation step calls — starts by asking the *save file*. `WorldGenRegion.isOldChunkAround` hands the question to the level's `ChunkMap`, which inherits `SimpleRegionStorage.isOldChunkAround` and lands on `IOWorker.isOldChunkAround`. That walks the region files covering a square of radius seven, and for each region it needs a `BitSet` with one bit per chunk, built by scanning all 1,024 chunks in the region for two NBT fields and nothing else: *DataVersion* and *blending_data*. A chunk counts as old if its stored *DataVersion* is below 4882 or if it already carries a *blending_data* compound. The scan runs on the background executor, the caller joins it, and the bitset is kept in a 1,024-entry region cache. **Every** chunk generated in **every** world pays for that lookup at least twice, whatever generator the dimension uses: `ChunkStatusTasks.generateBiomes` and `ChunkStatusTasks.generateNoise` both evaluate `Blender.of` eagerly, before knowing whether the answer can possibly be yes. The noise generator adds two more — `NoiseBasedChunkGenerator.buildSurface`, eagerly again, and `NoiseBasedChunkGenerator.applyCarvers`, the only call site written inside a supplier and so the only one that can be skipped. When nothing is old the bitsets come back empty and `Blender.of` hands back the shared empty blender, which is not an empty map but an anonymous subclass overriding the three answering methods with identities: alpha one, offset zero, density unchanged, resolver returned as given. ## The cast | class | what it decides | when | |---|---|---| | `Blender` | the four answers — the height alpha and offset, the blended density, the biome override, and where carvers may not dig | built per step, used on the worldgen executor | | `BlendingData` | one old chunk's measurements: a ring of sixteen columns holding a height, a density profile and a biome column each | measured once per loaded chunk object | | `IOWorker` | whether any chunk within seven is old, from a per-region `BitSet` scanned out of the region files | on the background executor, joined by the caller | | `NoiseChunk` | where the answers enter the density graph: two pre-filled flat caches and one wrapper | `ChunkStatus.BIOMES` onward, cached on the chunk | | `NoiseRouterData` | which router functions are blendable at all — three overworld splines and one post-process wrapper | world creation, once | | `CarvingMask` | the extra predicate that makes carvers treat old ground as already carved | `ChunkStatus.CARVERS` | | `BelowZeroRetrogen` | the separate world-deepening path that rides the same hooks | `ChunkStatus.NOISE` and `ChunkStatus.BIOMES` | | `SerializableChunkData` | the *blending_data* key: which chunks are flagged, and which measurements survive a save | chunk load and save | ## One measurement, five consumers `BlendingData` is gathered once and then read by five unrelated pieces of machinery at four chunk statuses — two inside the density graph, three nowhere near it. ```mermaid flowchart TB OLD["An old chunk: its blocks on disk, plus a blending_data tag"] BD["BlendingData: a ring of 16 columns round the chunk edge, each a height, a density profile and a biome column"] B["Blender: 193 chunk positions consulted for height and biome, the inner 9 of them also for density"] OLD --> BD BD --> B B --> R1["BIOMES: getBiomeResolver returns the nearest old biome, or defers"] B --> R2["BIOMES and NOISE: blend_alpha and blend_offset, two flat caches filled in the NoiseChunk constructor"] B --> R3["NOISE: blend_density, a marker wrapped round the final slide"] BD --> R4["CARVERS: an extra carving mask, so carvers skip old ground"] BD --> R5["FEATURES: border ticks on leaves and fluids, on the old chunk only"] R4 -.- SN["static on Blender, straight off each chunk's BlendingData — the two maps are not consulted"] R5 -.- SN ``` Two maps, not one. `Blender.of` sweeps the square from seven chunks west to seven chunks east and clips it to a circle — the test is that the squared offsets sum to no more than sixty-four — which leaves **193 positions**. Every one that yields data goes into the height-and-biome map; only the inner three by three also goes into the density map. That asymmetry is the whole reason the terrain seam is a hundred blocks wide and the cave seam is a handful. A neighbour yields data only if it passes two tests in `BlendingData.getOrUpdateBlendingData`: it carries a `BlendingData`, **and** `ChunkAccess.getHighestGeneratedStatus` is not before `ChunkStatus.BIOMES`. The second test is what keeps this honest — during the *BIOMES* step the dependency window only guarantees neighbours at *STRUCTURE_STARTS*, so a half-built chunk in the queue contributes nothing, and in practice the only chunks that pass are ones loaded whole from the save. **Seven** — the radius in chunks the height blend reaches, which `Blender` derives from the twenty-seven-cell height range: four quart cells per section across seven sections, less one, plus three, converted back to chunks. It sits inside the radius-eight dependency window every noise step declares, so none of the 193 reads can trip `WorldGenRegion.getChunk`'s out-of-range crash. ## Sixteen columns, read out of blocks `BlendingData` does not store a copy of the old terrain. It stores sixteen columns in a ring round the chunk's edge, measured out of that same old chunk's own blocks the first time anyone asks — `BlendingData.getOrUpdateBlendingData` fetches one chunk and hands it to its own `BlendingData.calculateData`. Seven of them are *inside* indices — the corner and three more along the north edge, three along the west — at block coordinates 0, 4, 8 and 12. The other nine are *outside* indices, sampled at block coordinate 15 along the east and south edges, which in cell arithmetic belong to the next chunk's first cell. Sixteen slots is what `BlendingData.Packed`'s codec validates the saved height array against, and it is exactly the array length the class computes from a chunk being four quarts wide. Which of the sixteen get filled depends on which way the chunk faces new ground. `BlendingData.sideByGenerationAge` asks each of the eight `Direction8` neighbours whether it is old, and `BlendingData.calculateData` fills only the columns on the sides that are **not** — the ones facing chunks the game is about to generate. There is one call site in the whole game and it passes *false*, so "sides by generation age" only ever means "sides facing new chunks". The method also guards on `BlendingData.hasCalculatedData`, so a chunk object measures itself once and never again: whichever sides were new at that moment are the sides it will carry until it is unloaded. Each filled column gets three things. **A height**: `BlendingData.getHeightAtXZ` starts at the *WORLD_SURFACE_WG* heightmap if the chunk has one primed and at the top of the old area if not, then walks straight down looking for one of eleven block types — podzol, gravel, grass, stone, coarse dirt, sand, red sand, mycelium, a snow *block*, terracotta or dirt — and returns the first Y at which it finds one, or the bottom of the old area if it never does. **A density profile**: for each eight-block-tall cell, `BlendingData` reads fifteen consecutive blocks downward, scoring each plus or minus one for whether it is ground, and divides by fifteen — ground meaning not air, not a leaf, not a log, not a mushroom block and with a non-empty collision shape, so a cave counts as air and a tree does not count as terrain. One more pass rewrites the two cells straddling the measured height so the surface lands where the height said it did. And **a biome column**: one `Biome` holder per four-block layer of the old area, read straight out of that chunk's own biome container. Of those three, **only the heights are saved.** `BlendingData.pack` writes the minimum section, the maximum section and the sixteen doubles, and omits the heights entirely if none of them was ever measured; the density array is declared *transient* and the biome list is not in the codec at all. Unload the region and come back and the game re-reads the old chunk's blocks to rebuild both. ## Following one chunk through ```mermaid sequenceDiagram participant CST as ChunkStatusTasks participant Blender as Blender participant CM as ChunkMap participant BD as BlendingData participant NBC as NoiseBasedChunkGenerator participant NC as NoiseChunk Note over CST: ChunkStatus.BIOMES, on the worldgen executor CST->>Blender: of — build a blender for this chunk Blender->>CM: isOldChunkAround, radius 7 chunks CM-->>Blender: yes, from a bitset scanned out of the region files loop 193 positions, clipped to a circle Blender->>BD: getOrUpdateBlendingData BD->>BD: measure the sides facing new chunks, once per chunk object end Blender-->>CST: a live blender, two maps of BlendingData CST->>NBC: createBiomes with the blender NBC->>NC: forChunk — 25 columns of alpha and offset, filled in the constructor NBC->>NBC: wrap the biome resolver, then wrap that in BelowZeroRetrogen Note over CST,NC: ChunkStatus.NOISE, the same NoiseChunk, cached on the chunk NC->>Blender: blendDensity, once per sampled point inside the marker Blender-->>NC: the old density outright, or a lerp toward the noise ``` The order matters in one non-obvious way. `NoiseChunk` is created at *BIOMES*, not at *NOISE*, because the biome step needs the chunk's climate sampler, and it is then cached on `ChunkAccess`. So the blender that reaches the density graph is the one built at *BIOMES* — the later three are passed to `ChunkAccess.getOrCreateNoiseChunk`, find it already created, and are thrown away. The twenty-five columns are the other thing worth noticing. Before any router mapping runs, the `NoiseChunk` constructor loops over the chunk's five-by-five grid of quart columns, calls `Blender.blendOffsetAndFactor` for each, and fills two `NoiseChunk.FlatCache` instances. Only afterwards does `NoiseChunk.wrapNew` swap `DensityFunctions.BlendAlpha` and `DensityFunctions.BlendOffset` — by object identity, the same trick the beardifier uses — for those already-full caches ([density functions](density-functions.md)). An empty blender skips the swap: the two singletons stay the constants one and zero that they are, and a *blend_density* marker is replaced by its own child. ## What the blender actually answers Three questions, three different shapes of answer, and only two of them are blends. **Height, as an alpha and an offset.** `Blender.blendOffsetAndFactor` first checks whether the sample point sits exactly on a measured column; if it does, it returns alpha zero and the old height, put through `Blender.heightToOffset` — a rational function, not a polynomial. If not, it walks every measured height in the height map, keeps those within twenty-seven quart cells — a hundred and eight blocks — and averages them weighted by the inverse fourth power of distance, with alpha the smoothstep of the *nearest* distance over twenty-eight. Nothing in range means alpha one and offset zero: the identity. Alpha is then used as a mixing weight in `NoiseRouterData.splineWithBlending`, which interpolates from a fixed target at alpha zero to the real spline at alpha one, and this is where the page's opening claim comes from. Exactly three router functions are built that way — *offset*, *factor* and *jaggedness*, for each of the three overworld variants — and their targets are, respectively, `DensityFunctions.BlendOffset`, `NoiseRouterData.BLENDING_FACTOR` (the constant ten) and `NoiseRouterData.BLENDING_JAGGEDNESS` (zero). Against the seam alpha is zero, so overworld terrain is not shaped by its splines at all: the ground height is whatever `BlendingData` measured, the factor is ten and the jaggedness is nothing. The shipped data pack agrees — *blend_alpha* appears in nine density function files and *blend_offset* in three, all of them under the three overworld directories. Because *offset* and *factor* are also the two inputs to `NoiseRouterData.preliminarySurfaceLevel`, which `Aquifer.NoiseBasedAquifer` reads to place its fluid levels, the water table follows the old ground. Nobody wrote a rule for that: it falls out of the aquifer sampling a blended function. **Density, as a lerp with a very short reach.** `Blender.blendDensity` is called per sample from inside `NoiseChunk.BlendDensity`, the wrapper the *blend_density* marker becomes. It measures distance in cells with the Y difference doubled — cells are twice as tall as they are wide — keeps neighbours within two, and mixes toward the noise with an alpha of the closest distance over three. An exact hit returns the old density with no mixing at all. This one wrapper is in every dimension's router: *blend_density* appears in all seven shipped noise settings, while the alpha and offset nodes are overworld-only. **Biome, as a replacement.** `Blender.getBiomeResolver` wraps the biome source in a resolver that asks `Blender.blendBiome` first and only falls through when it declines. And it is not a blend: it finds the nearest measured biome within twenty-seven cells, adds twelve cells' worth of a fixed shift noise to that distance, divides by twenty-eight, and returns the old biome if the result is below one half and nothing at all if it is above. So the biome boundary is a hard line at roughly half the terrain blending distance, roughened by noise — one biome or the other, never a mixture, which is the only answer a palette of biome holders can represent. ## The two consumers that never touch the noise router **Carvers are told to stay out.** At *CARVERS*, `Blender.addAroundOldChunksCarvingMaskFilter` collects the `BlendingData` of all eight `Direction8` neighbours plus the chunk's own, turns each into a `Blender.DistanceGetter` measuring distance to a box eight blocks either side of the chunk centre in X and Z — a whole chunk wide — and as tall as that chunk's old area, and installs the minimum of them as a `CarvingMask.Mask` on the chunk's carving mask. A position within four blocks of any such box — after each axis is displaced by the same shift noise, scaled by four — reads as already carved. Since `WorldCarver.carveEllipsoid` skips any position the mask already reports, the effect is that carvers refuse to dig into or right up against old ground. The mask is additional: it is consulted by `CarvingMask.get` alongside the real bits and never written to the saved array. **Leaves and water at the seam are marked for a second look.** At *FEATURES*, after decoration, `ChunkStatusTasks.generateFeatures` calls `Blender.generateBorderTicks` — and this is the one hook that fires on the *old* side rather than the new one. It returns immediately unless the chunk being generated carries a `BlendingData` of its own, which a chunk generated fresh today never does: it acts only on an old chunk that is still being carried through the statuses, one saved before *FEATURES* or one being deepened. Given such a chunk it sweeps four Y levels — one below and one at the bottom of the old area, one at and one above the top — across all 256 columns, and then, for each of the four horizontal neighbours that is *not* old, walks the whole sixteen-wide face from the bottom of the old area up to that column's *MOTION_BLOCKING* height. Every leaf block and every non-empty fluid it passes goes to `ChunkAccess.markPosForPostProcessing`. Nothing is changed: the positions are queued for the post-processing pass that runs when the chunk becomes live, which is what makes water at the seam flow and orphaned leaves decay instead of hanging there. The step's ordering is what makes the heightmap read safe — `ChunkStatusTasks.generateFeatures` primes the four final heightmaps before decorating, so *MOTION_BLOCKING* is current by the time the border walk reads it. ## The other passenger `BelowZeroRetrogen` is not blending, but it rides the same hooks and is easy to mistake for it. A chunk carrying one is being *deepened* rather than blended: `ChunkAccess.getHighestGeneratedStatus` folds its `BelowZeroRetrogen.targetStatus` in, `ChunkStatusTasks.generateNoise` calls `BelowZeroRetrogen.replaceOldBedrock` and then `BelowZeroRetrogen.applyBedrockMask` if there are holes, and `BelowZeroRetrogen.getBiomeResolver` wraps the blender's resolver in one more layer that keeps three cave biomes from the new generation and takes everything else from the chunk's existing biome column. ## Questions players ask **Why is the seam wide for hills and narrow for caves?** Two maps, two radii. Height and biome are averaged over every measured column within a hundred and eight blocks, gathered from up to 193 chunks; density is mixed only from the nine chunks nearest the one being generated, over two cells. The ground slopes for a hundred blocks and the caves change their mind in eight. **Can the blend leave a visible edge anyway?** Yes, twice over. A column whose old surface is none of the eleven blocks the height scan recognises reports the bottom of the old area instead, and that bogus height is averaged in with the rest. And the biome switch is a threshold rather than a ramp, so the surface rules can change block for block along a line while the terrain under them is still sloping. **Does any of this happen in a brand-new world?** The lookup does, the work does not — every chunk asks `Blender.of` whether an old chunk is near, and it returns the empty blender when none is. Even the lookup is cheap after the first time: the per-region bitset it consults is memoised behind `IOWorker.getOrCreateOldDataForRegion`, so the scan runs once per region and not once per chunk. Development builds can switch the whole thing off with the `SharedConstants.DEBUG_DISABLE_BLENDING` flag, which short-circuits `Blender.of`, the carving-mask filter and the border ticks alike. ## Where to look Start at `Blender.of` and read down: the two maps, the two radii, the three answering methods. Then `BlendingData.calculateData` and `BlendingData.getHeightAtXZ` for what a measurement is, and `BlendingData.Packed` for what survives a save. `NoiseChunk`'s constructor and `NoiseChunk.wrapNew` show where the answers enter the density graph, `NoiseRouterData` which three functions were built to receive them, and `ChunkStatusTasks` when each hook fires. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Features and placement > Verified against **Minecraft 26.2** · Part XII · A chunk decorates: a stream of positions folded through filters, an order every chunk in the dimension already agreed on, and a data pack that can stop the world from opening. The oak in the middle of a plains chunk was not placed by the plains biome. It was placed by a list that every biome in the dimension contributed to, sorted once at world load into one order per decoration step, with an index per entry that the random seed for each feature is derived from. That is how the same seed grows the same forest — and it is also why **two biomes that list the same two features in opposite orders make the world refuse to open.** The order is a topological sort of a graph, and a graph can have a cycle. Decoration is everything the terrain steps did not put there: trees, flowers, ores, lakes, patches, springs. The system separates three things a modder usually wants separately — *what* to build, *where* to try, and *who* wants it — and this page is how those three meet at `ChunkStatus.FEATURES`. The biggest single feature, the tree, has its own page ([trees](trees.md)); the terrain that decoration lands on is [terrain](terrain.md). ## The cast | class | its job | notes | |---|---|---| | `Feature` | the algorithm, with one method: `Feature.place`, which returns whether it wrote anything | **63** registered into `BuiltInRegistries.FEATURE` | | `FeatureConfiguration` | its parameters, per feature type | `NoneFeatureConfiguration` for the ones that need none | | `ConfiguredFeature` | a feature plus its configuration, and **no position logic at all** | the unit a sapling grows | | `PlacedFeature` | a configured feature plus an ordered list of modifiers | the unit a biome names — the only one of the three that owns placement modifiers | | `PlacementModifier` | one function from a position to a *stream* of positions | 15 registered types | | `GenerationStep.Decoration` | the eleven steps, in order, from raw generation to top-layer modification | a biome's list is a list of lists, by ordinal | | `FeatureSorter` | flattens every possible biome's per-step lists into one sorted list per step, with an index lookup | once per generator, memoised | | `WorldgenRandom` | the seed, reseeded absolutely twice: once per chunk, then once per feature | on the worldgen executor | ## The trace: a chunk decorates ```mermaid sequenceDiagram participant CST as ChunkStatusTasks participant ChunkG as ChunkGenerator participant FS as FeatureSorter participant WR as WorldgenRandom participant PlacedF as PlacedFeature participant PMod as PlacementModifier participant CF as ConfiguredFeature CST->>ChunkG: applyBiomeDecoration — write radius 1, four final heightmaps primed ChunkG->>FS: featuresPerStep — one sorted list per step, and an index per PlacedFeature ChunkG->>WR: setDecorationSeed(level seed, chunk corner) ChunkG->>ChunkG: union the biome palettes of the 3x3 chunks, intersect with possibleBiomes Note over ChunkG: per step: structures first, then features in sorted index order ChunkG->>WR: setFeatureSeed(decoration seed, feature index, step) ChunkG->>PlacedF: placeWithBiomeCheck, from the chunk's minimum corner PlacedF->>PMod: fold — each modifier flat-maps one position into zero or more PMod-->>PlacedF: the surviving positions PlacedF->>CF: place, once per surviving position CF->>CF: Feature.place — ensureCanWrite checked once, for the origin ``` **The driver.** `ChunkGenerator.applyBiomeDecoration` starts at the chunk's minimum corner, at the world's minimum Y. There is no eight-block population offset; the scatter comes later, from a modifier. **The seed.** A `WorldgenRandom` is built over a genuinely random seed and then reseeded absolutely, twice, before anything uses it. `WorldgenRandom.setDecorationSeed` derives a per-chunk seed from the world seed and the chunk corner, and each feature then gets `WorldgenRandom.setFeatureSeed` from that seed, its index within the step and the step number, which the seed multiplies by ten thousand to keep the steps apart. Features in a step therefore do *not* share a random stream: every one is reseeded absolutely before it runs, so an extra draw inside a feature perturbs the rest of *that* feature and nothing after it. **Who wants what.** The biome palettes of the surrounding 3×3 chunks are unioned and intersected with the biome source's possible biomes. Every placed feature any of those biomes lists for this step is collected by its index in that step's list, and the indices are **sorted** — that sort is the execution order, and it is the same for every chunk *in that dimension*, because `ChunkGenerator.featuresPerStep` is memoised per generator and built from that generator's possible biomes. The Nether's order has nothing to do with the Overworld's. Within each step, structures at that step are placed before its features ([structure placement](structure-placement.md)). ## The fold `PlacedFeature.placeWithBiomeCheck` starts a stream containing exactly one position — the chunk corner — and flat-maps it through each modifier in list order. Nothing about that is a filter chain in the usual sense: a modifier may return nothing, one position, or many. ```mermaid flowchart TB A["1 position: the chunk corner, at minimum Y"] --> B["RarityFilter — 1 or 0"] B --> C["CountPlacement — N copies of the SAME position"] C --> D["InSquarePlacement — each scattered inside the 16x16"] D --> E["SurfaceWaterDepthFilter — some drop out"] E --> F["HeightmapPlacement — Y is finally set, on top of the surface"] F --> G["BlockPredicateFilter — would a sapling survive here"] G --> H["BiomeFilter — does the biome HERE want this exact feature"] H --> I["ConfiguredFeature.place, once per surviving position"] ``` Three things about that chain are load-bearing and none of them is obvious from a data pack. **A repeating placement does not scatter.** `CountPlacement`, `NoiseBasedCountPlacement` and `NoiseThresholdCountPlacement` are `RepeatingPlacement`s: they emit the *same* position N times, and the scatter is a separate modifier downstream. List order decides the outcome — count-then-scatter gives ten trees in ten places, scatter-then-count gives ten trees in one. **Y is set late.** Positions travel through most of the chain at the world's minimum Y; a `HeightmapPlacement` or a `HeightRangePlacement` is what puts them on the ground, and a chain that forgets one places at the bottom of the world. `WorldGenRegion.getHeight` returns the stored height **plus one**, so a heightmap placement lands on top of the surface rather than in it. Two of vanilla's heightmap presets read the *worldgen* heightmaps, which are not among the four `ChunkStatusTasks.generateFeatures` primed on the way in. **The biome is checked twice.** A feature was selected because *some* biome in the 3×3 wanted it; `BiomeFilter` re-reads the biome at the scattered position and asks whether *that* biome's generation settings contain this exact placed feature. Without it every biome would bleed its trees a chunk in each direction. Vanilla is not consistent about where it goes: the base tree placement ends with the biome filter and the survival-checked variant appends its block predicate *after* it, so both orders ship. The rest of the fifteen modifiers move a position rather than counting or filtering it: `InSquarePlacement` scatters within the chunk, `RandomOffsetPlacement` jitters, `EnvironmentScanPlacement` searches up or down for a surface, `FixedPlacement` names absolute positions. One of the fifteen fits neither shape: `CountOnEveryLayerPlacement` is deprecated, extends `PlacementModifier` directly, and does its own scatter and cave-layer scan inside `PlacementModifier.getPositions`. ## A feature that is a tree of features Six of the sixty-three registered features write no blocks. `Feature.NO_OP` is a deliberate nothing; the other five take other placed features and choose between them, which is how a data pack builds decoration out of decoration rather than out of algorithms: `RandomSelectorFeature` walks a weighted list rolling each entry's chance and falls back to a default; `SimpleRandomSelectorFeature` picks a uniform index; `WeightedRandomSelectorFeature` draws from a weighted list; `RandomBooleanSelectorFeature` flips a coin between two; and `SequenceFeature` places every entry in order and **stops at the first failure**, reporting failure itself. The plains oak is one of these — a random selector between a fancy oak, a fallen oak and a plain oak with bees. All five call `PlacedFeature.place`, not `PlacedFeature.placeWithBiomeCheck` — which is exactly why a `BiomeFilter` inside a nested placed feature is an *error* rather than a no-op. The filter needs the context's top feature, and only the biome-check entry sets it. That is why the "checked" tree placements carry no biome filter and the biome-level ones do. ## What a feature may write, and where it may read `ChunkStatus.FEATURES` is the **only** step in the generation pyramid with a positive block write radius: one, so a tree may cross into a neighbour ([the chunk generation pipeline](../world/chunk-generation-pipeline.md)). The terrain steps declare zero and every other step declares minus one, which no position satisfies — a write from a step that has not declared a radius is logged and dropped. Writes and reads are guarded in different places, and the reads reach much further. `Feature.place` checks `WorldGenLevel.ensureCanWrite` for the origin once, and then each individual write is re-checked by `WorldGenRegion.ensureCanWrite`, which logs — and pauses, in a development environment — and does not write. A canopy that would reach two chunks out is **truncated**, not moved and not abandoned. A *read* outside the write zone is only warned about by `WorldGenRegion.warnIfReadOutsideWriteZone` and still happens. What makes cascading worldgen structurally impossible is one level further out: `WorldGenRegion.getChunk` **throws** rather than loading once the request passes the step's declared dependency radius — eight chunks of chessboard distance at this step — and what it may legally see there is a chunk at `ChunkStatus.STRUCTURE_STARTS`. The supporting value types are worth naming because they are handed three different amounts of world. An `IntProvider` gets a random source and nothing else. `HeightProvider.sample` and `VerticalAnchor.resolveY` get a `WorldGenerationContext`, which despite the name is two integers — the world's minimum Y and its height. Only `BlockPredicate` sees the world itself: it extends `BiPredicate`, and that is why a placement can ask what block is under the sapling. ## Questions players ask **How does a datapack make a world refuse to load?** Feature order is global: every biome's list contributes "this before that" edges to one graph, and `FeatureSorter.buildFeaturesPerStep` topologically sorts it. Two biomes listing the same two features in opposite orders form a cycle, and the sort throws rather than returning an order — it will even re-run itself, dropping one source at a time, to name the smallest offending set. Where you find out depends on which side you are: the **client** calls `ChunkGenerator.validate` from `WorldOpenFlows` while opening the world, catches the exception and offers safe mode. A dedicated server never calls `ChunkGenerator.validate` at all, so there the cycle surfaces later, as a crash report wrapped around the first chunk that tries to decorate. **Why does a chunk keep changing after it has decorated?** Because all eight neighbours write into it when *they* decorate. What makes that safe is not locking and not the dependency graph: every decoration step in a level runs on the single-threaded `ConsecutiveExecutor` named *worldgen*, so no two neighbours are ever inside the centre chunk at once. The dependency graph fixes the *order* — lighting requires the whole ring to have decorated first — not the exclusion. **Does a sapling grow the same way a worldgen tree does?** No — it skips this whole page. `SaplingBlock.advanceTree` and bone meal run on the **server main thread** and call `ConfiguredFeature.place` on the `ServerLevel` directly, so there is no placement chain, no biome filter and no write guard at all (`WorldGenLevel.ensureCanWrite` is an interface default that is always true). It also hand-manages the sapling block, which is a story of its own ([trees](trees.md)). **Is every `Feature` subclass in the registry?** No, and the exception is a good one: `EndPodiumFeature` is constructed directly by the dragon fight and appears in no registry at all. Being a `Feature` and being data-driven are different things. **Are structures and features really the same step?** They share the step loop and not the index space, so a structure and a feature at the same index in the same step draw the *same* feature seed. They also differ in reach: a structure piece gets an explicit writable box covering exactly the centre chunk, while a feature gets only the softer 3×3 write-zone check. ## Where to look `Feature.place` · `ConfiguredFeature` · `PlacedFeature.placeWithBiomeCheck` · `PlacementModifier.getPositions` · `PlacementFilter` · `RepeatingPlacement` · `CountPlacement` · `InSquarePlacement` · `HeightmapPlacement` · `BiomeFilter` · `RandomSelectorFeature` · `SequenceFeature` · `GenerationStep.Decoration` · `FeatureSorter.buildFeaturesPerStep` · `ChunkGenerator.applyBiomeDecoration` · `ChunkGenerator.validate` · `WorldgenRandom.setDecorationSeed` · `WorldgenRandom.setFeatureSeed` · `WorldGenRegion.ensureCanWrite` · `WorldGenRegion.getChunk` · `BlockPredicate` · `HeightProvider` · `VerticalAnchor` · `SaplingBlock.advanceTree` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Trees > Verified against **Minecraft 26.2** · Part XII · One sapling grows: five pluggable parts over one algorithm, a ceiling the crown's size was decided before, and the dark-oak sapling that will never grow on its own. Plant a single dark-oak sapling, feed it bone meal until you run out, and nothing happens. Nothing is wrong with the sapling; there is simply no tree for it to become. `TreeGrower` holds up to six configured features per species — a normal tree and a mega tree, each with a secondary variant, plus two flowering variants — and `TreeGrower.DARK_OAK` fills in exactly one of them, the mega tree. The single-sapling slot is left empty, and `TreeGrower.growTree` finds nothing to place. **The best-known growth rule in the game is implemented as an absence.** Which is a good introduction to this page, because the whole tree kit works like that: the thirty-nine configured tree features are one algorithm, `TreeFeature`, with five slots in it, and almost everything you can say about how a cherry differs from a mangrove is a statement about what is in the slots. [Features and placement](features-and-placement.md) is how a tree gets a position and whether it is attempted at all; this is what happens after `Feature.place` is entered. ## The cast | class | its slot | what varies | |---|---|---| | `TreeFeature` | the algorithm — *final*, one implementation, no subclasses | nothing | | `TreeConfiguration` | nine fields: five of them are the parts below, three are `BlockStateProvider`s (trunk, foliage, and the dirt column laid under the trunk) and one is the *ignore vines* flag | everything | | `TrunkPlacer` | writes the logs, returns where crowns hang | 9 registered types | | `FoliagePlacer` | writes the leaves around one attachment | 11 registered types | | `RootPlacer` | writes roots, and may lift the trunk off the ground | **1** registered type | | `FeatureSize` | the clearance profile — a horizontal radius per height | 2 registered types | | `TreeDecorator` | runs afterwards over what was placed | 10 registered types | | `FoliagePlacer.FoliageAttachment` | the only channel from trunk to crown: a position, a signed radius nudge, and *is the trunk under me two-by-two* | — | Every one of those five is a codec-dispatched type in a built-in registry, so a data pack composes trees freely and cannot add a new *kind* of placer ([the data-driven type pattern](../foundations/data-driven-types.md)). ## One algorithm, five slots ```mermaid sequenceDiagram participant TF as TreeFeature participant RootP as RootPlacer participant TP as TrunkPlacer participant FolP as FoliagePlacer participant TDec as TreeDecorator participant WGL as WorldGenLevel TF->>TP: getTreeHeight — two random draws TF->>FolP: foliageHeight, then foliageRadius — both from the UNCLIPPED height TF->>TF: build-height check, then getMaxFreeTreeHeight — the clearance scan Note over TF: clipped below the profile minimum, or none declared, abandons with nothing written TF->>RootP: placeRoots — false abandons the tree RootP->>WGL: roots, and the moss above them TF->>TP: placeTrunk(clipped height) TP->>WGL: logs TP-->>TF: a list of FoliageAttachments loop one per attachment TF->>FolP: createFoliage(clipped height, attachment, foliageHeight, leafRadius) FolP->>WGL: leaves, DISTANCE 7 as provided end TF->>TDec: place(Context) — logs, leaves and roots, each sorted by Y TDec->>WGL: hives, vines, podzol, propagules TF->>WGL: updateLeaves — a bucketed walk that rewrites every DISTANCE ``` Four things in that diagram are the page's real content. **The crown is sized before the ceiling is measured.** `TreeFeature` samples the trunk placer's proposed height, derives `FoliagePlacer.foliageHeight` and `FoliagePlacer.foliageRadius` from *that* number, and only then runs the clearance scan. The scan's answer, the *clipped* height, is passed on to `TrunkPlacer.placeTrunk` and `FoliagePlacer.createFoliage` — but the two crown numbers travel beside it, already decided. It is a real asymmetry that no shipped tree can express: the only species vanilla lets survive a clipping is the fancy oak, and `FancyTrunkPlacer` derives its cluster count from the clipped height, so a clipped fancy oak gets a *smaller* crown, not a bigger one. The one placer whose foliage height reads the tree height at all is `SpruceFoliagePlacer`, and no spruce declares a minimum clipped height, so a spruce under an overhang abandons itself instead. **Clipping usually kills the tree instead.** `FeatureSize.minClippedHeight` is the only thing that permits a clipped tree at all, and in vanilla exactly one tree declares it: the fancy oak, at four. Every other species abandons itself the moment the scan comes back short. The scan returns *two below* the first blocked layer, so an obstruction at head height yields a negative number and nothing survives it. **The scan asks the trunk placer what "free" means, not the feature size.** `FeatureSize` supplies only the radius to test. `TrunkPlacer.isFree` is air, anything in the replaceable-by-trees tag, **or an existing log** — which is how a new tree grows up through an old one — and it delegates to a *virtual* `TrunkPlacer.validTreePos`, so `UpwardsBranchingTrunkPlacer` quietly widens the definition with its own *can grow through* block set. A vine anywhere in the scanned column also fails, unless the configuration sets `TreeConfiguration.ignoreVines`. **Nothing rolls back.** There are three places a tree can abandon itself — the build-height check, the clipped-height check, and `RootPlacer.placeRoots` returning false — and all three happen before a single block is written. After `TrunkPlacer.placeTrunk` begins there is no undo, and `TreeFeature` reports success even for a tree that was truncated to a stump. ## The nine trunk placers The base contract is three numbers — `TrunkPlacer.getTreeHeight` is a base height plus two independent random draws — and one method that writes logs and returns attachments. | type | how it differs | |---|---| | `StraightTrunkPlacer` | one column, one attachment one block *above* the top log | | `ForkingTrunkPlacer` | leans near the top, then grows a side branch in a second random direction — and if that direction happens to equal the first, the branch is skipped and the draw is spent anyway. The main fork's attachment carries a radius nudge of +1 | | `GiantTrunkPlacer` | a 2×2 column, four dirt blocks beneath it, and only the (0,0) column placed on the very top layer. Its attachment sets *double trunk*, as `DarkOakTrunkPlacer`'s does | | `MegaJungleTrunkPlacer` | the giant trunk, plus side branches laid along a random angle every few levels, each attachment nudged **−2** | | `DarkOakTrunkPlacer` | a leaning 2×2 trunk whose lean is two minus a draw from three, so two steps, one or none with equal chance, plus a ring of downward log stubs on a one-in-three roll per position — placed relative to the *original* trunk, not the leaned one. Its main attachment sits on the top log rather than above it | | `FancyTrunkPlacer` | see below | | `BendingTrunkPlacer` | rises, nudges once, then walks *horizontally* for a sampled bend length — and emits an attachment at every position along the whole arc, including ones where the log was not placed | | `UpwardsBranchingTrunkPlacer` | a straight column that rolls a probability after each log and, on success, runs a diagonal staircase branch outward, attaching foliage at every branch log. The one placer that widens what counts as free | | `CherryTrunkPlacer` | one to three branches that random-walk toward a computed endpoint, choosing vertical or horizontal per step by the remaining ratio, with the log axis rotated sideways for the horizontal runs. It derives a **fourth branch-height provider in its constructor that no codec ever sees**, which is why the codec insists the declared range spans at least two blocks | `FancyTrunkPlacer` is the one worth watching, because it is the only placer that plans before it writes. It works out a crown position per level from a circle equation, then walks the line from trunk to crown *twice*: once with placement switched off, purely to ask whether every block on the way is free, and again for real only if it was. A branch whose base has slid below the trunk top is clamped, which is the whole "branches slope down as they go out" look. And it carries one computation that cannot do anything: **One** — crown candidates a fancy oak tries per level, always, because the count is a minimum against one, over an expression that is never below one (`FancyTrunkPlacer`). The named density constant it multiplies has no effect on any tree of any height. ## The eleven foliage placers A foliage placer gets one attachment and three numbers — a height, a radius, and an offset it samples itself — and its two real degrees of freedom are how the radius changes with height and which positions inside a row it *skips*. | type | how it differs | |---|---| | `BlobFoliagePlacer` | the plain oak blob: radius tapers by half the row index, corners clipped on a coin flip and always clipped on the row at *y* = 0 | | `FancyFoliagePlacer` | the blob's subclass, but the skip test is a genuine circle rather than a corner roll | | `BushFoliagePlacer` | the blob with a much steeper taper — the full row index, not half of it | | `SpruceFoliagePlacer` | the saw-tooth: a radius that grows a block per row and resets to nothing whenever it reaches a ceiling that is itself climbing. The only placer whose row loop is bounded by the foliage height alone rather than the offset | | `PineFoliagePlacer` | one cone, and the only placer that overrides `FoliagePlacer.foliageRadius` — it adds a draw scaled by the trunk height on top of the configured radius | | `AcaciaFoliagePlacer` | not a loop at all: three explicit rows, with a cross cut through the flat plate. Its declared foliage height is a constant zero | | `DarkOakFoliagePlacer` | two explicit rows, or three or four when the trunk is 2×2, wider with it, and **the only placer that overrides the signed skip test** — it removes the four true corners of the widest row before the signed-to-absolute fold can hide them | | `MegaJungleFoliagePlacer` | registered as *jungle_foliage_placer*, not *mega_jungle*. A circle plus a hard Manhattan cap that skips anything seven or more blocks out | | `MegaPineFoliagePlacer` | the only one that iterates absolute world Y, so it can make its taper jagged by widening every other row | | `RandomSpreadFoliagePlacer` | **never places a row.** It fires a configured number of shots at a box, each coordinate the difference of two draws, so the leaves cluster toward the attachment and thin out. Its skip test is unreachable dead code, and it ignores the offset the base class sampled for it | | `CherryFoliagePlacer` | two narrowing cap rows, a stack of full-radius rows, then the only two uses of the hanging-leaves row helper. It punches probabilistic holes: an edge hole on the bottom row, and on wide rows an unconditional corner removal plus a probabilistic diagonal band | Two of the contract's parameters are dead in all eleven implementations: `FoliagePlacer.createFoliage`'s tree height, and the `TreeConfiguration` that `FoliagePlacer.foliageHeight` receives. Nobody reads either. ## Roots, and the tree that plants itself by failing `RootPlacerType` registers one type. `MangroveRootPlacer` is the only root placer in the game, and the base class exists for it: `RootPlacer.trunkOffsetY` is what lifts a mangrove's trunk one to three blocks clear of the mud, and `AboveRootPlacement` is the moss carpet that lands on top of a root. It simulates the whole root system before writing anything. Starting from the trunk position it recurses outward in each of the four horizontal directions; each step's candidates are *straight down*, *sideways*, or both, depending on how far out the walk already is and a skew roll. The termination rule reads backwards: the recursion returns **true only when it has run out of placeable candidates**, and reaching the maximum root length returns false — which propagates all the way out and abandons the tree. A mangrove is therefore planted only where its roots find solid ground before they run out of length. One asymmetry inside it: a root that lands in mud is written from the muddy provider instead, and that branch skips the base implementation entirely — so **muddy mangrove roots never get their moss carpet.** ## The decorators, and the pass that undoes half of them `TreeFeature` accumulates four sets as it writes — roots, logs, leaves and decorations — and hands the first three to each `TreeDecorator` as a `TreeDecorator.Context`, which sorts all three **ascending by Y**. That sort is the reason four different decorators can say "the lowest log" and mean it. A decorator returns nothing, so one that finds no valid spot is indistinguishable from one that succeeded. Ten types, in four groups. The ones that hang things off the tree are `TrunkVineDecorator` and `LeaveVineDecorator` (vine curtains), `PaleMossDecorator`, `CocoaDecorator` and `AttachedToLeavesDecorator` — that last one blacklists an exclusion box around each placement so the propagules cannot crowd each other. The one that *changes* a block already placed is `CreakingHeartDecorator`, which shuffles the tree's logs and converts one that is completely surrounded by other logs — a random such log, not the first. `BeehiveDecorator` looks like a third but is not: it hangs its nest in an air block beside a log, and populates the block entity with two or three bees on the spot ([block entities](../blocks/block-entities.md)). The ones that write on the ground around the tree are `AlterGroundDecorator` (the podzol discs under a mega spruce, which reach several blocks beyond the trunk) and `PlaceOnGroundDecorator` (leaf litter, over an inflated box). And `AttachedToLogsDecorator` is used by `FallenTreeFeature` rather than by trees. Then the last step, and it is the one that reaches furthest. `TreeFeature.updateLeaves` runs a bucketed breadth-first walk out from the **log** set and rewrites `BlockStateProperties.DISTANCE` on every block in the tree's bounding box that has that property. Three consequences follow, and all three are visible in game. A neighbouring tree's leaves caught inside the box get rewritten too. Blocks in the prevents-nearby-decay tag report distance zero and act as extra roots for the walk. And the decoration and root sets are marked as *occupied* before the walk starts, so a decorator-placed block or a mangrove root **blocks leaf-distance propagation through itself**. Anything the walk cannot reach within six steps keeps the `BlockStateProperties.DISTANCE` of 7 the foliage provider gave it — already decaying — and falls apart on its first random tick. ## Five species, side by side | | trunk | foliage | roots | clearance | decorators | |---|---|---|---|---|---| | oak | straight, 4 + two draws | blob, radius 2 | — | two layers | — | | fancy oak | fancy, base 3 | fancy, radius 2, offset **4** | — | two layers, min clipped **4** | — | | dark oak | dark oak, 6 + draws | dark oak, radius **0** — all the width is hardcoded in the placer | — | three layers | — (pale oak adds moss, and a creaking heart) | | cherry | cherry, 7, one to three branches | cherry, radius 4, four hole probabilities | — | two layers | — (a 5% bee-nest variant exists) | | mangrove | upwards branching, per-log branch probability | random spread, 70 shots | mangrove, trunk lifted 1–3 | two layers | vines, propagules, a 1% bee nest | The columns nobody expects to matter are where the personality lives: dark oak's configured leaf radius is *zero*, and mangrove's foliage placer is the one that does not place rows. ## Questions players ask **Why does a bone-mealed oak sometimes come out enormous?** Because `TreeGrower` picks between two configured features on a probability — a plain oak most of the time, a fancy oak one time in ten — and separately checks for a flower within a 5×3×5 box, which swaps in the bee-nest variants. The same mechanism is why a spruce sapling grown as a 2×2 is a mega pine half the time rather than a mega spruce. **Why does a player-grown pale oak have no creaking heart?** `TreeGrower.PALE_OAK`'s mega tree is the *bone-meal* variant of the configured feature, which is the one with no decorators on it at all. The moss and the heart only arrive on a worldgen pale oak. **Why does a mangrove propagule grow underwater?** Sapling growth is the other entry into this machine and it hand-manages the sapling block: for the single-sapling path it replaces the sapling with whatever the fluid there would be, so a waterlogged propagule grows into water. The 2×2 path is stranger — it clears all four saplings with no-update writes and puts them back if the feature fails. **Do leaves know which tree they came from?** No. Nothing in the placed tree records its species; a leaf's only per-block state is `BlockStateProperties.DISTANCE` and `BlockStateProperties.WATERLOGGED`, the first written by the feature's own breadth-first pass and the second taken from what was already in the world. A log carries a `BlockStateProperties.AXIS` the trunk placer chooses, and that is the whole of it. `FoliagePlacer.tryPlaceLeaf` also refuses to overwrite a leaf a player placed, by testing the persistent flag. **Is any of this shared with the rest of decoration?** The clearance idea, no — it is `TreeFeature`'s alone. But the four *setter* consumers, the sets they fill and the final shape update are the same machinery `StructureTemplate` uses to fix block shapes at the edge of a placed structure, and every block a tree writes goes in with the same flags: update neighbours, update clients, and *known shape* ([blocks and states](../blocks/blocks-and-states.md)). ## Where to look `TreeFeature.place` · `TreeConfiguration` · `TreeConfiguration.TreeConfigurationBuilder` · `TrunkPlacer.placeTrunk` · `TrunkPlacer.getTreeHeight` · `TrunkPlacer.isFree` · `FancyTrunkPlacer` · `CherryTrunkPlacer` · `UpwardsBranchingTrunkPlacer` · `FoliagePlacer.createFoliage` · `FoliagePlacer.shouldSkipLocation` · `FoliagePlacer.FoliageAttachment` · `FoliagePlacer.tryPlaceLeaf` · `RandomSpreadFoliagePlacer` · `DarkOakFoliagePlacer` · `MangroveRootPlacer.placeRoots` · `MangroveRootPlacement` · `FeatureSize.getSizeAtHeight` · `TwoLayersFeatureSize` · `ThreeLayersFeatureSize` · `TreeDecorator.Context` · `BeehiveDecorator` · `AlterGroundDecorator` · `TreeFeatures` · `TreeGrower.growTree` · `SaplingBlock.advanceTree` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Structure placement > Verified against **Minecraft 26.2** · Part XII · A village is decided: a lottery that never looks at the world, a layout that is deferred and then run by the method that deferred it, an absence stored as a hole, and a command that generates chunks to answer a question. Type `/locate structure village` and one of two things happens. Usually the answer is instant, from a couple of thousand blocks away, in a direction you have never been. Occasionally the game stops for a second first. Both come out of the same machinery, and the difference is a cache. The reason the fast answer is possible at all is that **whether a village *could* be here is pure arithmetic on the world seed**. No biome is consulted, no terrain is sampled, no chunk is read. Divide the chunk coordinates by a spacing, seed a random source from the level seed and the grid cell, draw two offsets, and compare. Everything the world has a say in — the biome, the ground height, whether the layout fits — happens *afterwards*, and can still say no. A structure is a thing the generator decides to build **at** a place rather than **from** it. This page is the framework all sixteen structure types share: the decision, the caching, the reference scan, the way terrain bends around it, and the moment blocks are finally written. What builds the pieces is one of two assemblers — [jigsaw and templates](jigsaw-and-templates.md) for villages and their relatives, [hand-built structures](hand-built-structures.md) for the other fifteen types. ## The cast | class | the decision it owns | when | |---|---|---| | `StructureSet` | which structures share a grid, with weights, and which `StructurePlacement` lays that grid out | data pack | | `StructurePlacement` | where the grid falls. `RandomSpreadStructurePlacement` is the spacing-and-separation lottery; `ConcentricRingsStructurePlacement` is strongholds | world start, then per chunk | | `ChunkGeneratorStructureState` | which sets are possible in this dimension at all, and the stronghold ring positions | once per world, on the main thread | | `Structure` | the settings wrapper: allowed biomes, spawn overrides, the decoration step, the terrain adjustment — and `Structure.findGenerationPoint` | `ChunkStatus.STRUCTURE_STARTS` | | `StructureStart` | the answer: a structure, the chunk it started in, a `PiecesContainer`, a reference count and a cached box | stored on the chunk | | `StructureManager` | the per-level view of starts and references | worldgen and main thread | | `StructureCheck` | the presence cache — two caches over a partial-NBT reader — and the thing `/locate` actually asks | **main thread only**, unsynchronised | | `Beardifier` | how much the terrain bends, as a density term | built with the `NoiseChunk` at `ChunkStatus.BIOMES` | ## Four decisions, on four different clocks ```mermaid flowchart TB W["world start, main thread: filter the structure sets to biomes this dimension can host, fire the stronghold ring searches"] W --> S1["STRUCTURE_STARTS — the lottery, then the layout. No blocks, no neighbours read"] S1 --> S2["STRUCTURE_REFERENCES — each chunk scans the 17x17 around itself for starts overlapping it"] S2 --> N["BIOMES and NOISE — the Beardifier is built with the NoiseChunk, and bends the density field"] N --> F["FEATURES — StructureStart.placeInChunk writes blocks, before that step's features"] ``` The odd thing about that ladder is where it starts. `ChunkStatus.STRUCTURE_STARTS` is the **second** status a chunk passes through, two before `ChunkStatus.BIOMES` — so a structure is decided before the biomes and the terrain it will sit in exist. Everything the structure needs to know about the world it asks for directly, from the generator, rather than reading it out of a chunk. ## Which chunk: a grid, and nothing else `ChunkGenerator.createStructures` walks the possible structure sets. For a village that means `RandomSpreadStructurePlacement.getPotentialStructureChunk`: divide the chunk coordinates by the spacing, seed a `WorldgenRandom` from the level seed, the grid cell and the set's own salt, and draw two offsets inside the cell. This chunk is the village chunk only if the draw lands exactly here. `RandomSpreadType` decides whether the draw is uniform or triangular, and `StructurePlacement.isStructureChunk` adds a frequency roll, a `StructurePlacement.FrequencyReductionMethod` and a deprecated `StructurePlacement.ExclusionZone` that lets one set repel another. Two qualifiers, and both matter. The *other* placement type is not like this at all: `ConcentricRingsStructurePlacement` positions strongholds by asking `BiomeSource.findBiomeHorizontal` for real biome positions, on the background pool, at world start. And even for villages a coarse biome test has already happened once — when `ChunkGenerator.createState` built the `ChunkGeneratorStructureState` and dropped every set no biome in this dimension can host. ## Which structure, and whether the biome allows it The set has entries with weights, so a second `WorldgenRandom` picks one. Then `Structure.findValidGenerationPoint` runs `Structure.findGenerationPoint` and filters it through `Structure.isValidBiome` — **the biome is sampled at the proposed point, after the lottery has already chosen the chunk.** If that fails — most often the biome, but also an empty start pool, a missing start jigsaw, or a structure that would sit too close to the world height limits — the entry is *removed*, its weight subtracted, and the roll repeated. A chunk on a biome border therefore usually gets *a* village where a single-candidate set would get none. If every entry fails, the loop drains and the cell stays empty: the slot still exists, the village does not. ## Whether it is worth laying out `Structure.findGenerationPoint` does not return pieces. It returns a `Structure.GenerationStub`, and the stub holds the *child expansion* as an unexecuted consumer. `Structure.generate` then calls `Structure.GenerationStub.getPiecesBuilder` on the same line it takes the stub back, so on the generation path the deferral lasts one statement. What the deferral is actually for is `StructureCheck.canCreateStructure`, which calls `Structure.findValidGenerationPoint` and asks only whether the result is present: the presence question is answered without ever expanding the children, so the layout is run **once** and never twice. What is *not* deferred is the centre: the start template, its rotation and its ground height are all resolved before the stub comes back. `StructureCheck` is the cache in front of all of this, and it is two caches over a partial-NBT reader: chunk → structure → **reference count** (which is what makes "unreferenced only" searches possible), and structure → chunk → would-generate. On a miss it reads the chunk off disk through `ChunkScanAccess`, pulling only the data version and the structure starts and data-fixing that fragment alone. It is main-thread-only and unsynchronised, which is why `ServerLevel.onStructureStartsAvailable` hops back to the server thread from the worldgen executor to feed it. **Absence is stored as a hole, not as a marker.** An invalid start is never written at all: `ChunkGenerator.tryGenerateStructure` calls `StructureManager.setStartForStructure` only for a valid start, and `StructureStart.INVALID_START` is dropped on the floor. What lets a partial scan prove absence is that every saved chunk carries a *starts* compound unconditionally, empty or not — so a structure simply missing from that map is a definite "not here". The *INVALID* ids that do turn up in old saves are legacy, and `StructureCheck` skips them while loading. ## Who needs to know At `ChunkStatus.STRUCTURE_REFERENCES`, `ChunkGenerator.createReferences` scans the **17×17 chunk square around each chunk** and records the packed position of every start whose bounding box overlaps it. Discovery is outside-in: a village never walks its own pieces to announce itself, and this is why almost every later step in the generation pyramid requires structure starts within eight. The box that scan tests is not always the box the assembler produced. `Structure.adjustBoundingBox` inflates it by twelve the moment `TerrainAdjustment` is anything but *none* — and that inflated box is what the reference scan, `StructureManager.getStructureAt` and the spawn overrides all see. The margin the beardifier needs is therefore also the margin in which a village counts as "here" for mob spawning. The 128-block cage that keeps a 17×17 scan sufficient is enforced when the **data pack loads**, not when the structure generates: a jigsaw whose maximum distance plus the terrain margin exceeds `JigsawStructure.MAX_TOTAL_STRUCTURE_RANGE` fails validation. ## The ground bends, and then the blocks arrive `Beardifier.forStructuresInChunk` reads those references and turns the nearby pieces into `Beardifier.Rigid` boxes plus their junctions. It is built with the `NoiseChunk`, which the *biomes* step creates and the noise step only reuses, so the beardifier exists a status before the density field it bends. **No blocks are edited.** The flat shelf under a village is the density field being told to be solid there ([terrain](terrain.md)), and `TerrainAdjustment` picks the shape: only two of its five values use the kernel the name *beard* refers to — the two beard modes, where junctions contribute at half the weight of the pieces, which is where the smooth shoulders under village streets come from. *Bury* and *encapsulate* use a plain linear distance falloff instead. The *rigid* filter applies only to jigsaw pieces, which have a projection to test; a hand-built piece contributes unconditionally. Neither example is a desert pyramid or a mineshaft, because a structure that names no *terrain_adaptation* defaults to `TerrainAdjustment.NONE` and is filtered out before its pieces are looked at — twenty-three of the thirty-four shipped structure files, those two among them. The hand-built pieces that do reach the branch belong to the stronghold and the nether fossil. Then at `ChunkStatus.FEATURES`, `ChunkGenerator.applyBiomeDecoration` places structures at their declared decoration step, *before* that step's features. `StructureStart.placeInChunk` derives a reference position from **piece zero** — piece order is semantic, not cosmetic, and every `PosRuleTest` measures its distances from that point — and calls `StructurePiece.postProcess` on every piece overlapping this chunk's writable area. Every chunk the structure touches does this with its own box, so a house straddling four chunks is written in four slices, at four different times, and a piece's `StructurePiece.postProcess` must be idempotent. ## Questions players ask **Why does `/locate` sometimes pause?** Because on a cache miss it can drive world generation, from the server thread. `StructureCheck` re-runs the start-point and biome test — the grid arithmetic having already produced the candidate chunk — and on a result of `StructureCheckResult.CHUNK_LOAD_NEEDED` it loads the chunk to structure starts, **synchronously**, for up to a hundred expanding rings of grid cells. **Why do two exploration maps usually not point at the same monument?** `StructureStart.getMaxReferences` is one, and `ExplorationMapFunction` defaults *skip_existing_chunks* to true, so a map asks for an *unreferenced* structure and takes a reference when it finds one — and the reference count is exactly what `StructureCheck`'s first cache stores. It is a default, not a guarantee: the three buried-treasure map tables, in shipwrecks and both ocean ruins, set the flag to false and will happily send two players to the same chest. **Do structures override the biome for mob spawning?** Yes, and first. `ChunkGenerator.getMobsAt` consults `Structure.spawnOverrides` before `Biome.getMobSettings` ([biomes](biomes.md)), scoped either to the piece or to the whole start. Nether fortresses are special-cased earlier still, inside `NaturalSpawner`. **Which `StructureManager` is which?** There are two unrelated things with that shape of name, and both live on the level. `ServerLevel.structureManager` is the starts-and-references view on this page; `ServerLevel.getStructureManager` returns the `.nbt` template loader owned by the server ([jigsaw and templates](jigsaw-and-templates.md)). There is also a second, unrelated `StructureCheck` in the entity-variant package. **Is there dead code in here?** Some, and it reads as load-bearing. `PostPlacementProcessor` is referenced by nothing at all, and `PieceGenerator` and `PieceGeneratorSupplier` are referenced only by each other. The live post-placement hook is `Structure.afterPlace`. ## Where to look `Structure.generate` · `Structure.findValidGenerationPoint` · `Structure.StructureSettings` · `Structure.adjustBoundingBox` · `StructureSet` · `StructurePlacement.isStructureChunk` · `RandomSpreadStructurePlacement.getPotentialStructureChunk` · `ConcentricRingsStructurePlacement` · `ChunkGeneratorStructureState.generatePositions` · `ChunkGenerator.createStructures` · `ChunkGenerator.createReferences` · `ChunkGenerator.tryGenerateStructure` · `StructureStart.placeInChunk` · `StructureStart.INVALID_START` · `StructureManager.startsForStructure` · `StructureManager.addReferenceForStructure` · `StructureCheck.checkStart` · `ChunkScanAccess` · `Beardifier.forStructuresInChunk` · `TerrainAdjustment` · `ChunkGenerator.findNearestMapStructure` · `BuiltinStructures` · `BuiltinStructureSets` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Jigsaw and templates > Verified against **Minecraft 26.2** · Part XII · A village assembles itself: pieces that find each other through connector blocks, a priority queue instead of a stack, and a growth limit that works by taking the right pool away. A village stops somewhere. Follow a street out from the town centre and the houses run out and the path ends in a stub of dirt path with nothing on it. Nothing measured the distance and nothing counted the buildings. That stub is a *terminator*, and it comes from the street pool's **fallback** pool — which `JigsawPlacement.Placer` appends to the candidate list at **every** depth, behind the pool the piece actually asked for. A street ends wherever the street pieces stop fitting. What the depth limit does is stop offering the asked-for pool at all, so at the limit the fallback is the only thing left. The edge of a village is a substitution, not a stop condition. This is the assembler one of the sixteen structure types uses — the jigsaw — together with the `.nbt` template system that turns each of its pieces into blocks. Everything *outside* the assembler is [structure placement](structure-placement.md): the lottery that chose this chunk, `StructureStart`, the reference scan, the beardifier, and the moment `StructurePiece.postProcess` is called. The other fifteen types use a different assembler and reach this page only for the templates ([hand-built structures](hand-built-structures.md)). ## The cast | class | what it holds | when | |---|---|---| | `JigsawStructure` | the start pool, the start jigsaw name, a depth, a start height, a maximum distance and the pool aliases | data pack | | `StructureTemplatePool` | a weighted list of `StructurePoolElement`s and a **fallback** pool — the two fields its codec has | data pack | | `StructurePoolElement` | one candidate: a single template, a legacy single, a list, a placed *feature*, or nothing | data pack | | `JigsawPlacement.Placer` | the assembly loop and its priority queue; the `VoxelShape` of free space travels with each queue entry | `ChunkStatus.STRUCTURE_STARTS`, worldgen worker | | `JigsawBlock` | the connector, with `JigsawBlockEntity.JointType` deciding whether rotation must match | in the template | | `PoolElementStructurePiece` | one accepted candidate, with its junctions | in memory until `ChunkStatus.FEATURES` | | `StructureTemplate` | a parsed `.nbt` file: block palettes, entities, and the jigsaw blocks in it | loaded by `StructureTemplateManager` | | `StructurePlaceSettings` | rotation, mirror, the chunk box, liquid handling and an **ordered** list of `StructureProcessor`s | per piece, per chunk | ## The trace: a village, from town centre to blocks ```mermaid sequenceDiagram participant ChunkG as ChunkGenerator participant JS as JigsawStructure participant JPP as JigsawPlacement.Placer participant STP as StructureTemplatePool participant PESP as PoolElementStructurePiece participant STemp as StructureTemplate ChunkG->>JS: Structure.generate — the lottery already chose this chunk JS->>JPP: findGenerationPoint, which is JigsawPlacement.addPieces JPP->>JPP: sample the start height, pick a town centre from the start pool JPP->>JPP: drop it so its ground level sits on getFirstFreeHeight JPP-->>JS: a GenerationStub — the children are still a deferred consumer JS->>JPP: getPiecesBuilder runs it — build a free-space shape around the centre loop until the priority queue drains, depth within the limit JPP->>JPP: jigsaw blocks shuffled, then sorted by selection priority JPP->>STP: the target pool's shuffled templates, then the fallback's Note over JPP: at the depth limit the target pool is skipped entirely JPP->>JPP: canAttach — opposed faces, matching names, rotation if aligned JPP->>JPP: collide the candidate box against the free shape JPP->>PESP: accept — subtract the box, record a junction on BOTH sides end JPP-->>JS: the pieces builder, filled JS-->>ChunkG: StructurePiecesBuilder.build — a PiecesContainer inside a StructureStart Note over ChunkG: later, at FEATURES, once per chunk the village touches PESP->>STemp: placeInWorld, clipped to this chunk's writable area Note over STemp: processors in order, jigsaw blocks replaced, a fresh loot seed stamped ``` ## The pools A `StructureTemplatePool` is the unit of choice, and its codec has exactly two fields — all 188 shipped pool files carry those two and nothing else. The weighted *elements* list is the candidates. The **fallback** pool is a second pool appended behind them, tried whenever nothing in the first list fits and the only thing tried at the depth limit. The third thing you might expect on the pool is not there: `StructureTemplatePool.Projection` is a field of each *element*, so one pool can mix them. It decides how Y is chosen: *rigid* keeps the parent piece's vertical offset, and *terrain matching* asks the generator for the ground height and brings a gravity processor with it. Five kinds of element can sit in that list. `SinglePoolElement` is one template. `LegacySinglePoolElement` is the same with an older block-shape rule. `ListPoolElement` is several placed as a unit. `EmptyPoolElement` is a deliberate nothing. And `FeaturePoolElement` places a `PlacedFeature` instead of a template — which is how village trees arrive ([features and placement](features-and-placement.md)). `PoolAliasBinding` and `PoolAliasLookup` sit on top: one structure can swap which pool a name resolves to, per placement, resolved once from a positional random source. Trial chambers are the only vanilla structure that uses it, and what they swap is which pool the spawners draw their mobs from. ## The assembly loop `JigsawPlacement.addPieces` builds a `VoxelShape` of free space around the start piece and hands it to `JigsawPlacement.Placer`. From then on the algorithm is: take a placed piece, look at its jigsaw blocks, and for each one try to hang something off it. Four details in that loop are the ones worth watching. **It is a priority queue, not a stack.** Children are expanded in order of the jigsaw block's placement priority, with insertion order breaking ties — not depth-first — so a pool can insist its connections are made before its siblings'. `JigsawPlacement.Placer.placing` is that queue. **Attachment is a name match plus a geometry match.** `JigsawBlock.canAttach` requires the two jigsaw blocks to face each other and their target names to agree; an *aligned* joint additionally requires the rotations to match, while a rollable one does not. **Collision is against a shrinking shape, not against a list.** Every accepted piece subtracts its own box from the free-space shape, so the next candidate is tested against what is genuinely left. A candidate that intersects is simply not built, and the next one on the shuffled list is tried. **A junction is recorded on both sides.** Each connection writes a `JigsawJunction` into the parent piece *and* the child, which is what lets `Beardifier` treat junctions as their own terrain contribution rather than inferring them from the boxes ([structure placement](structure-placement.md)). ## From a piece to blocks Nothing above has written a block. When `ChunkStatus.FEATURES` finally calls `StructurePiece.postProcess` on a `PoolElementStructurePiece`, the element assembles a `StructurePlaceSettings` — the chunk box, the rotation, the ignore processor, then `JigsawReplacementProcessor`, then the element's own processor list, then the projection's. `LegacySinglePoolElement`, which is what every vanilla village piece is, then pops its ignore processor and re-appends a wider one at the **end** of that list, so for the pieces a player actually sees the ignore step runs last and drops the template's air as well as its structure blocks. `StructureTemplate.placeInWorld` runs every block in the template through `StructureTemplate.processBlockInfos`. A `StructureTemplate` is a parsed `.nbt` file: `StructureTemplate.Palette`s of `StructureTemplate.StructureBlockInfo`, an entity list, and `StructureTemplate.JigsawBlockInfo` for the connectors. Placing it writes what falls inside the box, loads block-entity data ([block entities](../blocks/block-entities.md)) and stamps a **fresh loot seed** into containers rather than a table's contents ([loot tables](../items/loot-tables.md)) — which is why a village chest's contents are decided when you open it, not when the village generated. The processors are the interesting layer, because they are shared with the other assembler and with the structure blocks a player can use. `RuleProcessor` applies `ProcessorRule`s, and each rule holds **two** block tests with different subjects: an *input predicate* against the template's own block and a *location predicate* against the block already in the world. A position test, a replacement state and an optional block-entity modifier follow, and the first rule that matches wins. `BlockRotProcessor` deletes a fraction of the blocks. `GravityProcessor` drops them to a heightmap. `BlockIgnoreProcessor` skips a named list of blocks, and its three presets name the structure block, air, or both — never structure void, which `JigsawReplacementProcessor` handles instead. And `JigsawReplacementProcessor` is the one that cleans up after the assembler: it swaps each jigsaw block for the state named in its final-state string, or removes it entirely. **The assembly graph is invisible in the finished village** unless the debug flag in `SharedConstants` is set. `StructureTemplateManager` loads templates in a fixed order — the world's generated directory, then the gametest source, then data packs — and the folder it looks in is *structure*, singular. > **For a 1.21-era reader.** The `.nbt` folder is > *data/<namespace>/structure/*, not *structures/*. The plural > directory is the one you remember and it is not read. ## Questions players ask **Why is one village bigger than another?** Because the growth limit is probabilistic in effect even though the depth cap is fixed. A village's declared *size* is six, well under the twenty `JigsawStructure.MAX_DEPTH` allows, and what usually ends a branch is not the cap at all: it is every candidate in the street pool failing the collision test, which hands the branch to the fallback's terminators. A street that happens to run downhill into free space grows further than one that turns back on itself. Villages also set *use_expansion_hack*, which inflates a candidate's box upward before the test, so a piece that would fit can be rejected for the children it would need room for. **Can I watch the assembler run?** Yes, two ways. The jigsaw *editor* runs in both directions: `ServerboundSetJigsawBlockPacket` and `ServerboundJigsawGeneratePacket` let a creative player run the assembler live against a loaded `ServerLevel`, and `JigsawBlockEntity` syncs its pool, target and joint back the other way. `/place jigsaw` does the same from a command, with a pool, a target and a depth. Both are exceptions to "structures cross the network as ordinary blocks", and there is a third that only a developer sees: `DebugSubscriptions.STRUCTURES` ships every piece's bounding box to the client for the debug renderer. **Why do the same houses appear in different rotations?** Because rotation is chosen per piece and applied to the block *states* as they are written, not to a pre-rotated template. Whether a neighbour may differ in rotation is the joint type's decision. **Does the layout depend on the terrain?** Yes, wherever a piece is terrain-matching — which is every village street. Whenever the source or the target is not rigid, `JigsawPlacement.Placer` asks `ChunkGenerator.getFirstFreeHeight` for the ground under the source's jigsaw block and puts the candidate's box there; that box is exactly what the collision test then tests, so the ground decides what fits. What the assembly never does is *read a chunk*: `ChunkGenerator.getFirstFreeHeight` samples the density graph, which is why the whole thing can run at `ChunkStatus.STRUCTURE_STARTS`, before any terrain has been written. **What is a village made of, if not blocks?** Until `ChunkStatus.FEATURES`, a `PiecesContainer` of `PoolElementStructurePiece`s inside a `StructureStart`, saved to the chunk as NBT and reloaded on demand. The village is data for its entire generated life and becomes blocks last. ## Where to look `JigsawStructure` · `JigsawStructure.MAX_DEPTH` · `JigsawPlacement.addPieces` · `JigsawPlacement.Placer` · `StructureTemplatePool` · `StructureTemplatePool.Projection` · `StructurePoolElement` · `SinglePoolElement` · `FeaturePoolElement` · `JigsawBlock.canAttach` · `JigsawBlockEntity.JointType` · `JigsawJunction` · `PoolAliasBinding` · `PoolElementStructurePiece.place` · `StructurePiecesBuilder` · `StructureTemplate.placeInWorld` · `StructureTemplate.processBlockInfos` · `StructureTemplate.Palette` · `StructureTemplateManager` · `StructurePlaceSettings` · `StructureProcessorList` · `RuleProcessor` · `ProcessorRule` · `GravityProcessor` · `JigsawReplacementProcessor` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Hand-built structures > Verified against **Minecraft 26.2** · Part XII · A stronghold is generated: a piece grammar written in Java, a graph assembled at an imaginary height and moved down afterwards, and a whole structure thrown away and rebuilt because it had no portal room. Every stronghold has exactly one end portal. Not usually, not almost always — exactly one, in every stronghold in every world, and the mechanism is not a counter or a guarantee. The portal room is weighted heavily, capped at one placement, and forbidden near the entrance; and if the maze finishes without one, `StrongholdStructure` **clears the whole builder, adds one to the seed and generates the entire stronghold again.** It is the only structure in the game that regenerates itself until it likes the result. [Jigsaw and templates](jigsaw-and-templates.md) traces a village, and a village is a jigsaw: pieces come from a data-pack registry and find each other through connector blocks. That is one of the sixteen structure types. **The other fifteen use an older assembler that is still the majority of the code** — 32 classes and about 10,200 lines under `levelgen/structure/structures`, against roughly 1,300 for the whole jigsaw package. Strongholds, mineshafts, nether fortresses, ocean monuments, woodland mansions, end cities, ruined portals, igloos, shipwrecks, ocean ruins, desert pyramids, jungle temples, swamp huts, buried treasure and nether fossils are all built this way. Everything *around* the assembler is shared, and belongs to [structure placement](structure-placement.md): the lottery, `Structure`, `StructureStart`, `StructureCheck`, the reference scan, `Beardifier`, and the per-chunk write. This page is only the part where the pieces come from. ## The idea There is no pool and no registry of pieces. A piece is a **Java class that knows how to write its own blocks**, and it grows the structure by constructing its own neighbours. | class | its role | |---|---| | `StructurePiece` | the base, and the reason the system holds together: a **mutable** `BoundingBox`, an orientation, a mirror, a rotation, a depth, and a piece type | | `StructurePiece.placeBlock` | the conventional write path — converts to world coordinates, drops anything outside the chunk box it was handed, applies the piece's mirror and rotation *to the block state*, and schedules a tick for whatever fluid is at the position **after** the write. Not a choke point: the structure classes call `LevelWriter.setBlock` on the level directly two dozen times | | `StructurePiece.BlockSelector` | a stateful per-block state chooser, and the entire visual character of a structure | | `StructurePieceAccessor` | eleven lines, two methods, and `StructurePiece.findCollisionPiece` is a **linear scan returning the first overlapping box**. There is no spatial index | | `StructurePiecesBuilder` | accumulates the pieces, and can move all of them vertically at once | | `TemplateStructurePiece` | the bridge to the `.nbt` machinery, for structures that are procedural in *layout* and templated in *content* | | `ScatteredFeaturePiece` | the base for one-shot surface buildings, with two ground-finders | | `SinglePieceStructure` | the forty-line `Structure` that places exactly one of those | Three things about that base class do most of the work. **Orientation is not independent of mirror and rotation.** `StructurePiece.setOrientation` derives both from the facing direction, and a south-facing piece is expressed as a **left-right mirror** rather than a 180° rotation. That trick is why every piece in this package is written once, in a north-facing local frame, and comes out correct four ways. **Local Y is measured from the box floor.** `StructurePiece.getWorldX`, `StructurePiece.getWorldY` and `StructurePiece.getWorldZ` map local coordinates into the world, and because Y is relative to the floor, moving a finished graph vertically is free. When the orientation is null the transform is the identity, which is how `BuriedTreasurePieces` gets away with a bounding box one block wide. **`StructurePiece.addChildren` is not a framework hook.** Its default body is empty and nothing in the framework ever calls it; every call site is a structure's own generation code. The recursion is arranged by each family for itself, in one of two shapes. Strongholds and nether fortresses use a **shuffled work queue**: a new piece goes into the builder *and* onto the start piece's pending list, and the structure drains that list by repeatedly removing a **random** index and expanding it, so growth is breadth-ish and unbiased. Mineshafts use **inline recursion** and expand each new piece immediately, so the first branch of a crossing is fully grown before the second is attempted. The vocabulary a piece writes with is the rest of the base class: `StructurePiece.generateBox` fills a local box while distinguishing edge cells from interior ones, and `StructurePiece.generateAirBox`, `StructurePiece.generateMaybeBox`, `StructurePiece.generateUpperHalfSphere`, `StructurePiece.fillColumnDown` and `StructurePiece.createChest` are the rest. `StrongholdPieces.SmoothStoneSelector` is the canonical block selector: on a box edge it rolls cracked, mossy or infested stone brick and otherwise plain, and interior cells become cave air. One small object is the whole look of a stronghold. `JungleTemplePiece.MossStoneSelector` is the other one. ## The trace: a stronghold All of this runs at `ChunkStatus.STRUCTURE_STARTS`, inside the same `Structure.GenerationStub` consumer the jigsaw assembler runs in — so the whole graph is built in memory, on a worldgen worker, with no world access and no blocks written. One structure skips the consumer: `MineshaftStructure.findGenerationPoint` hands the stub a builder it has already filled, which is the only *Either.right* in the game. ```mermaid sequenceDiagram participant ChunkG as ChunkGenerator participant SStr as StrongholdStructure participant SPie as StrongholdPieces participant SPB as StructurePiecesBuilder participant SStart as StructureStart ChunkG->>SStr: Structure.generate — findGenerationPoint, then the stub loop until a portal room exists SStr->>SPB: clear SStr->>SStr: setLargeFeatureSeed(world seed plus the try counter, chunk) SStr->>SPie: resetPieces — the static weight table and the imposed piece SStr->>SPie: a start room, then addChildren on it loop drain the pending list at a random index SPie->>SPie: pick by weight, reject the previous type, five attempts SPie->>SPB: findCollisionPiece — a linear scan of what is placed SPB-->>SPie: free, so construct it — or a hit, so try the next candidate SPie->>SPB: addPiece, and append to the pending list end SStr->>SPB: moveBelowSeaLevel — shift every piece at once end SPB-->>SStart: build — a PiecesContainer, then a StructureStart Note over SStart: at FEATURES: postProcess, once per chunk each piece overlaps ``` **It is built at an imaginary height.** The start piece is constructed at a fixed Y — sixty-four for strongholds and fortresses, fifty for mineshafts — with no idea where the ground is. Every collision test, every staircase descent and the floor guard that refuses a box below Y 10 happens in that frame. Only afterwards does `StructurePiecesBuilder.moveBelowSeaLevel` shift the whole graph so its top sits below sea level. Nether fortresses use `StructurePiecesBuilder.moveInsideHeights` to land in a band, and a mesa mineshaft uses `StructurePiecesBuilder.offsetPiecesVertically` to sit between sea level and the surface. This is the payoff for local-Y-from-the-floor. **Growth stops when the budget is spent, not when the depth runs out.** `StrongholdPieces.STRONGHOLD_PIECE_WEIGHTS` pairs each piece class with a weight *and* a maximum placement count: corridors and turns are unlimited, a room crossing may appear six times, a library twice, a portal room once — and the library and portal room additionally refuse to appear before a certain depth. The picker makes up to five weighted attempts, rejecting whichever type was placed immediately before, and falls back to a filler corridor. The depth cap is fifty, far more than any real stronghold reaches; what actually ends generation is the picker returning nothing once every limited type has hit its limit. **Collision is the other brake, and some pieces negotiate.** Each candidate constructor computes its box and asks `StructurePieceAccessor.findCollisionPiece`; a hit means the candidate simply is not built. A mineshaft corridor tries decreasing lengths until one fits, and a stronghold library falls back from its tall variant to its short one. ## The four families | family | how the pieces come to exist | members | |---|---|---| | **procedural piece graphs** | the pieces write their own blocks and construct their own neighbours — the pattern in its pure form | `StrongholdPieces`, `MineshaftPieces`, `NetherFortressPieces` | | **grid and graph solvers** | a layout is *solved* first and pieces are emitted afterwards, so neither ever calls `StructurePieceAccessor.findCollisionPiece` — the layout **is** the collision guarantee | `WoodlandMansionPieces`, `OceanMonumentPieces` | | **template-backed pieces** | procedural placement, `.nbt` content, and therefore the same processors and the same `StructureTemplate.placeInWorld` the jigsaw path uses | `EndCityPieces`, `RuinedPortalPiece`, `OceanRuinPieces`, `ShipwreckPieces`, `IglooPieces`, `NetherFossilPieces`, `WoodlandMansionPieces` | | **one-shot surface buildings** | no graph and no children: one box, dropped on the ground, over `ScatteredFeaturePiece` | `DesertPyramidPiece`, `JungleTemplePiece`, `SwampHutPiece` | The nether fortress is the most elaborate of the first family: it runs *two* weight tables and a mode switch, where a castle entrance is a one-way door out of bridge mode into castle mode, and only a T-balcony can fall back, on a one-in-eight roll per branch. Almost nothing here is data-driven, and that is the point. Piece choice, weights, budgets, layout rules and adjacency are all Java. `Registries.STRUCTURE` still supplies the settings wrapper, and the templated families read `.nbt` files, but **a data pack cannot add a room to a stronghold.** ## Where the families bend the idea **The mansion is grown and then tidied to a fixed point.** Corridors are recursed out from the entrance on an 11×11 grid, rooms are stamped alongside them, and then an edge-cleaning pass runs **repeatedly until nothing changes**, filling any cell with enough occupied neighbours. That pass is why a mansion is a solid block of building rather than the thin maze the corridor walk actually produced. Rooms are then greedily merged into 2×2, 1×2 and 1×1 units, with type, id and flags packed into a single integer per cell — and a room that ends up with no corridor edge becomes a **secret room**, reachable only from above. A mansion may also have two floors instead of three: the third needs a second-floor room with a door to hang its staircase on, and if there is none, or no free direction to grow into, the third-floor grid is blanked entirely. **The ocean monument carves its maze backwards.** It wires a lattice of rooms fully connected, then repeatedly closes a random opening and **keeps the closure only if both sides can still reach the entrance room**, using a depth-first reachability walk with an increasing scan counter in place of a visited set. Rooms are then fitted by a list of room-shape fitters in fixed order, first match wins, so the large double rooms get first refusal and the plain room is the fallback. **End city sections collide as groups, not as pieces.** Each candidate section is generated into a scratch list and tagged with one shared random `StructurePiece.genDepth` — used as a **group identity, not a depth**. The section is accepted only if every collision it finds is with a piece carrying the *parent's* tag; one foreign overlap discards the entire candidate list atomically. Bridges opt out with a tag of minus one, and the ship becomes likelier the longer the bridge gets, with at most one per city. **Ruined portal decay is a processor stack, not code.** The rot, the gold-block gaps, the lava-to-magma substitutions and the mossiness are `StructureProcessor`s assembled per portal and stored in the saved piece, so decay reproduces exactly on reload. Only some of that stack is shared with the jigsaw path ([jigsaw and templates](jigsaw-and-templates.md)): `BlockAgeProcessor` — which is the mossiness, not a separate step — `LavaSubmergedBlockProcessor` and `BlackstoneReplaceProcessor` are built in `RuinedPortalPiece` and appear in no data pack at all. The forty shipped processor lists between them use four types: rule, protected blocks, block rot and capped. ## Questions players ask **Does `/locate stronghold` point at the portal?** No — at the corner of the start chunk. `ChunkGenerator.findNearestMapStructure` returns `StructurePlacement.getLocatePos`, which is the chunk's minimum block plus the placement's own offset, and the eye of ender takes the same answer. The stronghold *does* keep a portal-room pointer — `StrongholdPieces.StartPiece.getLocatorPosition` overrides the base method to return it — but nothing in 26.2 calls that method. What the pointer is really for is the regeneration loop's exit condition: the portal room's entire `StructurePiece.addChildren` body is a record of itself on the start piece. **Would two strongholds generating at once interfere?** In principle, yes, and visibly so. `StrongholdPieces` keeps its remaining-piece list, its running weight total and a one-shot "force this piece next" override in **private static fields**, reset by `StrongholdPieces.resetPieces` from inside a generation lambda that runs on chunk workers. The nether fortress's placement counters live on static array elements merely reset at start-piece construction, so its per-structure budget is an illusion. It is rare enough not to bite, and it is the sharpest contrast with the stateless jigsaw path. **Why do some structures come back different after a reload?** One does. Ocean monument room pieces are held privately on the main building, never reach the builder, are therefore never saved — and their save method is empty anyway. `StructureStart.loadStaticStart` carries a hardcoded type check that calls `OceanMonumentStructure.regeneratePiecesAfterLoad`, which reads position and orientation from the save and rebuilds every room from the world seed. Every other structure deserialises what it wrote. **Is a saved bounding box where the structure is?** Not always. Buried treasure rewrites its own box while placing; igloos are built at a hardcoded Y 90 and re-seated at write time from the live heightmap, then put *back*; shipwrecks latch a flag so the second chunk does not move them again. For those types the persisted box is a placement hint, not a location. Two pieces go further and deliberately **widen the chunk they were given** — a ruined portal and a nether fossil both encapsulate the writable area so they are placed whole from a single chunk rather than sliced across several. Since `BoundingBox` is mutable and shared between the pieces of one start, that widening leaks; harmlessly today, because both structures have exactly one piece. **Does a hand-built template know about jigsaw blocks?** `TemplateStructurePiece.postProcess` scans what it placed for jigsaw blocks and replaces each with its final state, so a stray jigsaw block in a mansion `.nbt` resolves quietly instead of connecting to anything. `Beardifier`'s projection test is the only place at runtime where the two assemblers are told apart. **Is any of this on its way out?** The whole-graph move is. `StructurePiecesBuilder.moveBelowSeaLevel`, `StructurePiecesBuilder.offsetPiecesVertically`, `TemplateStructurePiece.move` and the mansion's siting helper are all marked for removal, and the jigsaw path has nothing deprecated in it at all. Mojang has flagged the idiom, not just the methods. Three separate *magic start Y* constants in this package are also read by nothing — the literals are retyped at their use sites, which is a trap for anyone changing one. And one discarded random draw is load-bearing: `MineshaftStructure.findGenerationPoint` opens by drawing a double and throwing it away, a random-stream alignment relic that has to stay or every mineshaft in every existing world moves. ## Where to look `StructurePiece` · `StructurePiece.addChildren` · `StructurePiece.placeBlock` · `StructurePiece.generateBox` · `StructurePiece.BlockSelector` · `StructurePiece.setOrientation` · `StructurePiece.getWorldY` · `StructurePieceAccessor.findCollisionPiece` · `StructurePiecesBuilder` · `StructurePiecesBuilder.moveBelowSeaLevel` · `StructurePiecesBuilder.moveInsideHeights` · `StrongholdStructure` · `StrongholdPieces.STRONGHOLD_PIECE_WEIGHTS` · `StrongholdPieces.resetPieces` · `MineshaftPieces` · `NetherFortressPieces` · `WoodlandMansionPieces` · `OceanMonumentPieces` · `EndCityPieces` · `RuinedPortalPiece` · `TemplateStructurePiece` · `ScatteredFeaturePiece` · `SinglePieceStructure` · `OceanMonumentStructure.regeneratePiecesAfterLoad` · `StructureStart.loadStaticStart` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Creating a world > Verified against **Minecraft 26.2** · Part XII · *Create New World*: a seed typed in, Superflat chosen, a layer deleted, an experiment switched on, and the settings object that comes out the other end. You click *Create New World* and nothing happens for a moment. Then a three-tab screen: a name, a game mode, a seed box, a world-type button, and buttons for game rules, data packs and experiments. You type a seed, cycle the type to *Superflat*, open *Customize* and delete the dirt layer, switch on an experiment, set one game rule, and press *Create*. That opening pause is what this page is about. Before the screen can draw a single widget the game has already run a **complete server-side data-pack load** — the same `WorldLoader.load` a dedicated server runs at startup — on a background thread, with the client's main thread parked on `BlockableEventLoop.managedBlock` until it finishes. The object the screen exists to edit, `WorldGenSettings`, was built halfway through that load, out of registries the load had just filled. Every widget on the *World* tab is an edit to it — the name, the game mode, the difficulty, Allow Commands and the game rules go somewhere else entirely, into the `LevelSettings` and the `GameRules` that ride beside it. Two of the buttons throw the whole load away and run it again. And *Create* barely touches it: by the time you press the button, the world's generation settings have existed in memory for as long as the screen has. Everything else in [Part XII](README.md) reads that object. This page is where it comes from, where it goes, and the three different programs that build one. ## The cast | class | what it owns | its thread | |---|---|---| | `WorldGenSettings` | the whole answer: a `WorldOptions` and a `WorldDimensions`, and nothing else. It is a `SavedData` | built on the worker, read on the server thread | | `WorldOptions` | the seed, *generate structures*, *bonus chest* — four fields, all immutable, each *with*-method returning a new one | — | | `WorldDimensions` | a map from `LevelStem` key to `LevelStem`, each a dimension type plus a `ChunkGenerator`. Refuses to exist without an overworld | — | | `WorldLoader` | the data-pack load every path shares, and the seam (`WorldLoader.WorldDataSupplier`) where the caller decides what the settings are | worker, with two hops to the main thread | | `WorldCreationContext` | the settings *plus* the loaded registries and `ReloadableServerResources` they were parsed against — the screen's whole world | render thread, replaced wholesale | | `WorldCreationUiState` | the widget-visible state, and the seven listeners that keep the tabs' widgets agreeing with it | render thread | | `WorldOpenFlows` | every route from the world list into a running server, each one a chain of methods that can interpose a confirmation | render thread | | `MinecraftServer` | takes the finished object out of the `WorldStem` and hands it to `SavedDataStorage`, which is what puts it on disk | server thread | ## Five stages, and only one of them is the screen ```mermaid flowchart TB A["1 · load — WorldLoader.load opens the packs, fills the WORLDGEN registries, then the LEVEL_STEM registry"] A --> B["2 · decide — the WorldDataSupplier callback builds a WorldGenSettings from the registries just loaded"] B --> C["3 · finish the load — ReloadableServerResources reads recipes, loot and functions against the dimensions stage 2 chose"] C --> D["4 · edit — WorldCreationUiState mutates the object, and any data-pack change restarts at stage 1"] D --> E["5 · commit — bake the dimensions, write level.dat, spin MinecraftServer"] ``` The ordering that matters is stage 2 before stage 3. `WorldLoader.load` takes the settings-building callback as a parameter and calls it **after** the worldgen registries and the `Registries.LEVEL_STEM` registry are loaded and **before** `ReloadableServerResources.loadResources` runs. The registry set that recipes, loot tables and functions are then parsed against includes the dimension registry that callback produced. So the seed and the dimension list are settled before a single recipe is read, and they are settled by a lambda the *caller* supplied — which is the only reason the client's create screen, the client's world-opener, the dedicated server and the game-test server can share one loader. `RegistryDataLoader.DIMENSION_REGISTRIES` is a list of exactly one registry, `Registries.LEVEL_STEM`, loaded in its own pass because its entries need every worldgen registry already in hand. That single-entry list is the *dimension/* folder of a data pack. ## The object, and what is not in it `WorldGenSettings` has two fields. `WorldOptions` holds the seed, a *generate structures* flag, a *bonus chest* flag and a string that only a very old save carries. `WorldDimensions` holds the map of `LevelStem`s. There is no world name in it, no difficulty, no game mode, no game rule and no data-pack list — those are `LevelSettings` and `GameRules`, and [level data and rules](../../reference/level-data-and-rules.md) says which file each of them ends up in. A seed is not a number the box gives you. `WorldOptions.parseSeed` trims the text, returns nothing at all for an empty string, parses a long if it can, and otherwise returns the Java string hash of what you typed — which is why a seed of *glacier* is a seed and a seed of *99999999999999999999* is the hash of that text rather than the number. And *nothing at all* is not zero: `WorldOptions.withSeed` turns an absent seed into `WorldOptions.randomSeed`, one draw from a fresh `RandomSource`. The seed field's responder calls `WorldCreationUiState.setSeed` on every keystroke, so an empty box is re-rolling a new random world every time you touch it. > **For a 1.21-era reader.** The seed has left *level.dat*. `WorldGenSettings` > extends `SavedData` and carries its own `SavedDataType`, so it is written to > *data/minecraft/world_gen_settings.dat* beside *raids.dat* — and the game rules to > *data/minecraft/game_rules.dat* — by the ordinary saved-data machinery rather than by > the level-data writer. `PrimaryLevelData` keeps the old key name only as the > constant `PrimaryLevelData.OLD_WORLD_GEN_SETTINGS`. ## Every widget is an edit to a live object `WorldCreationUiState` is not a form. It holds the `WorldCreationContext` itself, rebuilds it on almost every change, and then walks a listener list so that each widget re-reads what it should now show. The state is also opinionated about what it returns: `WorldCreationUiState.getDifficulty` reports *hard* in hardcore whatever the button last set, `WorldCreationUiState.isAllowCommands` reports true in a debug world and false in hardcore, and `WorldCreationUiState.isBonusChest` reports false in both. The buttons are disabled to match, but the state would lie to them anyway. The world-type button is the destructive one. `WorldCreationUiState.setWorldType` calls `WorldPreset.createWorldDimensions` and replaces **all** the dimensions with the preset's, so a trip through *Superflat* and back to *Default* discards every layer you edited. The button cycles the *normal* world-preset tag — five presets in 26.2 — and holding Alt swaps it for the *extended* tag, which is the same five plus *debug_all_block_states*. Seven world presets ship as JSON under *data/minecraft/worldgen/world_preset/*; the seventh, `WorldPresets.FLAT_ALL_DIMENSIONS`, is in neither tag and appears on no button: the only thing that ever selects it is `CreateWorldScreen.testWorld`, behind a *TW* button the title screen adds when `SharedConstants.IS_RUNNING_IN_IDE`. *Customize* is rarer than it looks. `PresetEditor.EDITORS` is a two-entry map: `WorldPresets.FLAT` opens `CreateFlatWorldScreen` and `WorldPresets.SINGLE_BIOME_SURFACE` opens `CreateBuffetWorldScreen`. For the other five presets the button is inactive. Both editors end the same way, in `WorldCreationContext.DimensionsUpdater` lambdas that call `WorldDimensions.replaceOverworldGenerator` — the overworld only. Nothing in the create screen can edit the nether or the end. ## The layer editor edits the generator you already have `FlatLevelGeneratorSettings` is the odd object in a part where everything else is a record. Its layer list is mutable, its *lakes* and *features* flags are set by void methods, and `FlatLevelGeneratorSettings.getLayersInfo` hands out the live list. `PresetEditor` passes `CreateFlatWorldScreen` the settings of the current overworld generator when that generator is already a `FlatLevelSource` — the same object, not a copy — and the *Remove Layer* button removes an entry from that list directly and calls `FlatLevelGeneratorSettings.updateLayers`. So **the *Cancel* button on the layer editor does not undo a layer deletion.** Cancel only skips the `WorldCreationContext.DimensionsUpdater` that would build a new `FlatLevelSource`; the list it would have been built from has already changed. The *Presets* screen is the well-behaved half of the same screen: `PresetFlatWorldScreen` reads and writes the layer stack as a text string and hands back a *new* settings object through `FlatLevelGeneratorSettings.withBiomeAndLayers`. Nine flat presets ship as JSON, one for each key `FlatLevelGeneratorPresets` registers a value for; the tenth key, `FlatLevelGeneratorPresets.TEST_WORLD`, is declared, never given a value, and read by nothing in the game. One thing the flat generator does not do is place all its own blocks. `FlatLevelGeneratorSettings.adjustGenerationSettings` walks the built layer stack and, for every layer whose block fails `Heightmap.Types.MOTION_BLOCKING`'s opacity test, replaces it with a null and re-adds it as an inline `Feature.FILL_LAYER` placed feature in the *TOP_LAYER_MODIFICATION* decoration step. That test is *blocks motion or holds a fluid*, so the water in *Water World* stays terrain; what leaves it are the air of *The Void* and the snow layer on top of *Snowy Kingdom*, which arrive as [features](features-and-placement.md). ## An experiment is a data pack, so switching one on reloads everything `ExperimentsScreen` looks like a toggle list and is a filtered pack browser: it walks the repository's available packs and keeps only those whose `Pack.getPackSource` is `PackSource.FEATURE`. Three ship in 26.2 — *minecart_improvements*, *redstone_experiments* and *trade_rebalance* — one per non-vanilla flag in `FeatureFlags`. Pressing *Done* rewrites the repository's selection and lands in `CreateWorldScreen` exactly where the data-pack screen lands, in `CreateWorldScreen.tryApplyNewDataPacks`. That method has a fast path and a slow one. If the enabled-pack list and the feature set both come back unchanged, `WorldCreationUiState.tryUpdateDataConfiguration` swaps the configuration in and nothing reloads. Otherwise `CreateWorldScreen.applyNewPackConfig` puts a *validating* message on screen and runs `WorldLoader.load` again from the top — and it has to carry your settings across a registry set that is about to be replaced. It does that by **serialising them**: `WorldGenSettings.CODEC` encodes the current options and dimensions to JSON using the old registries as context, and re-parses that JSON against the new ones. Every `Holder` in the object — every biome, every noise settings, every structure set the flat generator overrides — is written out as an id and looked up again. If the new packs have no world preset or no biome, or the re-parse fails, the future completes exceptionally and the player gets a retry-or-reset confirmation instead of a screen. The two routes into that method differ in one boolean. `CreateWorldScreen.tryApplyNewDataPacks` shows `ConfirmExperimentalFeaturesScreen` only when the requested flags are experimental **and** the caller was the data-pack screen. Toggling an experiment in the Experiments screen skips it — that screen carries a red warning line of its own instead. Data packs added here do not go into a world folder that does not exist yet. `CreateWorldScreen.getOrCreateTempDataPackDir` makes a temporary directory prefixed *mcworld-*, the pack browser is pointed at that, and the directory is copied into the new world's *datapacks* folder by `CreateWorldScreen.createNewWorldDirectory` at the very end. It is then deleted on **every** exit including that one: `CreateWorldScreen.removeTempDataPackDir` runs on the line after the create callback returns. ## What *Create* does ```mermaid sequenceDiagram autonumber participant CWS as CreateWorldScreen participant WCUS as WorldCreationUiState participant WOF as WorldOpenFlows participant MC as Minecraft participant MS as MinecraftServer participant Disk as Disk Note over CWS,Disk: render thread CWS->>WCUS: read the context one last time WCUS-->>CWS: WorldOptions and the selected WorldDimensions CWS->>CWS: WorldDimensions.bake into a frozen LEVEL_STEM registry, then allRegistriesLifecycle plus the feature flags' CWS->>WOF: confirmWorldCreation with that lifecycle WOF-->>CWS: proceed, or an experimental or deprecated warning first CWS->>Disk: create the world directory, copy the temp datapacks in CWS->>WOF: createLevelFromExistingSettings with the WorldStem parts WOF->>MC: doWorldLoad MC->>Disk: saveDataTag writes level.dat through a temp file Note over MC,MS: MinecraftServer.spin builds the server on the render thread, then starts the Server thread MC->>MS: new IntegratedServer with the WorldStem and the screen's GameRules MS->>MS: savedDataStorage.set marks WorldGenSettings dirty Note over MS,Disk: server thread, first save MS->>Disk: data/minecraft/world_gen_settings.dat and data/minecraft/game_rules.dat ``` Three details in that order are worth stopping on. `CreateWorldScreen.onCreate` bakes the dimensions to decide the *lifecycle* and the `PrimaryLevelData.SpecialWorldProperty`, but the `WorldGenSettings` it stores holds the **unbaked** selection — the bake is what runs, the selection is what is saved. The warning is skipped when the world is not a re-create and the baked registries are stable, and `WorldDimensions.checkStability` asks, per key, whether the built-in three carry the vanilla dimension type, the vanilla noise settings **and** the vanilla biome source — anything under a fourth key is experimental by construction. Not every shipped preset passes: *Flat (all dimensions)* gives the nether and the end flat generators, which fail on the noise settings, so this warning does come from a world type as well as from data packs. And *level.dat* is written by the client, in `Minecraft.doWorldLoad`, **before the server thread exists** — while the settings file is written by the server after it starts, because `MinecraftServer`'s constructor is the first thing to hand the object to `SavedDataStorage`. The game rules take a third path again. `CreateWorldScreen.onCreate` copies the screen's `GameRules` into an `Optional` that travels through `CreateWorldCallback`, `WorldOpenFlows.createLevelFromExistingSettings` and `Minecraft.doWorldLoad` to the `MinecraftServer` constructor, which builds a fresh rule set from the saved-data default and then overlays the screen's values on top. ## The same object, from a properties file The dedicated server never sees a screen, and the comparison is the clearest way to see which parts of this page are the subject and which are its interface. | | client create screen | dedicated server | *Re-Create* | |---|---|---|---| | who builds it | `CreateWorldScreen.onCreate` | `Main.createNewWorldData` | `WorldOpenFlows.recreateWorldData` | | the seed | the seed box, per keystroke | *level-seed*, once, in the `DedicatedServerProperties` constructor | copied from the old world's settings | | the dimensions | a `WorldPreset` plus screen edits | *level-type* as a world-preset id, with *default* and *largebiomes* as legacy aliases | the old world's saved `LevelStem` map | | customising | `PresetEditor`, overworld only | *generator-settings* JSON, parsed by `FlatLevelGeneratorSettings.CODEC` **and only when the preset is** `WorldPresets.FLAT` | the create screen again | | game rules | `WorldCreationGameRulesScreen` | one, and it is a legacy key: *announce-player-achievements* sets `GameRules.SHOW_ADVANCEMENT_MESSAGES` | read back from *game_rules.dat* | | when | only if the folder is new | only if there is no *level.dat* | always a new folder | Both seed paths are the same method. `DedicatedServerProperties` calls `WorldOptions.parseSeed` on *level-seed* and falls back to `WorldOptions.randomSeed`, exactly as the seed box does — so an empty *level-seed* draws its random seed the moment the properties file is parsed, whether or not a world is about to be created. An unrecognised *level-type* is a warning in the log and the *normal* preset, not a failure. *Re-Create* is the interesting column. `WorldOpenFlows.recreateWorldData` reads the old world with a deliberately **empty** `LevelStem` registry, so the dimensions come from the saved settings rather than from any pack, then hands `CreateWorldScreen.createFromExisting` a `LevelSettings` and a context. The result is a new world folder with the old seed pre-filled. Nothing in the family edits an existing world's `WorldGenSettings` in place: `EditWorldScreen` offers a rename, an icon reset, a folder button, a backup and *Optimize World*, and not one generation setting. ## The rest of the family `client/gui/screens/worldselection` holds nineteen classes, nine of them screens. `SelectWorldScreen` is a search box, a `WorldSelectionList` and six footer buttons; the list's rows are `LevelSummary` objects read by `LevelStorageSource.readLightweightData`, an NBT parse that deliberately skips the *Data/Player* and *Data/WorldGenSettings* subtrees so that listing a hundred worlds never costs a settings parse. `WorldOpenFlows.openWorld` is a chain of eight methods — itself, then level data, version compatibility, the world stem, stem compatibility, a bundled resource pack, disk space, and finally `Minecraft.doWorldLoad` — each of which can stop and put a confirmation screen in the way. `OptimizeWorldScreen` and `FileFixerProgressScreen` are the progress bars over save migration, which [this book does not cover](../anatomy/what-this-book-skips.md). ## Where to look Start with `net/minecraft/world/level/levelgen`: `WorldGenSettings` is fifty-one lines and tells you the whole shape, `WorldOptions` and `WorldDimensions` are the two halves, and `WorldDimensions.bake` is the method that turns a selection into a registry. Then `net/minecraft/server/WorldLoader` — one method, and the spine every path shares. Only then `net/minecraft/client/gui/screens/worldselection`, in the order `WorldCreationContext`, `WorldCreationUiState`, `CreateWorldScreen` and `WorldOpenFlows`, with `net/minecraft/client/gui/screens/CreateFlatWorldScreen` and `net/minecraft/world/level/levelgen/flat` beside them. The comparison is `DedicatedServerProperties.createDimensions` and `Main.createNewWorldData`, and the destination is the first forty lines of the `MinecraftServer` constructor. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # XIII · Commands and data packs > Verified against **Minecraft 26.2** · Part XIII · a string typed into a chat box becomes a call with typed arguments, on a queue, with a permission attached — and four whole systems are built on top of that and nothing else. Type a slash. The text turns grey and green and red as you type, a hint appears behind the cursor, and pressing Enter sends the *string* — the parse the client just did is thrown away. On the server the same string is parsed again, against a tree whose nodes carry permission requirements, and becomes a piece of work on a queue rather than a Java call. Everything else in this part rides that machinery: an advancement is a subscription delivered by a trigger and edited by `/advancement`, a scoreboard is a number written by `/scoreboard` or by `execute store`, a dialog is a data-pack form opened by `/dialog`, and a game test is a data-pack test run by `/test`. **None of those four needs any of the others.** What they need is the parse and the queue, and a reader who has those two can explain any of the four from them. Counting the nine packages [the atlas](../../maps/packages.md#where-each-part-lives) lists for this part, the way it counts everything else, that is **470 classes and 43,126 lines** — of which the command catalogue alone (`net/minecraft/server/commands`) is 102 classes and 12,800 lines, each of them a thin lambda over machinery some other part of this book owns. So what a command *does* once dispatched is almost always another part's page, and [Brigadier and commands](brigadier-and-commands.md) carries the list of which; the statistics, which are criteria, are in [what this book skips](../anatomy/what-this-book-skips.md). ## The shape of the part Part XIII is **a stack of three floors**, and for a watcher the dependency runs one way: all four systems on the top floor need both of the floors below, and none of them needs another. The code is less tidy than the lecture order — a selector's *advancements=* and *scores=* options reach straight up into two of the top-floor systems — but nothing on the top floor reaches sideways. ```mermaid flowchart TB subgraph P["PARSE — a string becomes a call"] direction LR L1["1 · Brigadier and commands"] --- L2["2 · Permissions"] --- L2b["3 · Entity selectors"] end P --> X subgraph X["EXECUTE — the call becomes work on a queue"] direction LR L3["4 · The execution engine"] --- L4["5 · Functions and macros"] end X --> U subgraph U["WHAT COMMANDS ARE FOR — four systems whose write surface is a command"] direction LR L5["6 · Advancements"] L6["7 · Scores, teams and stored data"] L7["8 · Dialogs"] L8["9 · Game tests"] end ``` The four pages on the top floor are peers, not a sequence: watch them in any order, or only the ones you care about. The two floors below them are not optional for any of the four. ## Before you start [The server tick](../server/server-tick.md#what-minecraftservertickchildren-runs-and-in-what-order) from Part III, because *when* turns out to matter twice: command functions run near the top of `MinecraftServer.tickChildren`, before any level ticks, and the **connection** phase — where `ServerGamePacketListenerImpl.tick` calls `ServerPlayer.doTick` — runs *after* the levels, which is what puts a periodic advancement trigger one tick behind the packet that should have carried it. [Codecs, NBT and JSON](../foundations/codecs-nbt-json.md) and [the data-driven type pattern](../foundations/data-driven-types.md#the-idea-stated-once) from Part II. Dialogs and game tests are the pattern's clearest two instances — a form and a test suite, both reduced to JSON dispatching on a registry of types — and the pattern page is where that argument is made. [The connection](../networking/the-connection.md#the-threads-underneath-it) from Part IX, for the Netty-thread / server-thread boundary that the command packets cross in two different ways on purpose. [Contexts and predicates](../items/contexts-and-predicates.md) from Part VII, if you are here for advancements: a trigger's conditions are loot conditions, evaluated against a loot context, and that page owns the machine. ## Watch in this order 1. [Brigadier and commands](brigadier-and-commands.md) — three parsers for one string, and a tab-completion whose fast path never leaves the machine. Also: which sixty-two of the four hundred and fifty-nine argument nodes do leave it, and why they feel like all of them. 2. [Permissions](permissions.md) — the biggest API break in the game since the flattening. A permission is no longer an integer, an operator does not have everything, and a permission failure is reported as a typo. 3. [Entity selectors](entity-selectors.md) — a selector is a compiled query, and eight of its twenty-one options are not filters but the query plan. Why *@p* crosses dimensions, why *sort=nearest* is what takes your *limit* away, and why one permission is checked twice. 4. [The execution engine](the-execution-engine.md) — a command engine with no Java recursion. A fan-out that materialises one player at a time, and a `/return` that deletes work out of a queue rather than unwinding a stack. 5. [Functions and macros](functions-and-macros.md) — what a `.mcfunction` file becomes, in two steps, the second of which usually does nothing. The one that fails silently every tick, forever. 6. [Advancements](advancements.md) — the game's general-purpose "tell me when the player does X", built as a per-player subscription table that shrinks as criteria are met and is rebuilt when one is revoked. The tree is laid out on the server and shipped. 7. [Scores, teams and stored data](scoreboard-and-data.md) — one number per thing, one query language for any tag, and the `execute store` seam that joins them. Why fake players exist. 8. [Dialogs](dialogs.md) — a data pack puts a form on your screen, possibly before you are in a world at all. The values are read at the moment of the click and not before. 9. [Game tests](game-tests.md) — the game's own test suite, as a data pack. The annotations are gone, a batch *is* an environment, and the shipped jar contains exactly one test. ## Reference this part uses [Packets](../../reference/packets.md) for this part's own traffic, which is almost all server → client: the scoreboard has five packets and no serverbound counterpart at all. [Registries](../../reference/registries.md) and [the data-driven type pattern](../foundations/data-driven-types.md) for the six type registries dialogs and tests dispatch on. [Loot context parameter sets](../../reference/loot-context-params.md) for the sets an advancement trigger and an advancement reward run in. [Diagram lanes](../../reference/lanes.md) for the abbreviations these figures use, and [the glossary](../../reference/glossary.md) for *Brigadier*, *selector head*, *world-limited*, *criterion*, *objective*, *macro*, *dialog* and *game test*. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Brigadier and commands > Verified against **Minecraft 26.2** · Part XIII · You type `/give @p diamond_sword[minecraft:damage=5]` into the chat box: three parsers see that string, two of them throw their answer away — and not one of the completions you accepted along the way left your machine. Open the chat box and type a slash. Before you have finished the word, the text is coloured, a grey hint has appeared behind the cursor and a completion popup is open — and every bit of that was produced by a **real Brigadier dispatcher running on your own machine**, with real parsers built against your own registries, from a tree the server sent you when you joined. The client is not pattern-matching strings. It parses the whole line on every keystroke, throws the parse away, and sends the string. Which raises the question this page exists to answer: if the client can parse the command, why is completing an item id instant and completing a loot table not? Because the tree the client rebuilt says, node by node, who is allowed to answer. Vanilla registers **459 argument nodes**, and only **62** of them serialise as *ask the server* — so the local path is the rule, not the exception. What makes the round trip feel ubiquitous is *which* nodes take it: they are the ones that complete over data the client was never sent. ## The cast | class | what it decides | side | |---|---|---| | `Commands` | the one server-side `CommandDispatcher`, every registration, and what a failed parse is called | server | | `CommandSourceStack` | *who is running this, from where* — position, rotation, level, entity, permissions, output sink. Immutable; a change returns a copy | server | | `CommandBuildContext` | the registries an argument type parses against, so a data pack's biome is completable with no code change | both | | `ArgumentTypeInfos` | the wire description of an argument type: a `ArgumentTypeInfo.Template` that can be written to a buffer and instantiated on the far side | both | | `SuggestionProviders` | the three named providers a node may ask for. Everything else serialises as *ask_server* | both | | `ClientSuggestionProvider` | the client's source: the tab list, the looked-at block and entity, and the one method that sends a packet | client | | `CommandSuggestions` | the 688-line widget over it — the highlighter, the usage hint, the popup and the parse cache | client | | `BrigadierExceptions` | installed once into Brigadier's global exception provider, which is why a parse error is a translatable `Component` | both | Brigadier itself — the dispatcher, the tree of literal and argument nodes, each with a requirement predicate and optionally executable — is Mojang's parsing library and lives outside the game's packages. Everything on this page is what Minecraft builds on top of it. ## Three parsers see one string ```mermaid sequenceDiagram participant CSug as CommandSuggestions participant CPL as ClientPacketListener participant CSP as ClientSuggestionProvider participant SGPL as ServerGamePacketListenerImpl participant Cmds as Commands participant GC as GiveCommand CSug->>CPL: parse the whole line against the client's dispatcher, every keystroke CSug->>CPL: getCompletionSuggestions on that parse, with CSP as the source CSP->>SGPL: ServerboundCommandSuggestionPacket — only if the node asks the server SGPL->>CPL: ClientboundCommandSuggestionsPacket — capped at a thousand, id-matched CPL->>SGPL: ServerboundChatCommandPacket — the raw string, no signatures for /give Note over SGPL: the illegal-character check runs on the Netty thread SGPL->>Cmds: hand to the server thread, then parse again with the player's real source Cmds->>Cmds: the node requirement is consulted inside the parse Cmds->>Cmds: performCommand — one queue, limits read from the level's game rules Cmds->>GC: the registered lambda — resolve the selector, read back the ItemInput GC->>GC: Inventory.add, then sendSuccess and a broadcast to admins ``` Each arrow is a decision. **The client parses first, and the parse never leaves the machine.** `CommandSuggestions.updateCommandInfo` runs the whole string through the client's dispatcher on every keystroke, and that parse produces the red underline, Brigadier's smart-usage hint and the completion list. What is sent is the string. **Item, block-state and component completion never leaves the machine either.** `ItemArgument` and `BlockStateArgument` are registered as **context-aware** singletons, so the client instantiated a real `ItemParser` and a real `BlockStateParser` against its own registries. Item ids, data components and block properties complete locally, and so does every argument type whose suggestion method reads a synced registry. **A node that asks for suggestions by hand almost always asks the server.** This is the part the shape of the code invites you to get backwards — not that the round trip is common, but that opting *in* to a provider is what costs you one. `SuggestionProviders.getName` returns the registered name for a `SuggestionProviders.RegisteredSuggestion` and *ask_server* **for everything else**, so any node whose suggestions come from a plain lambda serialises as a request. Of the **67** vanilla nodes that attach a provider at all, five name one of the three registered providers and the other **62** become *ask_server*. That is how `/function`, `/datapack`, `/bossbar`, `/scoreboard`, `/team`, `/schedule` and `/whitelist` complete. The other 392 argument nodes attach nothing and fall back to their argument type's own suggestions — which is why `/give`, the command in the line at the top of this page, never asks the server anything. A second route reaches the same packet: `ClientSuggestionProvider.suggestRegistryElements` failing to find a server-only registry — loot tables, advancements, recipes — and falling through. Two mechanisms, one packet. **Replies are matched by id, so a stale answer never flashes.** `ClientSuggestionProvider.customSuggestion` cancels the in-flight future and increments a counter; `ClientSuggestionProvider.completeCustomSuggestions` compares the reply's id against that counter and drops anything older. The reply itself is **truncated to a thousand entries, silently** — no marker, no message. **Enter parses a second time on the client, for one reason.** `ClientPacketListener.sendCommand` runs `SignableCommand.of` to find out whether any argument is a `SignedArgument`. `/give` has none, so the plain packet goes; a `/msg` would take a timestamp, a salt and the last-seen message set, sign each signable argument and send the signed variant. A command the player did *not* type — a dialog button, a click event, a sign — goes through `ClientPacketListener.sendUnattendedCommand` instead and is parsed twice more before anything is sent ([permissions](permissions.md)). **The two inbound packets cross the thread boundary differently, on purpose.** `ServerboundCommandSuggestionPacket` goes through `PacketUtils.ensureRunningOnSameThread`, so its parse happens on the main thread. `ServerboundChatCommandPacket` does not: `ServerGamePacketListenerImpl.tryHandleChat` runs the character-legality check on the Netty thread — and may disconnect from there — before handing the body to `MinecraftServer.execute`, calling `ServerPlayer.resetLastActionTime` on the way past, so a little `ServerPlayer` state really is written off the main thread. The signed variant does more still: `ServerGamePacketListenerImpl.handleSignedChatCommand` unpacks the last-seen message set under a lock, and can disconnect for chat-validation failure, before the legality check runs. This is the principle [the connection](../networking/the-connection.md) describes — cheap validation early — with the honest qualifier that "cheap" here includes two disconnect paths and one field write. **The authoritative parse is the server's**, with a `CommandSourceStack` from `ServerPlayer.createCommandSourceStack` carrying the real permission set, and Brigadier consults each node's requirement *during* that parse. Execution then is not a Java call: `Commands.performCommand` flattens the parse into a context chain and hands it to `Commands.executeCommandInContext`, which is [the execution engine](the-execution-engine.md). **`/give` itself is unremarkable and instructive.** `EntityArgument.getPlayers` resolves the selector, `ItemArgument.getItem` hands over the `ItemInput` that was built during *parsing*, `ItemInput.createItemStack` validates it, and the stacks go through `Inventory.add` with anything that will not fit dropped on the floor ([items and stacks](../items/items-and-stacks.md)). Success goes to `CommandSourceStack.sendSuccess` — which takes a *supplier*, so the message is never built when nobody will see it — and broadcasts to admins under two game rules. ## Arguments that are recipes, not values Three argument families do not produce a value at all. They produce something evaluated against the `CommandSourceStack` at run time, which is why one parsed command means different things at different links of an `/execute` chain. **`Coordinates` holds relativity, not a position.** `Coordinates.getPosition` resolves it against the source, so `~` means something different at every link and a single parsed argument yields N positions in a forked execution. `LocalCoordinates` (`^ ^ ^`) is the interesting one: it builds a basis from the source's rotation *and* its `EntityAnchorArgument.Anchor`, so it is the only `Coordinates` shape that depends on eye height — it is not itself an argument type, and both `Vec3Argument` and `BlockPosArgument` can produce one. `SwizzleArgument` parses an axis subset (*xz*), and `/execute align` is its only user. **`EntitySelector` is a compiled query, not a parse tree** — thirteen final fields with no reader and no grammar in them, assembled by `EntitySelectorParser` from `EntitySelectorOptions` and resolved against a `CommandSourceStack` much later, which is why one parsed selector yields a different set at every link of a chain. `ScoreHolderArgument` and `GameProfileArgument` reimplement the same selector-or-literal fork for their own value types. The grammar, the twenty-one options, the two-phase permission check and the two data structures a selector can be resolved against are [entity selectors](entity-selectors.md). **`FunctionArgument` reads an id and nothing else**, deferring the lookup to execution — which is what lets a function be compiled against a null server ([functions and macros](functions-and-macros.md)). ## The parser under the parser Five argument types and the whole SNBT reader are not hand-written `StringReader` walks. They are grammars, written against `net/minecraft/util/parsing/packrat` — Mojang's own parser-combinator framework, with `Term` as the combinator algebra, `Dictionary` and `NamedRule` binding named productions, `Scope` as the typed capture environment, and `CachedParseState` as the memo table keyed by position and rule. That memo table is the *packrat* in the name: a backtracking grammar that would otherwise re-parse the same prefix once per alternative looks it up instead. The reason it matters to a command page is `ErrorCollector` and `SuggestionSupplier`: **the grammar produces completions as a by-product of failing.** A hand-written argument type can only suggest at a token boundary it thought to check; a grammar knows every terminal that could have continued the parse, which is why `/clear @s minecraft:diamond_sword[…` still completes mid-token. The consumers are exactly nine — `ComponentArgument`, `NbtTagArgument`, `ResourceOrIdArgument`, `StyleArgument`, `ItemPredicateArgument`, `ComponentPredicateParser`, and `TagParser` / `SnbtGrammar` / `SnbtOperations` on the NBT side ([codecs, NBT and JSON](../foundations/codecs-nbt-json.md)). ## The tree on the wire `Commands.sendCommands` makes a deep copy of the dispatcher's tree, filtered by each node's requirement for that player's source (`Commands.fillUsableCommands`), and serialises it. An argument node carries the registry id of its `ArgumentTypeInfo` plus whatever the template writes: usually nothing (`SingletonArgumentInfo` writes zero bytes), often a flags byte from `ArgumentUtils.createNumberFlags` or `EntityArgument`'s single / players-only pair, sometimes a registry key. The client then *builds real parsers* from those templates against its own `CommandBuildContext`, which is why a data pack's biomes and dialogs are parseable on the client for free. `Commands.validate` is what keeps that honest — though only in development: `Bootstrap` calls it under `SharedConstants.IS_RUNNING_IN_IDE` alone, so a shipped client never runs it. It throws if any registered argument type is missing from `ArgumentTypeInfos`. **Thirty-eight** argument-type classes live in the top `net/minecraft/commands/arguments` package, plus the *blocks*, *item*, *coordinates* and *selector* subpackages; **fifty-seven** are registered on the wire. Two things about that packet surprise people. It has exactly **one call site**, `PlayerList.sendPlayerPermissionLevel`, so the tree and the op-level entity event are always sent together — on join, respawn, a dimension *change*, op and deop, and the four LAN toggles, and **not** after `/reload`. And an **unknown argument type deletes the node, not its children**: a modded type reaching a vanilla client decodes to a null stub, `ClientboundCommandsPacket.NodeResolver` substitutes a bare `RootCommandNode`, the children are resolved and attached to that throwaway root, and the parent then skips any child that is a `RootCommandNode` — so the node and everything under it vanish from the tree the player can see. The packet is not rejected. What the *filtering* means, and the second elision that rides the same packet, is [permissions](permissions.md). `/reload` builds a whole new `Commands` and a whole new dispatcher inside `ReloadableServerResources` and tells nobody. The consequence is narrower than the folklore on either side: both server-side parses read through `MinecraftServer.getCommands` and pick up the new dispatcher immediately, so a newly added function *does* tab-complete after a reload — that completion is an *ask_server* round trip. What goes stale on the client is the tree's *shape* and its flags, which no vanilla data pack can change. ## Commands that are a door to somewhere else Most of `net/minecraft/server/commands` — a hundred classes and 12,800 lines — is a thin lambda over machinery another part of this book owns. A reader looking for "how does `/locate` work" wants the mechanism page, so here is the index. | command | what it really reaches | where that lives | |---|---|---| | `LocateCommand` | three barely related parts: `LocateCommand.locateStructure` can **drive world generation on the server thread**, because deciding whether a structure is at a chunk means asking the structure check; `LocateCommand.locateBiome` asks the biome source and never reads a stored palette; `LocateCommand.locatePoi` asks the POI index | [structure placement](../worldgen/structure-placement.md), [biomes](../worldgen/biomes.md) | | `FillBiomeCommand` | writes the biome palette of the affected sections and resends them — the only command that edits a chunk's biomes | [chunk anatomy](../world/chunk-anatomy.md) | | `PlaceCommand` | four doors: a configured feature with no placement layer, a whole structure, jigsaw assembly directly, and a structure template with rotation, mirror, integrity and a seed | [features and placement](../worldgen/features-and-placement.md), [jigsaw and templates](../worldgen/jigsaw-and-templates.md) | | `LootCommand`, `ItemCommands` | `ItemCommands.applyModifier` runs a loot *function* over an existing stack — `/item modify`, and the `from … ` form of `/item replace`. Both take a table or modifier through `ResourceOrIdArgument`, so an inline literal works where an id does | [loot tables](../items/loot-tables.md) | | `EnchantCommand`, `ExperienceCommand` | thin faces over two systems | [enchanting](../items/enchanting.md), [hunger and experience](../player/hunger-and-experience.md) | | `ExecuteCommand`, `FunctionCommand` | not commands so much as the front end of the engine | [the execution engine](the-execution-engine.md) | | `ScoreboardCommand`, `TeamCommand`, `TriggerCommand`, `DataCommands` | the entire write surface of the scoreboard and of stored NBT | [scores, teams and stored data](scoreboard-and-data.md) | And one class of command a reader will look for in a shipped game and not find. `Commands` registers `DebugConfigCommand`, `RaidCommand`, `DebugPathCommand`, `DebugMobSpawningCommand`, `WardenSpawnTrackerCommand`, `SpawnArmorTrimsCommand` and `ServerPackCommand` only when `SharedConstants.DEBUG_DEV_COMMANDS` or `SharedConstants.IS_RUNNING_IN_IDE` is set, and `ChaseCommand` behind a flag of its own. `DebugConfigCommand` is additionally dedicated-server-only, which matters: it is the only vanilla caller of the play-to-configuration transition and back ([protocol phases](../networking/protocol-phases.md)). One more thing crosses the wire from here and belongs to nobody else: `ClientboundCustomChatCompletionsPacket`, a server pushing arbitrary non-command completions into the tab list, add / remove / set. ## Signed arguments, in one paragraph `MessageArgument` is the **only** signed argument in the game — the sole implementor of `SignedArgument` — and seven command classes register it under ten literals a player can type: `/ban-ip`, `/ban`, `/me`, `/kick`, `/say`, `/msg` with its `/tell` and `/w` redirects, and `/teammsg` with `/tm`. All of `SignableCommand`, `ArgumentSignatures`, `ArgumentVisitor` and `CommandSigningContext` exists to serve them: `ArgumentVisitor.visitArguments` walks a parse to find which arguments need a signature, and the map carries them afterwards. A command with signable arguments sent *unsigned* is refused outright when the server enforces secure profiles, and a signature that does not match the parse breaks the player's whole message chain ([chat and signing](../networking/chat-and-signing.md)). ## Where to look `Commands` for what exists and what a failed parse is called; `CommandSourceStack` for what a command knows; `ArgumentTypeInfos` for the catalogue of argument types and their wire forms; `EntitySelectorOptions` for the grammar players actually write; `CommandSuggestions` for everything that happens while you type; and `ClientboundCommandsPacket` for the one place the two sides agree on a shape. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Permissions > Verified against **Minecraft 26.2** · Part XIII · You are a level-four operator, you type `/msg`, and the server's own answer to *may this player send a chat command* is **no** — because an operator's permission set grants exactly one thing that is not a command level. Op yourself to four, the highest rung there is, and ask the server whether you hold `Permissions.CHAT_SEND_COMMANDS`. It says no. Not because you are restricted — because the set you were given is a `LevelBasedPermissionSet`, and its answer to any permission that is not a command level is *false*, with one hard-coded exception. The chat atoms live only in the *client's* set, which the server never sees and never sends. There are two permission universes in this game and they overlap in one place. That is the shape of the change 26.2 made, and it is the largest API break in this book: **a permission is no longer an integer.** The five levels are still there, still numbered nought to four, still stored as integers in *ops.json* — but a command node no longer asks for a number. It asks a `PermissionSet` a question, and a set is free to answer however it likes. ## The cast | class | what it decides | where it lives | |---|---|---| | `Permission` | *what is being asked for*: a `Permission.Atom` (a named capability with an `Identifier`) or a `Permission.HasCommandLevel` (a rung) | both jars | | `PermissionLevel` | the five rungs — *all*, *moderators*, *gamemasters*, *admins*, *owners* — and `PermissionLevel.isEqualOrHigherThan` | both | | `PermissionSet` | *the answer*. A functional interface with one method, `PermissionSet.hasPermission` | both | | `LevelBasedPermissionSet` | the ordinary answer: an interface with five constants, one rung each | both | | `PermissionSetUnion` | the OR of several sets, and the rule that a union may not contain a union | both | | `PermissionCheck` | *what a node demands*: `PermissionCheck.Require` or `PermissionCheck.AlwaysPass` | both | | `PermissionProviderCheck` | the `Predicate` Brigadier actually holds, over anything implementing `PermissionSetSupplier` | both | | `ChatAbilities` | the client's own set, built by **subtraction** from local reasons | client only | Eleven classes and 398 lines in `net/minecraft/server/permissions` — the smallest package in this book that changes how everything above it is written. ## A question, an answer, and a check Three types do three jobs that a single integer used to do, and keeping them apart is what makes the rest legible. ```mermaid flowchart TB subgraph Q["THE QUESTION — Permission"] A["Permission.Atom — an Identifier: commands/entity_selectors, chat/send_messages"] L["Permission.HasCommandLevel — a PermissionLevel: all, moderators, gamemasters, admins, owners"] end subgraph S["THE ANSWER — PermissionSet, one method"] LB["LevelBasedPermissionSet — a rung. Satisfies any level at or below it, plus the entity-selector atom from gamemaster up, and nothing else"] CH["ChatAbilities — a literal Set of the four chat atoms, minus whatever local restrictions removed"] CL["ClientPacketListener's two — the player's own set OR-ed with a synthetic restricted atom, and NO_PERMISSIONS"] UN["PermissionSetUnion — OR over the above. Refuses to contain another union"] end subgraph C["THE CHECK — PermissionCheck, what a node holds"] RQ["PermissionCheck.Require — asks the source's set one question"] AP["PermissionCheck.AlwaysPass — a singleton, and Commands.LEVEL_ALL is literally it"] end Q --> S S --> C ``` The **question** is data: both shapes are records, both are codec-dispatched over `BuiltInRegistries.PERMISSION_TYPE`, and `Permission.CODEC` accepts an atom written as a bare identifier as well as the full dispatched form. The **answer** is behaviour: `PermissionSet` is one method, so every set in the game above is a lambda or a small object, and there is no set-of-permissions data structure anywhere except inside `ChatAbilities`. The **check** is what a command node carries: `Commands.hasPermission` wraps a `PermissionCheck` in a `PermissionProviderCheck`, and that predicate is what Brigadier consults. **Ninety-five** — `Commands.hasPermission` call sites, which is every requirement predicate on every command node in the game. Ninety-four are server-side command registrations; the ninety-fifth is on the *client*, in `ClientPacketListener`'s node builder. Two things about `LevelBasedPermissionSet` decide most of this page. It is an **interface with five constants**, not a class carrying a level, so `LevelBasedPermissionSet.GAMEMASTER` is a singleton and comparing two sets is comparing two references. And its `LevelBasedPermissionSet.hasPermission` tests a `Permission.HasCommandLevel` against its own rung, grants `Permissions.COMMANDS_ENTITY_SELECTORS` from gamemaster upward as a special case written into the method, and returns **false to every other atom**. An operator does not have everything; an operator has a number and one exception. Union has a special case of its own, and it runs the opposite way to its name. `LevelBasedPermissionSet.union` of two level-based sets is not a `PermissionSetUnion` at all, and it is not the higher of the two: both branches of the override return the **lower**-levelled set, so it is a minimum. `CommandSourceStack.withMaximumPermission` is that union, which means "raising" a function body to gamemaster ([functions and macros](functions-and-macros.md)) *caps* an owner's source at gamemaster rather than leaving it alone. Only for sets that are not level-based does `PermissionSet.union` fall through to `PermissionSetUnion`, which does OR the two. ## Where a set comes from `MinecraftServer.getProfilePermissions` is the whole of it, and it returns a `LevelBasedPermissionSet` — never a union, never an atom set. It is consulted afresh every time `ServerPlayer.permissions` is called; nothing is cached on the player. Its cascade is short. Not on the operator list at all, and you get `LevelBasedPermissionSet.ALL` — rung zero, and deprecated in place. On the list, and the ops-file entry's own stored set wins. Failing that: the singleplayer owner gets `LevelBasedPermissionSet.OWNER`; any other singleplayer player gets owner or rung zero depending on the *allow cheats for other players* toggle; and on a dedicated server the fallback is the configured *op-permission-level* property. The integer survives at every edge of that model, which is worth knowing before you go looking for a permissions file. *ops.json* stores a number. *server.properties* stores a number. The op level reaches the client as one of five values on `ClientboundEntityEventPacket`. And `PermissionLevel.byId` **clamps** rather than failing, so an *ops.json* hand-edited to level 9 is an owner and level −1 is rung zero. What does not exist anywhere is a data pack that grants a permission: `PermissionCheck.CODEC` has exactly one consumer in the whole game, `ArgumentUtils.serializeNodeToJson` writing the generated command report, and both type registries are bootstrapped in code by `PermissionTypes` and `PermissionCheckTypes`. ## The requirement is consulted inside the parse This is the design decision a server administrator feels most often, and it is not a message-formatting choice. Brigadier evaluates a node's requirement predicate *while walking the tree*, so a node you may not use is a node that is not there. `Commands.getParseException` then reports an empty parse range as an **unknown command** and a non-empty one as an unknown *argument*. An unopped player typing `/give` is told there is no such command. The server cannot tell them otherwise without a second parse, and it does not do one. The entity-selector atom is checked **twice**, in two different phases, and it is the only permission in the game that is: `EntitySelectorParser.allowSelectors` tests it at parse time, and `EntitySelector.checkPermissions` tests it again when the selector is resolved against the world. A command holding a parsed selector can therefore be re-run later against a source that may no longer use it — which is exactly what an `/execute` chain does. Two more consequences worth naming. `Commands.LEVEL_MODERATORS` gates **nothing**: the rung exists, is settable and is stored, and no vanilla command asks for it. And of the ninety-one gates that name a level constant, sixty-six ask for gamemaster, sixteen for admin and nine for owner — while `Commands.LEVEL_ALL` appears exactly twice, both times as the *else* branch of a ternary, in `SeedCommand` and `VersionCommand`, which drop their requirement when the server is the integrated one. ## What the client is allowed to believe The client has permissions of its own, and they are not a copy of the server's — no packet carries a `PermissionSet`. It has three sources of belief, and they behave differently enough to be worth separating. **The op level**, which arrives on an entity event and is mapped by `LocalPlayer.handleEntityEvent` onto one of the five sets. Note the bottom rung: level zero maps to `PermissionSet.NO_PERMISSIONS`, not to `LevelBasedPermissionSet.ALL`. The two are indistinguishable in practice — rung zero satisfies no level and no atom either — but the client's copy is a different object from the server's. **The command tree**, whose nodes were filtered for this player before being sent. Two elisions ride that packet and they mean different things. A node whose requirement the player failed is simply **absent**, and the filter is recursive, so a gated literal takes its whole subtree with it. A node that a *no-permission* source would fail is **flagged** (`ClientboundCommandsPacket.FLAG_RESTRICTED`) — "this needs some permission", asserted independently of you. `Commands` computes that flag against a source it builds once from `PermissionSet.NO_PERMISSIONS`, and only for nodes that already survived your own filter. **Its own atoms.** `ClientPacketListener` mints a single synthetic `Permission.Atom` for restricted commands and keeps two sources over its one dispatcher: the ordinary one, whose set is the player's own **OR-ed with** that atom, so restricted nodes still highlight and complete; and a no-permission one. `ChatAbilities` is the other client-only set, and it is built the opposite way round from everything else here — it starts from all four chat atoms *granted* and lets each `ChatRestriction` remove some. Three of the four are decisions the machine you are sitting at makes — two chat options and a launcher flag — and the fourth, `ChatRestriction.DISABLED_BY_PROFILE`, comes from the account service: a user flag fetched with your profile. The client's chat permissions are never granted by the *game* server; they are only ever taken away, and only ever from outside it. ## Asking a question the client cannot answer Put those two client sources together and you get the one thing the client *can* diagnose: it can tell "you would need permission for this" apart from "that is a typo", for a command it was asked to send on your behalf. A dialog button and a chat click event both route through `ClientPacketListener.sendUnattendedCommand`, whose two callers are `Screen.clickCommandAction` and an adapter `ClientPacketListener` builds for itself. A **sign does not**: `SignBlockEntity` runs its click command on the server, through a `CommandSourceStack` it builds itself at a hard-coded `LevelBasedPermissionSet.GAMEMASTER`, and the client is never consulted. ```mermaid flowchart TB IN["an unattended command — a dialog button, a click event, a sign"] IN --> P1{"parses against the ordinary source?"} P1 -- no --> E1["PARSE_ERRORS — confirm: parse errors"] P1 -- yes --> P2{"any signable argument?"} P2 -- yes --> E2["SIGNATURE_REQUIRED — confirm: signature required"] P2 -- no --> P3{"parses against the NO_PERMISSIONS source too?"} P3 -- no --> E3["PERMISSIONS_REQUIRED — confirm: permissions required"] P3 -- yes --> OK["NO_ISSUES — send it"] E1 --> CS["a ConfirmScreen: the player decides"] E2 --> CS E3 --> CS ``` `ClientPacketListener.verifyCommand` parses the same string against both sources and reads the *difference*. Succeeding with your set and failing without it means some node on the path was gated — which is as much as the client can ever know, because it was never told which permission or whose. Three of the four outcomes pop a confirmation screen; the fourth, `ClientPacketListener.CommandCheckResult` *NO_ISSUES*, sends with no screen at all. So an unattended command that is merely *unusual* is always shown to you first — but a clean one goes straight out, and a waxed sign's command never came this way to begin with. The client runs *server* permission checks against its own set in several places — `WorldOptionsScreen` gates the hardcore and gamemode buttons on `Permissions.COMMANDS_OWNER` and `Permissions.COMMANDS_GAMEMASTER`, `KeyboardHandler` gates three debug keys — but only one of those checks is a constant the server itself uses. `GameModeCommand.PERMISSION_CHECK`, a `PermissionCheck.Require` for gamemaster exported from the command class, is read twice by `KeyboardHandler` and once by `GameModeSwitcherScreen` against `LocalPlayer`'s own set — which is why F3+F4 refuses to open the switcher at all, with *debug.gamemodes.error*, rather than opening a greyed-out one — and read again by `ServerGamePacketListenerImpl` when the packet arrives. One constant, five references in four classes, two sides of the network: the exception that shows what the rule costs. > **For a 1.21-era reader.** *ServerPlayer.hasPermissions(int)* and > *CommandSourceStack.hasPermission(int)* are gone. The nearest thing is > `PermissionSet.hasPermission(Permission)` reached through > `ServerPlayer.permissions` or `CommandSourceStack.permissions`, and the > names that survived the rewrite unchanged — `Commands.LEVEL_GAMEMASTERS` > and its siblings — **changed type**, from an integer to a `PermissionCheck`. Code > that compiles against the old signature does not exist; code that reads > the old *semantics* ("an op has everything") compiles and is wrong. ## Where to look `PermissionSet` first — seventeen lines, and the whole model is in them. Then `LevelBasedPermissionSet` for the two special cases that decide everything, `Permissions` for the nine vanilla permissions, and `Commands.hasPermission` for the one idiom every command registration uses. `MinecraftServer.getProfilePermissions` for where a player's set is decided, and `ClientPacketListener.verifyCommand` for the only place either side reasons about a permission it does not have. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Entity selectors > Verified against **Minecraft 26.2** · Part XIII · you type */kill @e[type=!player,distance=..8,sort=nearest,limit=1]*, and the characters inside the brackets decide, between them, which levels are searched and which of two entirely different data structures answers. Put a command block at the Overworld origin and give it */tp @p 0 100 0*. One player is two hundred blocks away across the Overworld; another is standing on the Nether roof directly "above" the block, at the Nether's own *0, 128, 0*. The command block teleports the one in the Nether. *@p* is not "the nearest player in this world": it is the player whose raw *x*, *y* and *z* are nearest, chosen from the list of everybody on the server, compared as though the dimensions were stacked in the same coordinate space. (Type the same command yourself and you always win it — a player's own source sits at distance zero from a player's own position.) Nothing in a selector is confined to one level unless you write one of **seven** options that says so — and the cheapest of those seven, *distance*, is also the one that decides whether the game walks every entity in the level or asks the chunk sections for a box. That is the shape of the whole subject. A selector looks like a filter language, and it is one, but a handful of its twenty-one option names are not filters at all: they are the query plan. This page is about which is which. ## The cast | class | what it decides | when it runs | |---|---|---| | `EntitySelectorParser` | the reader, the grammar and thirty-two half-built fields. It owns the selector's own syntax; the brace grammars of *scores* and *advancements* are read by the handlers themselves | parse time | | `EntitySelectorOptions` | the name-to-handler map — twenty-one entries, filled once by `Bootstrap.bootStrap` and never again | class init | | `InvertableSetOptionState` | the three-state machine behind *type=!zombie,!skeleton*: one positive assertion **or** any number of negations and tags, never both | parse time | | `SetOnceOptionState` | one boolean, for the four options that may appear at most once | parse time | | `EntitySelector` | the compiled query: thirteen final fields, no reader, no grammar, no string | built at parse time, run later | | `EntityArgument` | four argument shapes (single or many, entities or players) and the parse-time rejections that enforce them | parse time and on the wire | | `CommandSourceStack` | the only thing a selector can be resolved against — origin, level, server, permission set | resolve time | | `LevelEntityGetterAdapter` | the fork in the road: an `EntityLookup` walk, or an `EntitySectionStorage` box query | resolve time | `net/minecraft/commands/arguments/selector` is five classes and 1,717 lines, and every one of them is in the server jar *and* the client jar. That matters later. > **For a 1.21-era reader.** The parser's crowd of *hasNameEquals* / > *hasNameNotEquals* / *hasGamemodeEquals* booleans is gone, replaced by > eight state objects — four `InvertableSetOptionState` and four > `SetOnceOptionState` — that enforce the same rules structurally. *ResourceLocation* is `Identifier`. And the check > that used to be an op-level comparison is now an atom, > `Permissions.COMMANDS_ENTITY_SELECTORS` ([permissions](permissions.md)). ## Three stages, and the last one is not on the parser's clock ```mermaid flowchart LR S["the argument text"] subgraph P["PARSE"] direction TB P1["one head of six sets four defaults"] P2["each option name looked up in a hash map"] P3["its handler writes a field or appends a predicate"] P1 --> P2 --> P3 end subgraph C["COMPILE — once, still at parse time"] direction TB C1["thirteen final fields"] C2["a box, or null"] C3["a position resolver"] C1 --- C2 --- C3 end subgraph R["RESOLVE — server thread, once per execution"] direction TB R1["which levels"] R2["which of two data structures"] R3["order, then cut to the limit"] R1 --> R2 --> R3 end S --> P P --> C C --> R ``` The first two stages happen while Brigadier walks the command tree; the third happens when the command's lambda asks for its argument. In between, the selector is an ordinary immutable object sitting in a parsed context, which is why an */execute* chain can resolve the same parsed selector once per source and get a different answer each time ([the execution engine](the-execution-engine.md)). ## Parse: six heads and twenty-one names `EntitySelectorParser.parseSelector` reads the character after the *@* and sets four things — a result limit, whether non-players are in scope, an order, and sometimes a type. There are six, and no more: the default branch of that switch throws. | head | limit | non-players | order | extras | |---|---|---|---|---| | *@a* | unbounded | no | arbitrary | typed to player | | *@e* | unbounded | yes | arbitrary | adds an `Entity.isAlive` test | | *@n* | 1 | yes | nearest | adds an `Entity.isAlive` test | | *@p* | 1 | no | nearest | typed to player | | *@r* | 1 | no | random | typed to player | | *@s* | 1 | yes | arbitrary | resolves to the source's own entity | Only *@e* and *@n* add that test, and only `LivingEntity.isAlive` makes it mean anything — it is the override that adds "and has health left" to the base class's "and has not been removed". So a player sitting on the death screen is invisible to *@e* and still a target for *@a* and *@p*. Then the bracket. `EntitySelectorParser.parseOptions` reads a name, looks it up through `EntitySelectorOptions.get`, and hands the reader to the handler it finds. **Twenty-one** names are registered, counted by reading every `EntitySelectorOptions.register` call in `EntitySelectorOptions.bootStrap`. | option | what its handler does | repeatable? | |---|---|---| | *name* | compares `Nameable.getPlainTextName` | one positive, or many negatives | | *team* | compares `Entity.getTeam` — the empty string means *no team* | one positive, or many negatives | | *gamemode* | compares `ServerPlayer.gameMode`, **and drops non-players** | one positive, or many negatives | | *type* | an id sets the `EntityTypeTest`; a tag only adds a test | one positive id, or many negatives and tags | | *tag* | reads `Entity.entityTags` — empty means *no tags at all* | freely | | *nbt* | serialises the candidate and compares with `NbtUtils.compareNbt` | freely | | *predicate* | runs a loot condition in `LootContextParamSets.SELECTOR` | freely | | *scores* | a brace map of objective to `MinMaxBounds.Ints` | once | | *advancements* | a brace map of advancement to done-ness, **players only** | once | | *limit* | sets the result cap, rejects anything below 1 | once, never on *@s* | | *sort* | picks one of four orders | once, never on *@s* | | *distance* | a `MinMaxBounds.Doubles`, rejects negatives, **world-limits** | once | | *level* | a `MinMaxBounds.Ints`, rejects negatives, **drops non-players** | once | | *x*, *y*, *z* | override one axis of the resolve origin, **world-limit** | once each | | *dx*, *dy*, *dz* | build the box, **world-limit** | once each | | *x_rotation*, *y_rotation* | angle ranges that wrap through 360 | once each | Three of the twenty-one are freely repeatable — *tag*, *nbt* and *predicate* — because they are the three registered as always available, with no state object behind them at all. That is why *tag=a,tag=b* is the idiom for "has both" and *type=zombie,type=skeleton* is a parse error rather than an empty result: `InvertableSetOptionState` moves to a terminal state the moment a positive id is accepted, and `EntitySelectorOptions.get` then refuses the whole option by name before its handler ever runs. Its *other* terminal state is the permissive one — after a negation or a tag, more negations and more distinct tags are allowed, which is why *type=!zombie,!skeleton* works and why two entity tags can be written together and AND. ## Compile: what a box is, and where it comes from `EntitySelectorParser.getSelector` runs once, at the end of the parse, and turns the pile of fields into thirteen final ones. Two of the thirteen are the interesting decisions. **The box.** If any of *dx*, *dy* or *dz* was written, `EntitySelectorParser.createAabb` builds the box from those three, treating the missing ones as zero and adding one to each maximum — a *dx=0* volume is one block wide, not zero. Otherwise, if *distance* was written **and has a maximum**, the box is a cube of that radius, again with one added to the positive corner. Otherwise there is no box. So *distance=8..* — a minimum with no maximum — produces no box at all, and *dx=3,distance=..64* ignores the distance entirely when choosing the box, because the delta branch wins. **The origin.** If any of *x*, *y* or *z* was written, the position becomes a function that overrides those axes of the source's position and keeps the rest. Otherwise it is the identity. This is applied per execution, which is what makes *x=0* mean the same thing everywhere and *@s* mean something different at each link of a chain. Everything else that was written is already a test in a list, in written order — with exceptions appended afterwards whatever order they appeared in. `EntitySelectorParser.finalizePredicates` adds the two rotation tests and the experience-level test last, and `EntitySelector.getPredicate` then appends up to three more at resolve time: the feature-flag test, the exact box test and the range test. `Util.allOf` evaluates them in that order and short-circuits, so the range test — the cheapest thing in a selector — runs **after** an *nbt* comparison that serialised the whole entity. ## Resolve: which levels, which structure, which order ```mermaid flowchart TB A["findEntities, on the server thread"] --> B{"non-players in scope?"} B -- no --> P["findPlayers — a linear walk of a player list, always"] B -- yes --> C{"a bare name or a UUID?"} C -- name --> N["PlayerList.getPlayerByName — a linear case-insensitive scan"] C -- UUID --> U["PlayerList.getPlayer — the id map, one lookup"] C -- neither --> D{"is it the source itself?"} D -- yes --> S["test the source's own entity, or return nothing"] D -- no --> E{"world-limited?"} E -- yes --> F["this level only"] E -- no --> G["every level the server has"] F --> H{"is there a box?"} G --> H H -- yes --> I["EntitySectionStorage — only the sections the box touches"] H -- no --> J["EntityLookup — every visible entity in the level, one by one"] I --> K["order, then cut to the limit"] J --> K P --> K ``` **World-limited is a parse-time flag, not a runtime one.** Exactly seven option handlers call `EntitySelectorParser.setWorldLimited`: *distance*, *x*, *y*, *z*, *dx*, *dy* and *dz*. Write none of them and `EntitySelector.findEntities` iterates every level the server has. */kill @e[type=item]* is a three-dimension operation. **The two structures are genuinely different.** With a box, `Level.getEntities` goes through `EntitySectionStorage`, which visits only the accessible non-empty 16-cubes the box overlaps. Without one, `ServerLevel.getEntities` goes through `EntityLookup`, which walks the level's entire visible-entity map and calls `EntityTypeTest.tryCast` on each. **There is no index by entity type.** *type=zombie* narrows nothing structurally; it is a cast applied one entity at a time, ahead of the tests. Only the seven box options narrow the search itself, and only when they add up to a box. **And the box path finds things the walk cannot.** `Level.getEntities` also offers each ender dragon's eight `EnderDragonPart` sub-entities to the type test and the predicate, and every part reports its parent's type. So a box query in the End can return the dragon and up to eight more "ender dragons"; the same selector without a box returns one. **Players never get a box.** Both player paths — `ServerLevel.getPlayers` for one level, `PlayerList.getPlayers` for the whole server — are linear walks of a list, and the box survives only as one more test. *@a[distance=..8]* costs what *@a* costs. **Sort is what takes the limit away.** `EntitySelector.getResultLimit` returns the parsed limit **only when the order is arbitrary**, and the unbounded value otherwise, because a sort has to see everything before it can know what comes first. When it is the parsed limit, it reaches the level query as an early abort. So *@e[limit=1]* stops at the first match, and *@e[limit=1,sort=nearest]* collects every match in range, sorts the list and throws all but one away. *@n* and *@p* live permanently in the second mode: their heads set the nearest order, so they always collect first and cut afterwards. **So the query plan is written by eight of the twenty-one names.** Seven of them build the box and world-limit the search — *distance*, *x*, *y*, *z*, *dx*, *dy* and *dz* — and the eighth, *sort*, un-decides part of it by taking the limit away. The other thirteen only filter what the plan returns. ## One permission, checked in two places, for two different reasons The gate is a single atom, `Permissions.COMMANDS_ENTITY_SELECTORS`, granted by `LevelBasedPermissionSet` from gamemaster upward as the one hard-coded exception in that class ([permissions](permissions.md)). It is read in seven places, all of them under `commands/arguments`, and they divide cleanly in two. **At parse time**, `EntitySelectorParser.allowSelectors` asks the source and the answer becomes a constructor argument. If it is false, an *@* throws `EntitySelectorParser.ERROR_SELECTORS_NOT_ALLOWED` immediately, and a bare name or UUID still parses. This is the check that matters for the three vanilla commands that take a selector-capable argument with **no permission requirement at all** — `MsgCommand`, `EmoteCommands` and `TeamMsgCommand`, so */msg*, */tell*, */w*, */me*, */teammsg* and */tm*. For an ordinary player this atom is the only gate on those. `MessageArgument` alone treats a refusal as a formatting decision rather than an error: without the permission the message is taken as literal text, so an unopped */msg Bob @a* sends those two characters. With the permission, an *@* that is not a valid selector head is skipped and the scan continues — which is how an email address survives — but a *malformed* selector body throws, and the whole command fails to parse. **At resolve time**, `EntitySelector.checkPermissions` asks again, guarding on `EntitySelector.usesSelector`, which only `EntitySelectorParser.parseSelector` ever sets. A selector compiled from a bare player name is exempt. The second check is not redundant, because there is a whole route into the machinery that never passed the first one: `EntitySelector.COMPILABLE_CODEC` compiles a selector out of a **text component** — the *selector* content type, the *score* name field and the *entity* NBT data source — and does so with selectors unconditionally allowed, because a codec has no source to ask. The resolve-time check is what decides whether a */tellraw* written by a data pack may actually enumerate entities, and it asks the source the component is being resolved *against*, never whoever wrote it. ## Questions a command author asks **Does the client parse selectors?** Yes, by two routes, and it cannot resolve one. All five selector classes ship in the client jar. `EntityArgument.listSuggestions` builds a real `EntitySelectorParser` against the client's own permission set, parses as far as it can, swallows the exception and asks the half-finished parser for its suggestions — which is why completion inside a bracket knows which options are still legal. And `ComponentSerialization` decodes a *selector* content type on the client with the same compiler the server uses. What the client cannot do is run one: every find method takes a `CommandSourceStack`, and the client's suggestion source is a `ClientSuggestionProvider`. The one place a client resolves a component that might contain a selector is `ServerStatusPinger`, whose `ResolutionContext` deliberately carries no source, so a server-list description containing a selector renders as nothing at all. **Why did */damage @e 1* complain before it touched the world?** Because `EntityArgument` rejects on the compiled selector's *shape*, during the parse: a limit above one in a single-target slot — which is what `/damage`, `/ride` and `/data get entity` all take — or non-players in a players-only slot, as in */msg @e*. Note that */kill @e* is fine: `/kill` takes the many-entities shape, so neither rejection can fire on it. *@s* is exempt from the second test, so */msg @s* parses and then finds nobody when the source is not a player. **What does the client suggest for an entity argument?** Online player names, plus — from `ClientSuggestionProvider.getSelectedEntities` — the UUID of whatever your crosshair is on. Point at a cow, press tab, and it offers you that cow. **Is *sort=random* seeded?** No. It is the JDK's list shuffle, with no world seed and no `RandomSource` anywhere near it, so *@r* is not reproducible from a save. **Why does *distance=..8* not return something 8.9 blocks away?** Because the box is only a pre-filter. The cube built for a maximum of 8 spans −8 to +9 on each axis, deliberately larger than the sphere it approximates, and the exact test that follows is `MinMaxBounds.Doubles.matchesSqr`, which compares squared distances against pre-squared bounds and so never takes a square root. ## Where to look `EntitySelector` first — thirteen fields and four find methods, and the design is in them. Then `EntitySelectorParser.getSelector` for the one place those thirteen are decided, and `EntitySelectorOptions.bootStrap` for the grammar players actually write. `LevelEntityGetterAdapter` is six methods long and is where the cost of every selector is settled. Note that the name `EntitySelector` is used twice in the game: this one, and an unrelated bag of predicate constants in `world/entity` that the mob AI and the hoppers use. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # The execution engine > Verified against **Minecraft 26.2** · Part XIII · `/execute as @a at @s run say hi` on a server with four players: a command engine with no Java recursion, a fan-out that materialises one player at a time, and a `/return` that deletes work out of a queue rather than unwinding a stack. Write a data pack that calls a function that calls itself, load it, and the server does not crash. It runs a very large number of commands, logs one line at *info*, and carries on. That is not a recursion limit — there is no recursion limit, and no depth game rule either. It is that **nothing in command execution uses the Java call stack.** Every construct that used to nest — `/execute … run`, `/function`, `execute if function`, `/return run` — is expressed as *queued work* on a heap-allocated deque, and the driver is a flat loop. A stack made of heap objects costs you nothing except the obvious, and buys you three things a real stack cannot give: you can inspect it, you can **delete** entries out of the middle of it, and you can hand each entry to a tracer on the way past. `/return`, `/debug function` and the fork limit all exist because of that one decision. ## The cast | class | what it decides | notes | |---|---|---| | `ExecutionContext` | one per outermost command: the queue, the staging list, the budget, the fork limit, the tracer | 153 lines, and the whole engine is in its `ExecutionContext.runCommandQueue` loop | | `CommandQueueEntry` | a `Frame` and an `EntryAction`. That is the entire unit of work | — | | `Frame` | **not** a stack frame: a depth, a `CommandResultCallback` a `/return` feeds, and a `Frame.FrameControl` that knows how to delete this frame's pending work | one object shared by reference across a whole function body | | `BuildContexts` | walks the stages of a parsed chain, forking sources as it goes | `BuildContexts.TopLevel`, `BuildContexts.Continuation` and `BuildContexts.Unbound` | | `ContinuationTask` | the lazy fan-out: emits one element's entry, then re-queues itself | the reason N players cost N entries, not N at once | | `CallFunction` / `IsolatedCall` | the only two things besides the top level that open a frame | `IsolatedCall`'s `/return` cannot reach the caller | | `ExecutionCommandSource` | the interface the engine is generic over, which is why none of it mentions `CommandSourceStack` | `CommandSourceStack` implements it | | `CommandResultCallback` | a success flag and an integer. This pair is what "the result of a command" means everywhere in the game | `CommandResultCallback.EMPTY` short-circuits | `net/minecraft/commands/execution` is the whole engine and it is entirely server-side. Nothing here crosses the network; only the *effects* of commands produce packets. ## The queue, four moments apart `/execute as @a at @s run say hi`, four players online. Read each panel as the queue at one moment, head at the top. ```mermaid flowchart TB subgraph T1["1 · the command is queued"] direction TB A1["BuildContexts.TopLevel — the whole parsed chain, one entry, depth 0"] end subgraph T2["2 · that entry ran, ALL of it"] direction TB B1["ContinuationTask over 4 sources"] B2["as @a and at @s were walked inside entry 1 — 1 source became 4, and one cost unit was spent per stage"] B1 -.- B2 end subgraph T3["3 · the continuation ran once"] direction TB C1["ExecuteCommand for player A"] C2["ContinuationTask — B, C and D do not exist yet"] C1 --- C2 end subgraph T4["4 · A's say hi is done"] direction TB D1["ContinuationTask — and only NOW is B materialised"] end T1 --> T2 --> T3 --> T4 ``` **A fork does not create frames, and it does not create entries.** `BuildContexts.execute` walks every non-execute stage inside a *single* queue entry, spending one cost unit per stage no matter how many sources that stage produces, and turning a one-element source list into an N-element one. Frames are opened in exactly three places: `ExecutionContext.createTopFrame`, `CallFunction` and `IsolatedCall`. A hundred-player fork opens none. **The queue is a stack with a staging buffer, and that is what makes it depth-first.** An action does not push directly: it appends to `ExecutionContext.newTopCommands` while it runs, and `ExecutionContext.pushNewCommands` splices that list onto the *head* afterwards, in order. So whatever the current action spawned runs before whatever was already pending — the semantics of a call stack, out of an `ArrayDeque`. **The fan-out is lazy, and the arithmetic is exact.** `ContinuationTask.schedule` queues nothing for an empty list, one entry for one element, **two entries for two**, and for **three or more** queues exactly one: a `ContinuationTask` that emits the current element's entry and then re-queues itself behind it. Because staging preserves order, element *i* and everything it spawns runs to completion before element *i+1* is even materialised. The queue cost is constant in N. **A chain can split across entries, and only a custom modifier does it.** When the stage walk meets a `CustomModifierExecutor` it hands off and returns mid-walk; the rest of the chain resumes later as a `BuildContexts.Continuation`. That is how `execute if function` and `/return run` interrupt a chain that otherwise runs to its leaf inside one entry. ## Deleting work, which is what `/return` is `/return` does not unwind and does not throw. `Frame.returnSuccess` pushes the value sideways into the callback the caller installed on that frame, and `Frame.discard` splices the abandoned work out of the queue. There is no search. The splice is one rule: **pop from the head while the entry's depth is at least *d***. That works because the queue is depth-first, so entries deeper than a frame are always in front of that frame's own remaining entries — which means the rule removes exactly the callee's pending work plus the rest of this frame's body, and nothing older. Depth-zero frames are the special case: their frame control clears the queue outright. The laziness pays off here too. Discarding one `ContinuationTask` self-entry abandons every element not yet materialised, so `/return` out of a thousand-line function is the same cost as `/return` out of a two-line one. Who installed the callback decides where a returned value goes, and this is the part the page's shape invites getting backwards. Two corrections worth carrying: - The single-source reduction on the `/return run` path lives in `BuildContexts.execute`, not in `ReturnCommand`, and it is gated twice — on return mode, and on the leaf *not* being a `CustomCommandExecutor`. So `return run execute as @a run function foo` queues one function call per player with no reduction at all. - The chaining runs the opposite way from "inner onto outer": what is chained is the *source's own* callback with the current frame's return consumer, and on the `/return run function` path the outer frame's consumer is chained **into** the inner frame. ## A result is a flag and a number, and nothing aggregates The result of a command is always a `CommandResultCallback` pair: a success flag and an integer. There is no aggregation anywhere in the engine. A fork over N players delivers N independent results to N sources, so an `execute store result` writes N times and the last one wins — there is no success count. A sum exists in exactly one place: `/function` on a *tag*, and only when the caller installed a real callback. A command typed in chat has an *empty* frame callback and `Commands.performCommand` returns nothing — yet `execute store` still works on it, because the result also reaches the **source's** own callback, which is what `ExecuteCommand.wrapStores` decorated ([scores, teams and stored data](scoreboard-and-data.md)). `FallthroughTask` exists so that a chain which produced no sources still *fails* rather than returning nothing, and every site that queues it is inside a return or a conditional. Six classes implement the escape hatch for a command that wants the engine rather than Brigadier's plain "return an int" — `FunctionCommand.FunctionCustomExecutor`, `ReturnCommand.ReturnValueCustomExecutor`, `ReturnCommand.ReturnFailCustomExecutor`, `DebugCommand.TraceCustomExecutor`, `ExecuteCommand.ExecuteIfFunctionCustomModifier` and `ReturnCommand.ReturnFromCommandCustomModifier` — and `CustomCommandExecutor.WithErrorHandling` is the base *two* of the six use — `FunctionCommand.FunctionCustomExecutor` and `DebugCommand.TraceCustomExecutor` — routing a thrown `CommandSyntaxException` to both the source's error handler and its callback. The other four handle their own. ## Two ways to die, and they are not the same event **The quota runs out.** `ExecutionContext.runCommandQueue` checks at the top of every iteration, logs at *info*, and breaks. The queue is **not** cleared; it is simply abandoned with the context. Nothing reaches the player. **The queue overflows.** `ExecutionContext.queueNext` trips when staged plus queued entries exceed ten million; `ExecutionContext.handleQueueOverflow` clears *both* lists and sets a latch that silently drops every subsequent queue attempt, and the driver then logs at **error**. Different level, different clean-up, and a latch the quota path has no equivalent of. The budget itself is spent in exactly three places — `BuildContexts` on a modifier stage, `CallFunction` on a function call, and the leaf `ExecuteCommand` task on an executed command — and the first has a gate worth knowing. The increment happens only when the stage carries a non-null redirect modifier, and only after the custom-modifier hand-off has been ruled out. So a plain `execute run` costs nothing for its redirect, and **`execute if function` and `/return run` are free**: neither custom modifier ever reaches the counter. A `ContinuationTask` is free too, so an N-way fan-out costs N, not N+1. **Ten million** — the cap on *queue length*, staged plus queued (`ExecutionContext`). The constant that names it reads as though it bounded depth, and is never read by name. ## Questions a data-pack author asks **Can a function yield?** No. Work it queues drains inside the same driver loop, in the same tick, before the call returns. The only escape is `/schedule` ([functions and macros](functions-and-macros.md)). **How deep can recursion go?** Unbounded, structurally. Depth is used only to order discards and to indent the tracer. Recursion is bounded transitively by the cost budget and fan-out by the ten-million entry cap. **Why did my command fail silently inside `execute if`?** Because conditionals are fork nodes. `execute as @s`, `execute at @s`, `execute if block` and friends set the forked flag on `ChainModifiers` for the rest of the chain, and **a forked source suppresses failure messages**. Putting a harmless-looking conditional in front of a command converts its errors into nothing. They still reach the tracer, which is what `/debug function` is for. **Does `/return` inside a fork stop the other sources?** Not normally. `/function` dispatches its N sources eagerly in a plain Java loop, each opening its own frame at the next depth, so a `/return` in one discards only that callee. It is `return run …` that sets `CallFunction.returnParentFrame`, making the inner discard run at the outer frame's depth and delete the siblings. **Why does my fork stop one short?** The fork limit is checked per contributing source with a greater-or-equal comparison, so the effective ceiling is one below the configured value. When it trips the handler returns without queueing even a `FallthroughTask`, so a `/return run` chain that hits the fork limit yields nothing at all rather than a failure. The limits are `GameRules.MAX_COMMAND_FORKS` and `GameRules.MAX_COMMAND_SEQUENCE_LENGTH`, both 65536 by default — and both are read **once**, by the outermost command, so a `/gamerule` changed part way through a long fan-out does not take effect until the next top-level command. (There is nothing dimensional in this: `ServerLevel.getGameRules` returns the server's one `GameRules` instance, so no level has rules of its own to pick up.) **Does a nested command get its own budget?** No. `Commands.CURRENT_EXECUTION_CONTEXT` is a thread-local: a command that starts another top-level execution appends to the running queue rather than making a new context, and the limits were read once by the outermost call. Its top frame is nested one depth deeper, so its discards cannot eat the outer queue. ## The two commands that are part of the engine `ExecuteCommand.scheduleFunctionConditionsAndTest` is how `execute if function` works and it is the cleverest thing in the area: instantiate each function once, wrap every source in an `IsolatedCall` whose callback appends that source to a list, then queue a `BuildContexts.Continuation` over *the same mutable list*, which the isolated calls fill before the continuation reads it. The staging order is what makes that safe, and the inner `CallFunction` opens against the isolated frame, which is why a `/return` inside a condition function cannot reach the caller. `DebugCommand.Tracer` installs itself on the whole `ExecutionContext` rather than per frame, so it traces everything in that context. It refuses to nest, refuses return mode, and implements `CommandSource` as well, so a traced function's chat output lands in the trace file alongside the call lines. ## Where to look `ExecutionContext` and `Frame` first — 153 lines and 24 explain the whole design. Then `BuildContexts` for how a parse becomes work, `ContinuationTask` for the laziness both the fan-out and the function body ride on, and `ExecutionCommandSource` for why none of it names a command source. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Functions and macros > Verified against **Minecraft 26.2** · Part XIII · A macro function in `#minecraft:tick` is reached with no arguments: it fails, silently, twenty times a second, forever — nothing logged, nothing printed, nothing counted. Put a `$`-prefixed line in a function, add that function to `#minecraft:tick`, and the game will call it every tick with no argument compound at all. Instantiating it raises a `FunctionInstantiationException`, `ServerFunctionManager.execute` catches that one exception with an **empty body**, and the tick moves on. Any *other* exception from the same call is logged at *warn*; this one is the single case the manager was written to swallow, because the `/function` command is expected to report it instead — and `#minecraft:tick` is not the `/function` command. That is the sharp end of a two-step model that is otherwise very tidy: a `.mcfunction` file becomes a runnable thing in two steps, and for the overwhelming majority of functions the second step does nothing at all. ## The pipeline ```mermaid flowchart TB F["a .mcfunction text file, and the tag JSON beside it"] F --> C["1 · COMPILE, at reload — CommandFunction.fromLines, off the main thread, against a null level and a null server"] C --> P["a CommandFunction: a PlainTextFunction, or a MacroFunction if any line begins with a dollar"] T["TRIGGER — the tick tag, the load tag, /function, /schedule, an advancement reward, an enchantment effect, a test environment"] P --> I["2 · INSTANTIATE, per call — plain returns itself, a macro substitutes and RE-PARSES, cached eight deep"] T --> I I --> R["an InstantiatedFunction: an id and an ordered list of unbound actions"] R --> Q["3 · QUEUE — CallFunction opens a frame, ContinuationTask schedules the lines"] Q --> E["the execution engine — the lines run to completion inside this tick"] ``` The two halves of that live in `net/minecraft/commands/functions` (the model) and `net/minecraft/server` (the two managers), and both are entirely server-side. | class | what it decides | |---|---| | `ServerFunctionLibrary` | the reload listener: parses every file, holds the volatile function and tag maps, and the compile-time `PermissionSet` | | `ServerFunctionManager` | the runtime face: the two tags, the source they run as, and the one empty catch | | `CommandFunction` | the compiled, uninstantiated function — `CommandFunction.fromLines` is the compiler | | `PlainTextFunction` | a macro-free function. It is both the compiled *and* the instantiated form, so instantiating one allocates nothing | | `MacroFunction` | the other case: parameters, an eight-entry LRU, and a re-parse per miss | | `StringTemplate` | the `$(name)` syntax and what a valid variable name is | | `InstantiatedFunction` | an id and an ordered list of `UnboundEntryAction`s. That is the entire runnable representation | | `FunctionInstantiationException` | carries a `Component`, which is why `/function` can render the failure and the tick loop can swallow it | ## 1 · Compile, at reload **In:** the raw lines of one file. **Out:** a `CommandFunction`, or nothing at all. `CommandFunction.fromLines` walks the lines. A trailing backslash joins the next one (`CommandFunction.shouldConcatenateNextLine`), and a continuation at end of file is an error; blank lines and `#` comments are skipped; a leading `/` is a hard error with two different messages depending on whether you wrote one slash or two; a leading `$` is a macro line, kept as text; and anything else goes through `CommandFunction.parseCommand`, which produces a `BuildContexts.Unbound`. So **a compiled function line is literally a parsed context chain plus its input string, waiting for a source.** `CommandFunction.checkCommandLineLength` caps a line at two million characters. A syntax error on any line fails the *whole file*, which is logged at error and then simply absent from the map. There is no partial function. Two constraints make this stage unusual, and they are the same constraint seen twice. `ServerFunctionLibrary` parses every file **in parallel on the reload's background executor**, and it does so against a source built by `Commands.createCompilationContext` with a **null level and a null server**. Only the map swap happens on the main thread, and the maps are volatile because the library object is built on a background thread and read from several. An argument type that dereferenced the world during parsing would break a reload — which is exactly why `FunctionArgument` reads an id and defers the lookup. In 26.2 no argument type actually tests the constraint: the four that parse against a source consult only its permissions, and those come from the *function-permission-level* server property, gamemaster by default. ## 2 · Instantiate, per call **In:** a `CommandFunction` and an optional argument compound. **Out:** an `InstantiatedFunction`, or a `FunctionInstantiationException`. For a `PlainTextFunction` this step returns the very same object: nothing is allocated, nothing is parsed, and the overwhelming majority of functions in the world take this path. A `MacroFunction` looks up each declared parameter in the argument compound, stringifies it, and uses the **ordered list of strings** as a cache key over an eight-entry LRU (`MacroFunction.MAX_CACHE_ENTRIES`). On a miss it substitutes into every macro line and **re-parses** it, and a parse failure there is the exception above. Note what the cache is keyed on: the values, in parameter order — so nine distinct argument tuples cycling round will miss every time. Stringification is where macros surprise people, because it is not SNBT for everything. `MacroFunction` has explicit cases for float and double (a decimal format with up to fifteen fraction digits, so `1.0` becomes `1`), for byte, short and long, and for strings (the **unquoted** value). Everything else — integers, compounds, lists — falls through to SNBT. Integers merely happen to render bare; byte, short and long need their own cases precisely because SNBT would suffix them. Three smaller rules complete the model. One `$` line anywhere makes the whole *file* a macro function, though its non-macro lines keep their already-compiled form and are never re-parsed. A `$` line containing no substitution at all is a **load-time error**, not a plain command. And a macro function's instantiated variants all share **one synthetic id**, derived from the parameter *names* rather than the values. ## 3 · Queue **In:** an `InstantiatedFunction` and a source. **Out:** entries on the execution queue. `CallFunction` spends one cost unit, opens a frame at the next depth, and hands the function's line list to `ContinuationTask.schedule` — the same call the fan-out over an entity selector makes. **A hundred-line function and a hundred-player fork are therefore the same shape in the queue**, which is what makes `/return` cheap: discarding one self-entry abandons every line not yet materialised. Everything after this point is [the execution engine](the-execution-engine.md). ## What calls a function, and when `ServerFunctionManager.tick` is the **first** thing `MinecraftServer.tickChildren` does — before the clocks, before the time sync, before any level ticks, and therefore long before connections and players tick, which in 26.2 happen *after* the levels ([the server tick](../server/server-tick.md)). It no-ops entirely when the tick-rate manager is not running normally, so `/tick freeze` suspends data packs. `#minecraft:load` runs once after a reload or start, and `#minecraft:tick` runs every tick from a list **snapshotted at reload** and never consulted again, so nothing can join or leave the tick loop between reloads. Each function in a tag gets its **own** `ExecutionContext`, so the budget is per function rather than shared across the tag. `/schedule` is the one way out of the current tick, and its gate is narrower than it sounds. The callback goes into the server's timer queue — server-wide saved data, not per level — and `ServerLevel.tickTime` advances that queue immediately after setting the game time. That whole method sits behind the level's own *tickTime* flag, **which only the overworld has**, so a scheduled function fires once per tick rather than once per dimension. `ScheduleCommand` also refuses two things outright: a macro function, and a delay of zero. Everything else that runs a function is a short and exhaustive list: `/function` itself, `AdvancementRewards` for a reward function ([advancements](advancements.md)), the `RunFunction` enchantment effect ([enchantments](../items/enchantments.md)), and `TestEnvironmentDefinition.Functions` for a game test's environment setup ([game tests](game-tests.md)). ## The two permission verbs, three lines apart A function body is one of the places a command source's permission set is deliberately rewritten, and the game reaches the same answer down two differently-named routes. `ServerFunctionManager.getGameLoopSender` takes the server's own source — which is `LevelBasedPermissionSet.OWNER` — and calls `CommandSourceStack.withPermission` with gamemaster. That is a flat **replacement**: the tick and load tags run at gamemaster. `FunctionCommand` and `DebugCommand` instead call `CommandSourceStack.withMaximumPermission`, which is `PermissionSet.union`. The name promises a widening, and for two sets that are *not* level-based it delivers one — the default `PermissionSet.union` builds a `PermissionSetUnion` that ORs. But `LevelBasedPermissionSet` overrides it, and the override returns the **lower**-levelled set on both of its branches ([permissions](permissions.md)). So for the sets a player or the console actually carries it is a *minimum*, and `CommandSourceStack.withMaximumPermission` at gamemaster over an owner's source yields gamemaster too. Both routes land on the same rung: there is no way to reach a function body above gamemaster, and the method named for a ceiling is the one that enforces it. ## Where to look `CommandFunction` for the compiler, and `MacroFunction` for the only part of it that runs per call. `ServerFunctionLibrary` for what a reload does off the main thread, `ServerFunctionManager` for the tick hook and the one empty catch, and `StringTemplate` for the substitution rules a pack author will actually trip over. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Advancements > Verified against **Minecraft 26.2** · Part XIII · "Stone Age": a cobblestone lands in your inventory and one tick later the toast appears — delivered by a subscription table that only ever shrinks, over a packet that never says what the criterion was. Mine a stone block. Nothing about advancements happens when the item is picked up; nothing happens when it enters the inventory either. What happens is that `AbstractContainerMenu.broadcastChanges` — the same diff that keeps your client's inventory in sync — notices that a slot's contents differ from its remembered copy, and reports the difference. Detection is a **diff, not an event**, and the advancement system is a subscriber to it. That is the first of two things this system does backwards from expectation. The second is bigger: there is **no global list of who is listening for what**. Each player carries their own subscription table, it contains only the criteria that player has *not yet satisfied*, and it shrinks as they play. A veteran player is cheaper to run than a new one, and the cost of the whole system to a save file falls monotonically over its life. Which is also why advancements are the game's general-purpose *"tell me when the player does X"* facility rather than only a goal list. The recipe book is unlocked by advancements. `PlayerPredicate` reads advancement progress back out as a loot condition. All fifty-eight registered triggers exist because something in the game wanted a hook and this was the hook that already existed. ## The cast | class | what it decides | side | |---|---|---| | `Advancement` | the immutable definition — parent, `DisplayInfo`, rewards, criteria, requirements, and a **pre-rendered display name** built in the compact constructor, which is the `[Title]` every announcement quotes | both | | `AdvancementHolder` | the id plus the advancement, with **id-only equality** — so a map keyed by holder survives a pack changing an advancement's contents | both | | `AdvancementTree` | the parent/child graph, built by a fixed-point loop that refuses any advancement whose parent is not yet a node. An orphan is **discarded**, not re-rooted | both | | `Criterion` / `CriterionTrigger` | a trigger plus a decoded `CriterionTriggerInstance`, and nothing else — a criterion has no name of its own, the name is the map key, and the **trigger object is stateless** | both | | `SimpleCriterionTrigger` | the base class for all but one trigger, and the owner of the per-fire sweep | both | | `AdvancementRequirements` | a list of lists of criterion names: an **AND of ORs**. `AdvancementRequirements.size` counts *clauses*, not criteria | both | | `PlayerAdvancements` | the per-player subscription table and the dirty sets. The only class here with interesting state | server | | `TreeNodePosition` | a full tidy-tree layout — three walks, threads, ancestors, shifts — run on the **server**, mutating `DisplayInfo`'s coordinates in place | server | The shared model is `net/minecraft/advancements`, the triggers are in `advancements/triggers` and the predicates in `advancements/predicates` (with the entity half a level down). All 112 classes ship in both jars. `CriteriaTriggers` registers **fifty-eight** triggers into `BuiltInRegistries.TRIGGER_TYPES` over **forty-four** classes; the gap is re-use, with `PlayerTrigger` alone accounting for six registrations. ## The trace: "Stone Age" `minecraft:story/mine_stone` has one criterion, *get_stone*, whose trigger is `minecraft:inventory_changed` and whose condition is a single `ItemPredicate` over the `#minecraft:stone_tool_materials` tag. It has no rewards at all. ```mermaid sequenceDiagram participant ACM as AbstractContainerMenu participant SP as ServerPlayer participant ICT as InventoryChangeTrigger participant PA as PlayerAdvancements participant AR as AdvancementRewards participant CPL as ClientPacketListener participant CAdv as ClientAdvancements ACM->>ACM: broadcastChanges — this slot differs from lastSlots ACM->>SP: ContainerListener.slotChanged — which slot, which stack SP->>ICT: trigger(player, inventory, stack) — count the 43 slots FIRST ICT->>ICT: SimpleCriterionTrigger.trigger — is this player listening for this trigger at all? ICT->>ICT: TriggerInstance.matches — one predicate, so test only the changed stack ICT->>PA: award(mine_stone, get_stone) — after the sweep, never during PA->>PA: unregisterListeners — the criterion is done, stop watching PA->>AR: grant(player) — EMPTY here. XP, loot, recipes and a function otherwise PA->>PA: markForVisibilityUpdate — the ROOT, not the advancement Note over SP,PA: still the same tick — flushDirty is the last statement of ServerPlayer.tick SP->>PA: flushDirty PA->>PA: updateTreeVisibility — re-walk the whole story tree PA->>CPL: ClientboundUpdateAdvancementsPacket — added, removed, visible progress CPL->>CAdv: update — rebuild the tree, reconcile the progress CAdv->>CAdv: AdvancementToast — and silent unless it is a CHALLENGE ``` Each arrow is a decision. **Counting comes before knowing whether anyone cares.** `InventoryChangeTrigger.trigger` walks all forty-three slots — thirty-six inventory plus seven equipment — to compute the occupied, full and empty counts *before* it asks whether any criterion is listening. That is the floor cost of every slot change of every player, forever, and it is the most expensive trigger per fire. (The most *frequent* is `CriteriaTriggers.TICK`, which fires unconditionally twenty times a second per player.) **The sweep itself is the cheap part.** `SimpleCriterionTrigger.trigger` fetches this trigger's map from `PlayerAdvancements.getTriggerMapForType` and returns immediately if it is null — and it *is* null once every criterion for that trigger is satisfied, because `PlayerAdvancements.removeListener` deletes the per-trigger map when it empties. When there is work, it builds **one** `LootContext` and reuses it for the whole sweep, evaluates the caller's cheap matcher first and the *player* predicate only for matches, and does not allocate the results list until the first hit. Note the scope: one player. Nothing here is a broadcast. **Matches are collected, then awarded**, because awarding calls `PlayerAdvancements.unregisterListeners`, which mutates the very map being iterated. **Completion is checked against the requirements, not the criteria.** `AdvancementRequirements.test` is the AND of ORs. Here it is one clause of one name, so granting the criterion completes the advancement — which fires the rewards, the chat announcement (built by `AdvancementType.createAnnouncement`, broadcast to every player, gated on `GameRules.SHOW_ADVANCEMENT_MESSAGES`) and the visibility dirty flag. ## Visibility is per root, and it gates the wire `PlayerAdvancements.markForVisibilityUpdate` dirties the **root**, and the flush re-runs `AdvancementVisibilityEvaluator` — the "how far past your frontier can you see" rule, with a depth of two — over that root's entire subtree. Finishing one advancement in a large tree re-evaluates the whole tree, and only the nodes whose visibility actually *flipped* go on the wire. `PlayerAdvancements.flushDirty` then sends progress **only for advancements in `PlayerAdvancements.visible`**. Progress on something hidden, or beyond your frontier, accumulates server-side and reaches the client the moment it becomes visible. Where the flush sits in the tick is worth pinning down, because it produces a real one-tick delay that nobody expects. Inside `ServerPlayer.tick`, `AbstractContainerMenu.broadcastChanges` is the fifth statement, `CriteriaTriggers.TICK` fires mid-tick, and `PlayerAdvancements.flushDirty` is the **last**. So everything awarded between those points — a pickup, a kill, an `/advancement grant`, an item granted by another advancement's reward — coalesces into one packet, and so does everything that arrived in a packet, because `MinecraftServer.processPacketsAndTick` drains the inbound queue before the levels tick at all. But `ServerPlayer.tick` is not the last thing that happens to a player. `ServerGamePacketListenerImpl.tick` calls `ServerPlayer.doTick` during the **connection** phase, which in 26.2 runs *after* the levels ([the server tick](../server/server-tick.md)) — i.e. after `PlayerAdvancements.flushDirty` has already run. `CriteriaTriggers.LOCATION`, which fires there every twenty ticks and is what most vanilla biome and structure advancements hang on, therefore **always lands in the next tick's packet.** ## A criterion's conditions are loot conditions `ContextAwarePredicate` wraps a list of `LootItemCondition` and evaluates it against a `LootContext`, reached through `EntityPredicate.createContext`. So a trigger's conditions are exactly the machinery of [contexts and predicates](../items/contexts-and-predicates.md), and that is where most descriptions of the system stop. It is worth going one step further, because the predicate package is where four shapes were invented that the whole data-driven half of the game now reuses. | shape | what it generalises | where else it turns up | |---|---|---| | `MinMaxBounds` | the numeric range, with both a codec **and** a `StringReader` grammar | `3..7` means the same in a predicate, an entity selector and `/random` | | `CollectionPredicate` | one generic "N of these match", composing `CollectionContentsPredicate` and `CollectionCountsPredicate` | its only users are the six component predicates in `core/component/predicates` | | `EntitySubPredicate` | a per-mob test as a **registry element** instead of a code branch | the twenty-odd small entity predicates are each a record, a codec and nothing else | | `DataComponentMatchers` | testing a stack's components without knowing what any of them are | [data components](../foundations/data-components.md) | Two details change behaviour rather than shape. `EntityPredicate.ADVANCEMENT_CODEC` accepts *either* a condition list or a bare entity predicate — though vanilla's own JSON takes the long form every time, so the short one exists for pack authors rather than for the game. And `EntityPredicate` declares an explicit type-check-first, NBT-last ordering for its own sub-tests — a performance invariant hiding inside a predicate class. ## Questions players ask **Why does the client show "3/7" if it does not know what the criteria are?** Because `AdvancementRequirements` *is* on the wire and `AdvancementProgress.update` reconciles against it. `Advancement.read` reconstructs the record with an **empty criteria map** and `AdvancementRewards.EMPTY`, so a client cannot know what any criterion tests, or that an advancement grants anything at all. And `AdvancementProgress.getProgressText` returns nothing at all when there is one clause, which is why a single-criterion advancement never shows "1/1". (An `AdvancementProgress` with no requirement clauses is permanently incompletable — `AdvancementRequirements.test` returns false for an empty list rather than vacuously true — and the only way to hold one is to decode it off the wire, which is exactly what the client does before its first update.) **Why does the tree look the same on every client?** Because it was laid out on the server. `TreeNodePosition` runs inside `ServerAdvancementManager` and mutates `DisplayInfo` in place; the coordinates ride the packet. A root with no `DisplayInfo` is never laid out and never becomes a tab, and a display-less node in the middle of a tree is transparent — the layout skips it and adopts its children. One wrinkle in an otherwise deterministic algorithm: `AdvancementNode.children` is an unordered hash set, so sibling order inside a tidy-tree layout is hash-dependent. **Does `/reload` roll back my progress?** No, and the order is the point. `MinecraftServer.reloadResources` calls `PlayerList.saveAll` and *then* `PlayerList.reloadResources`, so `PlayerAdvancements.reload` re-reads a file written moments earlier. What is genuinely lost is progress for any advancement the new pack has removed or renamed — logged once each, and invisible to the player except as a full reset packet — plus the selected tab, which is silently forgotten with no packet, so the client keeps a stale one. **When is progress written to disk?** Only when the player is saved. There is no write on award: `PlayerAdvancements.save` runs from `PlayerList.save`, on disconnect, on a save-all, or from the reload above. The definitions come from `data//advancement/.json` through `Advancement.CODEC` — a duplicate id aborts the reload outright — and per-player state is one JSON file at `players/advancements/.json` (`LevelResource.PLAYER_ADVANCEMENTS_DIR`), data-fixed on load through `DataFixTypes.ADVANCEMENTS`. **How does the recipe book fit in?** Every recipe advancement is generated with a `RecipeUnlockedTrigger` criterion and an `AdvancementRewards` naming the recipe, so earning it calls `ServerPlayer.awardRecipes` ([recipes](../items/recipes.md)). `RecipeUnlockedTrigger` then closes the loop by letting *other* advancements observe an unlock — comparing the recipe key by **reference identity**, which is safe only because `ResourceKey`s are interned. **Does the listener set ever grow?** Twice. `PlayerAdvancements.registerListeners` subscribes only to criteria that are not yet done in advancements that are not yet done, and every award unsubscribes — *unless* somebody runs `/advancement revoke`, which re-subscribes, or `/reload`, which re-subscribes everything unfinished in the new pack. **Why is `/advancement grant` usable as a conditional?** Because a no-op is a hard failure: granting an advancement the player already has throws rather than reporting zero. `AdvancementCommands.Mode` — *only*, *through*, *from*, *until*, *everything* — is a graph traversal collecting parents or children or both, and `/advancement grant … everything` calls `PlayerAdvancements.flushDirty` before the loop with the packet's "show advancements" flag **true** and after it with the flag **false**, which is the only purpose that flag has: suppressing a toast storm from the batch it just granted. Three smaller surprises, for completeness. `ImpossibleTrigger` has no trigger method at all — it is the one trigger implementing `CriterionTrigger` directly, and it exists so a node can anchor a tree while being ungrantable except by command, which vanilla uses for exactly one file: the invisible root of every recipe advancement. (It has nothing to do with `/trigger`, which is a scoreboard feature.) `DisplayInfo`'s announce-to-chat flag is **write-only on the wire**: the serialiser packs three flags into an int and omits it, and the reader hard-codes it false while the codec defaults it true, so the client's copy is wrong for the common case rather than merely unused. And `PlayerAdvancements.checkForAutomaticTriggers` is dead code in the strictest sense: it walks every advancement on every player load, but its whole body sits behind *this advancement has no criteria at all*, and `Advancement`'s criteria codec rejects an empty map outright. No loaded advancement can satisfy the guard, so the loop never does anything. ## The screen at the other end The client's half is five classes in `net/minecraft/client/gui/screens/advancements` plus `ClientAdvancements` over in `client/multiplayer` — about 1,240 lines — and it is the payoff for everything the server did. It does **no tree layout**: it is drawing positions a data-pack reload decided. `ClientAdvancements` consumes `AdvancementTree.Listener`, and `AdvancementTree.setListener` replays every existing root and task at a new listener immediately, which is how the screen catches up on open. `AdvancementsScreen` owns the tab strip; `AdvancementTab` owns one root's pan-and-scroll bounds, auto-centres on first render and clamps the drag; `AdvancementWidget` scales the server-decided coordinates by a fixed factor, draws the connector lines to its parent, and wraps its own tooltip text. `AdvancementTabType` is the one with a hard limit in it: four header geometries with room for eight tabs above, eight below and five on each side. **A data pack's twenty-seventh root is silently unreachable.** Two more things ride this boundary and go nowhere. `ServerboundSeenAdvancementsPacket` has a "closed screen" action that is serialised, deserialised and dropped. And `Advancement.sendsTelemetryEvent` is consumed **only** on the client, by `WorldSessionTelemetryManager`, and only for advancements in the *minecraft* namespace — which is the whole reason a flag rides a wire form that drops the criteria and the rewards ([what this book skips](../anatomy/what-this-book-skips.md)). ## Where to look `Advancement` and `AdvancementRequirements` for the model; `PlayerAdvancements` for everything that actually happens; `SimpleCriterionTrigger` and `InventoryChangeTrigger` for the hot path; `MinMaxBounds` and `CollectionPredicate` for the predicate shapes that recur everywhere else; and `AdvancementVisibilityEvaluator` for the one rule nobody guesses right. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Scores, teams and stored data > Verified against **Minecraft 26.2** · Part XIII · `execute as @a store result score @s ticks_frozen run data get entity @s TicksFrozen`: one command writing a scoreboard through a callback the inner command has never heard of, and the two data models `execute store` exists to join. Look at the sidebar on a well-built server and some of the names in it are not players. *#total*, *constant*, *.timer* — rows belonging to nothing alive. That is not a hack layered on top of the scoreboard; it is the scoreboard working exactly as written, and one method override explains it. `Entity.getScoreboardName` returns the entity's UUID string. `Player.getScoreboardName` overrides it with the profile name. There is one flat map from *string* to a row of scores, holding players by name, mobs by UUID, and anything else you care to type. From that single override follows the whole of scoreboard folk practice: **why fake players exist** (nothing checks that a key belongs to an entity), **why a mob's score can never appear in the tab list** (whose holder is built from a profile name), and **why renaming a player orphans their scores**. Three systems share this page because they share a command and an instinct. The scoreboard is the game's general-purpose *number per thing*. The teams live in the same package and are read by five subsystems that have nothing to do with scores. Command storage and the NBT path language are the other half — a place to put a tag belonging to no block and no entity, and a query language for reaching into any tag at all. And `execute store` is the seam: the only construct in the game that takes the result of an arbitrary command and writes it somewhere. It has **three** sinks, and two of them are these two models — a score, and a path into a block, an entity or a storage. (The third is a boss bar's value or maximum, which shares its implementation with the score sink and belongs to [the HUD](../client/hud.md).) The instinct they share is worth stating before the classes, because it explains three otherwise-odd decisions: **the server is the only participant that knows anything.** The client is sent scores it can draw and nothing else — not an objective's criteria, not a score's lock bit, not which objectives exist. There is **no serverbound packet in this whole system**. Every write is a command. ## The cast | class | what it decides | side | |---|---|---| | `Scoreboard` | six maps and nothing else, plus ten empty hooks that are the entire extension surface. A pure data structure, used unmodified by the client | both | | `Objective` | criteria, display name, render type, number format, auto-update — and a **back-pointer to the scoreboard**, so every setter reports its own change | both | | `Score` | four mutable fields: the value, a lock bit, a display component and a number format. `ReadOnlyScoreInfo`, `PlayerScoreEntry` and `ScoreAccess` are the other three faces of it | both | | `ScoreHolder` | the interface `Entity` implements, with `ScoreHolder.forNameOnly` minting anonymous ones — the most consequential class on the page | both | | `PlayerTeam` | the **only** subclass of `Team`: the mutable state, the setters, a precomputed display style, and friendly-fire plus see-invisibles packed into one wire byte | both | | `ServerScoreboard` | three fields — the server, `ServerScoreboard.trackedObjectives`, and one dirty boolean — and thirteen overrides across ten hooks, each conditionally broadcasting, then marking dirty. It lives in `net/minecraft/server`, not beside `Scoreboard` | server | | `NbtPathArgument` | 874 lines, the largest argument type in the game, and a whole query language: six node kinds and a depth limit of 512 | both | | `DataCommands` | `/data`, over three `DataAccessor`s — `BlockDataAccessor`, `EntityDataAccessor` and `StorageDataAccessor` | server | `net/minecraft/world/scores` is sixteen files and 1,442 lines — the whole model — and every class in it ships in both jars. Beside it: `CommandStorage`, a lazy façade over one `SavedData` *per namespace*, so `minecraft:foo` and `mypack:foo` are different files; and `net/minecraft/network/chat/numbers`, seven files and 167 lines, holding `NumberFormat` with the three kinds `NumberFormatTypes` registers. ## The trace: a store through both models ```mermaid sequenceDiagram participant Cmds as Commands participant BC as BuildContexts participant ExecC as ExecuteCommand participant DataC as DataCommands participant SS as ServerScoreboard participant SA as ScoreAccess participant CPL as ClientPacketListener Cmds->>BC: the parsed chain — @s and the objective name are still strings BC->>BC: "as @a" forks: N sources, one per player BC->>ExecC: "store result score" is a redirect, run once per source ExecC->>ExecC: resolve @s and the objective NOW, and chain a callback onto the source BC->>DataC: the leaf runs: getData(accessor, path) DataC->>DataC: EntityDataAccessor.getData — the entire entity save, built fresh DataC->>DataC: NbtPath.get, then collapse the tag to one int by four rules DataC->>ExecC: the result reaches the SOURCE's callback, not the frame's ExecC->>SS: getOrCreatePlayerScore — without forceWritable SS->>SA: set(value) SA->>SS: onScoreChanged — only if the objective is in a display slot SS->>CPL: ClientboundSetScorePacket, broadcast to every player CPL->>CPL: forNameOnly(owner) — the client only ever has the string ``` Each arrow is a decision. **The store target is resolved before the inner command runs, not after.** `ExecuteCommand.wrapStores` builds the store node as a **redirect with a modifier**, not as a step after the leaf. The modifier resolves the score holder and the objective against *this* source, then decorates the source with a callback chained onto whatever was already there. So the store is a property of the *source*, which is why several stores compose, and why each forked player writes their own row without the leaf command knowing a scoreboard exists. **The result travels by callback, not by return value** — the source's own callback, which is precisely why `execute store` works on a command typed in chat whose *frame* callback is empty ([the execution engine](the-execution-engine.md)). **`/data get` builds the whole entity to read one field.** `EntityDataAccessor.getData` produces the entity's entire save tag, freshly, and the path then walks it. That is the real cost of `/data get`, and it is why a per-tick `/data get` on a busy entity is a measurable expense. **Four rules collapse a tag to one integer.** A numeric tag floors its double value; a collection and a compound both yield their *size*; a string yields its length. Nothing yields the value you might have meant: `data get entity @s Inventory` returns the number of stacks you are carrying, not 36 — `Inventory.save` writes only the non-empty slots, so the collection whose size you get is as short as your inventory is empty. A path matching more than one tag is an error; a path matching none is a different error. **The write can be a silent no-op, and may never reach the wire at all.** `ScoreAccess.set` writes nothing and sends nothing when the value is unchanged and the score is not new — unless the objective has auto-update on, in which case it refreshes the display name first and a changed display counts as a change on its own. And `ServerScoreboard.onScoreChanged` is gated on the objective being in `ServerScoreboard.trackedObjectives`, whose only entrance is occupying a display slot. ## Why a write is a handle `Scoreboard.getOrCreatePlayerScore` does not return a `Score`. It returns a `ScoreAccess` — an anonymous object closing over the score, the objective and the holder, plus **two decisions computed once, at handle-creation time**: may this be modified (did the caller ask for a force-writable handle, or is the criteria not read-only), and was this score *newly created* by the lookup that produced this handle. That is the whole answer to "why not a setter". A setter would have to re-derive the first fact on every call, and the second is unrecoverable after the fact — once the `Score` exists, nothing can tell whether *this* call created it. Newness is what decides whether an unchanged value still needs a packet, so it has to survive from the lookup to the write. The handle is also the one place that knows when to fire the change hook, which is why every write path in the game funnels through `ScoreAccess.set`, `ScoreAccess.add`, `ScoreAccess.increment`, `ScoreAccess.reset`, `ScoreAccess.lock`, `ScoreAccess.unlock` and `ScoreAccess.numberFormatOverride`. Nothing here is ticked, either. `MinecraftServer` mentions the scoreboard five times in total — the field, the constructor, the load, the getter, and one call to `ServerScoreboard.storeToSaveDataIfDirty` inside `MinecraftServer.saveAllChunks`. There is no periodic sweep and no dirty-queue drain: every mutation broadcasts its own packet synchronously, inside the call that made it, and there is **one scoreboard per server, not per level**, so scores and teams are global across dimensions. ## What a criterion can be, which is nearly anything `ObjectiveCriteria` looks like an enum of eleven values and is not. Forty-three constants exist, thirty-two of them generated — sixteen team-kill and sixteen killed-by-team criteria, one per team colour. Six are read-only: health, food, air, armour, experience and level. And then the tail. **`Stat` extends `ObjectiveCriteria`**, so every statistic in the game *is* a criterion, and `ObjectiveCriteria.byName` parses a colon-separated name by looking the left half up as a stat type and the right half in that stat type's own registry. `minecraft.mined:minecraft.stone` is not a special case; it is the statistics registry addressed through a string. Nine stat types over the block, item, entity-type and custom-stat registries make thousands of valid criteria names, which is why `/scoreboard objectives add` accepts far more than the forty-three bare criteria names — its suggestions offer those *and* every stat name in every stat type's registry. The identity-keyed reverse index is what makes that cheap. `Scoreboard.objectivesByCriteria` is an **identity** map from criteria to the objectives watching it, `ServerPlayer.awardStat` hands the `Stat` object itself to `Scoreboard.forAllObjectives`, and object identity finds the watchers — sound only because stat objects are interned in their registries. Criteria-driven scores are the one part of this page with a schedule, and it is narrower than it sounds. `Scoreboard.forAllObjectives` has **seven call sites and every one is in `ServerPlayer`**: the six read-only criteria, the death count, two kill counts, the two team-kill criteria and the two statistics hooks. No *criterion* is driven from `Entity`, `LivingEntity` or `Mob`, so a skeleton killing a zombie increments nobody's kill count — though `LivingEntity` does reach the scoreboard once, calling `Scoreboard.addPlayerToTeam` when it reads its own saved team back. The six read-only criteria are change-detection diffs — six consecutive comparisons against remembered fields — living in `ServerPlayer.doTick`, which runs in the **connection** phase, after the levels have ticked ([the level tick](../server/server-level-tick.md)). So damage taken during the level tick reaches the scoreboard, and the wire, later in the same tick rather than during it. ## The path language, and the accessor that is coarser than it looks Six node kinds — a named child, a match on an object, a match on the root object, a match on a list element, all elements, and an index — with a depth limit of 512. The elegant part is **creation**. The parent-creating walk goes through the nodes and, for each one, asks *the next node* what shape its parent has to be: a named child wants a compound, an index wants a list. So a *set* through `a.b[0].c` materialises a compound, a list and a compound without any node knowing more than its own type. Removal is the same walk with a plain lookup instead, so it never creates. The three accessors, by contrast, are coarse. `DataAccessor` has two methods that matter — read the whole tag, write the whole tag — and **no path-aware write anywhere**. Every `/data modify` is *read everything, mutate in memory, write everything back*, which is why a block-entity write reloads the block entity and marks the chunk dirty, and why an entity write round-trips the entity through its own load path and then restores the UUID by hand, because loading would have overwritten it. What each accessor *does* contribute is its own grammar subtree, which is how `DataCommands` builds the target half and the source half of every subcommand from one list of three providers applied twice. ## Teams, which five systems read and none of them are scores `Team` declares everything a reader asks for and `PlayerTeam` is its only subclass. What makes teams worth their own paragraph is who consults them, because it is not the scoreboard: collision through `EntitySelector.pushableBy`; nametag visibility through `LivingEntityRenderer.shouldShowName`; invisibility through `Entity.isInvisibleTo`; friendly fire through `Player.canHarmPlayer`; and death-message routing through `ServerPlayer.die`. Only `ServerPlayer.die` and `LivingEntityRenderer.shouldShowName` are reached from anywhere near one place; `EntitySelector.pushableBy` and `Player.canHarmPlayer` have six call sites each and `Entity.isInvisibleTo` two. The locator bar is a sixth reader, reached the other way round: every team join, leave and modification calls through to `ServerWaypointManager` to remake the connections and re-colour the icons — the team system driving a waypoint system. Two team behaviours are worth pinning. `Team.isAlliedTo` is **reference equality**, so two teams with byte-identical settings are never allied; every "same team?" test in the game is really "same object?", safe only because `Scoreboard.teamsByName` is the single owner of every instance. And a team has two visibility settings of which only one ships: the wire parameters carry nametag visibility, while death-message visibility has a single reader in `ServerPlayer.die` and the client never learns the rule because it does not need to. Nametag visibility also *widens*: when an entity has a team, `LivingEntityRenderer.shouldShowName` returns from the team switch directly and never reaches the checks that hide a name behind F1, for the camera entity, or for a vehicle — so a mob on a team set to *always* keeps its name through the HUD toggle. ## What the client is ever told Five packets, all server → client, and no serverbound counterpart exists: `ClientboundSetObjectivePacket`, `ClientboundSetDisplayObjectivePacket`, `ClientboundSetScorePacket`, `ClientboundResetScorePacket` and `ClientboundSetPlayerTeamPacket`. All five go through `PlayerList.broadcastAll` — no distance filter, no dimension filter ([what the client is told](../networking/what-the-client-is-told.md)). An objective in no display slot **does not exist on the network**. `ServerScoreboard.onObjectiveAdded` sends nothing at all; the only path into the tracked set is `ServerScoreboard.setDisplayObjective`. A scoreboard with two hundred objectives and an empty sidebar costs zero bandwidth — and putting one *into* a slot then ships every score it holds, to every player, at once. The join burst is the same shape: `PlayerList.updateEntireScoreboard` sends every team with its full member list, then walks all nineteen display slots and ships each distinct occupying objective with all of its scores. What the client is told is also less than it looks. `ClientPacketListener` constructs every objective it receives with `ObjectiveCriteria.DUMMY`, so a client cannot tell a health objective from a dummy one — it only knows to draw hearts — and the score packet carries no lock bit, which is why `/trigger`'s suggestions have to be computed on the server. There is a third route by which a score reaches a client, and it carries no score packet at all: a `{"score":…}` or `{"nbt":…}` in a text component. `ScoreContents` and `NbtContents` resolve on the **server**, against the authoritative scoreboard, and put the *result* on the wire — never the reference ([text components](../foundations/text-components.md)). A `/tellraw` is a photograph, not a subscription. Saving is one boolean for the entire scoreboard, cleared by re-packing the whole thing, and it happens only when the world is saved — the autosave, `/save-all`, or shutdown: **a score set and a crash a tick later is a score lost.** `ScoreboardSaveData` sits under `minecraft:scoreboard` beside the world, with one command-storage file per namespace, both through the data fixer ([level data and rules](../../reference/level-data-and-rules.md)). The NBT field names are the archaeology — *Objectives*, *PlayerScores*, *DisplaySlots*, *Teams*, and inside them *Name*, *CriteriaName*, *RenderType*, *Locked* — capitalised, pre-flattening conventions, preserved by codec. ## Questions players ask **Why does a `#` in front of a name hide the row?** Because `#` does two unrelated things. In `ScoreHolderArgument` it skips entity resolution entirely, so the token is taken as a literal name; and in the sidebar `PlayerScoreEntry.isHidden` filters the row out. One character, two mechanisms, and together they are the whole hidden-fake-player idiom. (The argument type has four resolution branches in order — the wildcard, a `#` name, a UUID searched across every level, an online player — and the last three fall back to a bare name. The wildcard does not: with no tracked holders at all it throws.) **Why is my sidebar not `DisplaySlot.SIDEBAR`?** If the local player is on a team *with a colour*, `Hud` uses that colour's own display slot and falls back to the plain sidebar otherwise. The colour-to-slot mapping lives on `TeamColor`, not on `DisplaySlot`, and it is what the sixteen team sidebars are for. The sidebar shows fifteen rows and **hides before it cuts**: hidden rows are filtered out, then the rest sorted by value descending and name case-insensitively, and *then* truncated to fifteen. **Why did `/trigger` say the objective is not enabled?** `Score`'s lock bit starts **locked** and its codec defaults **unlocked**, so a score created by `/scoreboard players set` is locked while a score loaded from a file that omits the field is not. `/trigger` is the only command an unprivileged player can use to write a score, and its gate is three-part — the criteria must be the trigger criteria, the score must already exist, and it must be unlocked — and the command re-locks it immediately, so each *enable* buys exactly one use. It is also the only command in this area registered with no permission requirement at all. **Why did my `execute store` throw an internal error?** Because `/scoreboard` refuses a read-only objective and `execute store` does not check. The command resolves its write targets through `ObjectiveArgument.getWritableObjective`; `ExecuteCommand.wrapStores` uses the plain lookup and never asks for a force-writable handle. So `execute store result score @s ` reaches `ScoreAccess.set` with modification disallowed and raises a raw runtime exception from inside a result callback rather than a command error. The same hole is reachable through `/scoreboard players operation` with `><`, the one operator that writes both sides. **Why did my `execute store` into a data target do nothing at all?** Its read-mutate-write is wrapped in a catch with an **empty body**: a malformed target, an uncreatable path, a too-deep path, a block that stopped being a block entity — no message, no failure, no write. The score sink has no such catch. **Why can I read a player's NBT but not write it?** The entity accessor's write path rejects any `Player` before doing anything else, and the read path has no such check. That one asymmetry is why every player-NBT technique is read-only. Relatedly, a no-op is a **hard failure** in four places — `/data merge`, `/data modify`, `/data remove` and `/scoreboard players enable` all throw when they changed nothing, which makes them usable as conditionals in a function, the same choice `/advancement grant` makes. **Do a mob's scores survive it despawning?** They survive *unloading* and die with the mob: `Scoreboard.entityRemoved` runs from the level's destruction callback and is gated on the entity being both non-player and not alive. Two smaller things. A number format can ignore the number entirely — `FixedFormat` renders a constant component whatever the score is — and resolution order is per-score override, then per-objective, then a per-site default: red in the sidebar, yellow in the tab list, unstyled below the name. And below-name numbers are computed in `Entity`, not in a renderer, with their range as an **attribute**: `Attributes.BELOW_NAME_DISTANCE`, syncable, default ten, maximum 512, so a server can change how far away a player's below-name score is legible, per entity ([attributes](../entities/attributes.md)). **A failing command under `store result` writes 0**, whichever kind it is. For the custom-executor path the answer is in the game's own packages — `CustomCommandExecutor.WithErrorHandling` reports failure through the callback and a failure result is a zero, so a failing `/function` writes 0. For an ordinary leaf the result consumer is driven by Brigadier, and `ContextChain.runExecutable` catches the `CommandSyntaxException` and calls the consumer with *success false, result 0* before rethrowing. The game hands that consumer straight through from the source's own callback, so the two paths agree: a store target written by a command that threw holds zero, not its previous value. ## Where to look `Scoreboard` for the six maps, `ScoreAccess` for why a write is a handle, and `ServerScoreboard.trackedObjectives` for the one field that decides what a client ever knows. `ObjectiveCriteria.byName` for the statistics bridge, `NbtPathArgument.NbtPath` for the nicest ten lines in the area, and `ExecuteCommand.wrapStores` for the one that makes `execute store` stop feeling like magic. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Dialogs > Verified against **Minecraft 26.2** · Part XIII · You click a server in the multiplayer list and, before the world has loaded — before you are in a world at all — a form appears with text boxes on it, and it is not part of Minecraft. A dialog is a data pack's form: a title, some body text, some inputs and some buttons, decoded from JSON and put on your screen. Nothing about that is surprising until you notice which protocol phase it works in. `ClientboundShowDialogPacket` is registered in **both** the play and the configuration protocols ([protocol phases](../networking/protocol-phases.md)), so a server can interrupt the join handshake to ask you something. Vanilla only ever does it from a dev-flag-gated command — but the machinery is there, complete, in the shipped jar. And the reason it works there is not a special case bolted on; it is a second codec, and it explains itself. The configuration buffer is a plain byte buffer with **no registry access**, so the packet cannot carry a holder id. `Dialog.CONTEXT_FREE_STREAM_CODEC` therefore sends the whole dialog inline. What is "context-free" is the *buffer*, not the payload. A dialog is also one of the two clearest instances of a move Mojang has been making everywhere — take something that used to be a Java class and make it a registry element loaded from a data pack. That argument is made once, for all its instances, in [the data-driven type pattern](../foundations/data-driven-types.md); this page assumes it. Four of the pattern's registries are dialog registries. ## The cast | class | what it decides | side | |---|---|---| | `Dialog` | the registry element. `Dialog.DIRECT_CODEC` dispatches on `BuiltInRegistries.DIALOG_TYPE`; there are two stream codecs, and which one is used decides the whole page | server | | `CommonDialogData` | what every dialog embeds: titles, whether escape closes it, whether it **pauses the game**, the after-action, the body elements and the inputs. Its `MapCodec` is where the pause validation lives | server | | `DialogAction` | close, none, or wait-for-response — and `DialogAction.willUnpause` is what that validation tests | server | | `InputControl` | `TextInput`, `SingleOptionInput`, `BooleanInput`, `NumberRangeInput`. An `Input` is a key plus a control, and the key must be a valid **macro** variable name | server | | `Action` | produces an optional `ClickEvent` from the *live* input values, through `Action.ValueGetter` | server | | `ClickEvent` | extended with `ClickEvent.ShowDialog` and `ClickEvent.Custom`, which is how anything clickable can open a dialog | both | | `DialogScreens` | the codec-to-screen-factory map, with `DialogScreen` as the base and `DialogControlSet` owning the live getters | client | | `DialogConnectionAccess` | the phase-specific way back to the server — and the configuration-phase one refuses to run commands | client | `net/minecraft/server/dialog` is thirty-one classes across four packages, all in the server jar; the screens that render them are client-only in `net/minecraft/client/gui/screens/dialog`. The five kinds `DialogTypes.bootstrap` registers are `NoticeDialog` and `ConfirmationDialog` (both `SimpleDialog`) and `MultiActionDialog`, `DialogListDialog` and `ServerLinksDialog` (all `ButtonListDialog`) — and both of those supertypes are interfaces, not classes. ## The trace: a data pack puts a form on the screen ```mermaid sequenceDiagram participant RDL as RegistryDataLoader participant DlgC as DialogCommand participant SP as ServerPlayer participant CComPL as ClientCommonPacketListenerImpl participant DlgS as DialogScreen participant MS as MinecraftServer RDL->>RDL: Registries.DIALOG from data/ns/dialog — and synced at configuration DlgC->>SP: openDialog(holder) — /dialog show, or a ClickEvent.ShowDialog anywhere SP->>CComPL: ClientboundShowDialogPacket — a holder id, or the whole dialog inline CComPL->>DlgS: DialogScreens.createFromData — pick the screen for the codec DlgS->>DlgS: DialogControlSet.addInput — each input registers an Action.ValueGetter DlgS->>DlgS: click — Action.createAction reads the getters NOW, not earlier DlgS->>MS: ServerboundCustomClickActionPacket — an id plus the inputs as NBT MS->>MS: handleCustomClickAction — vanilla logs it at debug and stops ``` The trace turns on one decision: **when are the input values read?** Not at packet time and not at screen construction. `DialogControlSet` keeps a map of live `Action.ValueGetter`s and `Action.createAction` calls them at the moment of the click — which is why the same `Action` object produces a different command each time, and why `CommandTemplate` can be a template rather than a string. `ActionTypes` registers nine kinds: the seven click-event kinds a server is allowed to send, plus `CommandTemplate` and `CustomAll`, which packs every input value into an NBT compound. That set of nine is derived from the click-event enum **at class-init**, so every click-event kind a server may send is automatically a dialog action of the same name — and the one kind that is not allowed, opening a local file, can never be one. Nothing on the server side of this ever ticks. A dialog is a packet send from whatever ran the command or handled the click; the reply hops off the Netty thread onto the server's `PacketProcessor` before `MinecraftServer.handleCustomClickAction` sees it, and on the client `ClientCommonPacketListenerImpl.handleShowDialog` hops to the client's processor before touching the screen stack. Exactly one thing in this system ticks: `WaitingForResponseScreen`, counting ticks to un-grey its escape button. ## Four ways a dialog opens, and one of them is not a click `ServerPlayer.openDialog` is the server-side entry point, and `/dialog show` is the obvious caller. The interesting ones are the click events, because "a component with a click event" is not the same as "a component whose click events are dispatched". There are three places on the client where they actually are — chat, a book, and `DialogScreen` itself, which dispatches its own buttons and body text — and one route that is not a click dispatch at all: `SignBlockEntity` reads the event **server-side** and calls `ServerPlayer.openDialog` directly. An item's name or lore is tooltip text and dispatches nothing. Two tags round it out. `DialogTags.PAUSE_SCREEN_ADDITIONS` and `DialogTags.QUICK_ACTIONS` let a data pack add buttons to the pause menu and to a hotkey, so a dialog need not be pushed by the server at all — and `Dialogs` holds the three the jar ships. Closing one from the server is `ClientboundClearDialogPacket`, registered in both phases like the other two. Inside a dialog, the parts dispatch on registries of their own the same way the dialog does: `DialogBody` over `BuiltInRegistries.DIALOG_BODY_TYPE` (`PlainMessage` and `ItemBody`), `InputControl` over `BuiltInRegistries.INPUT_CONTROL_TYPE`, and `ActionButton` carrying a `CommonButtonData` of label, tooltip and width. `DialogBodyHandlers` and `InputControlHandlers` are the client-side factory maps that mirror them. An input's key is validated by `ParsedTemplate` against `StringTemplate.isValidVariableName` — the same rule a macro function's parameters obey, which is the seam into [functions and macros](functions-and-macros.md), and why `CommandTemplate` can substitute a dialog's inputs into a command at all. ## What a data pack cannot do Three defences are built into the model rather than into any particular dialog, and each one exists because the feature would otherwise be a way to trap a player. **The exit is not optional.** `DialogScreen`'s initialisation is final: it *always* adds a warning button that opens a nested confirm screen offering to disconnect, and repositions it if a layout would push it off-screen. An action that waits for a response swaps in `WaitingForResponseScreen`, which reveals a Back button after a second and enables it after five. **Pausing is validated by the codec, wherever it decodes.** A dialog that pauses the game with an after-action that never unpauses is rejected, because it would strand the player in a paused world. The check sits on `CommonDialogData`'s codec rather than on the loader, and `MapCodec.validate` is applied to both directions of the codec — so it runs on the server as it encodes *and* on the client as it decodes, which is what covers a dialog sent inline in the configuration phase. **A button that runs a command is not simply a chat command.** It goes through `ClientPacketListener.sendUnattendedCommand`, which parses the string once — and a second time, against a no-permission source, only if that first parse succeeded and needs no signature — and pops a confirmation screen if the command fails to parse, needs a signature, or needs a permission the client believes it lacks ([permissions](permissions.md)). A command with none of those problems is sent with no screen at all. And the configuration-phase `DialogConnectionAccess` refuses to run commands at all, logging a warning instead. ## The extension point vanilla does not use `MinecraftServer.handleCustomClickAction` is one line, logging at debug. The entire custom-action mechanism — an arbitrary id plus an arbitrary NBT payload, sent by a screen the server described — exists for data packs and server software to build on. The game itself only defines the transport, and defends it with a 32 KB NBT accounter and a 64 KB cap on the payload's own length prefix. The same is true one level up: the only vanilla sender of a configuration-phase dialog is `DebugConfigCommand`, which is gated on `SharedConstants.DEBUG_DEV_COMMANDS` *or* `SharedConstants.IS_RUNNING_IN_IDE`, **and** dedicated-server-only. A server really can put a form in front of you before you are in the world. Vanilla never does. ## Where to look `Dialog` and `CommonDialogData` for the model, then `Action` — the value-getter indirection is the only subtle thing in the whole system. `DialogScreens` for how a codec becomes a screen, and `MinecraftServer.handleCustomClickAction` for the one line that is the extension point. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Game tests > Verified against **Minecraft 26.2** · Part XIII · Run `/test run *` on a vanilla server and one test runs, and passes. The suite is not in the game — a test is a data-pack file, the Java body is a value the JSON points at, and the shipped jar declares exactly one of each. Game tests are how Mojang checks that a piston still pushes and a hopper still pulls: a small structure is pasted into a spare corner of a world, a test body runs against it for a bounded number of ticks, and a block beside it turns green or red. That much has been true for years. What changed is where a test *lives*. There is no *GameTest* annotation any more, and no test registry class. A test is a **registry element** loaded from `data//test_instance/`, and the Java body — when there is one — is a value in a second registry, `Registries.TEST_FUNCTION`, that the JSON points at. The Java half is a payload, not the declaration. Which means the shipped jar contains `GameTestInstances`' single always-pass instance, `BuiltinTestFunctions`' body for it, and `GameTestEnvironments`' default environment — an empty `TestEnvironmentDefinition.AllOf` — and the real suite lives in Mojang's test sources, not in the game you downloaded. This is [the data-driven type pattern](../foundations/data-driven-types.md) again, and game tests are its most complete instance: two data-pack registries and two built-in type registries between them. ## The cast | class | what it decides | |---|---| | `GameTestInstance` | the registry element. `GameTestInstance.run` takes a `GameTestHelper` and is the body — `BlockBasedTestInstance` needs no Java at all, `FunctionGameTestInstance` invokes a `Registries.TEST_FUNCTION` entry | | `TestData` | the declaration record every instance delegates to: environment, structure, tick budgets, required, rotation, manual-only, the two retry counts, sky access, padding | | `TestEnvironmentDefinition` | the seven ways to bend the world for a test, shaped as an **undo log** | | `GameTestBatch` | a group of tests keyed by their environment holder. A batch *is* an environment | | `GameTestRunner` | owns the batches and the structure spawner, and re-queues a failure when the retry options say so | | `GameTestInfo` | one *run* of one test: its position, its timeout, its sequences, its outcome | | `GameTestHelper` | 1,353 lines, and the entire surface a test body sees — coordinate translation, world edits, spawning, assertions, outcomes | | `TestInstanceBlockEntity` | 551 lines: the block that owns a test's bounding box, status and beacon beam, and does the real work of placing, saving and encasing the structure | `net/minecraft/gametest/framework` is forty-four classes, all server-side, with `net/minecraft/gametest/Main` as the headless entry point beside it. ## The objects, and how they nest ```mermaid flowchart TB subgraph D["THE DECLARATION — data pack files"] TI["GameTestInstance — data/ns/test_instance"] TD["TestData — environment, structure, tick budgets, retries"] TE["TestEnvironmentDefinition — data/ns/test_environment"] ST["a structure — data/ns/structure"] TI --> TD TD --> TE TD --> ST end subgraph R["THE RUN — one object per attempt"] GB["GameTestBatch — every test sharing ONE environment holder, split into runs of fifty"] GI2["GameTestInfo — one run of one test: position, timeout, sequences, outcome"] GH["GameTestHelper — test-local coordinates, edits, assertions"] GB --> GI2 GI2 --> GH end subgraph W["THE WORLD — what a test costs"] TIB2["TestInstanceBlockEntity — bounding box, status, beacon beam, barrier shell, forced chunk"] TB2["TestBlock — start, log, fail and accept, for a test written with no Java"] end D --> R R --> W ``` **A batch is not a name and not a class: it is an environment.** `GameTestBatchFactory` groups tests by their `TestEnvironmentDefinition` holder, because that is what `GameTestBatch` is keyed by, and each group is split into runs of fifty (a default the builder can change, not a cap). One environment is active at a time on the runner, and moving between batches tears the old one down and stands the new one up. **The environment interface is an undo log.** `TestEnvironmentDefinition.setup` returns a value that `TestEnvironmentDefinition.teardown` is handed back. Five of the seven kinds return the *previous* state and restore it; `TestEnvironmentDefinition.Functions` returns nothing and runs a *different* data-pack function on the way out; and `TestEnvironmentDefinition.AllOf` returns its children's activations and unwinds them in reverse. ## The trace: one test runs ```mermaid sequenceDiagram participant TC as TestCommand participant GTR as GameTestRunner participant TIB as TestInstanceBlockEntity participant GTT as GameTestTicker participant GI as GameTestInfo participant RGL as ReportGameListener TC->>GTR: build one GameTestInfo per test, batched by environment GTR->>GI: spawn each info — prepareTestStructure GI->>TIB: placeStructure, then encaseStructure — a barrier shell round the test GTR->>GTR: TestEnvironmentDefinition.setup — returns the undo log GTR->>GTT: add every info whose structure was placed to the ticker GTT->>GI: tick — counting up from NEGATIVE: the setup ticks run before tick zero GI->>GI: GameTestInstance.run(helper) at tick zero, and sequences tick after GI->>RGL: succeed, or a GameTestException — a timeout is just another one RGL->>TIB: setSuccess or setErrorMessage — the beam turns green, red or orange RGL->>RGL: say to chat, and GlobalTestReporter to the log or to JUnit XML ``` Game tests tick on the server thread, from `MinecraftServer.tickChildren` in a profiler section named after the subsystem — after connections and players and the debug subscribers, before the server GUI refresh and chunk sending — and only when the tick-rate manager reports the game running normally, so `/tick freeze` suspends them. **Setup ticks run before tick zero.** `GameTestInfo.startExecution` starts its counter *negative* — by the declared setup ticks, plus the spawner's own tick delay, plus one — so the body runs when the count reaches zero. `GameTestSequence` is the "do this, wait, then assert that" chain — `GameTestSequence.thenExecuteAfter`, `GameTestSequence.thenWaitUntil`, `GameTestSequence.thenSucceed` — and it uses an exception as ordinary control flow, at most one thrown and swallowed per sequence per tick, and only for an assertion failure. A timeout is not caught there. **Reporting is a listener chain, and it writes to four places.** `ReportGameListener` is what says something in chat and what writes the outcome back to the `TestInstanceBlockEntity` that owns the beam; `MultipleTestTracker` is the progress bar, with five states including a space for *not started*; and `GlobalTestReporter` dispatches to `LogTestReporter` or `JUnitLikeTestReporter`. ## A test with no Java in it `BlockBasedTestInstance` runs a test built entirely from `TestBlock`s inside the structure. `TestBlockMode` has four values — start, log, fail and accept — and the rules are as simple as they sound: exactly one start block emits redstone to begin, an accept block being triggered is a pass, and a fail block being triggered is a failure carrying its stored message. That is a unit test authored in-game with a redstone circuit and shipped as a structure plus a JSON. The client half the framework's package list hides is what makes that practical: `TestInstanceBlockEditScreen` and `TestBlockEditScreen` are how a test is authored in game, `TestInstanceRenderer` draws the bounding box, and `GameTestBlockHighlightRenderer` is the sole consumer of `ClientboundGameTestHighlightPosPacket`. Both serverbound test packets are sent *by* the client, from those screens: this is the one system in the part whose *declaration* a client edits, though far from the only one whose client talks back — commands, suggestions, dialog clicks and advancement tab switches are all serverbound too. ## Two things a running server should know **`/test` exists on every server**, not only in a development environment. Only the export subcommands are gated on running from an IDE. The command sits at `Commands.LEVEL_GAMEMASTERS` like every other data-pack command in this part. **Test instance blocks are points of interest.** Locating every test within a 250-block radius is a POI query, not a block scan ([points of interest](../world/points-of-interest.md)), which is what makes `/test`'s radius subcommands cheap — and what makes a world full of saved tests carry them in its POI storage. Underneath both, `StructureUtils` and `StructureGridSpawner` are the layer that clears the space, lays tests out in a grid, transforms the far corner and finds every test block by position ([jigsaw and templates](../worldgen/jigsaw-and-templates.md) owns the template machinery they call). `GameTestServer` is a whole `MinecraftServer` subclass for headless runs, driven by `GameTestMainUtil`, and it overrides `GameTestServer.waitUntilNextTick` to drain tasks instead of sleeping: the headless test server runs flat out, and installs a no-op gizmo collector so debug drawing costs nothing ([what this book skips](../anatomy/what-this-book-skips.md)). ## Where to look `GameTestInstance` and `TestData` for what a test *is*, then `GameTestInfo` for what actually happens on a tick, then `TestInstanceBlockEntity` for what a test costs the world, and `GameTestHelper` when you want to write one. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Reference > Verified against **Minecraft 26.2** · Reference · The shelf behind the lectures: everything a viewer would pause the video to read, kept where no lecture has to stop for it. A lecture explains one thing at a time, and it cannot stop to list the 43 entity-data serializers or the ten bits of a flag word without losing the room. The rule for this tier is the one pass 3 set: *would a viewer pause the video to read this?* If yes, it lives here and the page links to it. Twenty pages on the shelf and this one in front of them, and the useful way to tell the twenty apart is not by subject but by **how each one is kept** — because a catalogue is only as good as the version it was read from, and the first question to ask of any page on this shelf is *what regenerates it.* ## The shelf ```mermaid flowchart LR D["the 26.2 decompile"] P["the system pages"] T["TEMPLATE.md, the lane key"] subgraph G["read off the decompile by gen_reference.py, rewritten on every deploy"] G1["packets, registries, data components, game rules"] G2["attributes, entity data serializers, enchantment hooks, loot context parameter sets"] end subgraph I["read off the corpus by the checkers, rewritten on every deploy"] I1["class index, from verify_names.py"] I2["diagram lanes, from check_lanes.py"] end subgraph H["hand-kept by the part sessions, name-verified, re-read every pass"] H1["block update flags, damage outside LivingEntity, what the HUD draws, submit phases, density-function nodes"] H2["threads, math and primitives, level data and rules"] H3["naming drift, glossary"] end D --> G P --> I1 T --> I2 D -. "a session reads one class at a time" .-> H ``` ## How each page is kept, and who leans on it | page | what it lists | kept by | the parts whose landing pages point at it | |---|---|---|---| | [Packets](packets.md) | every packet, by protocol group and direction | generated | III, V, VI, VII, VIII, IX, X, XIII | | [Registries](registries.md) | every registry key: built-in, data-pack, synced | generated | II, V, VI, VII, IX, XII, XIII | | [Data components](components.md) | every `DataComponentType`, persistent and synced | generated | II, V, VII, VIII, IX | | [Game rules](gamerules.md) | every rule, type, category, default | generated | III, IV, V, VI, VIII | | [Attributes](attributes.md) | every attribute: default, range, sentiment, syncable | generated | VI, VIII | | [Entity data serializers](entity-data-serializers.md) | all 43, in registration order, which is the wire id | generated | VI | | [Enchantment hooks](enchantment-hooks.md) | every public `EnchantmentHelper` entry point and its callers | generated | VII | | [Loot context parameter sets](loot-context-params.md) | all twenty-six, with required and optional keys | generated | VII, XIII | | [Block update flags](block-update-flags.md) | the ten bits of `Level.setBlock`'s flag word | hand-kept | IV, V | | [Damage outside `LivingEntity`](non-living-damage.md) | what each of the twenty-one non-living classes does when hit | hand-kept | VI | | [What the HUD draws, and when](hud-elements.md) | every HUD element and the condition it is behind | hand-kept | X | | [Submit phases and feature renderers](submit-phases.md) | the fifteen phases and the thirteen renderers, in declaration order | hand-kept | XI | | [Density-function nodes](density-function-nodes.md) | the thirty-four node types and what the rewrite installs for each | hand-kept | XII | | [Threads](threads.md) | every thread, who makes it, what may run on it | hand-kept | I, III, IV, IX, X, XI | | [Math and primitives](math-and-primitives.md) | the coordinate spaces, packings, shapes and random sources | hand-kept | II, IV, V, VI | | [Level data and rules](level-data-and-rules.md) | who owns the seed, spawn, rules and border, and which file each is in | hand-kept | IV, VIII, XII | | [Naming drift](naming-drift.md) | every 1.21-era name a reader will reach for, and what 26.2 calls it | hand-kept | I, II, XI, XII | | [Glossary](glossary.md) | one sentence per term, and the page that owns it | hand-kept | X, XI, XII, XIII | | [Diagram lanes](lanes.md) | every lane abbreviation and the class it means, and the nine that mean a thread, a process or a boundary instead | generated from the lane key | every part | | [Class index](class-index.md) | every class backticked on a page, and the pages that name it | generated from the pages | — | *Generated* means `python tools/gen_reference.py all` rewrites the file from the decompile's declaration lines, so a version bump re-derives it rather than re-reading it; the two indexes are rewritten by `python tools/verify_names.py --index` and `python tools/check_lanes.py --index`. *Hand-kept* means a part session read the classes and wrote the rows, `tools/verify_names.py` checks every name on the page, and the second fact-check re-reads the rows — declaration orders drift on a version bump, and two of these pages (submit phases, density-function nodes) are nothing but declaration order. For agents: the whole site is also served as one file at [/llms-full.txt](https://minecraftdocs.dev/llms-full.txt). --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Packets > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every packet the game defines, by the `PacketTypes` class that declares it. `common`, `cookie` and `ping` packets are shared by more than one protocol phase; the exact phase→packet bindings are in the `*Protocols` classes next to each `PacketTypes` class (`GameProtocols`, `ConfigurationProtocols`, `LoginProtocols`, `StatusProtocols`, `HandshakeProtocols`). See [Packets and stream codecs](../systems/networking/packets-and-stream-codecs.md). | group | clientbound | serverbound | |---|---:|---:| | `common` (`CommonPacketTypes`) | 13 | 6 | | `configuration` (`ConfigurationPacketTypes`) | 6 | 3 | | `cookie` (`CookiePacketTypes`) | 1 | 1 | | `game` (`GamePacketTypes`) | 127 | 61 | | `handshake` (`HandshakePacketTypes`) | 0 | 1 | | `login` (`LoginPacketTypes`) | 5 | 4 | | `ping` (`PingPacketTypes`) | 1 | 1 | | `status` (`StatusPacketTypes`) | 1 | 1 | | **total** | **154** | **78** | ## `common` — `CommonPacketTypes` — shared across phases | id | direction | class | |---|---|---| | `clear_dialog` | clientbound | `ClientboundClearDialogPacket` | | `custom_payload` | clientbound | `ClientboundCustomPayloadPacket` | | `custom_report_details` | clientbound | `ClientboundCustomReportDetailsPacket` | | `disconnect` | clientbound | `ClientboundDisconnectPacket` | | `keep_alive` | clientbound | `ClientboundKeepAlivePacket` | | `ping` | clientbound | `ClientboundPingPacket` | | `resource_pack_pop` | clientbound | `ClientboundResourcePackPopPacket` | | `resource_pack_push` | clientbound | `ClientboundResourcePackPushPacket` | | `server_links` | clientbound | `ClientboundServerLinksPacket` | | `show_dialog` | clientbound | `ClientboundShowDialogPacket` | | `store_cookie` | clientbound | `ClientboundStoreCookiePacket` | | `transfer` | clientbound | `ClientboundTransferPacket` | | `update_tags` | clientbound | `ClientboundUpdateTagsPacket` | | `client_information` | serverbound | `ServerboundClientInformationPacket` | | `custom_click_action` | serverbound | `ServerboundCustomClickActionPacket` | | `custom_payload` | serverbound | `ServerboundCustomPayloadPacket` | | `keep_alive` | serverbound | `ServerboundKeepAlivePacket` | | `pong` | serverbound | `ServerboundPongPacket` | | `resource_pack` | serverbound | `ServerboundResourcePackPacket` | ## `configuration` — `ConfigurationPacketTypes` | id | direction | class | |---|---|---| | `code_of_conduct` | clientbound | `ClientboundCodeOfConductPacket` | | `finish_configuration` | clientbound | `ClientboundFinishConfigurationPacket` | | `registry_data` | clientbound | `ClientboundRegistryDataPacket` | | `reset_chat` | clientbound | `ClientboundResetChatPacket` | | `select_known_packs` | clientbound | `ClientboundSelectKnownPacks` | | `update_enabled_features` | clientbound | `ClientboundUpdateEnabledFeaturesPacket` | | `accept_code_of_conduct` | serverbound | `ServerboundAcceptCodeOfConductPacket` | | `finish_configuration` | serverbound | `ServerboundFinishConfigurationPacket` | | `select_known_packs` | serverbound | `ServerboundSelectKnownPacks` | ## `cookie` — `CookiePacketTypes` — shared across phases | id | direction | class | |---|---|---| | `cookie_request` | clientbound | `ClientboundCookieRequestPacket` | | `cookie_response` | serverbound | `ServerboundCookieResponsePacket` | ## `game` — `GamePacketTypes` | id | direction | class | |---|---|---| | `add_entity` | clientbound | `ClientboundAddEntityPacket` | | `animate` | clientbound | `ClientboundAnimatePacket` | | `award_stats` | clientbound | `ClientboundAwardStatsPacket` | | `block_changed_ack` | clientbound | `ClientboundBlockChangedAckPacket` | | `block_destruction` | clientbound | `ClientboundBlockDestructionPacket` | | `block_entity_data` | clientbound | `ClientboundBlockEntityDataPacket` | | `block_event` | clientbound | `ClientboundBlockEventPacket` | | `block_update` | clientbound | `ClientboundBlockUpdatePacket` | | `boss_event` | clientbound | `ClientboundBossEventPacket` | | `bundle` | clientbound | `ClientboundBundlePacket` | | `bundle_delimiter` | clientbound | `ClientboundBundleDelimiterPacket` | | `change_difficulty` | clientbound | `ClientboundChangeDifficultyPacket` | | `chunk_batch_finished` | clientbound | `ClientboundChunkBatchFinishedPacket` | | `chunk_batch_start` | clientbound | `ClientboundChunkBatchStartPacket` | | `chunks_biomes` | clientbound | `ClientboundChunksBiomesPacket` | | `clear_titles` | clientbound | `ClientboundClearTitlesPacket` | | `command_suggestions` | clientbound | `ClientboundCommandSuggestionsPacket` | | `commands` | clientbound | `ClientboundCommandsPacket` | | `container_close` | clientbound | `ClientboundContainerClosePacket` | | `container_set_content` | clientbound | `ClientboundContainerSetContentPacket` | | `container_set_data` | clientbound | `ClientboundContainerSetDataPacket` | | `container_set_slot` | clientbound | `ClientboundContainerSetSlotPacket` | | `cooldown` | clientbound | `ClientboundCooldownPacket` | | `custom_chat_completions` | clientbound | `ClientboundCustomChatCompletionsPacket` | | `damage_event` | clientbound | `ClientboundDamageEventPacket` | | `debug/block_value` | clientbound | `ClientboundDebugBlockValuePacket` | | `debug/chunk_value` | clientbound | `ClientboundDebugChunkValuePacket` | | `debug/entity_value` | clientbound | `ClientboundDebugEntityValuePacket` | | `debug/event` | clientbound | `ClientboundDebugEventPacket` | | `debug_sample` | clientbound | `ClientboundDebugSamplePacket` | | `delete_chat` | clientbound | `ClientboundDeleteChatPacket` | | `disguised_chat` | clientbound | `ClientboundDisguisedChatPacket` | | `entity_event` | clientbound | `ClientboundEntityEventPacket` | | `entity_position_sync` | clientbound | `ClientboundEntityPositionSyncPacket` | | `explode` | clientbound | `ClientboundExplodePacket` | | `forget_level_chunk` | clientbound | `ClientboundForgetLevelChunkPacket` | | `game_event` | clientbound | `ClientboundGameEventPacket` | | `game_rule_values` | clientbound | `ClientboundGameRuleValuesPacket` | | `game_test_highlight_pos` | clientbound | `ClientboundGameTestHighlightPosPacket` | | `hurt_animation` | clientbound | `ClientboundHurtAnimationPacket` | | `initialize_border` | clientbound | `ClientboundInitializeBorderPacket` | | `level_chunk_with_light` | clientbound | `ClientboundLevelChunkWithLightPacket` | | `level_event` | clientbound | `ClientboundLevelEventPacket` | | `level_particles` | clientbound | `ClientboundLevelParticlesPacket` | | `light_update` | clientbound | `ClientboundLightUpdatePacket` | | `login` | clientbound | `ClientboundLoginPacket` | | `low_disk_space_warning` | clientbound | `ClientboundLowDiskSpaceWarningPacket` | | `map_item_data` | clientbound | `ClientboundMapItemDataPacket` | | `merchant_offers` | clientbound | `ClientboundMerchantOffersPacket` | | `mount_screen_open` | clientbound | `ClientboundMountScreenOpenPacket` | | `move_entity_pos` | clientbound | `ClientboundMoveEntityPacket.Pos` | | `move_entity_pos_rot` | clientbound | `ClientboundMoveEntityPacket.PosRot` | | `move_entity_rot` | clientbound | `ClientboundMoveEntityPacket.Rot` | | `move_minecart_along_track` | clientbound | `ClientboundMoveMinecartPacket` | | `move_vehicle` | clientbound | `ClientboundMoveVehiclePacket` | | `open_book` | clientbound | `ClientboundOpenBookPacket` | | `open_screen` | clientbound | `ClientboundOpenScreenPacket` | | `open_sign_editor` | clientbound | `ClientboundOpenSignEditorPacket` | | `place_ghost_recipe` | clientbound | `ClientboundPlaceGhostRecipePacket` | | `player_abilities` | clientbound | `ClientboundPlayerAbilitiesPacket` | | `player_chat` | clientbound | `ClientboundPlayerChatPacket` | | `player_combat_end` | clientbound | `ClientboundPlayerCombatEndPacket` | | `player_combat_enter` | clientbound | `ClientboundPlayerCombatEnterPacket` | | `player_combat_kill` | clientbound | `ClientboundPlayerCombatKillPacket` | | `player_info_remove` | clientbound | `ClientboundPlayerInfoRemovePacket` | | `player_info_update` | clientbound | `ClientboundPlayerInfoUpdatePacket` | | `player_look_at` | clientbound | `ClientboundPlayerLookAtPacket` | | `player_position` | clientbound | `ClientboundPlayerPositionPacket` | | `player_rotation` | clientbound | `ClientboundPlayerRotationPacket` | | `projectile_power` | clientbound | `ClientboundProjectilePowerPacket` | | `recipe_book_add` | clientbound | `ClientboundRecipeBookAddPacket` | | `recipe_book_remove` | clientbound | `ClientboundRecipeBookRemovePacket` | | `recipe_book_settings` | clientbound | `ClientboundRecipeBookSettingsPacket` | | `remove_entities` | clientbound | `ClientboundRemoveEntitiesPacket` | | `remove_mob_effect` | clientbound | `ClientboundRemoveMobEffectPacket` | | `reset_score` | clientbound | `ClientboundResetScorePacket` | | `respawn` | clientbound | `ClientboundRespawnPacket` | | `rotate_head` | clientbound | `ClientboundRotateHeadPacket` | | `section_blocks_update` | clientbound | `ClientboundSectionBlocksUpdatePacket` | | `select_advancements_tab` | clientbound | `ClientboundSelectAdvancementsTabPacket` | | `server_data` | clientbound | `ClientboundServerDataPacket` | | `set_action_bar_text` | clientbound | `ClientboundSetActionBarTextPacket` | | `set_border_center` | clientbound | `ClientboundSetBorderCenterPacket` | | `set_border_lerp_size` | clientbound | `ClientboundSetBorderLerpSizePacket` | | `set_border_size` | clientbound | `ClientboundSetBorderSizePacket` | | `set_border_warning_delay` | clientbound | `ClientboundSetBorderWarningDelayPacket` | | `set_border_warning_distance` | clientbound | `ClientboundSetBorderWarningDistancePacket` | | `set_camera` | clientbound | `ClientboundSetCameraPacket` | | `set_chunk_cache_center` | clientbound | `ClientboundSetChunkCacheCenterPacket` | | `set_chunk_cache_radius` | clientbound | `ClientboundSetChunkCacheRadiusPacket` | | `set_cursor_item` | clientbound | `ClientboundSetCursorItemPacket` | | `set_default_spawn_position` | clientbound | `ClientboundSetDefaultSpawnPositionPacket` | | `set_display_objective` | clientbound | `ClientboundSetDisplayObjectivePacket` | | `set_entity_data` | clientbound | `ClientboundSetEntityDataPacket` | | `set_entity_link` | clientbound | `ClientboundSetEntityLinkPacket` | | `set_entity_motion` | clientbound | `ClientboundSetEntityMotionPacket` | | `set_equipment` | clientbound | `ClientboundSetEquipmentPacket` | | `set_experience` | clientbound | `ClientboundSetExperiencePacket` | | `set_health` | clientbound | `ClientboundSetHealthPacket` | | `set_held_slot` | clientbound | `ClientboundSetHeldSlotPacket` | | `set_objective` | clientbound | `ClientboundSetObjectivePacket` | | `set_passengers` | clientbound | `ClientboundSetPassengersPacket` | | `set_player_inventory` | clientbound | `ClientboundSetPlayerInventoryPacket` | | `set_player_team` | clientbound | `ClientboundSetPlayerTeamPacket` | | `set_score` | clientbound | `ClientboundSetScorePacket` | | `set_simulation_distance` | clientbound | `ClientboundSetSimulationDistancePacket` | | `set_subtitle_text` | clientbound | `ClientboundSetSubtitleTextPacket` | | `set_time` | clientbound | `ClientboundSetTimePacket` | | `set_title_text` | clientbound | `ClientboundSetTitleTextPacket` | | `set_titles_animation` | clientbound | `ClientboundSetTitlesAnimationPacket` | | `sound` | clientbound | `ClientboundSoundPacket` | | `sound_entity` | clientbound | `ClientboundSoundEntityPacket` | | `start_configuration` | clientbound | `ClientboundStartConfigurationPacket` | | `stop_sound` | clientbound | `ClientboundStopSoundPacket` | | `system_chat` | clientbound | `ClientboundSystemChatPacket` | | `tab_list` | clientbound | `ClientboundTabListPacket` | | `tag_query` | clientbound | `ClientboundTagQueryPacket` | | `take_item_entity` | clientbound | `ClientboundTakeItemEntityPacket` | | `teleport_entity` | clientbound | `ClientboundTeleportEntityPacket` | | `test_instance_block_status` | clientbound | `ClientboundTestInstanceBlockStatus` | | `ticking_state` | clientbound | `ClientboundTickingStatePacket` | | `ticking_step` | clientbound | `ClientboundTickingStepPacket` | | `update_advancements` | clientbound | `ClientboundUpdateAdvancementsPacket` | | `update_attributes` | clientbound | `ClientboundUpdateAttributesPacket` | | `update_mob_effect` | clientbound | `ClientboundUpdateMobEffectPacket` | | `update_recipes` | clientbound | `ClientboundUpdateRecipesPacket` | | `waypoint` | clientbound | `ClientboundTrackedWaypointPacket` | | `accept_teleportation` | serverbound | `ServerboundAcceptTeleportationPacket` | | `attack` | serverbound | `ServerboundAttackPacket` | | `block_entity_tag_query` | serverbound | `ServerboundBlockEntityTagQueryPacket` | | `bundle_item_selected` | serverbound | `ServerboundSelectBundleItemPacket` | | `change_difficulty` | serverbound | `ServerboundChangeDifficultyPacket` | | `change_game_mode` | serverbound | `ServerboundChangeGameModePacket` | | `chat` | serverbound | `ServerboundChatPacket` | | `chat_ack` | serverbound | `ServerboundChatAckPacket` | | `chat_command` | serverbound | `ServerboundChatCommandPacket` | | `chat_command_signed` | serverbound | `ServerboundChatCommandSignedPacket` | | `chat_session_update` | serverbound | `ServerboundChatSessionUpdatePacket` | | `chunk_batch_received` | serverbound | `ServerboundChunkBatchReceivedPacket` | | `client_command` | serverbound | `ServerboundClientCommandPacket` | | `client_tick_end` | serverbound | `ServerboundClientTickEndPacket` | | `command_suggestion` | serverbound | `ServerboundCommandSuggestionPacket` | | `configuration_acknowledged` | serverbound | `ServerboundConfigurationAcknowledgedPacket` | | `container_button_click` | serverbound | `ServerboundContainerButtonClickPacket` | | `container_click` | serverbound | `ServerboundContainerClickPacket` | | `container_close` | serverbound | `ServerboundContainerClosePacket` | | `container_slot_state_changed` | serverbound | `ServerboundContainerSlotStateChangedPacket` | | `debug_subscription_request` | serverbound | `ServerboundDebugSubscriptionRequestPacket` | | `edit_book` | serverbound | `ServerboundEditBookPacket` | | `entity_tag_query` | serverbound | `ServerboundEntityTagQueryPacket` | | `interact` | serverbound | `ServerboundInteractPacket` | | `jigsaw_generate` | serverbound | `ServerboundJigsawGeneratePacket` | | `lock_difficulty` | serverbound | `ServerboundLockDifficultyPacket` | | `move_player_pos` | serverbound | `ServerboundMovePlayerPacket.Pos` | | `move_player_pos_rot` | serverbound | `ServerboundMovePlayerPacket.PosRot` | | `move_player_rot` | serverbound | `ServerboundMovePlayerPacket.Rot` | | `move_player_status_only` | serverbound | `ServerboundMovePlayerPacket.StatusOnly` | | `move_vehicle` | serverbound | `ServerboundMoveVehiclePacket` | | `paddle_boat` | serverbound | `ServerboundPaddleBoatPacket` | | `pick_item_from_block` | serverbound | `ServerboundPickItemFromBlockPacket` | | `pick_item_from_entity` | serverbound | `ServerboundPickItemFromEntityPacket` | | `place_recipe` | serverbound | `ServerboundPlaceRecipePacket` | | `player_abilities` | serverbound | `ServerboundPlayerAbilitiesPacket` | | `player_action` | serverbound | `ServerboundPlayerActionPacket` | | `player_command` | serverbound | `ServerboundPlayerCommandPacket` | | `player_input` | serverbound | `ServerboundPlayerInputPacket` | | `player_loaded` | serverbound | `ServerboundPlayerLoadedPacket` | | `recipe_book_change_settings` | serverbound | `ServerboundRecipeBookChangeSettingsPacket` | | `recipe_book_seen_recipe` | serverbound | `ServerboundRecipeBookSeenRecipePacket` | | `rename_item` | serverbound | `ServerboundRenameItemPacket` | | `seen_advancements` | serverbound | `ServerboundSeenAdvancementsPacket` | | `select_trade` | serverbound | `ServerboundSelectTradePacket` | | `set_beacon` | serverbound | `ServerboundSetBeaconPacket` | | `set_carried_item` | serverbound | `ServerboundSetCarriedItemPacket` | | `set_command_block` | serverbound | `ServerboundSetCommandBlockPacket` | | `set_command_minecart` | serverbound | `ServerboundSetCommandMinecartPacket` | | `set_creative_mode_slot` | serverbound | `ServerboundSetCreativeModeSlotPacket` | | `set_game_rule` | serverbound | `ServerboundSetGameRulePacket` | | `set_jigsaw_block` | serverbound | `ServerboundSetJigsawBlockPacket` | | `set_structure_block` | serverbound | `ServerboundSetStructureBlockPacket` | | `set_test_block` | serverbound | `ServerboundSetTestBlockPacket` | | `sign_update` | serverbound | `ServerboundSignUpdatePacket` | | `spectator_action` | serverbound | `ServerboundSpectatorActionPacket` | | `swing` | serverbound | `ServerboundSwingPacket` | | `teleport_to_entity` | serverbound | `ServerboundTeleportToEntityPacket` | | `test_instance_block_action` | serverbound | `ServerboundTestInstanceBlockActionPacket` | | `use_item` | serverbound | `ServerboundUseItemPacket` | | `use_item_on` | serverbound | `ServerboundUseItemOnPacket` | ## `handshake` — `HandshakePacketTypes` | id | direction | class | |---|---|---| | `intention` | serverbound | `ClientIntentionPacket` | ## `login` — `LoginPacketTypes` | id | direction | class | |---|---|---| | `custom_query` | clientbound | `ClientboundCustomQueryPacket` | | `hello` | clientbound | `ClientboundHelloPacket` | | `login_compression` | clientbound | `ClientboundLoginCompressionPacket` | | `login_disconnect` | clientbound | `ClientboundLoginDisconnectPacket` | | `login_finished` | clientbound | `ClientboundLoginFinishedPacket` | | `custom_query_answer` | serverbound | `ServerboundCustomQueryAnswerPacket` | | `hello` | serverbound | `ServerboundHelloPacket` | | `key` | serverbound | `ServerboundKeyPacket` | | `login_acknowledged` | serverbound | `ServerboundLoginAcknowledgedPacket` | ## `ping` — `PingPacketTypes` — shared across phases | id | direction | class | |---|---|---| | `pong_response` | clientbound | `ClientboundPongResponsePacket` | | `ping_request` | serverbound | `ServerboundPingRequestPacket` | ## `status` — `StatusPacketTypes` | id | direction | class | |---|---|---| | `status_response` | clientbound | `ClientboundStatusResponsePacket` | | `status_request` | serverbound | `ServerboundStatusRequestPacket` | --- # Registries > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every registry key declared in `Registries`. **Built-in** registries are populated from static code in `BuiltInRegistries` at class-load time and frozen; **data-pack** registries are loaded per world by `RegistryDataLoader` from JSON (`WORLDGEN_REGISTRIES`, or `DIMENSION_REGISTRIES` for level stems); **synced** ones are sent to the client in the configuration phase (`SYNCHRONIZED_REGISTRIES`). A key that is none of these is a registry *type* the game reasons about without a global instance (e.g. per-world or client-side). See [Identifiers and registries](../systems/foundations/identifiers-and-registries.md). 153 keys · 95 built-in · 47 data-pack · 29 synced | key | element type | kind | synced | |---|---|---|---| | `activity` (`Registries.ACTIVITY`) | `Activity` | built-in | | | `advancement` (`Registries.ADVANCEMENT`) | `Advancement` | — | | | `attribute` (`Registries.ATTRIBUTE`) | `Attribute` | built-in | | | `attribute_type` (`Registries.ATTRIBUTE_TYPE`) | `AttributeType<…>` | built-in | | | `banner_pattern` (`Registries.BANNER_PATTERN`) | `BannerPattern` | data-pack | yes | | `block` (`Registries.BLOCK`) | `Block` | built-in | | | `block_entity_type` (`Registries.BLOCK_ENTITY_TYPE`) | `BlockEntityType<…>` | built-in | | | `block_predicate_type` (`Registries.BLOCK_PREDICATE_TYPE`) | `BlockPredicateType<…>` | built-in | | | `block_type` (`Registries.BLOCK_TYPE`) | `MapCodec<…>` | built-in | | | `cat_sound_variant` (`Registries.CAT_SOUND_VARIANT`) | `CatSoundVariant` | data-pack | yes | | `cat_variant` (`Registries.CAT_VARIANT`) | `CatVariant` | data-pack | yes | | `chat_type` (`Registries.CHAT_TYPE`) | `ChatType` | data-pack | yes | | `chicken_sound_variant` (`Registries.CHICKEN_SOUND_VARIANT`) | `ChickenSoundVariant` | data-pack | yes | | `chicken_variant` (`Registries.CHICKEN_VARIANT`) | `ChickenVariant` | data-pack | yes | | `chunk_status` (`Registries.CHUNK_STATUS`) | `ChunkStatus` | built-in | | | `clock_time_marker` (`ClockTimeMarkers.ROOT_ID`) | `ClockTimeMarker` | — | | | `command_argument_type` (`Registries.COMMAND_ARGUMENT_TYPE`) | `ArgumentTypeInfo<…>` | built-in | | | `consume_effect_type` (`Registries.CONSUME_EFFECT_TYPE`) | `ConsumeEffect.Type<…>` | built-in | | | `cow_sound_variant` (`Registries.COW_SOUND_VARIANT`) | `CowSoundVariant` | data-pack | yes | | `cow_variant` (`Registries.COW_VARIANT`) | `CowVariant` | data-pack | yes | | `creative_mode_tab` (`Registries.CREATIVE_MODE_TAB`) | `CreativeModeTab` | built-in | | | `custom_stat` (`Registries.CUSTOM_STAT`) | `Identifier` | built-in | | | `damage_type` (`Registries.DAMAGE_TYPE`) | `DamageType` | data-pack | yes | | `data_component_predicate_type` (`Registries.DATA_COMPONENT_PREDICATE_TYPE`) | `DataComponentPredicate.Type<…>` | built-in | | | `data_component_type` (`Registries.DATA_COMPONENT_TYPE`) | `DataComponentType<…>` | built-in | | | `debug_subscription` (`Registries.DEBUG_SUBSCRIPTION`) | `DebugSubscription<…>` | built-in | | | `decorated_pot_pattern` (`Registries.DECORATED_POT_PATTERN`) | `DecoratedPotPattern` | built-in | | | `dialog` (`Registries.DIALOG`) | `Dialog` | data-pack | yes | | `dialog_action_type` (`Registries.DIALOG_ACTION_TYPE`) | `MapCodec<…>` | built-in | | | `dialog_body_type` (`Registries.DIALOG_BODY_TYPE`) | `MapCodec<…>` | built-in | | | `dialog_type` (`Registries.DIALOG_TYPE`) | `MapCodec<…>` | built-in | | | `dimension` (`Registries.DIMENSION`) | `Level` | — | | | `dimension` (`Registries.LEVEL_STEM`) | `LevelStem` | data-pack (dimension) | | | `dimension_type` (`Registries.DIMENSION_TYPE`) | `DimensionType` | data-pack | yes | | `enchantment` (`Registries.ENCHANTMENT`) | `Enchantment` | data-pack | yes | | `enchantment_effect_component_type` (`Registries.ENCHANTMENT_EFFECT_COMPONENT_TYPE`) | `DataComponentType<…>` | built-in | | | `enchantment_entity_effect_type` (`Registries.ENCHANTMENT_ENTITY_EFFECT_TYPE`) | `MapCodec<…>` | built-in | | | `enchantment_level_based_value_type` (`Registries.ENCHANTMENT_LEVEL_BASED_VALUE_TYPE`) | `MapCodec<…>` | built-in | | | `enchantment_location_based_effect_type` (`Registries.ENCHANTMENT_LOCATION_BASED_EFFECT_TYPE`) | `MapCodec<…>` | built-in | | | `enchantment_provider` (`Registries.ENCHANTMENT_PROVIDER`) | `EnchantmentProvider` | data-pack | | | `enchantment_provider_type` (`Registries.ENCHANTMENT_PROVIDER_TYPE`) | `MapCodec<…>` | built-in | | | `enchantment_value_effect_type` (`Registries.ENCHANTMENT_VALUE_EFFECT_TYPE`) | `MapCodec<…>` | built-in | | | `entity_sub_predicate_type` (`Registries.ENTITY_SUB_PREDICATE_TYPE`) | `Codec<…>` | built-in | | | `entity_type` (`Registries.ENTITY_TYPE`) | `EntityType<…>` | built-in | | | `environment_attribute` (`Registries.ENVIRONMENT_ATTRIBUTE`) | `EnvironmentAttribute<…>` | built-in | | | `equipment_asset` (`EquipmentAssets.ROOT_ID`) | `EquipmentAsset` | — | | | `float_provider_type` (`Registries.FLOAT_PROVIDER_TYPE`) | `MapCodec<…>` | built-in | | | `fluid` (`Registries.FLUID`) | `Fluid` | built-in | | | `frog_variant` (`Registries.FROG_VARIANT`) | `FrogVariant` | data-pack | yes | | `function` (`ServerFunctionLibrary.TYPE_KEY`) | `CommandFunction<…>` | — | | | `game_event` (`Registries.GAME_EVENT`) | `GameEvent` | built-in | | | `game_rule` (`Registries.GAME_RULE`) | `GameRule<…>` | built-in | | | `height_provider_type` (`Registries.HEIGHT_PROVIDER_TYPE`) | `HeightProviderType<…>` | built-in | | | `incoming_rpc_methods` (`Registries.INCOMING_RPC_METHOD`) | `IncomingRpcMethod<…>` | built-in | | | `input_control_type` (`Registries.INPUT_CONTROL_TYPE`) | `MapCodec<…>` | built-in | | | `instrument` (`Registries.INSTRUMENT`) | `Instrument` | data-pack | yes | | `int_provider_type` (`Registries.INT_PROVIDER_TYPE`) | `MapCodec<…>` | built-in | | | `item` (`Registries.ITEM`) | `Item` | built-in | | | `item_modifier` (`Registries.ITEM_MODIFIER`) | `LootItemFunction` | — | | | `jukebox_song` (`Registries.JUKEBOX_SONG`) | `JukeboxSong` | data-pack | yes | | `loot_condition_type` (`Registries.LOOT_CONDITION_TYPE`) | `MapCodec<…>` | built-in | | | `loot_function_type` (`Registries.LOOT_FUNCTION_TYPE`) | `MapCodec<…>` | built-in | | | `loot_nbt_provider_type` (`Registries.LOOT_NBT_PROVIDER_TYPE`) | `MapCodec<…>` | built-in | | | `loot_number_provider_type` (`Registries.LOOT_NUMBER_PROVIDER_TYPE`) | `MapCodec<…>` | built-in | | | `loot_pool_entry_type` (`Registries.LOOT_POOL_ENTRY_TYPE`) | `MapCodec<…>` | built-in | | | `loot_score_provider_type` (`Registries.LOOT_SCORE_PROVIDER_TYPE`) | `MapCodec<…>` | built-in | | | `loot_table` (`Registries.LOOT_TABLE`) | `LootTable` | — | | | `map_decoration_type` (`Registries.MAP_DECORATION_TYPE`) | `MapDecorationType` | built-in | | | `memory_module_type` (`Registries.MEMORY_MODULE_TYPE`) | `MemoryModuleType<…>` | built-in | | | `menu` (`Registries.MENU`) | `MenuType<…>` | built-in | | | `mob_effect` (`Registries.MOB_EFFECT`) | `MobEffect` | built-in | | | `number_format_type` (`Registries.NUMBER_FORMAT_TYPE`) | `NumberFormatType<…>` | built-in | | | `outgoing_rpc_methods` (`Registries.OUTGOING_RPC_METHOD`) | `OutgoingRpcMethod<…>` | built-in | | | `painting_variant` (`Registries.PAINTING_VARIANT`) | `PaintingVariant` | data-pack | yes | | `particle_type` (`Registries.PARTICLE_TYPE`) | `ParticleType<…>` | built-in | | | `permission_check_type` (`Registries.PERMISSION_CHECK_TYPE`) | `MapCodec<…>` | built-in | | | `permission_type` (`Registries.PERMISSION_TYPE`) | `MapCodec<…>` | built-in | | | `pig_sound_variant` (`Registries.PIG_SOUND_VARIANT`) | `PigSoundVariant` | data-pack | yes | | `pig_variant` (`Registries.PIG_VARIANT`) | `PigVariant` | data-pack | yes | | `point_of_interest_type` (`Registries.POINT_OF_INTEREST_TYPE`) | `PoiType` | built-in | | | `pos_rule_test` (`Registries.POS_RULE_TEST`) | `PosRuleTestType<…>` | built-in | | | `position_source_type` (`Registries.POSITION_SOURCE_TYPE`) | `PositionSourceType<…>` | built-in | | | `potion` (`Registries.POTION`) | `Potion` | built-in | | | `predicate` (`Registries.PREDICATE`) | `LootItemCondition` | — | | | `recipe` (`Registries.RECIPE`) | `Recipe<…>` | — | | | `recipe_book_category` (`Registries.RECIPE_BOOK_CATEGORY`) | `RecipeBookCategory` | built-in | | | `recipe_display` (`Registries.RECIPE_DISPLAY`) | `RecipeDisplay.Type<…>` | built-in | | | `recipe_property_set` (`RecipePropertySet.TYPE_KEY`) | `RecipePropertySet` | — | | | `recipe_serializer` (`Registries.RECIPE_SERIALIZER`) | `RecipeSerializer<…>` | built-in | | | `recipe_type` (`Registries.RECIPE_TYPE`) | `RecipeType<…>` | built-in | | | `rule_block_entity_modifier` (`Registries.RULE_BLOCK_ENTITY_MODIFIER`) | `RuleBlockEntityModifierType<…>` | built-in | | | `rule_test` (`Registries.RULE_TEST`) | `RuleTestType<…>` | built-in | | | `sensor_type` (`Registries.SENSOR_TYPE`) | `SensorType<…>` | built-in | | | `slot_display` (`Registries.SLOT_DISPLAY`) | `SlotDisplay.Type<…>` | built-in | | | `slot_source_type` (`Registries.SLOT_SOURCE_TYPE`) | `MapCodec<…>` | built-in | | | `sound_event` (`Registries.SOUND_EVENT`) | `SoundEvent` | built-in | | | `spawn_condition_type` (`Registries.SPAWN_CONDITION_TYPE`) | `MapCodec<…>` | built-in | | | `stat_type` (`Registries.STAT_TYPE`) | `StatType<…>` | built-in | | | `sulfur_cube_archetype` (`Registries.SULFUR_CUBE_ARCHETYPE`) | `SulfurCubeArchetype` | data-pack | yes | | `test_environment` (`Registries.TEST_ENVIRONMENT`) | `TestEnvironmentDefinition<…>` | data-pack | yes | | `test_environment_definition_type` (`Registries.TEST_ENVIRONMENT_DEFINITION_TYPE`) | `MapCodec<…>` | built-in | | | `test_function` (`Registries.TEST_FUNCTION`) | `Consumer<…>` | built-in | | | `test_instance` (`Registries.TEST_INSTANCE`) | `GameTestInstance` | data-pack | yes | | `test_instance_type` (`Registries.TEST_INSTANCE_TYPE`) | `MapCodec<…>` | built-in | | | `ticket_type` (`Registries.TICKET_TYPE`) | `TicketType` | built-in | | | `timeline` (`Registries.TIMELINE`) | `Timeline` | data-pack | yes | | `trade_set` (`Registries.TRADE_SET`) | `TradeSet` | data-pack | | | `trial_spawner` (`Registries.TRIAL_SPAWNER_CONFIG`) | `TrialSpawnerConfig` | data-pack | | | `trigger_type` (`Registries.TRIGGER_TYPE`) | `CriterionTrigger<…>` | built-in | | | `trim_material` (`Registries.TRIM_MATERIAL`) | `TrimMaterial` | data-pack | yes | | `trim_pattern` (`Registries.TRIM_PATTERN`) | `TrimPattern` | data-pack | yes | | `villager_profession` (`Registries.VILLAGER_PROFESSION`) | `VillagerProfession` | built-in | | | `villager_trade` (`Registries.VILLAGER_TRADE`) | `VillagerTrade` | data-pack | | | `villager_type` (`Registries.VILLAGER_TYPE`) | `VillagerType` | built-in | | | `waypoint_style_asset` (`WaypointStyleAssets.ROOT_ID`) | `WaypointStyleAsset` | — | | | `wolf_sound_variant` (`Registries.WOLF_SOUND_VARIANT`) | `WolfSoundVariant` | data-pack | yes | | `wolf_variant` (`Registries.WOLF_VARIANT`) | `WolfVariant` | data-pack | yes | | `world_clock` (`Registries.WORLD_CLOCK`) | `WorldClock` | data-pack | yes | | `worldgen/biome` (`Registries.BIOME`) | `Biome` | data-pack | yes | | `worldgen/biome_source` (`Registries.BIOME_SOURCE`) | `MapCodec<…>` | built-in | | | `worldgen/block_state_provider_type` (`Registries.BLOCK_STATE_PROVIDER_TYPE`) | `BlockStateProviderType<…>` | built-in | | | `worldgen/carver` (`Registries.CARVER`) | `WorldCarver<…>` | built-in | | | `worldgen/chunk_generator` (`Registries.CHUNK_GENERATOR`) | `MapCodec<…>` | built-in | | | `worldgen/configured_carver` (`Registries.CONFIGURED_CARVER`) | `ConfiguredWorldCarver<…>` | data-pack | | | `worldgen/configured_feature` (`Registries.CONFIGURED_FEATURE`) | `ConfiguredFeature<…>` | data-pack | | | `worldgen/density_function` (`Registries.DENSITY_FUNCTION`) | `DensityFunction` | data-pack | | | `worldgen/density_function_type` (`Registries.DENSITY_FUNCTION_TYPE`) | `MapCodec<…>` | built-in | | | `worldgen/feature` (`Registries.FEATURE`) | `Feature<…>` | built-in | | | `worldgen/feature_size_type` (`Registries.FEATURE_SIZE_TYPE`) | `FeatureSizeType<…>` | built-in | | | `worldgen/flat_level_generator_preset` (`Registries.FLAT_LEVEL_GENERATOR_PRESET`) | `FlatLevelGeneratorPreset` | data-pack | | | `worldgen/foliage_placer_type` (`Registries.FOLIAGE_PLACER_TYPE`) | `FoliagePlacerType<…>` | built-in | | | `worldgen/material_condition` (`Registries.MATERIAL_CONDITION`) | `MapCodec<…>` | built-in | | | `worldgen/material_rule` (`Registries.MATERIAL_RULE`) | `MapCodec<…>` | built-in | | | `worldgen/multi_noise_biome_source_parameter_list` (`Registries.MULTI_NOISE_BIOME_SOURCE_PARAMETER_LIST`) | `MultiNoiseBiomeSourceParameterList` | data-pack | | | `worldgen/noise` (`Registries.NOISE`) | `NormalNoise.NoiseParameters` | data-pack | | | `worldgen/noise_settings` (`Registries.NOISE_SETTINGS`) | `NoiseGeneratorSettings` | data-pack | | | `worldgen/placed_feature` (`Registries.PLACED_FEATURE`) | `PlacedFeature` | data-pack | | | `worldgen/placement_modifier_type` (`Registries.PLACEMENT_MODIFIER_TYPE`) | `PlacementModifierType<…>` | built-in | | | `worldgen/pool_alias_binding` (`Registries.POOL_ALIAS_BINDING`) | `MapCodec<…>` | built-in | | | `worldgen/processor_list` (`Registries.PROCESSOR_LIST`) | `StructureProcessorList` | data-pack | | | `worldgen/root_placer_type` (`Registries.ROOT_PLACER_TYPE`) | `RootPlacerType<…>` | built-in | | | `worldgen/structure` (`Registries.STRUCTURE`) | `Structure` | data-pack | | | `worldgen/structure_piece` (`Registries.STRUCTURE_PIECE`) | `StructurePieceType` | built-in | | | `worldgen/structure_placement` (`Registries.STRUCTURE_PLACEMENT`) | `StructurePlacementType<…>` | built-in | | | `worldgen/structure_pool_element` (`Registries.STRUCTURE_POOL_ELEMENT`) | `StructurePoolElementType<…>` | built-in | | | `worldgen/structure_processor` (`Registries.STRUCTURE_PROCESSOR`) | `MapCodec<…>` | built-in | | | `worldgen/structure_set` (`Registries.STRUCTURE_SET`) | `StructureSet` | data-pack | | | `worldgen/structure_type` (`Registries.STRUCTURE_TYPE`) | `StructureType<…>` | built-in | | | `worldgen/template_pool` (`Registries.TEMPLATE_POOL`) | `StructureTemplatePool` | data-pack | | | `worldgen/tree_decorator_type` (`Registries.TREE_DECORATOR_TYPE`) | `TreeDecoratorType<…>` | built-in | | | `worldgen/trunk_placer_type` (`Registries.TRUNK_PLACER_TYPE`) | `TrunkPlacerType<…>` | built-in | | | `worldgen/world_preset` (`Registries.WORLD_PRESET`) | `WorldPreset` | data-pack | | | `zombie_nautilus_variant` (`Registries.ZOMBIE_NAUTILUS_VARIANT`) | `ZombieNautilusVariant` | data-pack | yes | --- # Data components > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every `DataComponentType` registered in `DataComponents`. *Persistent* components have a `Codec` and are written to disk; *synced* ones have a `StreamCodec` and are sent to the client; *cache-encoded* ones use the shared `EncoderCache`. A type that is neither persistent nor synced is transient and lives only in memory. See [Data components](../systems/foundations/data-components.md). 111 components | id | value type | persistent | synced | |---|---|---|---| | `custom_data` (`DataComponents.CUSTOM_DATA`) | `CustomData` | yes | | | `max_stack_size` (`DataComponents.MAX_STACK_SIZE`) | `Integer` | yes | yes | | `max_damage` (`DataComponents.MAX_DAMAGE`) | `Integer` | yes | yes | | `damage` (`DataComponents.DAMAGE`) | `Integer` | yes | yes | | `unbreakable` (`DataComponents.UNBREAKABLE`) | `Unit` | yes | yes | | `use_effects` (`DataComponents.USE_EFFECTS`) | `UseEffects` | yes | yes | | `custom_name` (`DataComponents.CUSTOM_NAME`) | `Component` | yes (cached) | yes | | `minimum_attack_charge` (`DataComponents.MINIMUM_ATTACK_CHARGE`) | `Float` | yes | yes | | `damage_type` (`DataComponents.DAMAGE_TYPE`) | `Holder<…>` | yes | yes | | `item_name` (`DataComponents.ITEM_NAME`) | `Component` | yes (cached) | yes | | `item_model` (`DataComponents.ITEM_MODEL`) | `Identifier` | yes (cached) | yes | | `lore` (`DataComponents.LORE`) | `ItemLore` | yes (cached) | yes | | `rarity` (`DataComponents.RARITY`) | `Rarity` | yes | yes | | `enchantments` (`DataComponents.ENCHANTMENTS`) | `ItemEnchantments` | yes (cached) | yes | | `can_place_on` (`DataComponents.CAN_PLACE_ON`) | `AdventureModePredicate` | yes (cached) | yes | | `can_break` (`DataComponents.CAN_BREAK`) | `AdventureModePredicate` | yes (cached) | yes | | `attribute_modifiers` (`DataComponents.ATTRIBUTE_MODIFIERS`) | `ItemAttributeModifiers` | yes (cached) | yes | | `custom_model_data` (`DataComponents.CUSTOM_MODEL_DATA`) | `CustomModelData` | yes | yes | | `tooltip_display` (`DataComponents.TOOLTIP_DISPLAY`) | `TooltipDisplay` | yes (cached) | yes | | `repair_cost` (`DataComponents.REPAIR_COST`) | `Integer` | yes | yes | | `creative_slot_lock` (`DataComponents.CREATIVE_SLOT_LOCK`) | `Unit` | | yes | | `enchantment_glint_override` (`DataComponents.ENCHANTMENT_GLINT_OVERRIDE`) | `Boolean` | yes | yes | | `intangible_projectile` (`DataComponents.INTANGIBLE_PROJECTILE`) | `Unit` | yes | | | `food` (`DataComponents.FOOD`) | `FoodProperties` | yes (cached) | yes | | `consumable` (`DataComponents.CONSUMABLE`) | `Consumable` | yes (cached) | yes | | `use_remainder` (`DataComponents.USE_REMAINDER`) | `UseRemainder` | yes (cached) | yes | | `use_cooldown` (`DataComponents.USE_COOLDOWN`) | `UseCooldown` | yes (cached) | yes | | `damage_resistant` (`DataComponents.DAMAGE_RESISTANT`) | `DamageResistant` | yes (cached) | yes | | `tool` (`DataComponents.TOOL`) | `Tool` | yes (cached) | yes | | `weapon` (`DataComponents.WEAPON`) | `Weapon` | yes (cached) | yes | | `attack_range` (`DataComponents.ATTACK_RANGE`) | `AttackRange` | yes (cached) | yes | | `enchantable` (`DataComponents.ENCHANTABLE`) | `Enchantable` | yes (cached) | yes | | `equippable` (`DataComponents.EQUIPPABLE`) | `Equippable` | yes (cached) | yes | | `repairable` (`DataComponents.REPAIRABLE`) | `Repairable` | yes (cached) | yes | | `glider` (`DataComponents.GLIDER`) | `Unit` | yes | yes | | `tooltip_style` (`DataComponents.TOOLTIP_STYLE`) | `Identifier` | yes (cached) | yes | | `death_protection` (`DataComponents.DEATH_PROTECTION`) | `DeathProtection` | yes (cached) | yes | | `blocks_attacks` (`DataComponents.BLOCKS_ATTACKS`) | `BlocksAttacks` | yes (cached) | yes | | `piercing_weapon` (`DataComponents.PIERCING_WEAPON`) | `PiercingWeapon` | yes (cached) | yes | | `kinetic_weapon` (`DataComponents.KINETIC_WEAPON`) | `KineticWeapon` | yes (cached) | yes | | `swing_animation` (`DataComponents.SWING_ANIMATION`) | `SwingAnimation` | yes | yes | | `additional_trade_cost` (`DataComponents.ADDITIONAL_TRADE_COST`) | `Integer` | | yes | | `stored_enchantments` (`DataComponents.STORED_ENCHANTMENTS`) | `ItemEnchantments` | yes (cached) | yes | | `dye` (`DataComponents.DYE`) | `DyeColor` | yes | yes | | `dyed_color` (`DataComponents.DYED_COLOR`) | `DyedItemColor` | yes | yes | | `map_color` (`DataComponents.MAP_COLOR`) | `MapItemColor` | yes | yes | | `map_id` (`DataComponents.MAP_ID`) | `MapId` | yes | yes | | `map_decorations` (`DataComponents.MAP_DECORATIONS`) | `MapDecorations` | yes (cached) | | | `map_post_processing` (`DataComponents.MAP_POST_PROCESSING`) | `MapPostProcessing` | | yes | | `charged_projectiles` (`DataComponents.CHARGED_PROJECTILES`) | `ChargedProjectiles` | yes (cached) | yes | | `bundle_contents` (`DataComponents.BUNDLE_CONTENTS`) | `BundleContents` | yes (cached) | yes | | `potion_contents` (`DataComponents.POTION_CONTENTS`) | `PotionContents` | yes (cached) | yes | | `potion_duration_scale` (`DataComponents.POTION_DURATION_SCALE`) | `Float` | yes (cached) | yes | | `suspicious_stew_effects` (`DataComponents.SUSPICIOUS_STEW_EFFECTS`) | `SuspiciousStewEffects` | yes (cached) | yes | | `writable_book_content` (`DataComponents.WRITABLE_BOOK_CONTENT`) | `WritableBookContent` | yes (cached) | yes | | `written_book_content` (`DataComponents.WRITTEN_BOOK_CONTENT`) | `WrittenBookContent` | yes (cached) | yes | | `trim` (`DataComponents.TRIM`) | `ArmorTrim` | yes (cached) | yes | | `debug_stick_state` (`DataComponents.DEBUG_STICK_STATE`) | `DebugStickState` | yes (cached) | | | `entity_data` (`DataComponents.ENTITY_DATA`) | `TypedEntityData<…>` | yes | yes | | `bucket_entity_data` (`DataComponents.BUCKET_ENTITY_DATA`) | `CustomData` | yes | yes | | `block_entity_data` (`DataComponents.BLOCK_ENTITY_DATA`) | `TypedEntityData<…>` | yes | yes | | `instrument` (`DataComponents.INSTRUMENT`) | `InstrumentComponent` | yes (cached) | yes | | `provides_trim_material` (`DataComponents.PROVIDES_TRIM_MATERIAL`) | `Holder<…>` | yes (cached) | yes | | `ominous_bottle_amplifier` (`DataComponents.OMINOUS_BOTTLE_AMPLIFIER`) | `OminousBottleAmplifier` | yes | yes | | `jukebox_playable` (`DataComponents.JUKEBOX_PLAYABLE`) | `JukeboxPlayable` | yes | yes | | `provides_banner_patterns` (`DataComponents.PROVIDES_BANNER_PATTERNS`) | `HolderSet<…>` | yes (cached) | yes | | `recipes` (`DataComponents.RECIPES`) | `List<…>` | yes (cached) | | | `lodestone_tracker` (`DataComponents.LODESTONE_TRACKER`) | `LodestoneTracker` | yes (cached) | yes | | `firework_explosion` (`DataComponents.FIREWORK_EXPLOSION`) | `FireworkExplosion` | yes (cached) | yes | | `fireworks` (`DataComponents.FIREWORKS`) | `Fireworks` | yes (cached) | yes | | `profile` (`DataComponents.PROFILE`) | `ResolvableProfile` | yes (cached) | yes | | `note_block_sound` (`DataComponents.NOTE_BLOCK_SOUND`) | `Identifier` | yes | yes | | `banner_patterns` (`DataComponents.BANNER_PATTERNS`) | `BannerPatternLayers` | yes (cached) | yes | | `base_color` (`DataComponents.BASE_COLOR`) | `DyeColor` | yes | yes | | `pot_decorations` (`DataComponents.POT_DECORATIONS`) | `PotDecorations` | yes (cached) | yes | | `container` (`DataComponents.CONTAINER`) | `ItemContainerContents` | yes (cached) | yes | | `block_state` (`DataComponents.BLOCK_STATE`) | `BlockItemStateProperties` | yes (cached) | yes | | `bees` (`DataComponents.BEES`) | `Bees` | yes (cached) | yes | | `sulfur_cube_content` (`DataComponents.SULFUR_CUBE_CONTENT`) | `SulfurCubeContent` | yes (cached) | yes | | `lock` (`DataComponents.LOCK`) | `LockCode` | yes | | | `container_loot` (`DataComponents.CONTAINER_LOOT`) | `SeededContainerLoot` | yes | | | `break_sound` (`DataComponents.BREAK_SOUND`) | `Holder<…>` | yes (cached) | yes | | `villager/variant` (`DataComponents.VILLAGER_VARIANT`) | `Holder<…>` | yes | yes | | `wolf/variant` (`DataComponents.WOLF_VARIANT`) | `Holder<…>` | yes | yes | | `wolf/sound_variant` (`DataComponents.WOLF_SOUND_VARIANT`) | `Holder<…>` | yes | yes | | `wolf/collar` (`DataComponents.WOLF_COLLAR`) | `DyeColor` | yes | yes | | `fox/variant` (`DataComponents.FOX_VARIANT`) | `Fox.Variant` | yes | yes | | `salmon/size` (`DataComponents.SALMON_SIZE`) | `Salmon.Variant` | yes | yes | | `parrot/variant` (`DataComponents.PARROT_VARIANT`) | `Parrot.Variant` | yes | yes | | `tropical_fish/pattern` (`DataComponents.TROPICAL_FISH_PATTERN`) | `TropicalFish.Pattern` | yes | yes | | `tropical_fish/base_color` (`DataComponents.TROPICAL_FISH_BASE_COLOR`) | `DyeColor` | yes | yes | | `tropical_fish/pattern_color` (`DataComponents.TROPICAL_FISH_PATTERN_COLOR`) | `DyeColor` | yes | yes | | `mooshroom/variant` (`DataComponents.MOOSHROOM_VARIANT`) | `MushroomCow.Variant` | yes | yes | | `rabbit/variant` (`DataComponents.RABBIT_VARIANT`) | `Rabbit.Variant` | yes | yes | | `pig/variant` (`DataComponents.PIG_VARIANT`) | `Holder<…>` | yes | yes | | `pig/sound_variant` (`DataComponents.PIG_SOUND_VARIANT`) | `Holder<…>` | yes | yes | | `cow/variant` (`DataComponents.COW_VARIANT`) | `Holder<…>` | yes | yes | | `cow/sound_variant` (`DataComponents.COW_SOUND_VARIANT`) | `Holder<…>` | yes | yes | | `chicken/variant` (`DataComponents.CHICKEN_VARIANT`) | `Holder<…>` | yes | yes | | `chicken/sound_variant` (`DataComponents.CHICKEN_SOUND_VARIANT`) | `Holder<…>` | yes | yes | | `zombie_nautilus/variant` (`DataComponents.ZOMBIE_NAUTILUS_VARIANT`) | `Holder<…>` | yes | yes | | `frog/variant` (`DataComponents.FROG_VARIANT`) | `Holder<…>` | yes | yes | | `horse/variant` (`DataComponents.HORSE_VARIANT`) | `Variant` | yes | yes | | `painting/variant` (`DataComponents.PAINTING_VARIANT`) | `Holder<…>` | yes | yes | | `llama/variant` (`DataComponents.LLAMA_VARIANT`) | `Llama.Variant` | yes | yes | | `axolotl/variant` (`DataComponents.AXOLOTL_VARIANT`) | `Axolotl.Variant` | yes | yes | | `cat/variant` (`DataComponents.CAT_VARIANT`) | `Holder<…>` | yes | yes | | `cat/sound_variant` (`DataComponents.CAT_SOUND_VARIANT`) | `Holder<…>` | yes | yes | | `cat/collar` (`DataComponents.CAT_COLLAR`) | `DyeColor` | yes | yes | | `sheep/color` (`DataComponents.SHEEP_COLOR`) | `DyeColor` | yes | yes | | `shulker/color` (`DataComponents.SHULKER_COLOR`) | `DyeColor` | yes | yes | --- # Game rules > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every rule declared in `GameRules`, with its category (`GameRuleCategory`) and default. Integer rules list their bounds and any feature gate after the default. Values live in a `GameRuleMap` — a `SavedData` at *data/minecraft/game_rules.dat*, one set for the whole server rather than one per level. See [Level data and rules](level-data-and-rules.md). 59 rules | rule | type | category | default | |---|---|---|---| | `command_block_output` (`GameRules.COMMAND_BLOCK_OUTPUT`) | Boolean | chat | `true` | | `log_admin_commands` (`GameRules.LOG_ADMIN_COMMANDS`) | Boolean | chat | `true` | | `send_command_feedback` (`GameRules.SEND_COMMAND_FEEDBACK`) | Boolean | chat | `true` | | `show_advancement_messages` (`GameRules.SHOW_ADVANCEMENT_MESSAGES`) | Boolean | chat | `true` | | `show_death_messages` (`GameRules.SHOW_DEATH_MESSAGES`) | Boolean | chat | `true` | | `block_drops` (`GameRules.BLOCK_DROPS`) | Boolean | drops | `true` | | `block_explosion_drop_decay` (`GameRules.BLOCK_EXPLOSION_DROP_DECAY`) | Boolean | drops | `true` | | `entity_drops` (`GameRules.ENTITY_DROPS`) | Boolean | drops | `true` | | `mob_drops` (`GameRules.MOB_DROPS`) | Boolean | drops | `true` | | `mob_explosion_drop_decay` (`GameRules.MOB_EXPLOSION_DROP_DECAY`) | Boolean | drops | `true` | | `projectiles_can_break_blocks` (`GameRules.PROJECTILES_CAN_BREAK_BLOCKS`) | Boolean | drops | `true` | | `tnt_explosion_drop_decay` (`GameRules.TNT_EXPLOSION_DROP_DECAY`) | Boolean | drops | `false` | | `allow_entering_nether_using_portals` (`GameRules.ALLOW_ENTERING_NETHER_USING_PORTALS`) | Boolean | misc | `true` | | `command_blocks_work` (`GameRules.COMMAND_BLOCKS_WORK`) | Boolean | misc | `true` | | `global_sound_events` (`GameRules.GLOBAL_SOUND_EVENTS`) | Boolean | misc | `true` | | `max_block_modifications` (`GameRules.MAX_BLOCK_MODIFICATIONS`) | Integer | misc | `32768 (min 1)` | | `max_command_forks` (`GameRules.MAX_COMMAND_FORKS`) | Integer | misc | `65536 (min 0)` | | `max_command_sequence_length` (`GameRules.MAX_COMMAND_SEQUENCE_LENGTH`) | Integer | misc | `65536 (min 0)` | | `max_minecart_speed` (`GameRules.MAX_MINECART_SPEED`) | Integer | misc | `8 (min 1, max 1000, requires FeatureFlags.MINECART_IMPROVEMENTS)` | | `reduced_debug_info` (`GameRules.REDUCED_DEBUG_INFO`) | Boolean | misc | `false` | | `spawner_blocks_work` (`GameRules.SPAWNER_BLOCKS_WORK`) | Boolean | misc | `true` | | `tnt_explodes` (`GameRules.TNT_EXPLODES`) | Boolean | misc | `true` | | `forgive_dead_players` (`GameRules.FORGIVE_DEAD_PLAYERS`) | Boolean | mobs | `true` | | `max_entity_cramming` (`GameRules.MAX_ENTITY_CRAMMING`) | Integer | mobs | `24 (min 0)` | | `mob_griefing` (`GameRules.MOB_GRIEFING`) | Boolean | mobs | `true` | | `raids` (`GameRules.RAIDS`) | Boolean | mobs | `true` | | `universal_anger` (`GameRules.UNIVERSAL_ANGER`) | Boolean | mobs | `false` | | `drowning_damage` (`GameRules.DROWNING_DAMAGE`) | Boolean | player | `true` | | `elytra_movement_check` (`GameRules.ELYTRA_MOVEMENT_CHECK`) | Boolean | player | `true` | | `ender_pearls_vanish_on_death` (`GameRules.ENDER_PEARLS_VANISH_ON_DEATH`) | Boolean | player | `true` | | `fall_damage` (`GameRules.FALL_DAMAGE`) | Boolean | player | `true` | | `fire_damage` (`GameRules.FIRE_DAMAGE`) | Boolean | player | `true` | | `freeze_damage` (`GameRules.FREEZE_DAMAGE`) | Boolean | player | `true` | | `immediate_respawn` (`GameRules.IMMEDIATE_RESPAWN`) | Boolean | player | `false` | | `keep_inventory` (`GameRules.KEEP_INVENTORY`) | Boolean | player | `false` | | `limited_crafting` (`GameRules.LIMITED_CRAFTING`) | Boolean | player | `false` | | `locator_bar` (`GameRules.LOCATOR_BAR`) | Boolean | player | `true` | | `natural_health_regeneration` (`GameRules.NATURAL_HEALTH_REGENERATION`) | Boolean | player | `true` | | `player_movement_check` (`GameRules.PLAYER_MOVEMENT_CHECK`) | Boolean | player | `true` | | `players_nether_portal_creative_delay` (`GameRules.PLAYERS_NETHER_PORTAL_CREATIVE_DELAY`) | Integer | player | `0 (min 0)` | | `players_nether_portal_default_delay` (`GameRules.PLAYERS_NETHER_PORTAL_DEFAULT_DELAY`) | Integer | player | `80 (min 0)` | | `players_sleeping_percentage` (`GameRules.PLAYERS_SLEEPING_PERCENTAGE`) | Integer | player | `100 (min 0)` | | `pvp` (`GameRules.PVP`) | Boolean | player | `true` | | `respawn_radius` (`GameRules.RESPAWN_RADIUS`) | Integer | player | `10 (min 0)` | | `spectators_generate_chunks` (`GameRules.SPECTATORS_GENERATE_CHUNKS`) | Boolean | player | `true` | | `spawn_mobs` (`GameRules.SPAWN_MOBS`) | Boolean | spawning | `true` | | `spawn_monsters` (`GameRules.SPAWN_MONSTERS`) | Boolean | spawning | `true` | | `spawn_patrols` (`GameRules.SPAWN_PATROLS`) | Boolean | spawning | `true` | | `spawn_phantoms` (`GameRules.SPAWN_PHANTOMS`) | Boolean | spawning | `true` | | `spawn_wandering_traders` (`GameRules.SPAWN_WANDERING_TRADERS`) | Boolean | spawning | `true` | | `spawn_wardens` (`GameRules.SPAWN_WARDENS`) | Boolean | spawning | `true` | | `advance_time` (`GameRules.ADVANCE_TIME`) | Boolean | updates | `true` | | `advance_weather` (`GameRules.ADVANCE_WEATHER`) | Boolean | updates | `true` | | `fire_spread_radius_around_player` (`GameRules.FIRE_SPREAD_RADIUS_AROUND_PLAYER`) | Integer | updates | `128 (min -1)` | | `lava_source_conversion` (`GameRules.LAVA_SOURCE_CONVERSION`) | Boolean | updates | `false` | | `max_snow_accumulation_height` (`GameRules.MAX_SNOW_ACCUMULATION_HEIGHT`) | Integer | updates | `1 (min 0, max 8)` | | `random_tick_speed` (`GameRules.RANDOM_TICK_SPEED`) | Integer | updates | `3 (min 0)` | | `spread_vines` (`GameRules.SPREAD_VINES`) | Boolean | updates | `true` | | `water_source_conversion` (`GameRules.WATER_SOURCE_CONVERSION`) | Boolean | updates | `true` | --- # Attributes > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every attribute registered in `Attributes`. All of them are `RangedAttribute`s, so every one clamps to its range once, at the end of `AttributeInstance.calculateValue`. **Syncable** attributes are the only ones `ClientboundUpdateAttributesPacket` ever carries: a mutation to one of the others changes the server's number and never reaches the client at all. The sentiment decides tooltip colour and nothing else. Defaults here are the registry's, and most entity types override them in their own `AttributeSupplier`. See [Attributes](../systems/entities/attributes.md). 40 attributes, 32 syncable and 8 not | id | constant | default | min | max | syncable | sentiment | |---|---|---:|---:|---:|---|---| | `air_drag_modifier` | `Attributes.AIR_DRAG_MODIFIER` | 1 | 0 | 2048 | yes | positive | | `armor` | `Attributes.ARMOR` | 0 | 0 | 30 | yes | positive | | `armor_toughness` | `Attributes.ARMOR_TOUGHNESS` | 0 | 0 | 20 | yes | positive | | `attack_damage` | `Attributes.ATTACK_DAMAGE` | 2 | 0 | 2048 | | positive | | `attack_knockback` | `Attributes.ATTACK_KNOCKBACK` | 0 | 0 | 5 | | positive | | `attack_speed` | `Attributes.ATTACK_SPEED` | 4 | 0 | 1024 | yes | positive | | `below_name_distance` | `Attributes.BELOW_NAME_DISTANCE` | 10 | 0 | 512 | yes | positive | | `block_break_speed` | `Attributes.BLOCK_BREAK_SPEED` | 1 | 0 | 1024 | yes | positive | | `block_interaction_range` | `Attributes.BLOCK_INTERACTION_RANGE` | 4.5 | 0 | 64 | yes | positive | | `bounciness` | `Attributes.BOUNCINESS` | 0 | 0 | 1 | yes | positive | | `burning_time` | `Attributes.BURNING_TIME` | 1 | 0 | 1024 | yes | negative | | `camera_distance` | `Attributes.CAMERA_DISTANCE` | 4 | 0 | 32 | yes | positive | | `entity_interaction_range` | `Attributes.ENTITY_INTERACTION_RANGE` | 3 | 0 | 64 | yes | positive | | `explosion_knockback_resistance` | `Attributes.EXPLOSION_KNOCKBACK_RESISTANCE` | 0 | 0 | 1 | yes | positive | | `fall_damage_multiplier` | `Attributes.FALL_DAMAGE_MULTIPLIER` | 1 | 0 | 100 | yes | negative | | `flying_speed` | `Attributes.FLYING_SPEED` | 0.4 | 0 | 1024 | yes | positive | | `follow_range` | `Attributes.FOLLOW_RANGE` | 32 | 0 | 2048 | | positive | | `friction_modifier` | `Attributes.FRICTION_MODIFIER` | 1 | 0 | 2048 | yes | positive | | `gravity` | `Attributes.GRAVITY` | 0.08 | -1 | 1 | yes | neutral | | `jump_strength` | `Attributes.JUMP_STRENGTH` | 0.42 | 0 | 32 | yes | positive | | `knockback_resistance` | `Attributes.KNOCKBACK_RESISTANCE` | 0 | -2 | 1 | | positive | | `luck` | `Attributes.LUCK` | 0 | -1024 | 1024 | yes | positive | | `max_absorption` | `Attributes.MAX_ABSORPTION` | 0 | 0 | 2048 | yes | positive | | `max_health` | `Attributes.MAX_HEALTH` | 20 | 1 | 1024 | yes | positive | | `mining_efficiency` | `Attributes.MINING_EFFICIENCY` | 0 | 0 | 1024 | yes | positive | | `movement_efficiency` | `Attributes.MOVEMENT_EFFICIENCY` | 0 | 0 | 1 | yes | positive | | `movement_speed` | `Attributes.MOVEMENT_SPEED` | 0.7 | 0 | 1024 | yes | positive | | `name_tag_distance` | `Attributes.NAME_TAG_DISTANCE` | 64 | 0 | 512 | yes | positive | | `oxygen_bonus` | `Attributes.OXYGEN_BONUS` | 0 | 0 | 1024 | yes | positive | | `safe_fall_distance` | `Attributes.SAFE_FALL_DISTANCE` | 3 | -1024 | 1024 | yes | positive | | `scale` | `Attributes.SCALE` | 1 | 0.0625 | 16 | yes | neutral | | `sneaking_speed` | `Attributes.SNEAKING_SPEED` | 0.3 | 0 | 1 | yes | positive | | `spawn_reinforcements` | `Attributes.SPAWN_REINFORCEMENTS_CHANCE` | 0 | 0 | 1 | | positive | | `step_height` | `Attributes.STEP_HEIGHT` | 0.6 | 0 | 10 | yes | positive | | `submerged_mining_speed` | `Attributes.SUBMERGED_MINING_SPEED` | 0.2 | 0 | 20 | yes | positive | | `sweeping_damage_ratio` | `Attributes.SWEEPING_DAMAGE_RATIO` | 0 | 0 | 1 | yes | positive | | `tempt_range` | `Attributes.TEMPT_RANGE` | 10 | 0 | 2048 | | positive | | `water_movement_efficiency` | `Attributes.WATER_MOVEMENT_EFFICIENCY` | 0 | 0 | 1 | yes | positive | | `waypoint_receive_range` | `Attributes.WAYPOINT_RECEIVE_RANGE` | 0 | 0 | 60000000 | | neutral | | `waypoint_transmit_range` | `Attributes.WAYPOINT_TRANSMIT_RANGE` | 0 | 0 | 60000000 | | neutral | --- # Entity data serializers > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every `EntityDataSerializer` in `EntityDataSerializers`, in **registration order, which is the wire id** — `EntityDataSerializers.registerSerializer` pushes each one into a `CrudeIncrementalIntIdentityHashBiMap` that hands out the next int. A `SynchedEntityData.DataValue` on the wire is an unsigned byte accessor id, this var-int, and the encoded value. *For value type* marks the ones built by `EntityDataSerializer.forValueType`, the immutable case where `EntityDataSerializer.copy` is identity. See [Synched entity data](../systems/entities/synched-entity-data.md). 43 serializers, wire ids 0 to 42 | id | constant | value type | built by | |---:|---|---|---| | 0 | `EntityDataSerializers.BYTE` | `Byte` | for value type | | 1 | `EntityDataSerializers.INT` | `Integer` | for value type | | 2 | `EntityDataSerializers.LONG` | `Long` | for value type | | 3 | `EntityDataSerializers.FLOAT` | `Float` | for value type | | 4 | `EntityDataSerializers.STRING` | `String` | for value type | | 5 | `EntityDataSerializers.COMPONENT` | `Component` | for value type | | 6 | `EntityDataSerializers.OPTIONAL_COMPONENT` | `Optional` | for value type | | 7 | `EntityDataSerializers.ITEM_STACK` | `ItemStack` | an anonymous subclass | | 8 | `EntityDataSerializers.BOOLEAN` | `Boolean` | for value type | | 9 | `EntityDataSerializers.ROTATIONS` | `Rotations` | for value type | | 10 | `EntityDataSerializers.BLOCK_POS` | `BlockPos` | for value type | | 11 | `EntityDataSerializers.OPTIONAL_BLOCK_POS` | `Optional` | for value type | | 12 | `EntityDataSerializers.DIRECTION` | `Direction` | for value type | | 13 | `EntityDataSerializers.OPTIONAL_LIVING_ENTITY_REFERENCE` | `Optional>` | for value type | | 14 | `EntityDataSerializers.BLOCK_STATE` | `BlockState` | for value type | | 15 | `EntityDataSerializers.OPTIONAL_BLOCK_STATE` | `Optional` | for value type | | 16 | `EntityDataSerializers.PARTICLE` | `ParticleOptions` | for value type | | 17 | `EntityDataSerializers.PARTICLES` | `List` | for value type | | 18 | `EntityDataSerializers.VILLAGER_DATA` | `VillagerData` | for value type | | 19 | `EntityDataSerializers.OPTIONAL_UNSIGNED_INT` | `OptionalInt` | for value type | | 20 | `EntityDataSerializers.POSE` | `Pose` | for value type | | 21 | `EntityDataSerializers.CAT_VARIANT` | `Holder` | for value type | | 22 | `EntityDataSerializers.CAT_SOUND_VARIANT` | `Holder` | for value type | | 23 | `EntityDataSerializers.COW_VARIANT` | `Holder` | for value type | | 24 | `EntityDataSerializers.COW_SOUND_VARIANT` | `Holder` | for value type | | 25 | `EntityDataSerializers.WOLF_VARIANT` | `Holder` | for value type | | 26 | `EntityDataSerializers.WOLF_SOUND_VARIANT` | `Holder` | for value type | | 27 | `EntityDataSerializers.FROG_VARIANT` | `Holder` | for value type | | 28 | `EntityDataSerializers.PIG_VARIANT` | `Holder` | for value type | | 29 | `EntityDataSerializers.PIG_SOUND_VARIANT` | `Holder` | for value type | | 30 | `EntityDataSerializers.CHICKEN_VARIANT` | `Holder` | for value type | | 31 | `EntityDataSerializers.CHICKEN_SOUND_VARIANT` | `Holder` | for value type | | 32 | `EntityDataSerializers.ZOMBIE_NAUTILUS_VARIANT` | `Holder` | for value type | | 33 | `EntityDataSerializers.OPTIONAL_GLOBAL_POS` | `Optional` | for value type | | 34 | `EntityDataSerializers.PAINTING_VARIANT` | `Holder` | for value type | | 35 | `EntityDataSerializers.SNIFFER_STATE` | `Sniffer.State` | for value type | | 36 | `EntityDataSerializers.ARMADILLO_STATE` | `Armadillo.ArmadilloState` | for value type | | 37 | `EntityDataSerializers.COPPER_GOLEM_STATE` | `CopperGolemState` | for value type | | 38 | `EntityDataSerializers.WEATHERING_COPPER_STATE` | `WeatheringCopper.WeatherState` | for value type | | 39 | `EntityDataSerializers.VECTOR3` | `Vector3fc` | for value type | | 40 | `EntityDataSerializers.QUATERNION` | `Quaternionfc` | for value type | | 41 | `EntityDataSerializers.RESOLVABLE_PROFILE` | `ResolvableProfile` | for value type | | 42 | `EntityDataSerializers.HUMANOID_ARM` | `HumanoidArm` | for value type | --- # Enchantment hooks > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every public entry point of `EnchantmentHelper`, with the classes that call it. The enchantment package barely calls anything and everything calls it, so this table is the system's real interface: each row is a moment at which some other system asks whether an enchantment wants to change what happens. Callers are the declaring files, one per class, excluding `EnchantmentHelper` itself. See [Enchantments](../systems/items/enchantments.md) for what an enchantment is and [Enchanting](../systems/items/enchanting.md) for the selection half. 50 entry points, 47 of them called from outside the class | entry point | overloads | called from | |---|---:|---| | `EnchantmentHelper.canStoreEnchantments` | 1 | `AnvilMenu` | | `EnchantmentHelper.createBook` | 1 | `CreativeModeTabs` | | `EnchantmentHelper.doPostAttackEffects` | 1 | `AbstractCubeMob`, `AbstractWindCharge`, `Bee`, `ChargeAttack`, `EnderDragon`, `EvokerFangs`, `HoglinBase`, `IronGolem`, `LargeFireball`, `LivingEntity`, `LlamaSpit`, `Mob`, `Player`, `RamTarget`, `ShulkerBullet`, `SmallFireball`, `WitherSkull` | | `EnchantmentHelper.doPostAttackEffectsWithItemSource` | 1 | `AbstractArrow`, `Player` | | `EnchantmentHelper.doPostAttackEffectsWithItemSourceOnBreak` | 1 | `ThrownTrident` | | `EnchantmentHelper.doPostPiercingAttackEffects` | 1 | `LivingEntity` | | `EnchantmentHelper.enchantItem` | 2 | `EnchantWithLevelsFunction` | | `EnchantmentHelper.enchantItemFromProvider` | 1 | `EnderMan`, `Mob`, `Pillager`, `SkeletonTrapGoal`, `Vindicator` | | `EnchantmentHelper.filterCompatibleEnchantments` | 1 | *nothing outside the class* | | `EnchantmentHelper.forEachModifier` | 2 | `ItemStack` | | `EnchantmentHelper.getAvailableEnchantmentResults` | 1 | *nothing outside the class* | | `EnchantmentHelper.getDamageProtection` | 1 | `LivingEntity` | | `EnchantmentHelper.getEnchantmentCost` | 1 | `EnchantmentMenu` | | `EnchantmentHelper.getEnchantmentLevel` | 1 | `EnchantedCountIncreaseFunction`, `LootItemRandomChanceWithEnchantedBonusCondition` | | `EnchantmentHelper.getEnchantmentsForCrafting` | 1 | `AnvilMenu`, `EnchantCommand`, `GrindstoneMenu`, `RepairItemRecipe` | | `EnchantmentHelper.getFishingLuckBonus` | 1 | `FishingRodItem` | | `EnchantmentHelper.getFishingTimeReduction` | 1 | `FishingRodItem` | | `EnchantmentHelper.getHighestLevel` | 1 | *nothing outside the class* | | `EnchantmentHelper.getItemEnchantmentLevel` | 1 | `ApplyBonusCount`, `BonusLevelTableCondition` | | `EnchantmentHelper.getPiercingCount` | 1 | `AbstractArrow` | | `EnchantmentHelper.getRandomItemWith` | 1 | `ExperienceOrb` | | `EnchantmentHelper.getTridentReturnToOwnerAcceleration` | 1 | `ThrownTrident` | | `EnchantmentHelper.getTridentSpinAttackStrength` | 1 | `TridentItem` | | `EnchantmentHelper.has` | 1 | `AbstractHorse`, `Allay`, `ArmorSlot`, `ArmorStand`, `Equippable`, `Mob`, `Piglin`, `Player`, `ZombieVillager` | | `EnchantmentHelper.hasAnyEnchantments` | 1 | `GrindstoneMenu` | | `EnchantmentHelper.hasTag` | 1 | `BeehiveBlock`, `DecoratedPotBlock`, `IceBlock`, `InfestedBlock` | | `EnchantmentHelper.isEnchantmentCompatible` | 1 | `EnchantCommand` | | `EnchantmentHelper.isImmuneToDamage` | 1 | `LivingEntity` | | `EnchantmentHelper.modifyArmorEffectiveness` | 1 | `CombatRules` | | `EnchantmentHelper.modifyCrossbowChargingTime` | 1 | `CrossbowItem` | | `EnchantmentHelper.modifyDamage` | 1 | `AbstractArrow`, `LivingEntity`, `Mob`, `ServerPlayer`, `ThrownTrident` | | `EnchantmentHelper.modifyDurabilityToRepairFromXp` | 1 | `ExperienceOrb` | | `EnchantmentHelper.modifyFallBasedDamage` | 1 | `MaceItem` | | `EnchantmentHelper.modifyKnockback` | 1 | `AbstractArrow`, `LivingEntity` | | `EnchantmentHelper.onHitBlock` | 1 | `AbstractArrow`, `ServerPlayerGameMode`, `ThrownTrident` | | `EnchantmentHelper.onProjectileSpawned` | 1 | `Projectile` | | `EnchantmentHelper.pickHighestLevel` | 1 | `CrossbowItem`, `TridentItem` | | `EnchantmentHelper.processAmmoUse` | 1 | `ProjectileWeaponItem` | | `EnchantmentHelper.processBlockExperience` | 1 | `Block` | | `EnchantmentHelper.processDurabilityChange` | 1 | `ItemStack` | | `EnchantmentHelper.processEquipmentDropChance` | 1 | `Mob` | | `EnchantmentHelper.processMobExperience` | 1 | `LivingEntity` | | `EnchantmentHelper.processProjectileCount` | 1 | `ProjectileWeaponItem` | | `EnchantmentHelper.processProjectileSpread` | 1 | `ProjectileWeaponItem` | | `EnchantmentHelper.runLocationChangedEffects` | 2 | `LivingEntity`, `ServerPlayer` | | `EnchantmentHelper.selectEnchantment` | 1 | `EnchantmentMenu`, `EnchantmentsByCost`, `EnchantmentsByCostWithDifficulty` | | `EnchantmentHelper.setEnchantments` | 1 | `AnvilMenu` | | `EnchantmentHelper.stopLocationBasedEffects` | 2 | `LivingEntity`, `ServerPlayer` | | `EnchantmentHelper.tickEffects` | 1 | `LivingEntity` | | `EnchantmentHelper.updateEnchantments` | 1 | `GrindstoneMenu`, `ItemStack`, `RepairItemRecipe`, `SetEnchantmentsFunction` | --- # Loot context parameter sets > Generated from the **26.2** decompile by `tools/gen_reference.py`. Do not edit by hand. Every `ContextKeySet` registered in `LootContextParamSets`, with the keys its `ContextKeySet.Builder` declared. The set belongs to the **caller**, not to the loot table: `ContextMap.Builder.create` throws both on a required key that is absent and on a key the set does not declare at all, so this table is the contract each call site has to satisfy. A required key can be read with `LootContext.getParameter`, an optional one only with `LootContext.getOptionalParameter`. Twelve of these twenty-six sets never roll a `LootTable` at all — the engine is older and wider than the loot package. See [Contexts and predicates](../systems/items/contexts-and-predicates.md). 26 parameter sets | set | id | required | optional | |---|---|---|---| | `LootContextParamSets.ADVANCEMENT_ENTITY` | *advancement_entity* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ORIGIN` | — | | `LootContextParamSets.ADVANCEMENT_LOCATION` | *advancement_location* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ORIGIN`, `LootContextParams.TOOL`, `LootContextParams.BLOCK_STATE` | — | | `LootContextParamSets.ADVANCEMENT_REWARD` | *advancement_reward* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ORIGIN` | — | | `LootContextParamSets.ARCHAEOLOGY` | *archaeology* | `LootContextParams.ORIGIN`, `LootContextParams.THIS_ENTITY`, `LootContextParams.TOOL` | — | | `LootContextParamSets.PIGLIN_BARTER` | *barter* | `LootContextParams.THIS_ENTITY` | — | | `LootContextParamSets.BLOCK` | *block* | `LootContextParams.BLOCK_STATE`, `LootContextParams.ORIGIN`, `LootContextParams.TOOL` | `LootContextParams.THIS_ENTITY`, `LootContextParams.BLOCK_ENTITY`, `LootContextParams.EXPLOSION_RADIUS` | | `LootContextParamSets.BLOCK_INTERACT` | *block_interact* | `LootContextParams.BLOCK_STATE` | `LootContextParams.BLOCK_ENTITY`, `LootContextParams.INTERACTING_ENTITY`, `LootContextParams.TOOL` | | `LootContextParamSets.BLOCK_USE` | *block_use* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ORIGIN`, `LootContextParams.BLOCK_STATE` | — | | `LootContextParamSets.CHEST` | *chest* | `LootContextParams.ORIGIN` | `LootContextParams.THIS_ENTITY` | | `LootContextParamSets.COMMAND` | *command* | `LootContextParams.ORIGIN` | `LootContextParams.THIS_ENTITY` | | `LootContextParamSets.EMPTY` | *empty* | — | — | | `LootContextParamSets.ENCHANTED_DAMAGE` | *enchanted_damage* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ENCHANTMENT_LEVEL`, `LootContextParams.ORIGIN`, `LootContextParams.DAMAGE_SOURCE` | `LootContextParams.DIRECT_ATTACKING_ENTITY`, `LootContextParams.ATTACKING_ENTITY` | | `LootContextParamSets.ENCHANTED_ENTITY` | *enchanted_entity* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ENCHANTMENT_LEVEL`, `LootContextParams.ORIGIN` | — | | `LootContextParamSets.ENCHANTED_ITEM` | *enchanted_item* | `LootContextParams.TOOL`, `LootContextParams.ENCHANTMENT_LEVEL` | — | | `LootContextParamSets.ENCHANTED_LOCATION` | *enchanted_location* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ENCHANTMENT_LEVEL`, `LootContextParams.ORIGIN`, `LootContextParams.ENCHANTMENT_ACTIVE` | — | | `LootContextParamSets.ENTITY` | *entity* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ORIGIN`, `LootContextParams.DAMAGE_SOURCE` | `LootContextParams.ATTACKING_ENTITY`, `LootContextParams.DIRECT_ATTACKING_ENTITY`, `LootContextParams.LAST_DAMAGE_PLAYER` | | `LootContextParamSets.ENTITY_INTERACT` | *entity_interact* | `LootContextParams.TARGET_ENTITY`, `LootContextParams.TOOL` | `LootContextParams.INTERACTING_ENTITY` | | `LootContextParamSets.EQUIPMENT` | *equipment* | `LootContextParams.ORIGIN`, `LootContextParams.THIS_ENTITY` | — | | `LootContextParamSets.FISHING` | *fishing* | `LootContextParams.ORIGIN`, `LootContextParams.TOOL` | `LootContextParams.THIS_ENTITY` | | `LootContextParamSets.ALL_PARAMS` | *generic* | `LootContextParams.THIS_ENTITY`, `LootContextParams.LAST_DAMAGE_PLAYER`, `LootContextParams.DAMAGE_SOURCE`, `LootContextParams.ATTACKING_ENTITY`, `LootContextParams.DIRECT_ATTACKING_ENTITY`, `LootContextParams.ORIGIN`, `LootContextParams.BLOCK_STATE`, `LootContextParams.BLOCK_ENTITY`, `LootContextParams.TOOL`, `LootContextParams.EXPLOSION_RADIUS`, `LootContextParams.ADDITIONAL_COST_COMPONENT_ALLOWED` | — | | `LootContextParamSets.GIFT` | *gift* | `LootContextParams.ORIGIN`, `LootContextParams.THIS_ENTITY` | — | | `LootContextParamSets.HIT_BLOCK` | *hit_block* | `LootContextParams.THIS_ENTITY`, `LootContextParams.ENCHANTMENT_LEVEL`, `LootContextParams.ORIGIN`, `LootContextParams.BLOCK_STATE` | — | | `LootContextParamSets.SELECTOR` | *selector* | `LootContextParams.ORIGIN`, `LootContextParams.THIS_ENTITY` | — | | `LootContextParamSets.SHEARING` | *shearing* | `LootContextParams.ORIGIN`, `LootContextParams.THIS_ENTITY`, `LootContextParams.TOOL` | — | | `LootContextParamSets.VAULT` | *vault* | `LootContextParams.ORIGIN` | `LootContextParams.THIS_ENTITY`, `LootContextParams.TOOL` | | `LootContextParamSets.VILLAGER_TRADE` | *villager_trade* | `LootContextParams.ORIGIN`, `LootContextParams.THIS_ENTITY`, `LootContextParams.ADDITIONAL_COST_COMPONENT_ALLOWED` | — | --- # Block update flags > Verified against **Minecraft 26.2** · Reference · Hand-kept from `Block`'s > *UPDATE_* constants: the ten bits of `Level.setBlock`'s flag word, what > reads each, and the named combinations. The flag word is `Level.setBlock`'s third argument — the last one on the three-argument overload, and followed by an *update limit* on the four-argument one. It is a bit set, tagged in signatures by `Block.UpdateFlags`, an annotation that carries no values of its own. [Blocks and states](../systems/blocks/blocks-and-states.md) draws the tail of `Level.setBlock` as a flowchart whose gates name these bits by number; this is the table behind the numbers, and every other page that passes a flag word — [fluids](../systems/world/fluids.md)' bucket and [pistons and block events](../systems/blocks/pistons-and-block-events.md)' moves among them — means the same bits. | bit | constant | what reads it | |---:|---|---| | 1 | `Block.UPDATE_NEIGHBORS` | the neighbour fan-out in the tail, and the gate on `BlockBehaviour.BlockStateBase.affectNeighborsAfterRemoval` in the chunk write | | 2 | `Block.UPDATE_CLIENTS` | `Level.sendBlockUpdated` — the broadcast on the server, a re-mesh on the client, where it is `LevelExtractor.blockChanged` | | 4 | `Block.UPDATE_INVISIBLE` | suppresses whichever of those the side does | | 8 | `Block.UPDATE_IMMEDIATE` | one place in the game: `LevelExtractor.blockChanged`, which marks the re-mesh as player-caused | | 16 | `Block.UPDATE_KNOWN_SHAPE` | three readers: `Level.setBlock`, where it suppresses all three shape passes; `BlockInput.place`, where it decides whether the state is fixed up against its neighbours before the write; and `WorldGenRegion.setBlock`, where it suppresses the post-processing mark | | 32 | `Block.UPDATE_SUPPRESS_DROPS` | `Block.updateOrDestroy`, whose destroy branch drops resources unless it is set. A one-level flag: it is masked out of the word as it propagates, both to the neighbours and into the recursive write | | 64 | `Block.UPDATE_MOVE_BY_PISTON` | passed on as *movedByPiston*, and lets `BlockBehaviour.BlockStateBase.affectNeighborsAfterRemoval` run without bit 1 | | 128 | `Block.UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE` | `NeighborUpdater.executeShapeUpdate`, which then skips redstone wire | | 256 | `Block.UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS` | suppresses `BlockEntity.preRemoveSideEffects` | | 512 | `Block.UPDATE_SKIP_ON_PLACE` | suppresses `BlockBehaviour.BlockStateBase.onPlace` | The named combinations are `Block.UPDATE_ALL` (3), `Block.UPDATE_ALL_IMMEDIATE` (11, what placement uses), `Block.UPDATE_NONE` (260) and `Block.UPDATE_SKIP_ALL_SIDEEFFECTS` (816). `Block.UPDATE_LIMIT` is also 512, and is not a bit at all — it is the default recursion budget for the shape cascade. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Damage outside `LivingEntity` > Verified against **Minecraft 26.2** · Reference · Hand-kept from > `net/minecraft/world/entity/**`. `Entity.hurtServer` is **abstract**, so every branch has to answer for itself. (`Entity.hurtClient`, the other half of the pair, does have a default: it returns false, and twelve of the rows below inherit it unchanged.) The twenty-one classes are every non-`LivingEntity` class that declares `Entity.hurtServer`, and they answer with rules that share nothing with `LivingEntity`'s — no armour, no invulnerability window, no `CombatTracker`, no death sequence. The lecture that frames them is [damage and death](../systems/entities/damage-and-death.md); this is the per-class table. Two things happen before any of them. `Player.cannotAttack` asks `Entity.isAttackable` and then `Entity.skipAttackInteraction`, either of which can end the swing before the entity is asked anything — `Interaction` uses that hook to record who hit it and `BlockAttachedEntity` to re-enter through `Entity.hurtOrSimulate` with zero damage. `Entity.hurtOrSimulate` is what `Player.attack` calls, and it picks `Entity.hurtServer` or `Entity.hurtClient` off the level; its answer is not "did the hit land" but "was anything damaged", and it is what gates the knockback, the sweep, the durability loss and the hit particles. | class | what it checks first | what it does | returns | |---|---|---|---| | `AreaEffectCloud` | — | nothing | false | | `Display` | — | nothing | false | | `Interaction` | — | nothing; the attacker was already recorded in `Entity.skipAttackInteraction` | false | | `LightningBolt` | — | nothing | false | | `Marker` | — | nothing | false | | `OminousItemSpawner` | — | nothing | false | | `PrimedTnt` | — | nothing: a lit TNT block cannot be shot out of the air | false | | `EvokerFangs` | — | nothing | false | | `EyeOfEnder` | — | nothing | false | | `AbstractHurtingProjectile` | — | nothing — but a fireball or a wind charge is *deflected* before this is reached, since `Player.attack` calls `Player.deflectProjectile` first for anything in `EntityTypeTags.REDIRECTABLE_PROJECTILE`; the rest are unhittable by `Entity.isPickable` | false | | `Projectile` | `Entity.isInvulnerableToBase` | `Entity.markHurt` only, so the client sees a flinch and nothing changes | false | | `FallingBlockEntity` | `Entity.isInvulnerableToBase` | `Entity.markHurt` only | false | | `ExperienceOrb` | `Entity.isInvulnerableToBase` | subtracts the damage from an int of health, `Entity.discard` at zero | true | | `ItemEntity` | `Entity.isInvulnerableToBase`, then a `Mob` source under `GameRules.MOB_GRIEFING`, then `ItemStack.canBeHurtBy` | same int of health, plus `GameEvent.ENTITY_DAMAGE`, and `ItemStack.onDestroyed` before the discard | true | | `BlockAttachedEntity` | `Entity.isInvulnerableToBase`, then a `Mob` source under `GameRules.MOB_GRIEFING` | `Entity.kill`, `Entity.markHurt`, and drops its item — one hit, whatever the amount | true | | `ItemFrame` | `ItemFrame.fixed` gates everything: a fixed frame is hurt only by `DamageTypeTags.BYPASSES_INVULNERABILITY` or a creative player | a non-explosion hit on a frame **holding** something pops the item and stops there; otherwise it falls through to `BlockAttachedEntity` and the frame breaks | true | | `EndCrystal` | `Entity.isInvulnerableToBase`, then **is the source an `EnderDragon`** | removes itself with `Entity.RemovalReason.KILLED` and explodes with power 6 — unless the source was already an explosion — then `EndCrystal.onDestroyedBy` | true | | `ShulkerBullet` | — | plays `SoundEvents.SHULKER_BULLET_HURT`, spawns fifteen `ParticleTypes.CRIT`, destroys itself | true | | `EnderDragonPart` | `Entity.isInvulnerableToBase` | forwards the whole call to `EnderDragon.hurt` with itself as the part that was hit | the parent's answer | | `VehicleEntity` | already removed, then `Entity.isInvulnerableToBase` | flips `VehicleEntity.getHurtDir`, sets ten ticks of hurt time, adds *damage × 10* to `VehicleEntity.getDamage`, and destroys past 40 — a creative player skips to `Entity.discard` | true | | `MinecartTNT` | a **burning** `AbstractArrow` as the direct entity explodes it, scaled by the arrow's speed | on that path, nothing else: the explosion calls `Entity.discard`, so `VehicleEntity.hurtServer` returns at its already-removed test. Any other source falls through to everything `VehicleEntity` does | true | Six patterns account for all of it: *nothing happens* (ten classes), *a flinch and nothing else* (two), *an int of health with no armour and no window* (two), *one hit destroys* (four), *an accumulator* (two), and *forward it to something else* (one, the dragon part). The classes that read the damage **amount** at all are the pair with an int of health, the accumulator pair, and `EnderDragonPart`, which hands the number to the dragon; everything else is a yes-or-no. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # What the HUD draws, and when > Verified against **Minecraft 26.2** · Reference · Hand-kept from > `Hud.extractRenderState` and `Gui.extractRenderState`. The HUD is one ordered method, and almost every element in it is behind a condition — the contextual bar is the exception, recorded unconditionally and made to draw nothing by an empty state object instead. The lecture that frames this is [the HUD](../systems/client/hud.md); this is the table it is built on, in **record order** — which is also the order things appear in front of each other, since [the GUI render tree](../systems/client/the-gui-render-tree.md) infers layering from call order and bounding boxes. Three gates sit above everything below and none of them belongs to `Hud`: `GameRenderer.extract` computes them — resources loaded, the frame advancing game time, a level existing — and `Gui.extractRenderState` is what applies them, calling into `Hud` only when they hold. `Hud.extractRenderState` then short-circuits entirely while a `LevelLoadingScreen` is up — but it publishes `GuiRenderState.isHudHidden` **before** that check, so the flag is always current even when nothing is recorded. *Hidden* in the last column means `Hud.isHidden`, which `Options.keyToggleGui` — F1 — flips. | # | element | recorded by | hidden by F1? | its own condition | |---:|---|---|---|---| | 1 | vignette | `Hud.extractVignette` | yes | `Options.vignette` is on | | 2 | spyglass overlay | `Hud.extractSpyglassOverlay` | yes | first-person camera **and** the player is scoping | | 3 | equipment camera overlay | `Hud.extractTextureOverlay` | yes | first-person, not scoping, and some equipped item's `Equippable` declares a camera overlay for the slot it is in | | 4 | powder-snow outline | `Hud.extractTextureOverlay` | yes | `Entity.getTicksFrozen` above zero | | 5 | portal overlay | `Hud.extractPortalOverlay` | yes | the interpolated portal intensity is above zero | | 6 | nausea overlay | `Hud.extractConfusionOverlay` | yes | *else*: no portal effect, a nausea blend above zero, and `Options.screenEffectScale` below one | | 7 | crosshair | `Hud.extractCrosshair` | yes | first-person; either not a spectator or a hit result a spectator may see; **and** the F3 three-dimensional-crosshair entry is off | | — | *new stratum* | `GuiGraphicsExtractor.nextStratum` | — | a hard layering barrier — everything below is above everything above. `Hud` calls it ten times in all; this is the one that separates the overlays from the rest, and there is another inside the crosshair | | 8 | hotbar | `Hud.extractItemHotbar` | yes | a camera player exists — replaced by `SpectatorGui.extractHotbar` in spectator mode | | 9 | armour | `Hud.extractArmor` | yes | inside the health block, and `LivingEntity.getArmorValue` above zero | | 10 | hearts | `Hud.extractHearts` | yes | inside the health block | | 11 | food | `Hud.extractFood` | yes | inside the health block, **and** the vehicle contributes no hearts | | 12 | air bubbles | `Hud.extractAirBubbles` | yes | inside the health block, and the player's eyes are in water or the air supply is below its maximum | | 13 | mount health | `Hud.extractVehicleHealth` | yes | a ridden `LivingEntity` with a non-zero heart count — **outside** the health block, so creative shows it | | 14 | contextual bar, background | `ContextualBar.extractBackground` | yes | always recorded, but which of four states it is in is re-decided every frame by `Hud.nextContextualInfoState` | | 15 | experience level | `ContextualBar.extractExperienceLevel` | yes | the game mode has experience **and** the level is above zero — recorded between the bar's two passes, so it survives whichever bar wins | | 16 | contextual bar, foreground | `ContextualBar.extractRenderState` | yes | always recorded; empty in `ContextualBar.EMPTY`, `ExperienceBar` and `JumpableVehicleBar`, so `LocatorBar` is the only one of the four states that draws anything here | | 17 | selected item name | `Hud.extractSelectedItemName` | yes | not a spectator, `Hud.toolHighlightTimer` above zero, and the stack is not empty | | 18 | status effects | `Hud.extractEffects` | yes | the player has effects, no screen is showing them itself, and the instance sets `MobEffectInstance.showIcon` | | 19 | boss bars | `BossHealthOverlay` | yes | the overlay has events | | 20 | **sleep fade** | `Hud.extractSleepOverlay` | **no** | `Player.getSleepTimer` above zero — the one element between the two hidden-gated blocks | | 21 | demo text | `Hud.extractDemoOverlay` | yes | `Minecraft.isDemo` | | 22 | scoreboard sidebar | `Hud.displayScoreboardSidebar` | yes | a display objective for the team's colour slot, else for `DisplaySlot.SIDEBAR` | | 23 | action bar | `Hud.extractOverlayMessage` | yes | `Hud.overlayMessageString` is set and its timer has not run out | | 24 | title and subtitle | `Hud.extractTitle` | yes | `Hud.title` is set and `Hud.titleTime` is above zero | | 25 | chat | `ChatComponent.extractRenderState` | yes | a player exists and the chat *screen* is not focused | | 26 | tab list | `PlayerTabOverlay.extractRenderState` | yes | `Options.keyPlayerList` is down, and either this is not a local server, or more than one player is listed, or a `DisplaySlot.LIST` objective exists | | 27 | subtitles | `SubtitleOverlay` | `Options.showSubtitles` is on and something audible is playing | deferred when there is no screen or the screen declares itself in-game UI — and recorded even while hidden, if a screen declaring itself in-game UI is up | The four elements a reader expects at the end of that list are not on `Hud.extractRenderState`'s list at all — three of them are still `Hud` methods, and only the toasts are outside `Hud`. `Gui.extractRenderState` records them, after the overlay or screen: | # | element | its own condition | hidden by F1? | |---:|---|---|---| | 28 | saving indicator | `Options.showAutosaveIndicator` is on, the frame is drawing a level, and a save is still animating | **no** | | 29 | toasts | resources are loaded | checks the flag itself | | 30 | debug overlay | the current screen is not `DebugOptionsScreen` | checks the flag itself | | 31 | deferred subtitles | row 27 deferred them — but only when no screen is up: a screen draws them itself from `Screen.extractBackground`, below its own widgets, and this call then finds nothing left | — | Two consequences worth carrying away. Toasts and the debug overlay are always **above** a screen, because `Gui` records them after it. And the deferred subtitles are called from a screen's *background* pass, so they land under the screen's widgets rather than over them. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Submit phases and feature renderers > Verified against **Minecraft 26.2** · Reference · Hand-kept from > `SubmitNodeCollection` and the `FeatureRenderDispatcher` constructor. Everything an entity, block entity, particle or debug renderer draws in a level arrives as a *submit node* — the sky, the clouds, the weather and the world border are drawn by their own renderers in their own frame-graph passes, and terrain by its chunk sections. `SubmitNodeCollection` sorts those nodes into fifteen named phases as they come in, and thirteen feature renderers turn them into vertices. The lecture that frames both is [entity rendering](../systems/rendering/entity-rendering.md), which names four of the phases and one of the renderers. Three more renderers are named elsewhere — `TextFeatureRenderer` and `NameTagFeatureRenderer` on [text and fonts](../systems/client/text-and-fonts.md), `QuadParticleFeatureRenderer` on [particles](../systems/rendering/particles.md) — and everything else here is only here. ## The fifteen phases In **declaration order**, which is also the order of `SubmitNodeCollection.allPhases`. It is *not* the order they are drawn in, and neither is the last column: `FeatureRenderDispatcher.PreparedFrame.executeTranslucent` alone makes three separate sweeps over every order bucket, so the phases it drains are numbered by sweep — but *within* a sweep the order is the one the sweep's own statements run in, not the order of the rows. Sweep 1 drains shadows, then translucent models, then see-through name tags, then name tags, then texts, then translucent custom geometry — so a see-through name tag is drawn **before** the opaque one, which is the row order reversed. Three phases are a `TranslucentFeatureRenderPhase` and the other twelve a `SimpleFeatureRenderPhase`. The simple phase groups its nodes by feature type and then by batch key — which only two of the thirteen submit kinds have, a model and a piece of custom geometry, everything else grouping by adjacency — and leaves `RenderTypeFeatureRenderer.Group` free to fold a node's geometry into **any** earlier draw of the same render type, not only the adjacent one. The translucent phase keeps every node, sorts them back to front by squared distance to the camera, and marks the group strictly ordered. That does not stop merging: consecutive submits of one render type still share a draw, on a test that never consults the flag. What it stops is the fold into a *non-adjacent* earlier draw — the one merge that would move geometry ahead of the draws between it and its target, and so undo the sort. A render type whose primitives are chained opts out of both, through `RenderType.canConsolidateConsecutiveGeometry`. | # | phase | what lands in it | drained by | |---:|---|---|---| | 1 | `SubmitNodeCollection.solid` | the opaque default: models and block models whose `RenderType` does not blend, moving blocks whose model does not declare the translucent material flag, items with no translucent quad, custom geometry that neither blends nor outlines, the opaque half of every quad-particle group, plus every flame and every leash | `.executeSolid` | | 2 | `SubmitNodeCollection.shadows` | one node per `SubmitNodeCollection.submitShadow`, carrying the radius and the `EntityRenderState.ShadowPiece` list sampled at extract | `.executeTranslucent`, sweep 1 | | 3 | `SubmitNodeCollection.nameTags` | every name tag gets a node here, see-through or not — a see-through one lands with an emission bump on its light, opaque white, and no background | `.executeTranslucent`, sweep 1 | | 4 | `SubmitNodeCollection.seeThroughNameTags` | the *second* node a see-through name tag emits, in `Font.DisplayMode.SEE_THROUGH` with the background restored | `.executeTranslucent`, sweep 1 | | 5 | `SubmitNodeCollection.texts` | world-space text that is not a name tag, from `SubmitNodeCollection.submitText` | `.executeTranslucent`, sweep 1 | | 6 | `SubmitNodeCollection.shapeOutlines` | `VoxelShape` edge outlines submitted **without** the after-terrain flag | `.executeTranslucent`, sweep 2 | | 7 | `SubmitNodeCollection.translucentBlocksAndItems` | items with a translucent quad, block models whose render type blends, and moving blocks whose model declares the translucent material flag | `.executeTranslucent`, sweep 3 | | 8 | `SubmitNodeCollection.translucentModels` | entity models whose `RenderType` blends | `.executeTranslucent`, sweep 1 | | 9 | `SubmitNodeCollection.translucentCustomGeometry` | custom geometry whose `RenderType` blends — a simple phase in spite of the name, so it is not distance-sorted | `.executeTranslucent`, sweep 1 | | 10 | `SubmitNodeCollection.gizmos` | debug primitive groups submitted **without** the on-top flag | `.executeTranslucent`, sweep 2 | | 11 | `SubmitNodeCollection.breakingOverlay` | the crumbling decal: a model submitted with a `ModelFeatureRenderer.CrumblingOverlay` whose render type admits one, and every `SubmitNodeCollection.submitBreakingBlockModel` | `.executeTranslucent`, sweep 3 | | 12 | `SubmitNodeCollection.waterMask` | models submitted with the water-mask render type | `.executeTranslucent`, sweep 3 | | 13 | `SubmitNodeCollection.afterTerrain` | outlines flagged after-terrain, plus the translucent half of every quad-particle group | `.executeTranslucentAfterTerrain` | | 14 | `SubmitNodeCollection.alwaysOnTop` | gizmo groups flagged on top | `.executeAlwaysOnTop` | | 15 | `SubmitNodeCollection.outline` | a second copy of a model, block model, moving block or item whose outline colour was non-zero — a model or block model only where its render type has an outline variant, and a moving block or item with its ordinary type, re-typed inside the feature renderer or dropped there — plus custom geometry, which is not a second copy at all: an outline render type routes the *only* copy here | `.executeOutline`, which `FeatureRenderDispatcher.renderAllFeatures` never calls — `LevelRenderer` does, into its own target | Two rows are worth reading twice. A quad-particle group is submitted **once** and lands in two phases at once, *solid* and *afterTerrain*, with a flag that picks which of its layers each half draws. And *outline* is not simply *solid* submitted twice: the glow is a second submission for a model, a block model, a moving block or an item, but flames, leashes and quad particles never reach it at all, a blending model pairs its outline copy with *translucentModels* rather than *solid*, and custom geometry goes to one phase or the other and never both. ## The thirteen feature renderers In the order `FeatureRenderDispatcher`'s constructor registers them. All but one extend `RenderTypeFeatureRenderer`, which owns the shared `StagedVertexBuffer` draw and the merging of consecutive same-render-type geometry. | feature renderer | what it writes | |---|---| | `ShadowFeatureRenderer` | four vertices per `EntityRenderState.ShadowPiece` onto one shared shadow render type, with the piece's alpha as the colour and UVs derived from its bounds and the shadow radius | | `FlameFeatureRenderer` | a stack of fire quads scaled to the entity's bounding box, alternating two block-atlas sprites and flipping their U every other pair, at full block light | | `ModelFeatureRenderer` | the entity models: `Model.setupAnim` and then `Model.renderToBuffer` over the `ModelPart` tree, into a buffer optionally wrapped for a sheeted decal or a single sprite | | `NameTagFeatureRenderer` | the glyph quads and background of a name tag, prepared through `Font` at the submitted pose and display mode | | `TextFeatureRenderer` | arbitrary world-space text, with an eight-direction outline pass and a polygon-offset second pass when an outline colour is set | | `LeashFeatureRenderer` | a ribbon between the two ends of an `EntityRenderState.LeashState`, walked twice — twenty-four steps out and twenty-four back for its two faces, a hundred vertices in all — with its light interpolated between the endpoints and its colour alternating per step | | `ItemFeatureRenderer` | item quads, in two passes over the same submits — geometry first, then the enchantment foil for every submit that has one | | `CustomFeatureRenderer` | nothing of its own: it hands the caller's `SubmitNodeCollector.CustomGeometryRenderer` a vertex consumer for the requested render type | | `BlockModelFeatureRenderer` | the quads of a `BlockStateModelPart` list, in `Direction` order, at one fixed light and overlay coordinate for the whole submit | | `MovingBlockFeatureRenderer` | a whole block state re-tesselated through `ModelBlockRenderer` — pistons and falling blocks — honouring the ambient-occlusion and cutout-leaves options | | `QuadParticleFeatureRenderer` | the only one outside `RenderTypeFeatureRenderer`: it appends particle draws to the `StagedVertexBuffer` per `SingleQuadParticle.Layer` and opens its own `RenderPass` to issue them | | `ShapeOutlineFeatureRenderer` | two line vertices per edge of a `VoxelShape`, each carrying a per-vertex line width | | `GizmoFeatureRenderer` | the debug vocabulary: quads, triangle fans, lines, texts and points out of a `DrawableGizmoPrimitives.Group`, camera-relative | That list is the answer to *what can be drawn in a level*. Anything a mod or a renderer wants that is not one of the other twelve has to go through `CustomFeatureRenderer`. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Density-function nodes > Verified against **Minecraft 26.2** · Reference · the thirty-four node types a > *worldgen/density_function* file may name, what each one takes, and what the > per-chunk rewrite turns it into. [Density functions](../systems/worldgen/density-functions.md) is the lecture: three forms of one graph, two rewrites, and the six caches. This is the catalogue behind it — the table you would pause the video to read. `DensityFunctions.bootstrap` registers every entry below into `BuiltInRegistries.DENSITY_FUNCTION_TYPE`, in this order, under the *minecraft* namespace. That registry is **built-in and frozen at startup**, which is why adding a new *kind* of node takes code while adding a new graph takes a JSON file. **34** — registered node types (`DensityFunctions.bootstrap`): four by name, then six markers, nine more by name, seven mapped transforms, four arithmetic, and four last. ## The table *children* counts the density-function slots; each one accepts an id string, an inline object or a bare number, because every child slot is typed `DensityFunction.CODEC`. | id | class | children | other fields | what it computes | |---|---|---:|---|---| | *blend_alpha* | `DensityFunctions.BlendAlpha` | 0 | — | constant 1.0 as data — a placeholder the chunk swaps out | | *blend_offset* | `DensityFunctions.BlendOffset` | 0 | — | constant 0.0 as data — likewise a placeholder | | *beardifier* | `DensityFunctions.BeardifierMarker` | 0 | — | constant 0.0 as data — the structure-terrain placeholder | | *old_blended_noise* | `BlendedNoise` | 0 | *xz_scale*, *y_scale*, *xz_factor*, *y_factor*, *smear_scale_multiplier* | the pre-1.18 terrain noise, decoded unseeded | | *interpolated* | `DensityFunctions.Marker` | 1 | — | delegates — requests cell-corner interpolation | | *flat_cache* | `DensityFunctions.Marker` | 1 | — | delegates — requests a quart-resolution 2-D cache | | *cache_2d* | `DensityFunctions.Marker` | 1 | — | delegates — requests a one-entry XZ memo | | *cache_once* | `DensityFunctions.Marker` | 1 | — | delegates — requests reuse within one interpolation step | | *cache_all_in_cell* | `DensityFunctions.Marker` | 1 | — | delegates — requests a whole-cell block cache | | *blend_density* | `DensityFunctions.Marker` | 1 | — | delegates — requests old-terrain density blending | | *noise* | `DensityFunctions.Noise` | 0 | *noise*, *xz_scale*, *y_scale* | samples a `NormalNoise` at the scaled position | | *end_islands* | `DensityFunctions.EndIslandDensityFunction` | 0 | — | the End's simplex island field, as a density | | *shifted_noise* | `DensityFunctions.ShiftedNoise` | 3 | *noise*, *xz_scale*, *y_scale* | samples noise at position × scale plus three offsets | | *range_choice* | `DensityFunctions.RangeChoice` | 3 | *min_inclusive*, *max_exclusive* | one of two branches, by whether the input is in range | | *interval_select* | `DensityFunctions.IntervalSelect` | 1 + a list | *thresholds* | the branch whose ascending threshold the input first falls below | | *shift_a* | `DensityFunctions.ShiftA` | 0 | *argument* (a noise) | domain warp read at x, 0, z | | *shift_b* | `DensityFunctions.ShiftB` | 0 | *argument* (a noise) | domain warp read at z, x, 0 | | *shift* | `DensityFunctions.Shift` | 0 | *argument* (a noise) | domain warp read at x, y, z | | *clamp* | `DensityFunctions.Clamp` | 1 | *min*, *max* | the child, clamped | | *abs* | `DensityFunctions.Mapped` | 1 | — | absolute value | | *square* | `DensityFunctions.Mapped` | 1 | — | the child squared | | *cube* | `DensityFunctions.Mapped` | 1 | — | the child cubed | | *half_negative* | `DensityFunctions.Mapped` | 1 | — | identity above zero, halved below | | *quarter_negative* | `DensityFunctions.Mapped` | 1 | — | identity above zero, quartered below | | *invert* | `DensityFunctions.Mapped` | 1 | — | the reciprocal | | *squeeze* | `DensityFunctions.Mapped` | 1 | — | clamp to ±1, then a soft odd cubic | | *add* | `DensityFunctions.Ap2` or `DensityFunctions.MulOrAdd` | 2 | — | the sum | | *mul* | `DensityFunctions.Ap2` or `DensityFunctions.MulOrAdd` | 2 | — | the product, short-circuiting on an exact zero | | *min* | `DensityFunctions.Ap2` | 2 | — | the minimum, skipping the second child when the first is already below its bound | | *max* | `DensityFunctions.Ap2` | 2 | — | the maximum, with the symmetric skip | | *spline* | `DensityFunctions.Spline` | inside the spline | *spline* | a `CubicSpline` whose coordinates are themselves density functions | | *constant* | `DensityFunctions.Constant` | 0 | *argument* | a fixed value | | *y_clamped_gradient* | `DensityFunctions.YClampedGradient` | 0 | *from_y*, *to_y*, *from_value*, *to_value* | block Y mapped onto a value range | | *find_top_surface* | `DensityFunctions.FindTopSurface` | 2 | *lower_bound*, *cell_height* | steps down in strides until the density goes positive, and returns that **Y** | **Where one class serves several ids.** The six markers are all `DensityFunctions.Marker`, a record of a `DensityFunctions.Marker.Type` and a wrapped function; the seven transforms are all `DensityFunctions.Mapped`; the four arithmetic ids share `DensityFunctions.TwoArgumentSimpleFunction`. In each case the *enum constant* carries its own codec, and the node's *codec()* returns its type's — which is how a re-serialised graph comes back with the right id. `DensityFunctions.MulOrAdd` is the specialisation `DensityFunctions.TwoArgumentSimpleFunction.create` picks when the id is *add* or *mul* and one argument folded to a `DensityFunctions.Constant`, so *add* in the JSON may come back as either class. ## What the caches become `NoiseChunk.wrapNew` is the per-chunk rewrite. A marker is a *request*; this is what is installed instead. All six replacements implement `DensityFunctions.MarkerOrMarked`, so they still report their marker type and would re-serialise unchanged. | marker type | installed | keyed on | |---|---|---| | `DensityFunctions.Marker.Type.Interpolated` | `NoiseChunk.NoiseInterpolator` | nothing — two slices of cell-corner values, and eight corners loaded per cell. Serves a foreign context by delegating to the wrapped function; only a sample whose context *is* the `NoiseChunk` throws outside the loop | | `DensityFunctions.Marker.Type.FlatCache` | `NoiseChunk.FlatCache` | **position**, at quart resolution: one array entry per 4×4 block column group, filled at construction | | `DensityFunctions.Marker.Type.Cache2D` | `NoiseChunk.Cache2D` | **position**, one entry — the packed XZ of the last sample | | `DensityFunctions.Marker.Type.CacheOnce` | `NoiseChunk.CacheOnce` | **a counter** — `NoiseChunk.interpolationCounter` for the scalar, a second counter for the array form | | `DensityFunctions.Marker.Type.CacheAllInCell` | `NoiseChunk.CacheAllInCell` | **the cell** — one array entry per block in the cell, Y stored inverted | | `DensityFunctions.Marker.Type.BlendDensity` | `NoiseChunk.BlendDensity`, **or nothing at all** if the level's `Blender` is empty, in which case the marker is replaced by its own child | not cached | The same rewrite resolves three singletons by object identity: `DensityFunctions.BlendAlpha` and `DensityFunctions.BlendOffset` become flat caches the `NoiseChunk` constructor has *already filled* (or survive as the constants 1.0 and 0.0 when there is no blending to do), and `DensityFunctions.BeardifierMarker` becomes this chunk's `Beardifier`. And `DensityFunctions.HolderHolder` — the in-memory stand-in for an id reference, which is not registered and has no codec — is resolved to its value once instead of on every sample. ## Bounds Every node answers `DensityFunction.minValue` and `DensityFunction.maxValue` without a position. The arithmetic family — the two-argument nodes, the mapped ones and *clamp* — stores its bounds as record components filled once at construction, and so does `BlendedNoise`; a few answer with literals of their own and the rest delegate to their input or walk their list again on each call. The rules worth knowing: The arithmetic bounds are **sign-aware** and eager: *mul* takes the four cross products and picks by the signs of the operands' ends, and *min* and *max* take the element-wise minimum and maximum of the ends. Building a *min* or a *max* over two ranges that cannot overlap logs a warning and proceeds. `DensityFunctions.Mapped.create` transforms the child's two endpoints, with *abs* and *square* clamping the minimum up to zero and *invert* reporting **±infinity** whenever the child's range straddles zero. *clamp* is the clearest of the nodes whose bounds are not derived from a child: its record components are literally named *minValue* and *maxValue*, so the codec's *min* and *max* fields *are* the interface's bound methods. *shifted_noise* takes its bounds from the noise and ignores all three of its children, and *blend_density* reports infinity whatever its child says. Three nodes report bounds that are not densities or not final. `DensityFunctions.Marker` passes its child's bounds through except when its type is `DensityFunctions.Marker.Type.BlendDensity`, where it reports ±infinity — the one place a marker is not transparent. `DensityFunctions.HolderHolder` reports ±infinity while its holder is unbound, which is what lets forward references parse. And `DensityFunctions.FindTopSurface` reports its *lower bound* and its upper bound's maximum, which are **Y coordinates** — this node's range is on a different scale from every other node in the table. One more, on the unseeded graph: `DensityFunction.NoiseHolder` answers a maximum of 2.0 while its `NormalNoise` is still null. Every one of the sixty-three shipped noise definitions computes a maximum between 2.57 and 7.32 once seeded, so a freshly parsed router reports noise bounds that are too **narrow**, and seeding widens them. ## What vanilla actually uses Thirty-five JSON files ship under *worldgen/density_function* — four at the top level plus the per-dimension directories — and between them they use twenty-five of the thirty-four ids. Five more appear only inline, in the seven `Registries.NOISE_SETTINGS` files: *blend_density* and *squeeze* in all seven, and *square*, *invert* and *find_top_surface* in the three overworld variants. That leaves four ids vanilla data never writes. *constant* is never written as a typed object, because a bare number is one. *cache_all_in_cell* and *beardifier* are added **in code**, by `NoiseChunk`'s constructor, around the router's final density. And *shift* — the three-dimensional domain warp — is used by nothing: `DensityFunctions.shift` has no callers anywhere in the decompile, and no shipped file names the id. `DensityFunctions.ShiftA` and `DensityFunctions.ShiftB` cover the two two-dimensional warps vanilla wants. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Threads > Verified against **Minecraft 26.2** · Reference · Hand-kept from `net/minecraft/util/thread` and every `Thread` the game starts, beside [Anatomy](../systems/anatomy/anatomy.md)'s four — looked up, not watched. Every thread the game creates, who creates it, what runs on it, and what is *allowed* to run on it. The last column is the rule the rest of the documentation leans on: game state belongs to exactly one thread, and anything else submits a task to that thread's event loop. ## The picture Two threads own game state — the Render thread owns the client's, the Server thread owns the world's — and everything else is a way of getting work to them or from them. Work crosses a thread boundary in exactly three ways, and the figure labels each edge with which: a **posted task** (a `Runnable` on the owner's `BlockableEventLoop`), a **completed future** (a worker's result, completed onto the owner's executor), or a **hopped handler** (a packet decoded on Netty and re-posted to its owner by `PacketUtils.ensureRunningOnSameThread`). ```mermaid flowchart LR subgraph ClientSide["the client process"] RT["Render thread
Minecraft.runTick: a frame, then 0 to 10 client ticks"] SND["Sound engine
SoundEngineExecutor: the OpenAL calls"] end subgraph Shared["shared by both halves"] NET["Netty IO
Connection: split, decode, encode, plus the handshake and login handlers"] WK["Worker-Main-n
Util.backgroundExecutor: generation, lighting, meshing"] end subgraph ServerSide["the server"] ST["Server thread
MinecraftServer.runServer: a tick every 50 ms"] IO["IO-Worker-n
Util.ioPool: region file reads and writes"] WD["Server Watchdog
dedicated only"] LST["Console, RCON, query, management
dedicated only"] end RT -- "serverbound packets, written on the caller's thread" --> NET NET -- "clientbound play packets: hopped handler" --> RT NET -- "serverbound play packets: hopped handler" --> ST ST -- "clientbound packets" --> NET ST -- "chunk generation and lighting: posted task" --> WK WK -- "a generated chunk: completed future" --> ST RT -- "section meshing: posted task" --> WK WK -- "a built mesh: completed future" --> RT ST -- "region reads and writes: posted task" --> IO IO -- "a loaded chunk's data: completed future" --> ST RT -- "play, stop, move a source: posted task" --> SND LST -- "a command line: posted task" --> ST WD -. "reads tick state unsynchronised, kills the JVM past max-tick-time" .-> ST ``` The table is the figure's rows. Netty is drawn once and shared because it is: in singleplayer the client's `Connection` and the integrated server's run on the same `Netty Local IO` threads, and the packets between them are real. ## The threads a lecture leans on | thread | made by | runs | may touch | |---|---|---|---| | **Render thread** (client) | the JVM main thread, renamed in `client/main/Main` | `Minecraft.run` → `Minecraft.runTick` once per frame; `Minecraft.tick` 0–10 times inside it | Everything client-side: `ClientLevel`, `LocalPlayer`, the GPU (`RenderSystem.assertOnRenderThread`), screens, options. It is also the client's event loop (`Minecraft` is a `ReentrantBlockableEventLoop`), so packet handlers run here after `PacketUtils.ensureRunningOnSameThread`. | | **Server thread** | `MinecraftServer.spin` | `MinecraftServer.runServer` → `MinecraftServer.processPacketsAndTick` every `TickRateManager.nanosecondsPerTick` (50 ms by default); `MinecraftServer.waitUntilNextTick` drains the task queue in the slack | Every `ServerLevel`, every chunk, entity and block entity, the `PlayerList`. Serverbound *play* packet handlers run here, not on Netty. One per server; singleplayer has exactly one. | | **Netty IO** (`Netty NIO IO #n`, `Netty Epoll IO #n`, `Netty Kqueue IO #n`, `Netty Local IO #n`) | `EventLoopGroupHolder` | the `Connection` pipeline: split, decrypt, decompress, decode; encode, compress, encrypt — **and the handshake and login handlers** | Bytes and `Packet` objects — plus, in handshake and login, the handlers themselves: `ServerHandshakePacketListenerImpl` and `ServerLoginPacketListenerImpl` never hop. The login *state machine* is not all theirs, though: `ServerLoginPacketListenerImpl` is a `TickablePacketListener`, so the Server thread advances it once a tick through `MinecraftServer.tickConnection`. A *play* handler that needs game state re-posts to the owning thread. *Local* is the in-process channel of singleplayer. | | **Worker-Main-n** | `Util.backgroundExecutor` — a `ForkJoinPool` sized to the JDK's available-processor count minus one, clamped by `Util.maxAllowedExecutorThreads` and capped by the *max.bg.threads* property (`Util.getMaxThreads`) | chunk generation and lighting via `ChunkTaskDispatcher`; section meshing via `SectionRenderDispatcher`; resource-reload *prepare* phases; chunk serialisation | Its own inputs. Results return to the owning thread as a `CompletableFuture` completed onto that thread's executor. Never `Level` state directly. | | **IO-Worker-n** | `Util.ioPool` | region file reads and writes through `IOWorker`, one `PriorityConsecutiveExecutor` per storage so writes to one file stay ordered | Files. `Util.nonCriticalIoPool` (`Download-n`) is the same shape for downloads, telemetry and sound decoding. | | **Sound engine** | `SoundEngineExecutor` | a `BlockableEventLoop` that owns the per-source OpenAL calls | The `SoundEngine`'s channels; see [the sound engine](../systems/client/sound-engine.md). Device open/close and buffer deletion stay on the Render thread. | | **Server Watchdog** | `DedicatedServer.initServer` (dedicated only, positive limit only) | `ServerWatchdog` | Writes nothing, but *reads* game state unsynchronised while the Server thread is mid-tick — the game rules and every level's `ServerLevel.getWatchdogStats`, for the crash report. It kills the JVM past `DedicatedServerProperties.maxTickTime`, and that kill does **not** save the world. | | **Server console handler** | `DedicatedServer.initServer` | reads stdin | Queues each line to the server thread as a command; runs nothing itself. | | **RCON Listener #n** / **RCON Client** | `RconThread` from `DedicatedServer.initServer`, when *enable-rcon* **and** *rcon.password* is set | accepts RCON sockets; one `RconClient` thread per connection | Sockets. Each command is queued to the server thread. Dedicated only, and **non-daemon** — `DedicatedServer.onServerExit` must stop it or the JVM will not exit. | | **Query Listener #n** | `QueryThreadGs4` from `DedicatedServer.initServer`, when *enable-query* | the GS4 query protocol | Its own cached status. Dedicated only, and **non-daemon**, like RCON. | | **Management server IO #n** | `ManagementServer`, built by `JsonRpc` in `server/Main` | a second, independent Netty event-loop group: the JSON-RPC/WebSocket management API and its heartbeat | Its own pipeline; management calls reach the game through the server's task queue. Dedicated only. | | Timer hack thread | `Util.startTimerHackThread` | sleeps forever | Nothing. Keeps the JVM's timer resolution high by existing. | ## The nine client handlers that never hop `Connection.channelRead0` calls a packet's handler on the Netty thread, and a handler's first line is normally `PacketUtils.ensureRunningOnSameThread`, which re-posts it to the owning thread and aborts ([the connection](../systems/networking/the-connection.md)). Nine handlers on the client's play listener omit it, so they run to completion on Netty and must touch nothing the Render thread owns. Seven are declared in `ClientPacketListener` itself; the last two are inherited from `ClientCommonPacketListenerImpl` and are the two that matter most, because a keep-alive is answered and a disconnect is acted on without the game thread being involved at all. | handler | what it does on the Netty thread | |---|---| | `ClientPacketListener.handlePlayerCombatEnter` | nothing — the body is empty | | `ClientPacketListener.handlePlayerCombatEnd` | nothing — the body is empty | | `ClientPacketListener.handleChunkBatchStart` | starts the `ChunkBatchSizeCalculator`'s clock | | `ClientPacketListener.handleChunkBatchFinished` | stops it and sends `ServerboundChunkBatchReceivedPacket` with the chunks-per-tick it now wants — so the loop in [what the client is told](../systems/networking/what-the-client-is-told.md) times packet decode, not mesh building | | `ClientPacketListener.handleDebugSample` | hands the sample to `DebugScreenOverlay.logRemoteSample` | | `ClientPacketListener.handlePongResponse` | records the round trip in `PingDebugMonitor` | | `ClientPacketListener.handleLowDiskSpaceWarning` | calls `Minecraft.sendLowDiskSpaceWarning`, which posts the toast to the Render thread itself — the one that crosses after all, by `Minecraft.execute` rather than by the hop | | `ClientCommonPacketListenerImpl.handleKeepAlive` | replies with `ServerboundKeepAlivePacket` through `ClientCommonPacketListenerImpl.sendWhen`, deferred while the window is frozen at `RenderSystem.isFrozenAtPollEvents` — so the answer that keeps a connection alive never waits for a frame | | `ClientCommonPacketListenerImpl.handleDisconnect` | calls `Connection.disconnect` straight from the event loop | ## Situational threads Real, but nothing in the corpus hangs on them: *User Authenticator* (one per login, for the session-server call), *Chat-Filter-Worker*, *Server Pinger* and *Server Connector* (the multiplayer screen), *Telemetry-Sender*, `LanServerPinger` and its detector, *World Upgrader*, *Datafixer Bootstrap* (priority 1, so it yields to everything), the client and server shutdown hooks with `ClientShutdownWatchdog` behind them, Swing's event dispatch thread when a dedicated server is started without *--nogui* and runs its `MinecraftServerGui`, the *Friends List* fetcher behind the social screen, and `ChaseServer`'s two threads and `ChaseClient`'s one, which exist only behind `SharedConstants.DEBUG_CHASE_COMMAND` and the */chase* command it registers. Realms starts nine more, and is out of scope with the rest of *com/mojang/realmsclient*. ## The rules that follow - **Two owners, one wire.** Client state is the Render thread's; server state is the Server thread's; in singleplayer they share a JVM and still only talk about the *world* through packets over the local channel. (Settings — pause, view distance, publishing — do cross by direct call; see Anatomy.) - **Handlers hop.** A play packet is decoded on Netty and *handled* on the owning game thread; `PacketUtils.ensureRunningOnSameThread` is the hop. Handshake and login are the exception: their handlers run to completion on Netty. The login state machine is still advanced from the Server thread, which ticks the listener once a tick. - **Workers compute, owners commit.** Chunk generation, lighting and meshing produce results on the worker pool; only the owning thread installs them. - **Waiting drains.** An owning thread never blocks idle: `BlockableEventLoop.managedBlock` keeps running its own queue while it waits on a future, which is why a server tick that waits for a chunk does not deadlock the chunk that needs the server tick. `ServerChunkCache.MainThreadExecutor` is the extra event loop that makes that work. --- # Math and primitives > Verified against **Minecraft 26.2** · Reference · The coordinate spaces, geometry and randomness every system page assumes — looked up, not watched. Every system in the game speaks in a handful of value types: an integer block position, a chunk column, a 16³ section, a double-precision world position, a direction, a box, a collision shape, a random source. They are the types the codebase reaches for most — `BlockPos` alone has 1,221 importers, more than any other Minecraft class — and most of what is confusing about "which coordinate is this" is answered by knowing which type a method takes. ## The coordinate spaces Six integer spaces and one double one, and every conversion is a named method — which is the figure: the spaces as nodes, the conversions as the edges between them, and the three that also pack to a long key. ```mermaid flowchart LR V["Vec3: a double world position"] -- "BlockPos.containing, which floors" --> B["BlockPos: one block, int"] B -- "Vec3.atCenterOf, atLowerCornerOf, atBottomCenterOf" --> V B -- "ChunkPos.containing, shift 4" --> C["ChunkPos: a 16-block column"] C -- "ChunkPos.getMinBlockX, getWorldPosition" --> B B -- "SectionPos.of, blockToSectionCoord, shift 4" --> S["SectionPos: a 16-cubed section"] S -- "sectionToBlockCoord, sectionRelative masks 15" --> B C -- "SectionPos.of, with a section y" --> S B -- "QuartPos.fromBlock, shift 2" --> Q["QuartPos: a 4-block biome cell"] Q -- "QuartPos.toBlock" --> B S -- "QuartPos.fromSection" --> Q Q -- "QuartPos.toSection" --> S C -- "ChunkPos.getRegionX, getRegionLocalX, shift 5" --> R["region: 32 chunks, one .mca file"] R -- "ChunkPos.minFromRegion" --> C B -- "GlobalPos.of, plus a Level key" --> G["GlobalPos: a dimension and a block"] B -- "BlockPos.asLong: 26-bit x and z, 12-bit y" --> L["long keys"] S -- "SectionPos.asLong: 22-bit x and z, 20-bit y" --> L C -- "ChunkPos.pack: 32 and 32" --> L ``` | space | unit | type | owner / notes | conversions | |---|---|---|---|---| | **block** | 1 block, int | `BlockPos` (extends `Vec3i`) | immutable; `BlockPos.MutableBlockPos` for loops | `BlockPos.containing` floors a double position; `BlockPos.asLong` packs to a long and `BlockPos.of` unpacks one | | **world position** | 1 block, double | `Vec3` (implements `Position`) | entity positions, ray casts, velocities | `Vec3.atCenterOf`, `Vec3.atLowerCornerOf`, `Vec3.atBottomCenterOf` from a `Vec3i`; `Vec3.directionFromRotation` from pitch/yaw | | **chunk column** | 16 blocks | `ChunkPos` — a **record** of x and z | the key of every chunk map | `ChunkPos.containing` from a `BlockPos`; `ChunkPos.pack` / `ChunkPos.unpack`; `ChunkPos.getMinBlockX`, `ChunkPos.getWorldPosition` | | **section** | 16³ cube | `SectionPos` (extends `Vec3i`) | lighting, entity sections, render sections | `SectionPos.of` from block/chunk/entity; `SectionPos.blockToSectionCoord` (shift 4), `SectionPos.sectionToBlockCoord`, `SectionPos.sectionRelative` (mask 15); `SectionPos.asLong` | | **quart / biome** | 4 blocks | `QuartPos` (static only) | biome sampling | `QuartPos.fromBlock` (shift 2), `QuartPos.toBlock`, `QuartPos.fromSection`, `QuartPos.toSection` | | **region** | 32 chunks | none — methods on `ChunkPos` | the `.mca` file grid | `ChunkPos.getRegionX`, `ChunkPos.getRegionLocalX`, `ChunkPos.minFromRegion`; `ChunkPos.REGION_SIZE` | | **dimension-qualified block** | — | `GlobalPos` — record of a `Level` key and a `BlockPos` | compass targets, beds, portals | `GlobalPos.of` | | **integer box** | blocks | `BoundingBox` for structures; `BlockBox` is a newer record that nothing yet uses | structure bounds, piece placement | `BoundingBox.intersects`, `BoundingBox.encapsulate`; `BlockBox.aabb`, `BlockBox.contains` | | **double box** | blocks | `AABB` (a class of six public final doubles) | entity bounding boxes, block shapes' bounds | `AABB.move`, `AABB.inflate`, `AABB.intersects`, `AABB.clip` | | **pitch / yaw** | degrees, float | `Vec2` | look direction, as (xRot, yRot) — except the *Rotation* NBT tag, which stores it the other way round | `Direction.fromYRot`, `Direction.toYRot`; `Vec3.xRot`, `Vec3.yRot` | | **pose rotation** | degrees, float ×3 | `Rotations` | `ArmorStand` poses, and nothing else | — | | **model / render space** | float | JOML `Vector3f`, `Matrix4f`, `Quaternionf` (external) | everything under `client/renderer` | `Vec3.toVector3f`; `Direction.step`, `Direction.getRotation`; `com/mojang/math` `Axis` builds quaternions | `Position` is the three-double interface `Vec3` implements. `Vec3i` is the mutable-under-the-hood int triple with the arithmetic (`Vec3i.offset`, `Vec3i.relative`, `Vec3i.distSqr`, `Vec3i.distManhattan`); `BlockPos` adds the iteration helpers (`BlockPos.betweenClosed`, `BlockPos.withinManhattan`, `BlockPos.spiralAround`), each of which walks a single reused `BlockPos.MutableBlockPos` rather than allocating. `BlockPos.breadthFirstTraversal` is the exception in the same class: it is not an iterator at all, and allocates a queue of nodes and a set of visited longs. `Cursor3D` is a *different* allocation-free box cursor — the one that also classifies each position as inside, face, edge or corner (`Cursor3D.TYPE_INSIDE`, `Cursor3D.TYPE_FACE`, `Cursor3D.TYPE_EDGE`, `Cursor3D.TYPE_CORNER`) — and it is used by `SectionPos`, `BlockCollisions` and `ClientLevel`, not by `BlockPos`. Colours are a primitive too, and they live in `net/minecraft/util`: `ARGB` is where every pack, unpack, lerp, multiply and alpha helper is, with `CommonColors` for the named constants, `ColorRGBA` for the codec-friendly value and `Brightness` for the packed block/sky light pair. ## Three long keys Three long-packings appear everywhere as map keys. - **`BlockPos.asLong`** — 26 bits X, 26 bits Z, 12 bits Y, high to low. `BlockPos.PACKED_HORIZONTAL_LENGTH` is literally derived from the world border's 30,000,000, which is why it is 26 (`BlockPos.MAX_HORIZONTAL_COORDINATE` is 33,554,431); the remaining `BlockPos.PACKED_Y_LENGTH` is 12, giving −2048 to 2047. The *usable* range is narrower: `DimensionType` reserves a 32-block margin, so `DimensionType.MIN_Y` is −2032, `DimensionType.MAX_Y` is 2031 and `DimensionType.Y_SIZE` is 4064. `BlockPos.getFlatIndex` masks off the low four Y bits — it snaps a packed position to the bottom of its own 16-block section, which is the skylight walk-up idiom, not a per-column key (the top eight Y bits survive). `BlockPos.STREAM_CODEC` sends the packed long; `Vec3i.STREAM_CODEC` sends three varints. - **`SectionPos.asLong`** — 22 bits X, 22 bits Z, 20 bits Y. A section-relative position packs into a short (`SectionPos.sectionRelativePos`). - **`ChunkPos.pack`** — X in the low 32 bits, Z in the high 32. `ChunkPos.INVALID_CHUNK_POS` is a sentinel; `ChunkPos.isValid` is bounded by `ChunkPyramid.MAX_CHUNK_COORDINATE_VALUE`, which lives with the chunk status pyramid because the safety margin is derived from how many neighbours generation reads. ## Directions and symmetry `Direction` is the six-valued enum in 3D-data order `DOWN, UP, NORTH, SOUTH, WEST, EAST` (`Direction.get3DDataValue`); its horizontal subset has its own order starting at south (`Direction.get2DDataValue`). `Direction.Axis` (X, Y, Z), `Direction.AxisDirection` and `Direction.Plane` (horizontal, vertical) are the nested helpers; `Direction8` the compass points; `FrontAndTop` the twelve jigsaw and crafter orientations; `AxisCycle` the axis permutation `VoxelShape` lookups use. Block rotation is `Rotation` and `Mirror` (`world/level/block`), each of which maps onto `OctahedralGroup` (`com/mojang/math`) — the 48-element symmetry group of a cube, each element a permutation plus three inversion flags. That group is not decoration: `Shapes.rotate`, `Shapes.rotateAll`, `Shapes.rotateHorizontal` and `Shapes.rotateAttachFace` are how a block declares one shape and is handed the rest — three more from `Shapes.rotateHorizontal`, five from `Shapes.rotateAll`, and eleven from `Shapes.rotateAttachFace`, which is `Shapes.rotateHorizontal` run once per `AttachFace`. `Transformation` wraps a JOML matrix and lazily decomposes it into translation, left rotation, scale and right rotation for model JSON. There are two things called `Axis`: `Direction.Axis` and the quaternion factory in `com/mojang/math`. They are unrelated. ## Shapes and collision A `VoxelShape` is a set of boxes on a per-axis coordinate grid, backed by a `DiscreteVoxelShape` bit grid (`BitSetDiscreteVoxelShape`). `Shapes` is the factory and algebra: `Shapes.block`, `Shapes.empty`, `Shapes.box`, `Shapes.or`, `Shapes.join` with a `BooleanOp`, `Shapes.joinIsNotEmpty`, `Shapes.collide`, `Shapes.blockOccludes`, with `Shapes.EPSILON` and `Shapes.BIG_EPSILON` the tolerances every comparison uses. Implementations differ by how the grid is stored — `CubeVoxelShape` (even divisions), `ArrayVoxelShape` (explicit coordinate lists), `SliceShape` (a one-cell-thick view, used for face culling and occlusion) — and `Shapes.join` picks an `IndexMerger` strategy per axis, returning a `CubeVoxelShape` only when all three merge evenly. Shape queries are cheap because they are mostly not computed: `BlockBehaviour.BlockStateBase.initCache` builds a `BlockBehaviour.BlockStateBase.Cache` per block state holding the collision shape, the large-collision shape, whether the collision shape is a full block, and a per-face sturdiness array — but **only for a block whose shape is not dynamic**, and the occlusion shape is not in it. That one is a field on `BlockBehaviour.BlockStateBase` itself, built whether the cache is or not, and a dynamic-shape block answers every collision query live. `CollisionContext` is what a shape query knows about who is asking: `CollisionContext.of` an entity (`EntityCollisionContext` — descending, bottom Y, held item, whether fluids collide), `CollisionContext.empty`, `CollisionContext.placementContext` — all three of which are `EntityCollisionContext`s, as is the `MinecartCollisionContext` `CollisionContext.of` returns for a minecart under the experimental movement flag. The one case that is neither is `PositionCollisionContext`, from `CollisionContext.positionContext`. Ray casts return a `HitResult`: `BlockHitResult` (position, face, inside, world-border) or `EntityHitResult`. ## Two random families, and two that are neither `RandomSource` (`net/minecraft/util`) is the interface. Most implementations live in `world/level/levelgen`, and the legacy family shares `BitRandomSource`, which defines `RandomSource.nextInt` and friends on top of a raw bit generator; `XoroshiroRandomSource` implements `RandomSource` directly, and `RandomSequences` keeps one more in `world/`. Two families coexist in one process: - **Legacy LCG** — `LegacyRandomSource` (the java.util.Random algorithm), `SingleThreadedRandomSource` (same, no atomics), `ThreadSafeLegacyRandomSource`. `RandomSource.create` returns a `LegacyRandomSource` with a uniquified seed; this is `Level.random` (inherited unchanged by `ServerLevel` and `ClientLevel`), `Entity.random`, `GameRenderer.random`, `ParticleEngine.random`. `RandomSource.createThreadLocalInstance` returns a `SingleThreadedRandomSource` and is what `ClientLevel.animateTick` uses for block animation, and `LevelRenderer` for the block-destroy overlay. - **Xoroshiro** — `XoroshiroRandomSource` (128-bit state via `RandomSupport.Seed128bit`), the newer of the two and the one a noise settings file gets unless it asks otherwise: `NoiseGeneratorSettings.getRandomSource` returns `WorldgenRandom.Algorithm.XOROSHIRO` unless the settings opt into legacy. **Four of the seven shipped noise settings do opt in**, and two of them are dimensions of an ordinary world — *nether* and *end*, alongside *caves* and *floating_islands* — so most of a new world's generation is legacy, not Xoroshiro. `RandomState` forks it positionally (`PositionalRandomFactory.at`, `PositionalRandomFactory.fromHashOf`) for the named noise consumers — the aquifer and the ore placer each get their own deterministic stream from the seed and position. `WorldgenRandom` wraps any delegate and adds the seeding conventions — `WorldgenRandom.setDecorationSeed`, `WorldgenRandom.setFeatureSeed`, `WorldgenRandom.setLargeFeatureWithSalt`, `WorldgenRandom.seedSlimeChunk` — that make a structure land in the same place for the same seed. Features go through *those*, not through `RandomState`. There is a third randomness path that is neither: `RandomSequence` and `RandomSequences`, a saved, `Identifier`-keyed table of `XoroshiroRandomSource` streams derived from the world seed, which is what makes a loot table and `/random` reproducible across sessions. And a fourth that is not a `RandomSource` at all: `LinearCongruentialGenerator`, the bare mixer `BiomeManager` uses for biome fuzzing. `RandomSource.nextGaussian` is produced by `MarsagliaPolarGaussian`, which caches a spare value — which is why reseeding a source must reset it. ## `Mth` and `Util` `Mth` is the maths grab-bag (677 importers): `Mth.floor`, `Mth.clamp`, `Mth.lerp`, `Mth.wrapDegrees`, `Mth.rotLerp`, `Mth.smallestEncompassingPowerOfTwo`, `Mth.log2`, `Mth.positiveModulo`, `Mth.hsvToRgb`. `Mth.sin` and `Mth.cos` are lookups in a 65,536-entry table (`Mth.cos` is the same table with a quarter-turn phase shift), and the table is filled from the JDK's ordinary sine rather than its strict one — so the platform-dependent step, if you are chasing animation determinism, is the table's construction and not the lookup. `Util` (in `net/minecraft/util`, 454 importers) is where the executors live — `Util.backgroundExecutor`, `Util.ioPool`, `Util.nonCriticalIoPool` — along with time sources and collection helpers; `Unit` is the single-valued "void" type codecs and futures use. The other numeric odds and ends worth knowing by name are `CubicSpline` (terrain shaping), `InclusiveRange`, and `BitStorage`, the packed-integer array underneath every palette. ## What trips people up **`ChunkPos` is a record with no *asLong*.** The names are `ChunkPos.pack` and `ChunkPos.unpack`; construction from a block is `ChunkPos.containing`; the components are accessed as x() and z(). **`Vec3i.toMutable` returns a JOML `Vector3i`,** not a `BlockPos.MutableBlockPos`; the mutable block position is constructed directly and its `BlockPos.MutableBlockPos.set` / `BlockPos.MutableBlockPos.move` are the loop idiom. **`BlockPos` is immutable, `Vec3i` only pretends to be.** `Vec3i` keeps protected setters that `BlockPos.MutableBlockPos` uses; every other subclass treats them as final. `BlockPos.immutable` is the copy to call before storing a mutable one. **`Level.random` deliberately crashes on cross-thread use.** `LegacyRandomSource` holds an atomic seed not for safety but as a *detector*: any concurrent use fails the compare-and-set and raises a `ThreadingDetector` exception — both the reseed and every draw test it. The genuinely safe variant, `ThreadSafeLegacyRandomSource`, and `RandomSource.createThreadSafe` are both deprecated. Touching a level's random from a worker is meant to be loud. **Tick randomness and worldgen randomness are different generators.** The LCG drives every `Level` and `Entity`; the saved `RandomSequences` behind loot and `/random` are Xoroshiro, and so is terrain wherever the noise settings have not opted into legacy — which the nether and the end have. `PositionalRandomFactory.parityConfigString` is implemented by **both** families, so the parity dumps cover whichever one a dimension is on. **`BlockBox` is declared and unused.** It is a tidy `BlockPos`-pair record in `net/minecraft/core`, and in 26.2 nothing calls it; structure bounds are still `BoundingBox` in `world/level/levelgen/structure`. Worth knowing before assuming a rename happened. **`Direction.getRotation` treats UP as identity**, and `Direction.step` returns a fresh JOML vector while `Direction.getUnitVec3f` returns the read- only shared one. **`SectionPos` has `SectionPos.x` as both an instance and a static method,** instance and static, same name. **`BlockUtil` is in `net/minecraft/util`, not `net/minecraft/core`;** `BlockBox`, `BlockMath` and `Cursor3D` are in `net/minecraft/core`. `BlockMath` is model-rotation plumbing, not coordinates. ## Where to look `Vec3i` · `BlockPos` · `ChunkPos` · `SectionPos` · `QuartPos` · `GlobalPos` · `Cursor3D` · `Vec3` · `AABB` · `BoundingBox` · `Direction` · `Rotation` · `OctahedralGroup` · `VoxelShape` · `Shapes` · `CollisionContext` · `HitResult` · `ARGB` · `Mth` · `RandomSource` · `BitRandomSource` · `WorldgenRandom` · `PositionalRandomFactory` · `RandomSequences` · `Util` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Level data and rules > Verified against **Minecraft 26.2** · Reference · Who owns the seed, the spawn, the rules, the border and the dimensions, where each is saved, and what tells the client — looked up, not watched. The facts about a world that are not blocks or entities: its seed and dimensions, its spawn, its game time, its difficulty, its rules, its border, its scoreboard and maps. In 26.2 about half of them have left *level.dat* — `PrimaryLevelData` is a stub and everything else is a `SavedData` file under a *data/* folder, one server-global and one per dimension — so the question this page answers is always the same one: *which file remembers this, and who is allowed to change it.* The table under [who owns what](#who-owns-what) is the page; the sections after it are the prose behind the rows that need it, and the rest are one line each because one line is all there is. [Part IV](../systems/world/README.md) is where these things are used; [the level tick](../systems/server/server-level-tick.md) is where most of them are read. ## Who owns what | datum | owner | saved as | told to the client by | |---|---|---|---| | seed, structures, bonus chest, dimension list | `WorldGenSettings` (`MinecraftServer.getWorldGenSettings`) | *data/minecraft/world_gen_settings.dat* | the obfuscated seed in `CommonPlayerSpawnInfo`, the dimension list in `ClientboundLoginPacket`; structures and the bonus chest, nothing | | world spawn | `PrimaryLevelData.respawnData` | *level.dat* | `ClientboundSetDefaultSpawnPositionPacket` | | game time | `PrimaryLevelData.gameTime` (shared by every level) | *level.dat* | `ClientboundSetTimePacket` | | day time | `ServerClockManager` | *data/minecraft/world_clocks.dat* | `ClientboundSetTimePacket` | | difficulty, lock, hardcore | `LevelSettings.DifficultySettings` | *level.dat* | `ClientboundChangeDifficultyPacket` — hardcore alone rides in `ClientboundLoginPacket` | | game type, allow-commands, name, data packs | `LevelSettings` | *level.dat* | the game type only as a new player's default, through `CommonPlayerSpawnInfo`; the enabled features at the configuration phase; the name and allow-commands, nothing | | game rules | `GameRuleMap` via `GameRules` | *data/minecraft/game_rules.dat* | five rules only, plus `ClientboundGameRuleValuesPacket` on request | | weather | `WeatherData` | *data/minecraft/weather.dat* | `ClientboundGameEventPacket` | | world border | `WorldBorder`, one per `ServerLevel` | *dimensions/minecraft/…/data/minecraft/world_border.dat* | `ClientboundInitializeBorderPacket` and the five `ClientboundSetBorder…` packets | | scoreboard | `ServerScoreboard` (`MinecraftServer.scoreboard`), buffered by `ScoreboardSaveData` at save time | *data/minecraft/scoreboard.dat* | `ClientboundSetObjectivePacket`, `ClientboundSetScorePacket`, `ClientboundSetPlayerTeamPacket` | | maps | `MapItemSavedData` per `MapId`, `MapIndex` for the counter | *data/minecraft/maps/\.dat*, *data/minecraft/maps/last_id.dat* | `ClientboundMapItemDataPacket` | | raids | `Raids` per level | *dimensions/minecraft/…/data/minecraft/raids.dat* | boss bars | | chunk tickets | `TicketStorage` per level ([tickets](../systems/world/tickets-and-loading.md)) | *dimensions/minecraft/…/data/minecraft/chunk_tickets.dat* | — | | dragon fight | `EnderDragonFight`, where `DimensionType.hasEnderDragonFight` | *dimensions/minecraft/…/data/minecraft/ender_dragon_fight.dat* | boss bars | | boss bars, scheduled functions, random sequences, stopwatches, trader timers, command storage | `CustomBossEvents`, `TimerQueue`, `RandomSequences`, `Stopwatches`, `WanderingTraderData`, `CommandStorage` | *data/minecraft/\.dat*, and one *data/\/command_storage.dat* per namespace | boss bars only | | player data | `PlayerDataStorage` | *players/data/\.dat* | — | ## What is left in *level.dat* `LevelData` is the read-only interface every `Level` exposes, and it is small: `LevelData.getRespawnData`, `LevelData.getGameTime`, `LevelData.isHardcore`, `LevelData.getDifficulty` and `LevelData.isDifficultyLocked`. No day time — that has left level data entirely for `ServerClockManager` ([the server tick](../systems/server/server-tick.md)) — no weather, no rules, no border. `LevelData.RespawnData` is the world spawn as a `GlobalPos` with yaw *and* pitch — it carries a dimension, so `/setworldspawn` can point anywhere. `WritableLevelData` adds `WritableLevelData.setSpawn`; `ServerLevelData` adds eight more — the level name, the game type and its setter, `ServerLevelData.isInitialized` and its setter, `ServerLevelData.isAllowCommands` and its setter, and `ServerLevelData.setGameTime`; `WorldData` is the server-wide view (`WorldData.getLevelSettings`, `WorldData.getDataConfiguration`, `WorldData.enabledFeatures`, `WorldData.wasModded`, `WorldData.getKnownServerBrands`, `WorldData.overworldData`). `PrimaryLevelData` implements all of them — the overworld's level data and the whole server's — with about ten fields: `PrimaryLevelData.settings` (a `LevelSettings`: name, `GameType`, a `LevelSettings.DifficultySettings` of difficulty, hardcore and lock, allow-commands, `WorldDataConfiguration`), `PrimaryLevelData.respawnData`, `PrimaryLevelData.gameTime`, `PrimaryLevelData.initialized`, `PrimaryLevelData.knownServerBrands`, `PrimaryLevelData.wasModded`, `PrimaryLevelData.removedFeatureFlags`, `PrimaryLevelData.singlePlayerUUID`, `PrimaryLevelData.version`, and `PrimaryLevelData.specialWorldProperty` (`PrimaryLevelData.SpecialWorldProperty.FLAT` / `PrimaryLevelData.SpecialWorldProperty.DEBUG` / none). That is the whole stub: seed, dimensions, rules, border, weather, dragon fight, boss bars, scheduled events and trader timers are all `SavedData` files. `PrimaryLevelData.createTag` builds the payload — flat, no wrapper — and it is `LevelStorageSource.LevelStorageAccess.saveDataTag` that nests it under *Data*; `PrimaryLevelData.parse` reads it back. `DerivedLevelData` is what every other `ServerLevel` gets, built in `MinecraftServer.createLevels`: spawn, game time and initialised state forwarded to the overworld's data, and everything the whole server shares — name, game type, hardcore, allow-commands, difficulty and its lock — read straight off the `WorldData`. Only `DerivedLevelData.setSpawn` writes anything; `DerivedLevelData.setGameTime`, `DerivedLevelData.setGameType`, `DerivedLevelData.setAllowCommands` and `DerivedLevelData.setInitialized` are no-ops. That forwarding is how game time comes to be shared by every level. The file is written by `LevelStorageSource.LevelStorageAccess.saveDataTag` → `LevelStorageSource.LevelStorageAccess.saveLevelData`: a temp file, then `Util.safeReplaceFile` renames the old *level.dat* to `level.dat_old` and the temp into place, ten retries per step with a rollback. `LevelSummary` (the world-select row) is read from it by `LevelStorageSource.readLevelSummary`. `LevelResource` names the paths: `LevelResource.LEVEL_DATA_FILE`, `LevelResource.DATA`, `LevelResource.PLAYER_DATA_DIR` (*players/data/*, a new sub-folder), `LevelResource.LOCK_FILE`. ### The spawn every level reports is the server's, not each level's `ServerLevel.getRespawnData` forwards to `MinecraftServer.getRespawnData`, which returns `MinecraftServer.effectiveRespawnData` — recomputed by `MinecraftServer.updateEffectiveRespawnData` through `Level.getWorldBorderAdjustedRespawnData`, which **relocates a spawn that has fallen outside the border** to the border centre's surface, and by `MinecraftServer.findRespawnDimension`, which falls back to the overworld when the stored dimension no longer exists. So every level reports the same spawn, and it need not be the one *level.dat* holds: it is the stored one wherever that is still inside the border and its dimension still exists, and a recomputed one where it is not. ## Two saved-data storages, neither of them the overworld's `SavedData` is one flag, `SavedData.dirty` (`SavedData.setDirty`); a `SavedDataType` is an id, a constructor, a `Codec` and a `DataFixTypes`. `SavedDataStorage` — the class that *was* *DimensionDataStorage* — caches them per folder (`SavedDataStorage.computeIfAbsent`, `SavedDataStorage.get`, `SavedDataStorage.set`), writes `.dat` as *{ data, DataVersion }*, gzip-compressed, and saves through `SavedDataStorage.scheduleSave`: dirty entries are encoded on the caller's thread and written on `Util.ioPool` in at most `Util.maxAllowedExecutorThreads` tasks, chained through `SavedDataStorage.pendingWriteFuture`; `SavedDataStorage.saveAndJoin` waits. The id is an `Identifier`, so every saved-data file lives under a namespace folder — the path is *data/\/\.dat* — and vanilla's are all under *data/minecraft/*. Command storage is the one place the namespace is not *minecraft*: each gets its own *data/\/command_storage.dat*. There are two storages. `MinecraftServer.savedDataStorage` (`MinecraftServer.getDataStorage`) is server-global at *\/data/*; maps, scoreboard, rules, weather and clocks are server-wide. `ServerChunkCache.savedDataStorage` (`ServerChunkCache.getDataStorage`, forwarded by `ServerLevel.getDataStorage`) is per dimension at *dimensions/\/\/data/* — the overworld included ([chunk storage](../systems/world/chunk-storage.md)); raids, tickets, the border and the dragon fight are per dimension, the overworld's under *dimensions/* like everyone else's. Neither is "the overworld's". ## Game rules are a registry `GameRule` is a registry entry in `Registries.GAME_RULE` / `BuiltInRegistries.GAME_RULE`, bootstrapped by `GameRules.bootstrap`: `GameRule.category` (a `GameRuleCategory` — `GameRuleCategory.PLAYER`, `GameRuleCategory.MOBS`, `GameRuleCategory.SPAWNING`, `GameRuleCategory.DROPS`, `GameRuleCategory.UPDATES`, `GameRuleCategory.CHAT`, `GameRuleCategory.MISC`), `GameRule.gameRuleType` (`GameRuleType.INT` or `GameRuleType.BOOL`), `GameRule.argument` (a Brigadier type), `GameRule.valueCodec`, `GameRule.defaultValue` and `GameRule.requiredFeatures`. Ids are snake_case and namespaceable — `GameRules.ADVANCE_TIME`, `GameRules.SPAWN_MOBS`, `GameRules.SPAWN_MONSTERS`, `GameRules.KEEP_INVENTORY`, `GameRules.RANDOM_TICK_SPEED` (3), `GameRules.PLAYERS_SLEEPING_PERCENTAGE` (100), `GameRules.RESPAWN_RADIUS` (10), `GameRules.MAX_ENTITY_CRAMMING` (24), `GameRules.MAX_SNOW_ACCUMULATION_HEIGHT` (1), `GameRules.MAX_MINECART_SPEED` (feature-gated) … fifty-nine of them, all in [the reference](gamerules.md). The 1.21 names (*doDaylightCycle*, *doMobSpawning*) and the *GameRules.BooleanValue* / *IntegerValue* / *Key* classes are gone. The values are saved data, not level data: a `GameRuleMap` — `SavedData`, *game_rules.dat*, server-global — wrapped by the `GameRules` instance in `MinecraftServer.gameRules`. `ServerLevel.getGameRules` returns the server's: **one set for every dimension**, and `Level` has no rules accessor at all, so no `ClientLevel` can read a rule — the client's only `GameRules` objects belong to the two rules screens, and neither drives gameplay. The accessors are `GameRules.get`, `GameRules.set` (which calls `MinecraftServer.onGameRuleChanged`) and `GameRules.visitGameRuleTypes` (how `GameRuleCommand.register` builds **two** literals per rule — the bare id and the namespaced one). ### What the client hears Five rules, and everything else is server-only. All three of `GameRules.REDUCED_DEBUG_INFO`, `GameRules.LIMITED_CRAFTING` and `GameRules.IMMEDIATE_RESPAWN` ride in `ClientboundLoginPacket` at join (the last inverted, as *showDeathScreen*); a change afterwards goes as a `ClientboundEntityEventPacket` for the first and a `ClientboundGameEventPacket` for the other two (`ClientboundGameEventPacket.LIMITED_CRAFTING`, `ClientboundGameEventPacket.IMMEDIATE_RESPAWN`); `GameRules.LOCATOR_BAR` through `ServerWaypointManager`; and `GameRules.ADVANCE_TIME`, which broadcasts a full clock sync because a paused clock is expressed on the wire as rate 0 ([environment attributes](../systems/world/environment-attributes-and-timelines.md)). `MinecraftServer.updateMobSpawningFlags` sends nothing; it only calls `Level.setSpawnSettings`, which forwards to `ServerChunkCache.setSpawnSettings`. New is an in-game editor: `ServerboundClientCommandPacket.Action.REQUEST_GAMERULE_VALUES` → `ServerGamePacketListenerImpl.sendGameRuleValues` → `ClientboundGameRuleValuesPacket` → `InWorldGameRulesScreen`, and edits back as `ServerboundSetGameRulePacket` → `ServerGamePacketListenerImpl.handleSetGameRule` (gated on `Permissions.COMMANDS_GAMEMASTER`). ## The border is per dimension `WorldBorder` is `SavedData` — *world_border.dat*, **per dimension**, fetched by `ServerLevel.getWorldBorder` through the cache on every call. Nothing scales the Nether's border by `DimensionType.coordinateScale` — each dimension's file has its own values, so the Nether has the same numbers unless someone sets them. `WorldBorder.settings` (`WorldBorder.Settings`: centre, damage per block, safe zone, warning blocks and time, size, lerp time and target; `WorldBorder.Settings.DEFAULT` is 0,0 / 0.2 / 5 / 5 / 300 / `WorldBorder.MAX_SIZE`) is the *loaded* snapshot, never written again; `WorldBorder.applyInitialSettings` pushes it into the live fields once, restarting a lerp in progress, and saving reads the live fields back out. The live defaults are not the persisted ones — a fresh `WorldBorder` starts with a warning time of 15, not 300. The live extent is a `WorldBorder.BorderExtent` — `WorldBorder.StaticBorderExtent` or `WorldBorder.MovingBorderExtent`, which `WorldBorder.tick` advances ([the level tick](../systems/server/server-level-tick.md)). A moving border re-saves itself every tick: `WorldBorder.MovingBorderExtent` marks the saved data dirty on every advance, and a stationary one never does. `WorldBorder.MAX_SIZE` is 59,999,968; `MinecraftServer.getAbsoluteMaxWorldSize` is applied to every level's border in `MinecraftServer.createLevels` — 29,999,984 on the integrated server, but `DedicatedServer` overrides it with *max-world-size*. `WorldBorder.isWithinBounds`, `WorldBorder.clampToBounds`, `WorldBorder.getDistanceToBorder`, `WorldBorder.getCollisionShape` are the readers; `BorderStatus` colours the client's wall. For sync, `PlayerList.addWorldborderListener` registers a `BorderChangeListener` per level that broadcasts, dimension-scoped, `ClientboundSetBorderSizePacket`, `ClientboundSetBorderLerpSizePacket`, `ClientboundSetBorderCenterPacket`, `ClientboundSetBorderWarningDelayPacket` and `ClientboundSetBorderWarningDistancePacket`; `PlayerList.sendLevelInfo` sends `ClientboundInitializeBorderPacket` on join and dimension change. `ClientLevel.worldBorder` is a plain `WorldBorder` ticked in `ClientLevel.tick`. ## Dimensions and the seed `DimensionType` is a record: `DimensionType.hasSkyLight`, `DimensionType.hasCeiling`, `DimensionType.hasFixedTime`, `DimensionType.hasEnderDragonFight` (the dragon fight is a dimension flag now, not hard-wired to `Level.END`), `DimensionType.coordinateScale`, `DimensionType.minY`, `DimensionType.height`, `DimensionType.logicalHeight`, `DimensionType.infiniburn`, `DimensionType.ambientLight`, `DimensionType.monsterSettings`, `DimensionType.skybox` (`DimensionType.Skybox`), `DimensionType.cardinalLightType`, `DimensionType.attributes` (an `EnvironmentAttributeMap` — where *ultrawarm*, *natural*, *bed_works*, *respawn_anchor_works*, *piglin_safe*, *has_raids* and the fast-lava flag went), `DimensionType.timelines` and `DimensionType.defaultClock` (a `WorldClock` holder; `WorldClocks.OVERWORLD`, `WorldClocks.THE_END`). That is where `DimensionType` lost its booleans: to the attribute map and the timelines. `DimensionType.getStorageFolder` names the on-disk folder. Defaults are `DimensionDefaults` (`DimensionDefaults.OVERWORLD_MIN_Y` −64, `DimensionDefaults.OVERWORLD_LEVEL_HEIGHT` 384, `DimensionDefaults.NETHER_LOGICAL_HEIGHT` 128); the built-in keys are `BuiltinDimensionTypes.OVERWORLD`, `BuiltinDimensionTypes.NETHER`, `BuiltinDimensionTypes.END`, `BuiltinDimensionTypes.OVERWORLD_CAVES`. `LevelStem` is a `DimensionType` holder plus a `ChunkGenerator` (`LevelStem.OVERWORLD`, `LevelStem.NETHER`, `LevelStem.END`). `Registries.LEVEL_STEM` and `Registries.DIMENSION` share the id *dimension* ([identifiers and registries](../systems/foundations/identifiers-and-registries.md)); `Level.OVERWORLD`, `Level.NETHER`, `Level.END` are `ResourceKey`s under the latter, and `Level.dimension` / `Level.dimensionType` are the accessors. `Level.canHaveWeather` is sky light, no ceiling, not the End. The seed and the dimension list are `WorldGenSettings` — `SavedData`, *world_gen_settings.dat*, server-global, and written not at world creation but by the `MinecraftServer` constructor, which pushes it into the storage on every boot ([creating a world](../systems/worldgen/creating-a-world.md) is where it comes from) — holding `WorldOptions` (`WorldOptions.seed`, `WorldOptions.generateStructures`, `WorldOptions.generateBonusChest`) and `WorldDimensions` (the stem map; `WorldDimensions.bake` produces the registry). It is read before the server exists by `LevelStorageSource.getLevelDataAndDimensions` and pushed in with `SavedDataStorage.set`; if the file is missing or unreadable the loader logs an error and substitutes a whole default `WorldGenSettings` — a random seed, structures on, no bonus chest, and the data packs' dimension list in place of the saved one — then carries on loading the world. `MinecraftServer.createLevels` makes one `ServerLevel` per stem; `MinecraftServer.levelKeys` go out in `ClientboundLoginPacket`, the dimension type via registry sync, and the biome-zoom-obfuscated seed in `CommonPlayerSpawnInfo`. ## Difficulty and weather `Difficulty` (`Difficulty.PEACEFUL` … `Difficulty.HARD`) sits in `LevelSettings.DifficultySettings`. `MinecraftServer.setDifficulty` writes it, `MinecraftServer.updateMobSpawningFlags`, and `MinecraftServer.sendDifficultyUpdate` → `ClientboundChangeDifficultyPacket`. `DedicatedServer.forceDifficulty` applies *server.properties* at boot with the lock ignored; there is no *getForcedDifficulty*. `DifficultyInstance` — local difficulty — is built by `ServerLevel.getCurrentDifficultyAt` from `ChunkAccess.getInhabitedTime`, `Level.getOverworldClockTime` and the moon phase (an environment attribute, `EnvironmentAttributes.MOON_PHASE`, indexed into `DimensionType.MOON_BRIGHTNESS_PER_PHASE` — the one piece of the old moon logic still on `DimensionType`; see [environment attributes](../systems/world/environment-attributes-and-timelines.md)). `WeatherData` — server-global `SavedData`, `MinecraftServer.getWeatherData` — was covered in [the level tick](../systems/server/server-level-tick.md). `PrimaryLevelData` stores no rain fields. ## Where to look `PrimaryLevelData.setTagData` · `DerivedLevelData` · `LevelSettings` · `LevelStorageSource.getLevelDataAndDimensions` · `LevelStorageSource.LevelStorageAccess.saveDataTag` · `SavedDataStorage.scheduleSave` · `SavedDataType` · `MinecraftServer.createLevels` · `MinecraftServer.getDataStorage` · `ServerChunkCache.getDataStorage` · `GameRules.bootstrap` · `GameRules.set` · `MinecraftServer.onGameRuleChanged` · `GameRuleMap` · `GameRuleCommand.register` · `WorldBorder.applyInitialSettings` · `WorldBorder.tick` · `PlayerList.addWorldborderListener` · `DimensionType` · `LevelStem` · `WorldGenSettings` · `WorldDimensions.bake` · `MinecraftServer.setDifficulty` · `ServerLevel.getCurrentDifficultyAt` --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Naming drift > Verified against **Minecraft 26.2** · Reference · The translation > layer: every name a 1.21-era reader will reach for that 26.2 does not have, > and what it is called now. Rule three of this corpus is *newest version only*: no page says "in 1.21 this was…", because version-difference prose is the first thing to rot and the last thing anyone rereads. That rule costs something, and this page is where the cost is paid back once. Every page assumes you are reading the 26.2 tree; this page assumes you are not, yet, and are still typing the names you learned somewhere else. Two audiences. A reader coming from **1.21** — the version most public writing, most tutorials and most model weights are anchored to — needs the first table: the old name on the left, what to grep for on the right. A reader coming from **Yarn** (Fabric's community mappings) needs the last one: not a version difference at all, just a different name for the same class in the same version. The one sentence: *if a name in your head does not appear in the tree, it is probably on this page.* ## How to read the tables The left column is **italic, not backticked**. Most of these names do not exist in 26.2, and `tools/verify_names.py` — which checks every backticked identifier on every page against the decompile — would reject the page if they were. Italics is the corpus's mark for *a name, but not a 26.2 name*. The right column is backticked and therefore verified: those names are in the tree. "gone" in the right column means exactly that: there is no replacement class, the responsibility moved into something structurally different, and the entry names where it went. Those are the interesting rows — a rename is a nuisance, a disappearance is a design change, and the page named beside each part explains it. Every row here was found the same way: a fact-sheet agent reading the 26.2 decompile went looking for a name it expected and did not find it. The table is therefore *not* exhaustive — it is exhaustive over the names the corpus needed. Two hundred and forty-three rows, and the distribution is itself a finding: the three biggest tables are **commands** (36), **the server** (31) and **items** (30), and the fourth is **rendering** (27). The client was rewritten around extract-then-render, which is why almost nothing at the top of the render stack kept its name — but the permission rewrite and the game-rule registry moved more names than the renderer did. ## The four you will hit in the first ten minutes `Identifier` is *ResourceLocation*. `Lightmap` is *LightTexture*. `DeltaTracker` is *Timer*, and `partialTick` is now a `DeltaTracker.Timer` you ask rather than a float you are handed. And `Gui` no longer means the HUD: the HUD is `Hud`, held as `Gui.hud`, while `Gui` is the screen and overlay manager that also owns `Gui.screen` and `Gui.setScreen` — so a 1.21-era `Minecraft.setScreen` call site is now on `Gui`. Both `Gui` and `Hud` exist, which is the single most confusing pair of names in the tree. ## The tables ### Everywhere | the name you remember | 26.2 | |---|---| | *ResourceLocation* | `Identifier` | | *LightTexture* | `Lightmap` | | *Timer* | `DeltaTracker` | ### Part II — Foundations | the name you remember | 26.2 | |---|---| | *TagManager* | gone | | *Minecraft.reloadResources* | `Minecraft.reloadResourcePacks` | | *ItemStack.save* / *parse* | `ValueOutput` / `ItemStack.CODEC` | | *ChunkPos.asLong* | `ChunkPos.pack` / `ChunkPos.unpack` (a record) | ### Part III — The server | the name you remember | 26.2 | |---|---| | *DO_DAYLIGHT_CYCLE* | `GameRules.ADVANCE_TIME` | | *DO_MOB_SPAWNING* | `GameRules.SPAWN_MOBS` | | *DO_WEATHER_CYCLE* | `GameRules.ADVANCE_WEATHER` | | *GameRules* package | `world/level/gamerules` | | *GameRules.Key<T>* / *GameRules.Value* / *BooleanValue* / *IntegerValue* / *GameRules.Type* (all nested) | top-level `GameRule`, with `GameRuleType`, `GameRuleTypeVisitor`, `GameRuleMap` for the values and `GameRuleCategory` for the grouping | | game rules as a hard-coded map | a **registry** — `Registries.GAME_RULE` / `BuiltInRegistries.GAME_RULE`, bootstrapped by `GameRules` | | *level.dat* field *GameRules*, ids camelCase and unnamespaced | field *game_rules*, ids namespaced (*minecraft:advance_time*) — the whole rename table is `GameRuleRegistryFix` | | *doEntityDrops* | `GameRules.ENTITY_DROPS` | | *doImmediateRespawn* | `GameRules.IMMEDIATE_RESPAWN` | | *doInsomnia* | `GameRules.SPAWN_PHANTOMS` | | *doLimitedCrafting* | `GameRules.LIMITED_CRAFTING` — the name survives elsewhere, as a component of `ClientboundLoginPacket` and a field on `LocalPlayer` | | *doPatrolSpawning* / *doTraderSpawning* / *doWardenSpawning* | `GameRules.SPAWN_PATROLS` / `GameRules.SPAWN_WANDERING_TRADERS` / `GameRules.SPAWN_WARDENS` | | *doVinesSpread* | `GameRules.SPREAD_VINES` | | *enableCommandBlocks* **and** *commandBlocksEnabled* | one rule, `GameRules.COMMAND_BLOCKS_WORK` | | *spawnerBlocksEnabled* | `GameRules.SPAWNER_BLOCKS_WORK` | | *commandModificationBlockLimit* | `GameRules.MAX_BLOCK_MODIFICATIONS` | | *minecartMaxSpeed* | `GameRules.MAX_MINECART_SPEED` | | *snowAccumulationHeight* | `GameRules.MAX_SNOW_ACCUMULATION_HEIGHT` | | *spawnRadius* | `GameRules.RESPAWN_RADIUS` | | *disableElytraMovementCheck* | `GameRules.ELYTRA_MOVEMENT_CHECK` — **inverted** | | *disablePlayerMovementCheck* | `GameRules.PLAYER_MOVEMENT_CHECK` — **inverted** | | *disableRaids* | `GameRules.RAIDS` — **inverted** | | *doFireTick* + *allowFireTicksAwayFromPlayer* (two booleans) | one integer, `GameRules.FIRE_SPREAD_RADIUS_AROUND_PLAYER` (0 none, 128 near players only, −1 everywhere) | | *spawnChunkRadius*, *entitiesWithPassengersCanUsePortals*, *gameLoopFunction* | gone with no replacement — the fix simply deletes them | | day time on *ServerLevel* | `ServerClockManager` (`world/clock`) | | per-level weather | server-global `WeatherData` | | *GameProfile* on the player lists | `NameAndId` (a record of UUID and name) — `PlayerList.canPlayerLogin`, `PlayerList.isWhiteListed`, `PlayerList.op`, the ban/op/whitelist files | | *ServerPlayer.sendAllPlayerInfo* / *sendActivePlayerEffects* | `PlayerList.sendAllPlayerInfo` / `PlayerList.sendActivePlayerEffects` | | *MinecraftServer.getScheduledEvents* returning a per-level queue | the same name, returning a server-wide `TimerQueue` saved data, advanced only by the overworld's `ServerLevel.tickTime` | | *ServerLevel.updateSkyBrightness* reading day time | the method survives, declared on `Level`, and now reads `EnvironmentAttributes.SKY_LIGHT_LEVEL` through `EnvironmentAttributeSystem` | | *ChunkMap.forEachBlockTickingChunk* meaning block-ticking | it walks the **entity**-ticking set; the name did not follow the split | ### Part IV — The world | the name you remember | 26.2 | |---|---| | *ChunkStorage* | gone — `ChunkMap extends SimpleRegionStorage` | | *DimensionDataStorage* | `SavedDataStorage` (two of them) | | *getLightBlock* | `BlockBehaviour.BlockStateBase.getLightDampening` | | *PalettedContainer.Strategy* | top-level `Strategy` + `Configuration` | | *ForcedChunksSavedData* | `TicketStorage` | | *TicketType<T>* | a registry record with flag bits | | *DimensionType* booleans | `EnvironmentAttributeMap` | | *DimensionType.ultraWarm* | split four ways: `EnvironmentAttributes.FAST_LAVA`, `EnvironmentAttributes.WATER_EVAPORATES`, `EnvironmentAttributes.INCREASED_FIRE_BURNOUT`, `EnvironmentAttributes.SNOW_GOLEM_MELTS` | | *DimensionType.piglinSafe* | `EnvironmentAttributes.PIGLINS_ZOMBIFY` — **inverted** | | *DimensionType.bedWorks* | `EnvironmentAttributes.BED_RULE`, a `BedRule` record, not a boolean | | *DimensionType.hasRaids* | `EnvironmentAttributes.CAN_START_RAID` | | *DimensionType.natural* | `EnvironmentAttributes.NETHER_PORTAL_SPAWNS_PIGLINS` and neighbours | | *DimensionType.fixedTime* | `DimensionType.hasFixedTime`, a bare boolean — the time itself moved to `WorldClock` and `Timelines.OVERWORLD_DAY` | | *DimensionType.ambientLight* | unchanged; one of the three visual fields that did not become an attribute, with `DimensionType.skybox` and `DimensionType.cardinalLightType` | | *Schedule* (the villager's) | `EnvironmentAttributes.VILLAGER_ACTIVITY` on `Timelines.VILLAGER_SCHEDULE` | | *Level.dayTime* | `ServerClockManager`, keyed by `WorldClock` | | *data/<id>.dat* | *data/<namespace>/<id>.dat* — every saved-data file gained a namespace folder | ### Part V — Blocks | the name you remember | 26.2 | |---|---| | *ItemInteractionResult* | gone — `InteractionResult.TryEmptyHandInteraction` | | *DirectionProperty* | gone — `EnumProperty` | | *Level.markAndNotifyBlock* | gone — inline in `Level.setBlock` | | *BlockBehaviour.onRemove* | `BlockBehaviour.affectNeighborsAfterRemoval` + `BlockEntity.preRemoveSideEffects` | | *doTileDrops* | `GameRules.BLOCK_DROPS` | | *BlockModelShaper* | `BlockStateModelSet` / `BlockModelSet` | | *RenderShape.ENTITYBLOCK_ANIMATED* | gone — `RenderShape.INVISIBLE` / `RenderShape.MODEL` only | | *Player.canInteractWithBlock* | `Player.isWithinBlockInteractionRange` | | *Block.rebuildCache* | gone — `BlockBehaviour.BlockStateBase.initCache` from the `Blocks` static init | | *Material* | gone — individual `BlockBehaviour.Properties` flags | | *BlockEntity.saveToItem* | `BlockItem.setBlockEntityData` + `BlockEntity.collectComponents` | | *MobEffects.DIG_SPEED* / *DIG_SLOWDOWN* | `MobEffects.HASTE` / `MobEffects.MINING_FATIGUE` | ### Part VI — Entities | the name you remember | 26.2 | |---|---| | *Player extends LivingEntity* | `Player extends Avatar extends LivingEntity` | | *EntityType.PIG* (constants) | `EntityTypes.PIG` + `EntityTypeIds.PIG` | | *MobSpawnType* | `EntitySpawnReason` (+ `EntitySpawnRequest`) | | *SpawnPlacements.Type* | `SpawnPlacementType` / `SpawnPlacementTypes` | | *Entity.hurt(DamageSource, float)* returning a boolean | split into `Entity.hurtServer` and `Entity.hurtClient`. Both old shapes survive as deprecated finals — `Entity.hurt` delegating to the server half, `Entity.hurtOrSimulate` as the boolean-returning successor — so grep still finds the name | | *doMobLoot* | `GameRules.MOB_DROPS` | | *LivingEntity.isDamageSourceBlocked* | gone — `DataComponents.BLOCKS_ATTACKS` | | *Schedule* / *ScheduleBuilder* | gone — `Timeline` + `EnvironmentAttribute` | | *BlockPathTypes* | `PathType` | | *Mob.brainProvider* | `LivingEntity.makeBrain(Brain.Packed)` | | *Entity.moveTo* / *absMoveTo* | `Entity.snapTo` / `Entity.absSnapTo` | | *Entity.maxUpStep* (field) | `Attributes.STEP_HEIGHT` | | *Entity.updateFluidHeightAndDoFluidPushing* | `EntityFluidInteraction` | | *Entity.lerpTo* | `Entity.moveOrInterpolateTo` + `InterpolationHandler` | | *EntityDataSerializers.OPTIONAL_UUID* / *COMPOUND_TAG* | gone | | UUID-keyed *AttributeModifier* | `Identifier`-keyed record | | *AttributeMap.getDirtyAttributes* | `AttributeMap.getAttributesToSync` + `AttributeMap.getAttributesToUpdate` | | *PlayerRenderer* | `AvatarRenderer` (serves players and mannequins, keyed by skin model) | ### Part VII — Items and inventories | the name you remember | 26.2 | |---|---| | *InteractionResultHolder* | gone — `InteractionResult.Success.heldItemTransformedTo` | | *UseAnim* | `ItemUseAnimation` | | *Item.getFoodProperties* | `DataComponents.FOOD` on the stack | | *ItemStack.getTag* / *getOrCreateTag* | gone — components | | *LivingEntity.triggerItemUseEffects* | `Consumable.emitParticlesAndSounds` | | *FoodProperties* effects list | `Consumable.onConsumeEffects` | | *ClickType* | `ContainerInput` | | *MultiPlayerGameMode.handleInventoryMouseClick* | `MultiPlayerGameMode.handleContainerInput` | | *ClientboundSetCarriedItemPacket* | split: `ClientboundSetCursorItemPacket` + `ClientboundSetHeldSlotPacket` | | *ClientboundSetSlotPacket* | `ClientboundContainerSetSlotPacket` | | *ClientboundHorseScreenOpenPacket* | `ClientboundMountScreenOpenPacket` | | *Container.startOpen(Player)* | `Container.startOpen(ContainerUser)` | | *Recipe.getResultItem* / *getIngredients* | `Recipe.assemble` / `PlacementInfo`. The first is gone outright; the second is off the `Recipe` interface and survives only as a test-visible method on `ShapedRecipe` | | *Ingredient.EMPTY* | gone — `Ingredient.CODEC` rejects an empty literal list, but a tag that resolves to nothing still yields an empty one, hence `Ingredient.isEmpty` | | *ClientboundUpdateRecipesPacket* carrying recipes | property sets + the stonecutter input set; the book gets `RecipeDisplayEntry`s | | *net.minecraft.advancements.CriteriaTriggers* | `CriteriaTriggers`, moved to `net/minecraft/advancements/triggers` | | *Player.permissionLevel* / *hasPermissions(int)* | `Player.permissions` → a `PermissionSet`, queried by named `Permissions` keys | | *ServerboundPlayerCommandPacket.Action.PRESS_SHIFT_KEY* / *RELEASE_SHIFT_KEY* | gone — sneak rides `ServerboundPlayerInputPacket` → `Entity.setShiftKeyDown` | | *Mannequin* on the client | `ClientMannequin`, installed by swapping the mutable `Mannequin.constructor` factory at client startup | | *data/<ns>/recipes/* | `data//recipe/` (singular) | | *EnchantmentCategory* | `Enchantment.EnchantmentDefinition` item sets | | *Enchantment.getDamageBonus*, *EnchantmentHelper.getFireAspect*… | gone — `EnchantmentEffectComponents` | | *EnchantedBookItem* | gone — `DataComponents.STORED_ENCHANTMENTS` | | *Item.getEnchantmentValue* | `DataComponents.ENCHANTABLE` | | *LootContextParam* / *LootContextParamSet* | `ContextKey` / `ContextKeySet` (`util/context`) | | *LootDataManager* / *LootTables* | `ReloadableServerRegistries` + `BuiltInLootTables` | | *LootTableReference* | `NestedLootTable` | | *LootingEnchantFunction* | `EnchantedCountIncreaseFunction` | | *SetCountFunction* | `SetItemCountFunction` | | *LootContextParams.KILLER_ENTITY* | `LootContextParams.ATTACKING_ENTITY` | ### Part VIII — The player | the name you remember | 26.2 | |---|---| | *Inventory.armor* / *offhand* / *compartments* | one 36-slot `Inventory.items` + `Inventory.EQUIPMENT_SLOT_MAPPING` | | *Inventory.setPickedItem* | `Inventory.addAndPickItem` / `Inventory.pickSlot` | | *Entity.moveTo* | `Entity.absSnapTo` / `Entity.snapTo` | | *GameRenderer.pick* | `Minecraft.pick` → `LocalPlayer.raycastHitResult` | | *ServerboundInteractPacket.Action.ATTACK* | `ServerboundAttackPacket` (a record of one int) | | *GameRules.NATURAL_REGENERATION* | `GameRules.NATURAL_HEALTH_REGENERATION` | | *isCritArrow* / *Player.sweepAttack* | `Player.canCriticalAttack` / `Player.isSweepAttack` + `Player.doSweepAttack`, all three private. *isCritArrow* was never a `Player` method and is still live on `AbstractArrow` | | *LivingEntity.eat* / *Player.eat* | gone — `Consumable.onConsume` → `FoodProperties` → `FoodData.eat` | | *MobEffect.createModifier* | `MobEffect.createModifiers` (plural) | ### Part IX — Networking | the name you remember | 26.2 | |---|---| | *Connection.setListener* / *setProtocol* / *getCurrentProtocol* | gone — `Connection.setupInboundProtocol` / `Connection.setupOutboundProtocol` | | *ConnectionProtocol.getById* / packet tables | gone — a bare enum; ids are `ProtocolInfoBuilder.addPacket` order in `IdDispatchCodec` | | *Connection.NETWORK_WORKER_GROUP* etc. | `EventLoopGroupHolder` (in `server/network`) | | *MemoryConnection* | gone — `Connection.isMemoryConnection` | | *ensureRunningOnSameThread(…, BlockableEventLoop)* | `PacketUtils.ensureRunningOnSameThread` with a `PacketProcessor` | | *Packet.write(FriendlyByteBuf)* | gone — a per-packet `StreamCodec` constant the protocol reads | | *ClientboundAddPlayerPacket* / *ClientboundAddMobPacket* | gone — `ClientboundAddEntityPacket` | | *ClientboundUpdateViewPositionPacket* | `ClientboundSetChunkCacheCenterPacket` | | *ClientboundUpdateViewDistancePacket* | `ClientboundSetChunkCacheRadiusPacket` | | *ClientboundLevelChunkPacket* | `ClientboundLevelChunkWithLightPacket` | | routine *ClientboundTeleportEntityPacket* | `ClientboundEntityPositionSyncPacket` | | *ClientboundGameProfilePacket* | `ClientboundLoginFinishedPacket` (+ a session id) | | *ServerboundLoginStartPacket* | `ServerboundHelloPacket` | | *ClientboundEncryptionRequestPacket* / response | `ClientboundHelloPacket` / `ServerboundKeyPacket` | | *ClientboundSetCompressionPacket* | `ClientboundLoginCompressionPacket` | | *ClientboundResourcePackPacket* | `ClientboundResourcePackPushPacket` / `…PopPacket` | | *MinecraftServer.getSessionService* | `MinecraftServer.services` | | *PlayerChunkSender* in *server/level* | `server/network` | | *Component.Serializer* (Gson) | `ComponentSerialization` (codecs; NBT on the wire) | | *TextComponent* / *TranslatableComponent* / … | `network/chat/contents` — `PlainTextContents` etc. | | *ComponentUtils.updateForEntity* | `ComponentUtils.resolve` with a `ResolutionContext` | | *SignedMessageHeader* / *MessageSigner* | `SignedMessageLink` / `SignedMessageChain.Encoder` | | *ChatPreview* and its packets | gone | | *ClientboundSetTimePacket(gameTime, dayTime, …)* | a game time plus a `WorldClock` update map | ### Part X — The client | the name you remember | 26.2 | |---|---| | *Gui* (the HUD) | `Hud`, held as `Gui.hud`; the name `Gui` now means the screen/overlay manager | | *Minecraft.screen* / *Minecraft.setScreen* | `Gui.screen` / `Gui.setScreen` | | *GuiGraphics* | `GuiGraphicsExtractor` (records states; does not draw) | | *Screen.render* / every *render** on *Gui* | `Screen.extractRenderState` / every *extract\** on `Hud` | | *LayeredDraw* | call order plus `GuiRenderState.nextStratum` | | *Options.hideGui* | `Hud.isHidden`, published as `GuiRenderState.isHudHidden` | | *Minecraft.getPartialTick*, *Timer* | `DeltaTracker.Timer` and its three questions | | *Minecraft.destroy* | gone — `Minecraft.stop`, then `Minecraft.exitWorldAndClose` and `Minecraft.close` | | *Options.keyBindings* | `Options.keyMappings`; `KeyMapping.Category` is a registrable record, not a string | | *Options.mouseSensitivity* | the field is `Options.sensitivity` with an accessor of that name; *mouseSensitivity* survives only as the key in *options.txt* | | *MouseHandler.lastMouseEventTime* | gone | | raw *(key, scancode, modifiers, action)* on every `Screen` method | the `client/input` records: `KeyEvent`, `MouseButtonEvent`, `CharacterEvent`, `PreeditEvent` | | *ClientChunkCache.ChunkArray* | `ClientChunkCache.Storage` | | *Font.drawInBatch* and every *drawString* variant | `Font.prepareText` → `Font.PreparedText`; the drawing verbs are on `GuiGraphicsExtractor` | | *Font.StringRenderOutput* | `Font.PreparedText` plus `Font.GlyphVisitor` | | *BakedGlyph* (a class) | an interface; the sheet implementation is `BakedSheetGlyph`, effects are `EffectGlyph` | | *RawGlyph* / *SheetGlyphInfo* | `UnbakedGlyph` (info and bake) and `GlyphBitmap` (pixels and upload) | | *GlyphProviderBuilder* / *GlyphProviderBuilderType* | `GlyphProviderDefinition` / `GlyphProviderType` | | *Style.withFont* taking an id | still `Style.withFont`, but the type is `FontDescription`, which may be a sprite rather than a font | | *FontSet.getGlyph* as public API | private — `FontSet.source` then `GlyphSource.getGlyph` | ### Part XI — Rendering Twenty-seven rows, and almost all of them are one refactor: extract then render. | the name you remember | 26.2 | |---|---| | *MultiBufferSource* / *BufferSource* | `SubmitNodeCollector` / `SubmitNodeStorage` / `FeatureRenderDispatcher` | | *ShaderInstance*, *RenderStateShard* | `RenderPipeline` + `RenderPipelines` + `BindGroupLayouts` | | *VertexBuffer*, *Tesselator*, *BufferUploader* | `GpuBuffer` / `GpuBufferSlice`, `ByteBufferBuilder` → `MeshData`, `UberGpuBuffer` | | *RenderSystem.setShader* / *enableBlend* / *depthMask* … | fields of a `RenderPipeline` | | *VertexFormat.Mode*, *VertexFormat.IndexType*, *TextureFormat* | `PrimitiveTopology`, `IndexType`, `GpuFormat` | | *Window.updateDisplay*, vsync as a swap interval | `GpuSurface.present`, vsync as a `GpuSurface.PresentMode` | | *LightTexture.pack* and friends | `LightCoordsUtil` | | *DimensionSpecialEffects* | `DimensionType.skybox` + `EnvironmentAttributes` + `Timeline` | | *FogParameters*, *RenderSystem.setShaderFogColor* | `FogData`, `RenderSystem.setShaderFog` (a uniform slice) | | *Level.getSkyColor*, *ClientLevel.getStarBrightness*, *ClientLevel.effects* | `EnvironmentAttributeProbe.getValue` on an `EnvironmentAttribute` | | *LevelRenderer.renderLevel* / *renderSky* / *renderChunkLayer* | `LevelRenderer.render` and the `LevelRenderer.addSkyPass` family of frame-graph passes | | *LevelRenderer.blockChanged* / *setSectionDirty* / *allChanged* | the same names on `LevelExtractor` | | *ChunkRenderDispatcher*, *RenderChunk*, *CompiledChunk* | `SectionRenderDispatcher`, its `SectionRenderDispatcher.RenderSection`, `CompiledSectionMesh` | | *RenderType.chunkBufferLayers* (five layers) | `ChunkSectionLayer` — three layers | | *BakedModel*, *ModelResourceLocation* | `BlockStateModel` / `ItemModel`; block models keyed by `BlockState` | | *BlockModelShaper*, *ItemModelShaper*, *BlockRenderDispatcher*, *ItemRenderer* | `BlockStateModelSet`, `ItemModelResolver`, `ModelBlockRenderer` | | *BlockElement* / *BlockElementFace*, *AtlasSet*, *ItemColors* | `CuboidModelElement` / `CuboidFace`, `AtlasManager`, `ItemTintSource` | | *EntityRenderer.render*, *RenderLayer.render* | `EntityRenderer.extractRenderState` + `EntityRenderer.submit` | | *TextureSheetParticle*, sheet *ParticleRenderType*s | `SingleQuadParticle` + `SingleQuadParticle.Layer` | | *ParticleGroup* (a limit record) | `ParticleLimit`; `ParticleGroup` is now the per-render-type bucket | | *Camera.setup* | `Camera.update` + `Camera.extractRenderState` | | *RenderStateShard* composition (the texture/target/layering half) | `RenderType` over a `RenderPipeline`, catalogued in `RenderTypes`, built by `RenderSetup` | | *BakedQuad* as four vertices | a ten-component record, with a `BakedQuad.MaterialInfo` of six | | *LiquidBlockRenderer* | `FluidRenderer`, over a `FluidModel` | | *ItemOverrides* / *getPropertyOverride* | `SelectItemModel` / `RangeSelectItemModel` / `ConditionalItemModel` | | *ScreenManager* (the Blaze3D monitor manager) | `MonitorManager`, with `Monitor` and `VideoMode` — same package, same GLFW monitor callback | | *Window.setVsync* | a `GpuSurface.PresentMode` in the surface configuration | ### Part XII — World generation | the name you remember | 26.2 | |---|---| | *GenerationStep.Carving* | gone — `BiomeGenerationSettings.carvers` is one flat `HolderSet` | | *DensityFunctions.WeirdScaledSampler* | `DensityFunctions.IntervalSelect` | | *StructureFeature* / *ConfiguredStructureFeature* | `Structure` / `Registries.STRUCTURE` | | *Feature.RANDOM_PATCH*, *Feature.FLOWER* | gone — composed from `Feature.SIMPLE_BLOCK` + placement | | *Feature.POINTED_DRIPSTONE* / *DRIPSTONE_CLUSTER* | `Feature.SPELEOTHEM` / `Feature.SPELEOTHEM_CLUSTER` | | *AbstractTreeGrower* and its subclasses | one final `TreeGrower` with constants | | *TreeConfiguration.dirtProvider* | `TreeConfiguration.belowTrunkProvider` | | *Biome.BiomeCategory* / *Biome.getDownfall* | gone | | *MultiNoiseBiomeSource.Preset* | `MultiNoiseBiomeSourceParameterList.Preset` | | *BiomeSpecialEffects.fogColor* / *skyColor* / music / ambient sound | `EnvironmentAttributes.*` via `Biome.getAttributes` | | the +8 chunk population offset | gone — decoration starts at the chunk corner, `InSquarePlacement` scatters | | *StructureTemplateManager* folder *structures/* | *structure/* | ### Part XIII — Commands and data packs The permission rewrite is the largest single break in this table: the integer permission level is gone from the whole command API, replaced by `PermissionSet` and `PermissionCheck` in `net/minecraft/server/permissions`. The ints survive only in *ops.json*, in *server.properties* and on the wire. | the name you remember | 26.2 | |---|---| | *ResourceLocationArgument* | `IdentifierArgument` (the registry id is unchanged) | | *CommandSourceStack.hasPermission(int)* | `CommandSourceStack.permissions` + `PermissionSet.hasPermission` | | *CommandSourceStack.getPermissionLevel* | gone — a source carries a `PermissionSet`. `PermissionLevel` itself is very much alive: `LevelBasedPermissionSet`, *server.properties*, `ServerOpListEntry` and the JSON-RPC schema all still speak it | | *CommandSourceStack.withPermission(int)* | `CommandSourceStack.withPermission` taking a `PermissionSet` | | *SharedSuggestionProvider.hasPermission(int)* | gone — the interface extends `PermissionSetSupplier` | | *Commands.LEVEL_GAMEMASTERS* as an int | same name, now a `PermissionCheck` | | *Commands.hasPermission(int)* | `Commands.hasPermission` taking a `PermissionCheck`, returning a `PermissionProviderCheck` | | *ServerPlayer.hasPermissions(int)* | `ServerPlayer.permissions` | | *MinecraftServer.getProfilePermissions* returning an int | the same name returning a `LevelBasedPermissionSet` | | *MinecraftServer.getFunctionCompilationLevel* | `MinecraftServer.getFunctionCompilationPermissions` | | *Commands.LEVEL_ALL* / *LEVEL_MODERATORS* / *LEVEL_ADMINS* / *LEVEL_OWNERS* as ints | all four are `PermissionCheck`s too — `PermissionCheck.AlwaysPass` for the first, `PermissionCheck.Require` for the rest | | *ServerPlayer.setPermissionLevel(int)* | `PlayerList.sendPlayerPermissionLevel` on the server; `LocalPlayer.setPermissions` on the client | | *ColorArgument* | `TeamColorArgument`, yielding a `TeamColor` rather than a `ChatFormatting` | | *PlayerTeam.getColor* returning a *ChatFormatting* | returns an optional `TeamColor`, its own enum carrying a `TextColor` | | *TestFunctionArgument* / *TestClassNameArgument* | gone — `/test` addresses tests as registry ids through `ResourceSelectorArgument` and `TestFinder` | | *net.minecraft.advancements.Criterion* / *CriterionTrigger* / *SimpleCriterionTrigger* | all moved to `net/minecraft/advancements/triggers`; `CriterionTriggerInstance` is the one that stayed behind in `net/minecraft/advancements` | | *ServerOpListEntry.getLevel* | `ServerOpListEntry.permissions` | | *ParserUtils.parseJson* | gone — `SnbtGrammar` plus `ParserBasedArgument` | | *ItemInput.createItemStack(int, boolean)* | `ItemInput.createItemStack` with one argument; the guard is `GiveCommand.MAX_ALLOWED_ITEMSTACKS` | | *ServerFunctionManager.ExecutionContext* (nested) | top-level `ExecutionContext` in `net/minecraft/commands/execution` | | *CommandFunction.Entry* / *CommandEntry* / *FunctionEntry* | gone — a line is a `BuildContexts.Unbound`, a macro line a `MacroFunction.MacroEntry` | | *CommandFunction.CacheableFunction* (nested) | top-level `CacheableFunction`, codec-backed | | *Commands.performCommand* returning a success count | returns nothing; results are a `CommandResultCallback` pair | | `data//functions/`, `data//tags/functions/` | singular — *function/* and *tags/function/* | | *maxCommandChainLength* | `GameRules.MAX_COMMAND_SEQUENCE_LENGTH` | | *maxCommandForkCount* | `GameRules.MAX_COMMAND_FORKS` | | *announceAdvancements* | `GameRules.SHOW_ADVANCEMENT_MESSAGES` | | *net.minecraft.advancements.critereon* | split **three** ways: `net/minecraft/advancements/triggers`, `net/minecraft/advancements/predicates`, and `advancements/predicates/entity` for the entity half | | *AdvancementList* | `AdvancementTree` (+ `AdvancementNode`, `AdvancementHolder`) | | *FrameType* | `AdvancementType` | | *CriterionTrigger.addPlayerListener* / *removePlayerListener* | gone — triggers are stateless; subscriptions live in `PlayerAdvancements` | | *LootContextParamSet* | `ContextKeySet` | | *@GameTest*, *@GameTestGenerator*, *@BeforeBatch*, *@AfterBatch* | gone — `GameTestInstance` in `Registries.TEST_INSTANCE` | | *GameTestRegistry* / *TestFunction* | gone — `Registries.TEST_FUNCTION` + `TestFunctionLoader`, and `TestData` | | a test's batch as a string | `GameTestInstance.batch` — the batch *is* a `TestEnvironmentDefinition` | | the structure block as the test host | `TestInstanceBlock` / `TestInstanceBlockEntity` | ## The shape changes, not just the names A rename table flatters the reader: it suggests that if you learn the two hundred and forty-three rows above you can read the tree. You cannot, because a dozen of these rows are one design change each, and the change is what the corresponding page is about. The recurring ones: - **Tags on an item became components.** *ItemStack.getTag* / *getOrCreateTag* have no replacement; a stack is an `Item` plus a `PatchedDataComponentMap` and every former tag key is a `DataComponentType` in `DataComponents` — [data components](../systems/foundations/data-components.md), [items and stacks](../systems/items/items-and-stacks.md). - **Hand-written serialisation became codecs.** *Packet.write* is gone: a packet is a record with a `StreamCodec` the protocol table reads. *ItemStack.save* is gone: there is `ItemStack.CODEC` and, for saved data, the `ValueOutput` façade — [packets and stream codecs](../systems/networking/packets-and-stream-codecs.md), [codecs, NBT and JSON](../systems/foundations/codecs-nbt-json.md). - **Rendering split into extract and render.** *GuiGraphics*, *EntityRenderer.render*, *LevelRenderer.renderLevel*, *MultiBufferSource* and every `RenderSystem` state setter are gone or repurposed, because the frame now builds an immutable render state on the game thread and draws from it — [the frame](../systems/rendering/the-frame.md), [Blaze3D](../systems/rendering/blaze3d.md), [entity rendering](../systems/rendering/entity-rendering.md). - **Per-dimension and per-biome constants became one attribute system.** *DimensionSpecialEffects* and most of *BiomeSpecialEffects* are gone; fog, sky, water colour, ambient sound and music are `EnvironmentAttribute`s resolved through an `EnvironmentAttributeProbe` over a stack of layers — [lightmap, fog and sky](../systems/rendering/lightmap-fog-and-sky.md), [biomes](../systems/worldgen/biomes.md). - **Enums of behaviour became registries of data.** *EnchantmentCategory*, *MobSpawnType*, *BlockPathTypes*, *GenerationStep.Carving* and *Biome.BiomeCategory* are all gone, replaced by item sets, registry records, `HolderSet`s or nothing at all. - **UUIDs became identifiers.** An `AttributeModifier` is keyed by `Identifier`, not a UUID, which is why a data pack can now name one — [attributes](../systems/entities/attributes.md). - **Sides split.** *Entity.hurt* became `Entity.hurtServer` and `Entity.hurtClient`; *Player.attack* is still there but the packet that reaches it is `ServerboundAttackPacket`, a record of one integer, and `ServerboundInteractPacket` is right-click only. The general rule: where 1.21 had one method that checked `Level.isClientSide`, 26.2 tends to have two methods — [damage and death](../systems/entities/damage-and-death.md), [the sword swing](../systems/player/the-sword-swing.md). ## Yarn Yarn is Fabric's community mapping set. It is not a different version of the game and nothing on this list is a *change*: it is the same 26.2 class under the name a Fabric modder has in their head. Only the ones that actually trip people are listed — where the Yarn and Mojang names differ enough that grep fails. Both columns are italic here, because Yarn names are not in the decompile and `verify_names.py` cannot check them; the Mojang column is the one this corpus uses everywhere else, and every one of those names is backticked and verified on its own page. | Yarn | Mojang (this corpus) | |---|---| | *World* / *ServerWorld* / *ClientWorld* | *Level* / *ServerLevel* / *ClientLevel* | | *WorldChunk* | *LevelChunk* | | *MinecraftClient* | *Minecraft* | | *PlayerEntity* / *ServerPlayerEntity* / *ClientPlayerEntity* | *Player* / *ServerPlayer* / *LocalPlayer* | | *ClientPlayNetworkHandler* | *ClientPacketListener* | | *ServerPlayNetworkHandler* | *ServerGamePacketListenerImpl* | | *ClientConnection* | *Connection* | | *Text* / *MutableText* | *Component* / *MutableComponent* | | *TextRenderer* | *Font* | | *TextHandler* | *StringSplitter* | | *TextVisitFactory* | *StringDecomposer* | | *OrderedText* | *FormattedCharSequence* | | *StringVisitable* | *FormattedText* | | *FontStorage* | *FontSet* | | *GlyphAtlasTexture* | *FontTexture* | | *DrawContext* | *GuiGraphicsExtractor* — and in 26.2 it does not draw; see the drift table | | *NbtCompound* / *NbtList* / *NbtElement* | *CompoundTag* / *ListTag* / *Tag* | | *RegistryEntry* / *RegistryEntryList* | *Holder* / *HolderSet* | | *Registries* / *RegistryKeys* | *BuiltInRegistries* / *Registries* | | *Vec3d* | *Vec3* | | *Box* | *AABB* | | *Hand* | *InteractionHand* | | *ActionResult* | *InteractionResult* | | *Inventory* (the interface) | *Container* | | *PlayerInventory* | *Inventory* | | *ScreenHandler* / *ScreenHandlerType* | *AbstractContainerMenu* / *MenuType* | | *StatusEffect* / *StatusEffectInstance* | *MobEffect* / *MobEffectInstance* | | *EntityAttribute* / *EntityAttributeInstance* | *Attribute* / *AttributeInstance* | | *ParticleEffect* | *ParticleOptions* | | *BlockPos.Mutable* | *BlockPos.MutableBlockPos* | | *Identifier* | *Identifier* — Yarn was right first; Mojang renamed to match in 26.2 | The last row is the joke that keeps giving: the single most-cited example of "Yarn names are better" stopped being an example, and a decade of Fabric code now compiles against a Mojang-named class with the Yarn name. ## What a rename table cannot tell you - **A verified name is not a correct claim.** `verify_names.py` proves the right-hand column exists; it cannot prove the left-hand column ever did. The 1.21 side of this table is the only unverifiable content in the corpus, which is why it is confined to one page. - **The names did not move where you would guess.** Rendering is the fourth-largest table, behind commands, the server and items. Two rewrites nobody advertised — permissions ceasing to be integers, and game rules becoming a registry — renamed more identifiers than the render-stack refactor did, and the render one is the famous half only because its classes are the ones tutorials name. - **Renames cluster with rewrites.** No part of the tree renamed a class and kept its design; where the name changed, the responsibility usually moved too. Reading the row is not enough, which is what the linked page is for. - **Two subsystems have no rows at all, and that is the answer.** Dialogs (`net/minecraft/server/dialog` and its client screens) and the JSON-RPC management server postdate 1.21 entirely: there is no old name to look up, and a reader who cannot find one is not missing a row. Game tests are the opposite case — the *whole* 1.21 API is gone, which is why they have five. - **`Minecraft.setScreen` is a trap rather than a rename.** `Minecraft.setScreenAndShow` exists in 26.2 and a 1.21-era reader grepping for the old name will land on it, then wonder why the screen stack behaves differently. The method that replaced the old one is `Gui.setScreen`. - **Some names survived and changed meaning**, which is worse than a rename because grep still finds them: `Gui` (now the screen manager, not the HUD), `Material` (now a texture reference in `client/resources/model/sprite`, not a block property), `ParticleGroup` (now a per-render-type bucket, not a count limit), `Strategy` (now top-level, not nested in `PalettedContainer`), and `MultiVariant`, whose name survives only in the data-generator package while the runtime type is gone. ## Where to look `Identifier`, then `Holder` and `HolderSet` (`net/minecraft/core`), then `DataComponents` — those three carry more of the drift than any others. After that, pick the part you are lost in and read its page rather than its rows. --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Glossary > Verified against **Minecraft 26.2** · Reference · One sentence > per term the rest of the corpus uses, and a link to the page that owns it. Every page in this corpus assumes the vocabulary of the pages before it. That is deliberate — a lecture that redefines "chunk holder" every time it appears is unwatchable — but it means a reader who arrives in the middle has no way back. This page is the way back: the terms, alphabetically, one sentence each, each pointing at the page where the term is actually explained. A sentence here is a *reminder*, not a definition to rely on. If the sentence is all you needed, good; if it is not, the link is the point of the entry. Where a term is a class name, the class name is the entry — this corpus uses Mojang's names for concepts as well as for types, and inventing a second vocabulary to sit beside them would only double the work. ## A **Activity** — the filter that decides which of a brain's behaviours are asked at all, rather than a mode it runs in: the active set is always the core activities plus exactly one other, and an `ActivityData` declares each one's prioritised behaviour list. → [AI](../systems/entities/ai-goals-and-brains.md) **Advancement** — a data-pack-defined goal: criteria, a requirements expression over them, an optional display entry and a reward. → [advancements](../systems/commands/advancements.md) **Aquifer** — the worldgen component that decides what a point is made of once the density is known — stone, air, water or lava — from its own barrier and fluid-level noises; a carver writes the block itself but asks the aquifer which block to write. → [terrain](../systems/worldgen/terrain.md) **Argument type** — a Brigadier `ArgumentType` that parses one *argument* off the command line, however many words that takes — three for a `Vec3Argument`, all of the rest for a `MessageArgument`; vanilla's live in `net/minecraft/commands/arguments` and are described to the client through an `ArgumentTypeInfo`. → [Brigadier and commands](../systems/commands/brigadier-and-commands.md) **Atlas** — one large texture stitched at load time out of many sprite files, so a chunk section can be drawn with a single bound texture. → [models and atlases](../systems/rendering/models-and-atlases.md) **Attribute** — a named, ranged, modifiable number on a `LivingEntity`; modifiers are keyed by `Identifier`, and eight attributes are not client-syncable at all. → [attributes](../systems/entities/attributes.md) **Authority** — whose copy of an entity produces the position that counts: the server for a mob, the owning client for its own player and for the boat that player is steering — the server still runs your player's physics, then overwrites its answer with the number your client sent. Five predicates on `Entity` decide it, starting with the final `Entity.isLocalInstanceAuthoritative`. → [authority](../systems/entities/authority.md) **Avatar** — the class between `LivingEntity` and `Player`; a `Mannequin` is an `Avatar` that is not a player, and `AvatarRenderer` draws both. → [player anatomy](../systems/player/player-anatomy.md) ## B **Batch** — a group of game tests keyed by the environment they share; a batch *is* an environment, not a name and not a class. → [game tests](../systems/commands/game-tests.md) **Beardifier** — the density-function term that bends terrain around a structure; terrain adaptation writes no blocks, it changes the noise. → [structure placement](../systems/worldgen/structure-placement.md) **Behaviour** — one unit of brain AI, gated on memories and asked once a tick; unless it overrides `Behavior.canStillUse` it stops inside the same `Brain.tick` that started it, so everything it does it does in `Behavior.start`. → [AI](../systems/entities/ai-goals-and-brains.md) **Biome** — a named bundle of generation settings, mob spawns, block tints and environment attributes, attached to a 4×4×4 volume of the world. → [biomes](../systems/worldgen/biomes.md) **BiomeSource** — the object that answers "which biome is at this quart position": by a climate search, from one fixed biome, from a checkerboard of a listed few, or — in the End — off a single erosion sample. → [biomes](../systems/worldgen/biomes.md) **Blaze3D** — Mojang's GPU abstraction, with OpenGL and Vulkan backends behind one `GpuDevice`. → [Blaze3D](../systems/rendering/blaze3d.md) **Blend alpha** — the mixing weight `Blender` computes from the distance to the nearest measured old column: zero against the seam (use the old measurement), one out of range (use the noise). → [blending at the old-chunk border](../systems/worldgen/blending.md) **Blending data** — the sixteen-column ring of heights, densities and biomes a `BlendingData` measures out of its **own** chunk's blocks, on the sides facing ground the game has yet to generate; its presence on a chunk is what makes the chunk old. → [blending at the old-chunk border](../systems/worldgen/blending.md) **Block** — the singleton describing a kind of block: its behaviour, its property set and its state table. One `Block`, many `BlockState`s. → [blocks and states](../systems/blocks/blocks-and-states.md) **Block entity** — the per-position mutable state a block cannot fit into its state (a chest's contents, a furnace's progress), stored on the chunk. → [block entities](../systems/blocks/block-entities.md) **Block event** — a message from a block to itself (a piston push, a note block, a chest lid), queued on `ServerLevel` and drained at one fixed point in the level tick — so it lands late, usually within the same tick — and mirrored to nearby clients as a packet. → [pistons and block events](../systems/blocks/pistons-and-block-events.md) **BlockState** — one combination of a block's property values, built once by the block's `StateDefinition` and compared by identity; `Block.BLOCK_STATE_REGISTRY` is the flat table that numbers it for the wire and the global palette. → [blocks and states](../systems/blocks/blocks-and-states.md) **Border tick** — a position along an old chunk's seam queued for post-processing, so a leaf or a fluid there is re-evaluated when the chunk goes live. → [blending at the old-chunk border](../systems/worldgen/blending.md) **Brain** — the memory-and-behaviour AI used by villagers, piglins and axolotls, as opposed to the older goal system. → [AI](../systems/entities/ai-goals-and-brains.md) **Brigadier** — Mojang's command-parsing library: a tree of literal and argument nodes with per-node requirements, shared by client and server. → [Brigadier and commands](../systems/commands/brigadier-and-commands.md) **Built-in block model** — a block model attached in code by `BuiltInBlockModels` rather than by a resource pack, living in a second model table that terrain never reads; how a minecart or a block display draws a chest. → [block-entity rendering](../systems/rendering/block-entity-rendering.md) ## C **Camera** — the client's eye: position, rotation and the cull frustum, copied into a `CameraRenderState` once per frame — plus an `EnvironmentAttributeProbe` that `Camera.tick` advances and no render state carries. → [the frame](../systems/rendering/the-frame.md) **Carver** — a worldgen pass that hollows out caves and ravines by writing air, water or lava, asking the `Aquifer` which — except the nether carver, which does not ask. → [terrain](../systems/worldgen/terrain.md) **Cell** — the lattice unit of terrain noise, four blocks wide and deep and eight tall in the overworld, 768 to a chunk: the expensive three-dimensional density terms are evaluated at its corners and interpolated within it, and the caches keyed on it mean nothing outside the cell loop. → [terrain](../systems/worldgen/terrain.md), [density functions](../systems/worldgen/density-functions.md) **Chunk** — a 16-by-16 column of the world's full height: sections, heightmaps, block entities, tick queues and a status. → [chunk anatomy](../systems/world/chunk-anatomy.md) **Chunk layer** — which of the three `ChunkSectionLayer` buffers a block's quads are meshed into — solid, cutout or translucent — decided at bake time from the alpha inside that quad's own patch of its sprite rather than from the block. → [models and atlases](../systems/rendering/models-and-atlases.md), [section meshing](../systems/rendering/section-meshing.md) **ChunkHolder** — the server's per-chunk record of the *level* the two graphs computed for it, a future per threshold and status, and what changed in it this tick; which tickets asked for that level is `TicketStorage`'s business, not the holder's. → [tickets and loading](../systems/world/tickets-and-loading.md) **ChunkMap** — the server's chunk table: holders, the ticket-driven level graphs, entity tracking, and the region-file storage underneath. → [tickets and loading](../systems/world/tickets-and-loading.md) **ChunkStatus** — one rung of the generation ladder; a chunk advances one status at a time, and the `ChunkStep` for each status declares the neighbour radius that step needs. → [the chunk generation pipeline](../systems/world/chunk-generation-pipeline.md) **Climate** — the noise sample (temperature, humidity, continentalness, erosion, depth, weirdness) a biome is chosen by. → [biomes](../systems/worldgen/biomes.md) **Codec** — a DataFixerUpper object that both encodes and decodes one type against any `DynamicOps`; the corpus's universal serialisation vocabulary. → [codecs, NBT and JSON](../systems/foundations/codecs-nbt-json.md) **CommandSourceStack** — *who is running this command, and from where*: position, rotation, level, entity, a `PermissionSet` and an output sink, immutable, so every `with…` returns a copy. → [Brigadier and commands](../systems/commands/brigadier-and-commands.md) **Compiled query** — the immutable `EntitySelector` a parse produces: thirteen fields, no reader and no grammar, resolvable any number of times against different sources. → [entity selectors](../systems/commands/entity-selectors.md) **Component** — two different things the corpus keeps apart: a *data component* on an item stack, and a `Component` of chat text. → [data components](../systems/foundations/data-components.md), [text components](../systems/foundations/text-components.md) **Connection** — the tail handler of one Netty pipeline plus the channel it holds, with exactly one packet listener at a time, swapped when the protocol phase changes. → [the connection](../systems/networking/the-connection.md) **Container** — the interface a thing with item slots implements (a chest, a hopper, an inventory), as distinct from the *menu* a player interacts with it through. → [containers and menus](../systems/items/containers-and-menus.md) **Criterion** — one condition inside an advancement, backed by a `CriterionTrigger` the server fires when the relevant thing happens. → [advancements](../systems/commands/advancements.md) ## D **DamageSource** — the *what hit you, and who is responsible* record every damage calculation and death message reads. → [damage and death](../systems/entities/damage-and-death.md) **Data component** — a typed, codec-backed value keyed by a `DataComponentType`: a patch over the item's prototype on a stack, a whole map on a block entity, read-only on an entity; what NBT item tags became. → [data components](../systems/foundations/data-components.md) **Data pack** — a pack of JSON, structure NBT and function files supplying the server's data-driven content; the server half of the resource system. → [the resource system](../systems/foundations/resource-system.md) **DataLayer** — the nibble array one section's block light or sky light lives in, owned by the light engine and never by the section. → [lighting](../systems/world/lighting.md) **Debug subscription** — a registered kind of debug value a client can ask the server for; most kinds the server polls, diffs and sends only when they change, and the rest it pushes as they happen. → [debugging the running game](../systems/client/debugging-the-running-game.md) **DeltaTracker** — the client's clock: how much of a tick has elapsed, and the source of every partial tick in the frame but the lightmap's, which is a literal one. → [the client loop](../systems/client/the-client-loop.md) **Density function** — a node in the JSON-defined graph that turns a position into a number; the graph in the registry is never the graph that actually runs. → [density functions](../systems/worldgen/density-functions.md) **Dialog** — a data-pack-defined form the server can put on a player's screen, whose submitted values come back as a packet. → [dialogs](../systems/commands/dialogs.md) **Dimension** — one `ServerLevel` and its `DimensionType`: a height range, a set of environment attributes and its own chunk storage. → [level data and rules](level-data-and-rules.md) ## E **Enchantment** — a data-pack record of effect components conditioned on loot predicates; its registry is synchronised, but a client that already has the pack is sent only the id. → [enchantments](../systems/items/enchantments.md) **Entity** — a thing the level ticks in its own right: a position, a bounding box, synched data and a tick method. A *block entity* is not one. → [entity anatomy](../systems/entities/entity-anatomy.md) **EntityType** — the registry entry for a kind of entity: its factory, category, size, feature flags and the two numbers that decide how it reaches clients. Spawn rules are keyed *by* the type in `SpawnPlacements`, not held on it. → [entity anatomy](../systems/entities/entity-anatomy.md) **EnvironmentAttribute** — a per-dimension, per-biome, per-time-of-day, per-weather value resolved through a stack of layers: directly on the server, through the camera's smoothing probe on the client. Not only the visual ones: alongside fog and sky colour sit twenty gameplay attributes — whether lava flows fast, whether piglins zombify, whether a bed works, and the villager's schedule. → [environment attributes and timelines](../systems/world/environment-attributes-and-timelines.md) **Event loop** — the queue-and-thread pairing `BlockableEventLoop` is: an owning thread that drains posted tasks and, through `BlockableEventLoop.managedBlock`, keeps draining while it waits. `Minecraft` and `MinecraftServer` are both one — but only the server rations the drain against a time budget; the client empties the queue every frame. → [the server tick](../systems/server/server-tick.md#the-event-loop-and-what-a-ticks-spare-time-buys) **Experiment** — a built-in data pack whose `PackSource` is `PackSource.FEATURE`, enabling one non-vanilla `FeatureFlag`; switching one on in the create-world screen is a data-pack reload. → [creating a world](../systems/worldgen/creating-a-world.md) **Extract** — the first half of the client's frame: walk the game state, cull it, and write the render states, so that the drawing half reads no live game object from `LevelRenderer.render` down — the top of the render half still does. The top-level states are single objects re-filled each frame, not fresh immutable values. → [the frame](../systems/rendering/the-frame.md) ## F **Feature** — the algorithm half of decoration: what to build, with no say in which positions it is offered. → [features and placement](../systems/worldgen/features-and-placement.md) **Flat level generator preset** — a `FlatLevelGeneratorPreset`: a display item plus a `FlatLevelGeneratorSettings`, one row of the Superflat *Presets* screen. → [creating a world](../systems/worldgen/creating-a-world.md) **Fluid** — the registry object behind a `FluidState`, a source and a flowing instance per liquid, with `FlowingFluid` holding the spread algorithm and `LiquidBlock` the block form. → [fluids](../systems/world/fluids.md) **Font** — a resource-pack-defined glyph source plus the measuring and wrapping API on top of it; a glyph is baked into a texture the first time it is asked for. → [text and fonts](../systems/client/text-and-fonts.md) **Frame** — the execution engine's unit of a running function: a depth, a result callback that `/return` feeds sideways, and a control that can delete the frame's pending work — one object shared by reference across a whole function body, and deliberately *not* a stack frame. → [the execution engine](../systems/commands/the-execution-engine.md) **Frame graph** — the client's per-frame declaration of render passes and the targets each reads and writes, resolved before anything is drawn. → [visibility and the frame graph](../systems/rendering/visibility-and-the-frame-graph.md) **Function** — a `.mcfunction` file: a list of commands loaded as a `CommandFunction`, optionally with macro lines. → [functions and macros](../systems/commands/functions-and-macros.md) ## G **Game event** — a broadcast fact about something that just happened at a position (a block placed, a door opened) that sculk sensors and mobs listen for. → [game events and vibrations](../systems/world/game-events-and-vibrations.md) **Game rule** — one typed, server-wide switch or number in `GameRules`, saved with the world and sometimes sent to the client. → [level data and rules](level-data-and-rules.md) **Game test** — a data-driven test instance: a structure, an environment and a check the server runs and reports on. → [game tests](../systems/commands/game-tests.md) **Globally-rendered block entity** — one whose renderer says `BlockEntityRenderer.shouldRenderOffScreen`, so the client keeps it in a level-wide set and draws it whether or not its section is visible; three renderers qualify. → [block-entity rendering](../systems/rendering/block-entity-rendering.md) **Goal** — one unit of the older mob AI: a start condition, an answer to whether it may be interrupted, and the set of `Goal.Flag` controls it claims while running. The priority belongs to the `WrappedGoal` that holds it, and the flag table rather than the priority is what arbitrates. → [AI](../systems/entities/ai-goals-and-brains.md) **GpuDevice** — the façade every GPU resource is created through; both graphics backends sit behind it as `GpuDeviceBackend` implementations. A draw reaches the driver through the `CommandEncoder` it hands out and the `RenderPass` that opens. → [Blaze3D](../systems/rendering/blaze3d.md) **GuiRenderState** — the 2D render tree: strata of nodes that infer their own layering from bounding boxes and are batched into draw calls at the end of the frame. → [the GUI render tree](../systems/client/the-gui-render-tree.md) ## H **Heightmap** — a per-chunk 2D array holding the first *free* Y above the topmost block matching a predicate; six types exist, and a live chunk keeps the four that survive worldgen. → [chunk anatomy](../systems/world/chunk-anatomy.md) **Holder** — a reference to a registry entry that can exist before the entry is bound: `Holder.Reference` for a registered value, `Holder.Direct` for an inline one. → [identifiers and registries](../systems/foundations/identifiers-and-registries.md) **HolderSet** — a set of holders: either a tag (`HolderSet.Named`) or a literal list. → [tags](../systems/foundations/tags.md) **HUD** — the in-world overlay (hotbar, hearts, chat, boss bars), which in 26.2 is the class `Hud` — `Gui` now means the screen manager. → [the HUD](../systems/client/hud.md) ## I **Identifier** — a namespace and a path; the id of everything. A 1.21-era reader knows it as *ResourceLocation*. → [identifiers and registries](../systems/foundations/identifiers-and-registries.md) **Ingredient** — a recipe's "any of these items" test. It cannot be an empty inline list, but a tag that resolves to nothing makes one empty — and a recipe holding it is never placeable. → [recipes](../systems/items/recipes.md) **Integrated server** — the `MinecraftServer` a singleplayer client runs on its own Server thread. Every change to the *world* still crosses as a packet; a handful of settings cross by direct call. → [anatomy](../systems/anatomy/anatomy.md) **InteractionResult** — the answer a block or item gives to a click: was the input consumed, should the arm swing, did the held item change. → [block interaction](../systems/blocks/block-interaction.md) **Item** — the singleton for a kind of item, holding none of the components a stack shows; a stack is a holder to one of these, a count, a pop time and a patched component map — the item's defaults plus the ways this stack differs from them. → [items and stacks](../systems/items/items-and-stacks.md) **ItemStackTemplate** — the immutable item-shaped record (an item holder, a count, a component patch) that data uses where a live, mutable `ItemStack` would be wrong. → [items and stacks](../systems/items/items-and-stacks.md) ## J **Jigsaw** — the structure-assembly system that grows a village out of template pieces by matching connector blocks. → [jigsaw and templates](../systems/worldgen/jigsaw-and-templates.md) ## K **KeyMapping** — one bindable action: whether its key is down, plus a counter of owed clicks that `KeyMapping.consumeClick` **drains** rather than edge-detects. → [input and keybinds](../systems/client/input-and-keybinds.md) ## L **Level** — a world: `ServerLevel` on the server, `ClientLevel` on the client, sharing an abstract `Level` and remarkably little else. → [the level tick](../systems/server/server-level-tick.md), [the client level](../systems/client/the-client-level.md) **Lightmap** — the small texture the client samples to turn a block-light / sky-light pair into a colour; drawn on the GPU once per tick. → [lightmap, fog and sky](../systems/rendering/lightmap-fog-and-sky.md) **LocalPlayer** — the `Player` a human steers: its own `ClientInput`, its own prediction, and the last input and position it sent. → [player anatomy](../systems/player/player-anatomy.md) **Loot table** — the data-driven roll that turns an event (a block broken, a mob killed, anything at all reading a container that has not been rolled yet) into item stacks. → [loot tables](../systems/items/loot-tables.md) ## M **Macro** — a function line beginning with a `$` substitution. The plain lines of the file are parsed once at load; a macro line is substituted and **re-parsed** per distinct argument tuple, cached only eight deep. → [functions and macros](../systems/commands/functions-and-macros.md) **Memory** — one typed, optionally expiring value in a `Brain`; behaviours are gated on which memories are present. → [AI](../systems/entities/ai-goals-and-brains.md) **Menu** — the server-authoritative object behind an open container screen: slots, a synchroniser and a state id. → [containers and menus](../systems/items/containers-and-menus.md) **MultiPlayerGameMode** — the client's only channel for acting on the world: every break, place, use and attack goes through it, and the ones that predict open a prediction window before they send. → [prediction and acknowledgement](../systems/client/prediction-and-acks.md) ## N **NBT** — Minecraft's binary tag format; in 26.2 a sealed `Tag` hierarchy whose scalars are records and whose containers (`CompoundTag`, `ListTag`) are final classes, read and written through `NbtIo` and reached by codecs through `NbtOps`. → [codecs, NBT and JSON](../systems/foundations/codecs-nbt-json.md) **NBT path** — a compiled query over a tag, six node kinds deep — a named child, an index, all elements, and three kinds of match — that `/data` uses to read and write, and which materialises the structure it walks through on a write. → [scores, teams and stored data](../systems/commands/scoreboard-and-data.md) **Neighbour update** — the server-only notification a block sends its six neighbours after a change; distinct from a *shape update*, which runs on both sides. → [blocks and states](../systems/blocks/blocks-and-states.md) **NoiseChunk** — the per-chunk machine that fills the noise lattice and installs the caches the density-function graph asked for. → [density functions](../systems/worldgen/density-functions.md) **NoiseRouter** — the density functions a generator asks for, as one record; `NoiseRouter.mapAll` rebuilds them all at once, which is how a whole graph gets its caches installed in one pass. → [density functions](../systems/worldgen/density-functions.md) ## O **Objective** — a named scoreboard column: a criterion, a display name, a render type and a number format. Only the *dummy* and *trigger* criteria wait for commands; every other one — including every statistic in the game, since `Stat` extends `ObjectiveCriteria` — is driven from `ServerPlayer`. → [scores, teams and stored data](../systems/commands/scoreboard-and-data.md) **Old chunk** — a chunk whose `ChunkAccess.blendingData` is non-null, which is to say one whose save data carried a *blending_data* compound; `ChunkAccess.isOldNoiseGeneration` is the test. → [blending at the old-chunk border](../systems/worldgen/blending.md) ## P **Packet** — an interface: a `PacketType`, which is a name and a direction, and one handler method. Roughly half the implementations are records, the wire form is a `StreamCodec` the phase's protocol description holds rather than something the class owns, and a few types are registered into more than one phase. → [packets and stream codecs](../systems/networking/packets-and-stream-codecs.md) **PalettedContainer** — the bit-packed storage a chunk section keeps its block states and biomes in, with a palette that grows as the section gets more varied. → [chunk anatomy](../systems/world/chunk-anatomy.md) **Partial tick** — the fraction of a tick elapsed at the moment a frame is drawn, used to interpolate the world. There is no single one: a frame carries six values, they disagree on purpose, and the one screens are handed is not a fraction of a tick at all. → [the frame](../systems/rendering/the-frame.md) **Path** — the list of nodes a navigator is following, produced by `PathFinder`'s A\* over a snapshot of already-loaded chunks, with a `NodeEvaluator` deciding what each candidate block *is* to this mob. → [pathfinding](../systems/entities/pathfinding.md) **Permission atom** — a named capability with an `Identifier` (`Permission.Atom`), the other kind of permission besides a command level; an operator's level-based set grants exactly one, the entity-selector atom, from gamemaster up. → [permissions](../systems/commands/permissions.md) **Permission level** — one rung of `PermissionLevel` (all, moderators, gamemasters, admins, owners), and only the *ordered* half of a permission: a command source carries a `PermissionSet` and a node requires a `PermissionCheck`, neither of which is an integer. → [permissions](../systems/commands/permissions.md) **Permission set** — what a command source carries and a node's check is asked against: on the server a level-based set (a rung plus one atom). The client rebuilds four of those from the op level it is told — rung zero maps to `PermissionSet.NO_PERMISSIONS` instead — and keeps a chat set built by subtraction beside them. No packet carries a `PermissionSet` itself. → [permissions](../systems/commands/permissions.md) **PlacedFeature** — a configured feature plus an ordered list of placement modifiers; the unit a biome actually names. → [features and placement](../systems/worldgen/features-and-placement.md) **Point of interest** — a block state the game has decided is worth walking to: a bed, a job site, a portal. `PoiManager` indexes them by position, in its own files beside the chunks. → [points of interest](../systems/world/points-of-interest.md) **Prediction ledger** — the corpus's name for `BlockStatePredictionHandler`: the client's record of what the server is known to have at a block it changed optimistically. The ack is a receipt for a number, not a verdict — it settles every entry at or below it, writing back a correction if one arrived and rolling the block back if none did. → [prediction and acknowledgement](../systems/client/prediction-and-acks.md) **Protocol phase** — one of handshake, status, login, configuration and play; each has its own packet table and its own listener. → [protocol phases](../systems/networking/protocol-phases.md) ## Q **Quart** — a four-block cell, the resolution biomes are stored and sampled at; `QuartPos` is the arithmetic. → [biomes](../systems/worldgen/biomes.md), [math and primitives](math-and-primitives.md) ## R **Recipe** — a server-side matcher and assembler; no `Recipe` ever crosses the wire. The client gets a `RecipeDisplay` and a `RecipeDisplayId`, which is a position in a list rather than the recipe's name. → [recipes](../systems/items/recipes.md) **Region file** — the 32-by-32-chunk container file chunks are stored in, addressed by a sector table at its head. → [chunk storage](../systems/world/chunk-storage.md) **Registry** — a frozen, id-assigning table of one kind of thing; some are built into the jar, some are loaded from data packs, some are sent to the client. → [identifiers and registries](../systems/foundations/identifiers-and-registries.md) **Reload listener** — the unit of a reload: one object that reads what it needs off the worker pool and swaps its live state on the owning thread, every apply running in order behind a `PreparableReloadListener.PreparationBarrier`. → [the resource system](../systems/foundations/resource-system.md) **Render state** — the write-once snapshot of what to draw, produced by the extract half of the frame and consumed by the drawing half. The property that matters is that the drawing half reads no game object, not that the state is an immutable value. → [the frame](../systems/rendering/the-frame.md), [entity rendering](../systems/rendering/entity-rendering.md) **RenderPipeline** — the client's declaration of how to rasterise: shaders, blend, depth, cull, vertex format, topology. It says nothing about which textures to bind or which target to draw into; that is `RenderType`. → [Blaze3D](../systems/rendering/blaze3d.md) **RenderType** — a `RenderPipeline` plus everything a pipeline does not say: which target to draw into, which textures to bind, and the layering and batching rules. → [Blaze3D](../systems/rendering/blaze3d.md) **Resource pack** — a pack of assets; the client half of the same pack machinery data packs use. → [the resource system](../systems/foundations/resource-system.md) ## S **SavedData** — a named, codec-backed blob stored beside the world (the border, the weather, the rules, raids, the dragon fight); `level.dat` itself is nearly a stub. → [level data and rules](level-data-and-rules.md) **Scheduled tick** — a block or fluid position queued to run at a named future tick, with a priority breaking ties inside that tick. → [scheduled ticks](../systems/world/scheduled-ticks.md) **Score** — one number for one holder under one objective, reached through a `ScoreAccess` handle rather than a setter. → [scores, teams and stored data](../systems/commands/scoreboard-and-data.md) **Screen** — one full-window client UI with its own widget tree and lifecycle; the server is told nothing about most of them. → [GUI and screens](../systems/client/gui-and-screens.md) **Section** — a 16-cubed piece of a chunk: one paletted container of block states, one of biomes, and four counters. Its light lives in the light engine's own storage, not on the section. → [chunk anatomy](../systems/world/chunk-anatomy.md) **Section mesh** — the compiled vertex buffers for one section (`CompiledSectionMesh`), rebuilt when the section is both dirty and visible — usually on a worker, but inline on the client thread when the chunk-builder option asks for it. → [section meshing](../systems/rendering/section-meshing.md) **Selector head** — the single character after the *@* (*a*, *e*, *n*, *p*, *r*, *s*) that sets a selector's default limit, order and entity scope before any option is read; three of the six also pin the type to player, and two instead add an aliveness test. → [entity selectors](../systems/commands/entity-selectors.md) **Sensor** — the half of brain AI that writes memories from the world, on a fixed interval. → [AI](../systems/entities/ai-goals-and-brains.md) **ServerEntity** — the server's per-tracked-entity bookkeeping: what the watching clients were last told, and what to send them next. → [what the client is told](../systems/networking/what-the-client-is-told.md) **Shape update** — the "your neighbour changed, recompute yourself" call that runs on both client and server, unlike a neighbour update. → [blocks and states](../systems/blocks/blocks-and-states.md) **Signed message** — a chat message carrying a signature over its content and its place in a per-player chain, so the server can prove who said it. → [chat and signing](../systems/networking/chat-and-signing.md) **Simulation distance** — how far the world *ticks*, as against how far you can see: the radius behind `TicketType.PLAYER_SIMULATION`, deciding which chunks tick blocks, fluids and entities. → [tickets and loading](../systems/world/tickets-and-loading.md) **Special model renderer** — a hand-written submitter for a shape no cuboid model can express, reached from an item model or a block state rather than from a block entity; thirteen of them. → [block-entity rendering](../systems/rendering/block-entity-rendering.md) **Staging buffer** — the list an executing action appends its spawned commands to, spliced onto the *head* of the queue after it runs — which is what makes an `ArrayDeque` behave as a call stack. → [the execution engine](../systems/commands/the-execution-engine.md) **StreamCodec** — the wire counterpart of a `Codec`: encodes to and decodes from a `ByteBuf`, with no schema and no field names. → [packets and stream codecs](../systems/networking/packets-and-stream-codecs.md) **Structure** — a generated building or landmark: a placement lottery, a start assembled in memory, and pieces written a chunk at a time. → [structure placement](../systems/worldgen/structure-placement.md) **StructurePiece** — one room, corridor or slab of a structure. In the hand-built half it is a Java class that writes its own blocks and constructs its own neighbours, chosen by no pool; the jigsaw half's `PoolElementStructurePiece` is one too. Every piece carries a registered `StructurePieceType`, which is how it comes back off disk. → [hand-built structures](../systems/worldgen/hand-built-structures.md) **StructureStart** — one decided structure: the `Structure`, the chunk it started in, a `PiecesContainer`, a reference count and a cached bounding box, stored on the chunk it began in. → [structure placement](../systems/worldgen/structure-placement.md) **Submit node** — one thing to draw that is not terrain, written into `SubmitNodeStorage` by the *submit* pass out of the render states extract left behind, and sorted into a phase before the feature renderers turn it into vertices. → [entity rendering](../systems/rendering/entity-rendering.md), [submit phases](submit-phases.md) **SynchedEntityData** — the per-entity table of small values the server pushes to watching clients, keyed by class-tree ordinal. → [synched entity data](../systems/entities/synched-entity-data.md) ## T **Tag** — a named set of registry entries defined by data packs and merged across them, unless a higher pack sets *replace*. (The unrelated NBT sense of the word belongs to [NBT](../systems/foundations/codecs-nbt-json.md).) → [tags](../systems/foundations/tags.md) **Team** — a named set of score holders carrying a colour, a friendly-fire flag, a collision rule and a nametag rule — so a class in the scores package is read by collision and by rendering. → [scores, teams and stored data](../systems/commands/scoreboard-and-data.md) **Tick** — one step of the server's simulation, 50 ms at the default rate that `/tick rate` can change, or one step of the client's; a client behind the clock catches up to ten accumulated ticks in a frame and discards the rest. → [the server tick](../systems/server/server-tick.md), [the client loop](../systems/client/the-client-loop.md) **Ticket** — the reason a chunk is loaded: a type and a level, fed into two separate graphs, with `ChunkLevel` deciding what the level buys — a holder only, then full, then block-ticking, then entity-ticking. → [tickets and loading](../systems/world/tickets-and-loading.md) **Timeline** — one clock's data-driven curve set: an optional period, the named instants on that clock, and one `AttributeTrack` per environment attribute — keyframed over *modifier arguments*, not over values. → [environment attributes and timelines](../systems/world/environment-attributes-and-timelines.md) **Trigger** — the server-side hook that tells **one** player's advancement state that something happened, by sweeping that player's listener map for this trigger. Nothing broadcasts. → [advancements](../systems/commands/advancements.md) ## U **Unattended command** — a command the player did not type: a dialog button or a chat click event, sent through `ClientPacketListener.sendUnattendedCommand`. The client re-parses it and asks first if it fails to parse, needs a signature, or needs a permission the client believes it lacks; a clean one goes without a prompt. A sign's command is not one of these — it runs on the server at gamemaster level and the client is never asked. → [permissions](../systems/commands/permissions.md) ## V **View distance** — how far the *server* sends chunks. A client's render-distance request only clamps what it is sent; the ticket radius comes from the server's own number. Not *simulation distance*, which is how far the world ticks. → [tickets and loading](../systems/world/tickets-and-loading.md) **VoxelShape** — the collision or outline volume of a block state, held as a set of boxes with fast merge and sweep operations. → [math and primitives](math-and-primitives.md) ## W **Watchdog** — `ServerWatchdog`, the daemon that treats a tick longer than *max-tick-time* as a dead server: it writes a crash report, calls `System.exit`, and halts the JVM ten seconds later whether the shutdown finished or not. → [how a server dies](../systems/server/how-a-server-dies.md) **Window** — the GLFW handle the whole client hangs off: the framebuffer size, the GUI scale, fullscreen, and the six window callbacks — not the input ones, which `KeyboardHandler` and `MouseHandler` register. → [the window](../systems/rendering/the-window.md) **World clock** — the identity a timeline is sampled against: a unit record in `Registries.WORLD_CLOCK`, two of them in vanilla, holding nothing at all. The tick count, the rate and the paused flag are `ServerClockManager.ClockInstance`'s, one per clock, and `/time` can move or pause each independently. → [environment attributes and timelines](../systems/world/environment-attributes-and-timelines.md) **World gen settings** — `WorldGenSettings`: a `WorldOptions` (the seed, *generate structures*, *bonus chest*) and the `LevelStem` map, a `SavedData` written to *data/minecraft/world_gen_settings.dat*. It is the only part of world generation that is saved; everything else Part XII reads is re-read from the enabled packs on every world open. → [creating a world](../systems/worldgen/creating-a-world.md) **World preset** — a `WorldPreset` registry entry holding one `LevelStem` per dimension; what the world-type button selects, and what *level-type* names on a dedicated server. → [creating a world](../systems/worldgen/creating-a-world.md) **World stem** — `WorldStem`, the four things `WorldLoader.load` hands the server constructor in one bundle: the resource manager, the reloadable server resources, the layered registries, and the level data with its gen settings. → [starting a server](../systems/server/starting-a-server.md) **World-limited** — a parse-time flag, set by any of seven positional selector options, that confines a selector's resolve to the source's own level instead of every level on the server. → [entity selectors](../systems/commands/entity-selectors.md) **WorldGenRegion** — the bounded, write-guarded view of the world a generation step is given; it throws rather than loading a chunk, which is why cascading worldgen cannot happen. → [the chunk generation pipeline](../systems/world/chunk-generation-pipeline.md) --- *Rules: names, never code · how the system works, not how the code reads · newest version only · every backticked name passes `tools/verify_names.py`.* --- # Diagram lanes > Generated from `TEMPLATE.md`'s lane key by `python tools/check_lanes.py --index`. Do not edit by hand. Almost every lane in a sequence diagram is a class, and a lane means the same thing on every page: the key in `TEMPLATE.md` is the authority, and `check_lanes.py` fails a deploy on a page that disagrees with it. The last rows are the exceptions — lanes that stand for a thread or a boundary rather than for one class. 333 lanes are classes and 9 are not. A lane is normally the initials of the class's CamelCase words (`ServerGamePacketListenerImpl` is `SGPL`), but three other rules make about a third of them: a short one-word class is its own lane (`Player`, `Sheep`), a longer one-word class takes a fixed prefix (`Connection` is `Conn`, `Enchantment` is `Ench`), and a collision is resolved by lengthening the **later** claimant, never by reassigning a row — which is why `ChestMenu` is `ChestM` and not `CM` (`ChunkMap` had it first). Derive nothing from a lane; read it off this table. | lane | class | |---|---| | `AA` | `AbstractArrow` | | `AB` | `AbstractBoat` | | `ACM` | `AbstractContainerMenu` | | `AFBE` | `AbstractFurnaceBlockEntity` | | `AM` | `AtlasManager` | | `AP` | `AcquirePoi` | | `AR` | `AdvancementRewards` | | `ATS` | `AttributeTrackSampler` | | `AttrI` | `AttributeInstance` | | `AttrM` | `AttributeMap` | | `BC` | `BuildContexts` | | `BD` | `BlendingData` | | `BDR` | `BrainDebugRenderer` | | `BEL` | `BlockableEventLoop` | | `BERD` | `BlockEntityRenderDispatcher` | | `BI` | `BucketItem` | | `BIR` | `BuiltInRegistries` | | `BLE` | `BlockLightEngine` | | `Blender` | `Blender` | | `Block` | `Block` | | `Boot` | `Bootstrap` | | `BowI` | `BowItem` | | `Brain` | `Brain` | | `BSPH` | `BlockStatePredictionHandler` | | `CA` | `ChunkAccess` | | `CAdv` | `ClientAdvancements` | | `CallF` | `CallFunction` | | `Camera` | `Camera` | | `CBE` | `ChestBlockEntity` | | `CCC` | `ClientChunkCache` | | `CComPL` | `ClientCommonPacketListenerImpl` | | `CCPL` | `ClientConfigurationPacketListenerImpl` | | `CDS` | `ClientDebugSubscriber` | | `CE` | `CommandEncoder` | | `CF` | `ConfiguredFeature` | | `CG` | `CollisionGetter` | | `CGT` | `ChunkGenerationTask` | | `CH` | `ChunkHolder` | | `ChanA` | `ChannelAccess` | | `Channel` | `Channel` | | `ChatC` | `ChatComponent` | | `CHelp` | `ContainerHelper` | | `ChestM` | `ChestMenu` | | `ChestR` | `ChestRenderer` | | `CHPL` | `ClientHandshakePacketListenerImpl` | | `ChunkG` | `ChunkGenerator` | | `CI` | `CraftingInput` | | `CL` | `ClientLevel` | | `ClimS` | `Climate.Sampler` | | `CLis` | `ChatListener` | | `CM` | `ChunkMap` | | `CMap` | `ContextMap` | | `Cmds` | `Commands` | | `CMTE` | `ChunkMap.TrackedEntity` | | `CNU` | `CollectingNeighborUpdater` | | `Comp` | `Component` | | `Conn` | `Connection` | | `Cons` | `Consumable` | | `ContT` | `ContinuationTask` | | `CPL` | `ClientPacketListener` | | `CPList` | `Climate.ParameterList` | | `CR` | `CombatRules` | | `CraftM` | `CraftingMenu` | | `CRT` | `Climate.RTree` | | `CRU` | `ComponentRenderUtils` | | `CS` | `ComponentSerialization` | | `CScr` | `ChatScreen` | | `CSP` | `ClientSuggestionProvider` | | `CSR` | `ChestSpecialRenderer` | | `CST` | `ChunkStatusTasks` | | `CSug` | `CommandSuggestions` | | `CSync` | `ContainerSynchronizer` | | `CT` | `CombatTracker` | | `CTD` | `ChunkTaskDispatcher` | | `CU` | `ComponentUtils` | | `CWS` | `CreateWorldScreen` | | `DataC` | `DataCommands` | | `DB` | `DoorBlock` | | `DCP` | `DataComponentPatch` | | `DL` | `DirectoryLock` | | `DlgC` | `DialogCommand` | | `DlgS` | `DialogScreen` | | `DM` | `DistanceManager` | | `DMR` | `DefaultedMappedRegistry` | | `DRWE` | `DefaultRedstoneWireEvaluator` | | `DS` | `DedicatedServer` | | `DScr` | `DeathScreen` | | `EAP` | `EnvironmentAttributeProbe` | | `EAS` | `EnvironmentAttributeSystem` | | `EC` | `ExecutionContext` | | `EffC` | `EffectCommands` | | `EH` | `EnchantmentHelper` | | `EM` | `EnchantmentMenu` | | `Ench` | `Enchantment` | | `Entity` | `Entity` | | `ERD` | `EntityRenderDispatcher` | | `ES` | `EntityStorage` | | `EScr` | `EnchantmentScreen` | | `ET` | `EntityType` | | `ETL` | `EntityTickList` | | `EVS` | `EnvironmentAttributeSystem.ValueSampler` | | `ExecC` | `ExecuteCommand` | | `FBR` | `FormattedBidiReorder` | | `FD` | `FoodData` | | `FF` | `FlowingFluid` | | `FGB` | `FrameGraphBuilder` | | `FM` | `FurnaceMenu` | | `FolP` | `FoliagePlacer` | | `Font` | `Font` | | `FP` | `FoodProperties` | | `FR` | `FogRenderer` | | `FRD` | `FeatureRenderDispatcher` | | `FS` | `FeatureSorter` | | `FSet` | `FontSet` | | `GB` | `GpuBackend` | | `GC` | `GiveCommand` | | `GD` | `GpuDevice` | | `GED` | `GameEventDispatcher` | | `GGE` | `GuiGraphicsExtractor` | | `GI` | `GameTestInfo` | | `GlCE` | `GlCommandEncoder` | | `GLX` | `GLX` | | `GpuS` | `GpuSurface` | | `GR` | `GameRenderer` | | `GS` | `GaussianSampler` | | `GStit` | `GlyphStitcher` | | `GTR` | `GameTestRunner` | | `GTT` | `GameTestTicker` | | `Gui` | `Gui` | | `GuiR` | `GuiRenderer` | | `HS` | `HashedStack` | | `Hud` | `Hud` | | `ICT` | `InventoryChangeTrigger` | | `Ignite` | `Ignite` | | `IIHR` | `ItemInHandRenderer` | | `IMR` | `ItemModelResolver` | | `Inv` | `Inventory` | | `InvS` | `InventoryScreen` | | `IOW` | `IOWorker` | | `IP` | `ItemParser` | | `IS` | `IntegratedServer` | | `IStack` | `ItemStack` | | `Item` | `Item` | | `Items` | `Items` | | `JPP` | `JigsawPlacement.Placer` | | `JS` | `JigsawStructure` | | `JWT` | `JoinWorldTask` | | `KH` | `KeyboardHandler` | | `KI` | `KeyboardInput` | | `KM` | `KeyMapping` | | `KTS` | `KeyframeTrackSampler` | | `Language` | `Language` | | `LB` | `LiquidBlock` | | `LC` | `LevelChunk` | | `LCS` | `LevelChunkSection` | | `LCT` | `LoadingChunkTracker` | | `LCTs` | `LevelChunkTicks` | | `LDS` | `LevelDebugSynchronizers` | | `LE` | `LivingEntity` | | `LEH` | `LevelEventHandler` | | `LevB` | `LeverBlock` | | `Level` | `Level` | | `Library` | `Library` | | `LIC` | `LootItemCondition` | | `LIF` | `LootItemFunctions` | | `LLE` | `LevelLightEngine` | | `LLSS` | `LayerLightSectionStorage` | | `LM` | `Lightmap` | | `LO` | `LoadingOverlay` | | `LootC` | `LootContext` | | `LootP` | `LootParams` | | `LP` | `LocalPlayer` | | `LPool` | `LootPool` | | `LR` | `LevelRenderer` | | `LRA` | `LayeredRegistryAccess` | | `LRSE` | `LightmapRenderStateExtractor` | | `LSA` | `LevelStorageSource.LevelStorageAccess` | | `LT` | `LootTable` | | `LTs` | `LevelTicks` | | `LX` | `LevelExtractor` | | `MB` | `ModelBakery` | | `MC` | `Minecraft` | | `MComp` | `MutableComponent` | | `ME` | `MobEffect` | | `MEI` | `MobEffectInstance` | | `MH` | `MouseHandler` | | `MM` | `ModelManager` | | `MNBS` | `MultiNoiseBiomeSource` | | `Mob` | `Mob` | | `MonM` | `MonitorManager` | | `MoveC` | `MoveControl` | | `MPGM` | `MultiPlayerGameMode` | | `MPRM` | `MultiPackResourceManager` | | `MR` | `MappedRegistry` | | `MS` | `MinecraftServer` | | `MTS` | `MoveToTargetSink` | | `NBC` | `NoiseBasedChunkGenerator` | | `NbtIo` | `NbtIo` | | `NC` | `NoiseChunk` | | `NE` | `NodeEvaluator` | | `NS` | `NaturalSpawner` | | `PA` | `PlayerAdvancements` | | `Parrot` | `Parrot` | | `PBB` | `PistonBaseBlock` | | `PChain` | `PostChain` | | `PCS` | `PlayerChunkSender` | | `PDec` | `PacketDecoder` | | `PDM` | `PatchedDataComponentMap` | | `PDS` | `PlayerDataStorage` | | `PE` | `ParticleEngine` | | `PEnc` | `PacketEncoder` | | `PESM` | `PersistentEntitySectionManager` | | `PESP` | `PoolElementStructurePiece` | | `PF` | `PathFinder` | | `PL` | `PlayerList` | | `PlacedF` | `PlacedFeature` | | `Player` | `Player` | | `PM` | `PoiManager` | | `PMBE` | `PistonMovingBlockEntity` | | `PMod` | `PlacementModifier` | | `PN` | `PathNavigation` | | `PNR` | `PathNavigationRegion` | | `PP` | `PacketProcessor` | | `PPass` | `PostPass` | | `PR` | `PackRepository` | | `PRL` | `PreparableReloadListener` | | `PSR` | `PistonStructureResolver` | | `PST` | `PrepareSpawnTask` | | `PTT` | `DistanceManager.PlayerTicketTracker` | | `RB` | `RepeaterBlock` | | `RC` | `ReloadCommand` | | `RCont` | `RandomizableContainer` | | `RCPL` | `ClientPacketListener` | | `RDC` | `RegistryDataCollector` | | `RDL` | `RegistryDataLoader` | | `RemS` | `RemoteSlot` | | `ResultC` | `ResultContainer` | | `ResultS` | `ResultSlot` | | `RGL` | `ReportGameListener` | | `RLT` | `RegistryLoadTask` | | `RM` | `RecipeManager` | | `RMRLT` | `ResourceManagerRegistryLoadTask` | | `RootP` | `RootPlacer` | | `RP` | `RenderPass` | | `RRM` | `ReloadableResourceManager` | | `RS` | `RenderSystem` | | `RSR` | `ReloadableServerResources` | | `RSReg` | `ReloadableServerRegistries` | | `RSWB` | `RedStoneWireBlock` | | `RSyn` | `RegistrySynchronization` | | `SA` | `ScoreAccess` | | `SAI` | `SpatialAttributeInterpolator` | | `SBL` | `SoundBufferLibrary` | | `SC` | `StopCommand` | | `SCC` | `ServerChunkCache` | | `SCD` | `SerializableChunkData` | | `SCL` | `ServerConnectionListener` | | `SCM` | `ServerClockManager` | | `SCPL` | `ServerConfigurationPacketListenerImpl` | | `Screen` | `Screen` | | `SCT` | `SimulationChunkTracker` | | `SDS` | `ServerDebugSubscribers` | | `SE` | `ServerEntity` | | `SectC` | `SectionCompiler` | | `SED` | `SynchedEntityData` | | `SFL` | `ServerFunctionLibrary` | | `SFM` | `ServerFunctionManager` | | `SGPL` | `ServerGamePacketListenerImpl` | | `ShadM` | `ShaderManager` | | `Shapes` | `Shapes` | | `Sheep` | `Sheep` | | `SHPL` | `ServerHandshakePacketListenerImpl` | | `SIB` | `SleepInBed` | | `SICF` | `SetItemCountFunction` | | `SL` | `ServerLevel` | | `SLPL` | `ServerLoginPacketListenerImpl` | | `SndE` | `SoundEngine` | | `SndM` | `SoundManager` | | `SNS` | `SubmitNodeStorage` | | `SOG` | `SectionOcclusionGraph` | | `SP` | `ServerPlayer` | | `SPB` | `StructurePiecesBuilder` | | `SPGM` | `ServerPlayerGameMode` | | `SPie` | `StrongholdPieces` | | `SprL` | `SpriteLoader` | | `SR` | `SkyRenderer` | | `SRB` | `ServerRecipeBook` | | `SRD` | `SectionRenderDispatcher` | | `SRI` | `SimpleReloadInstance` | | `SRT` | `SynchronizeRegistriesTask` | | `SS` | `ServerScoreboard` | | `SSB` | `SculkSensorBlock` | | `SSPL` | `ServerStatusPacketListenerImpl` | | `SSpl` | `StringSplitter` | | `SStart` | `StructureStart` | | `SStr` | `StrongholdStructure` | | `STemp` | `StructureTemplate` | | `STP` | `StructureTemplatePool` | | `SumC` | `SummonCommand` | | `SUT` | `SectionUpdateTracker` | | `SW` | `ServerWatchdog` | | `TA` | `TextureAtlas` | | `TagP` | `TagParser` | | `TC` | `TestCommand` | | `TCTD` | `ThrottlingChunkTaskDispatcher` | | `TDec` | `TreeDecorator` | | `TDSS` | `TrackingDebugSynchronizer.SourceSynchronizer` | | `TF` | `TreeFeature` | | `TIB` | `TestInstanceBlockEntity` | | `Time` | `Timelines` | | `TL` | `TagLoader` | | `TLE` | `ThreadedLevelLightEngine` | | `TP` | `TrunkPlacer` | | `TrC` | `TranslatableContents` | | `TRM` | `ServerTickRateManager` | | `TS` | `TicketStorage` | | `TVO` | `TagValueOutput` | | `UAFS` | `UpdateActivityFromSchedule` | | `VNP` | `ValidateNearbyPoi` | | `VSel` | `VibrationSelector` | | `VSL` | `VibrationSystem.Listener` | | `VST` | `VibrationSystem.Ticker` | | `WB` | `WorldBorder` | | `WCUS` | `WorldCreationUiState` | | `WGL` | `WorldGenLevel` | | `Window` | `Window` | | `WL` | `WorldLoader` | | `WOF` | `WorldOpenFlows` | | `WR` | `WorldgenRandom` | | `WS` | `WorldStem` | | `ZM` | `ZombieModel` | | `ZR` | `ZombieRenderer` | | `ZS` | `ZombieRenderState` | | `Auth` | *the User Authenticator thread, not a class* | | `Disk` | *the save on disk, not a class* | | `Game` | *the game's own code above Blaze3D, not a class* | | `Hook` | *the Server Shutdown Thread JVM hook, not a class* | | `JVM` | *the process itself, not a class* | | `Main` | *the JVM main thread, running whichever program's Main the diagram is about — the server's or the client's, so not one class* | | `Netty` | *the Netty event loop, not a class* | | `Wire` | *the network between the two programs, not a class* | | `Worker` | *the `Util.backgroundExecutor` pool, not a class* | --- # Class index > Generated by `python tools/verify_names.py --index` on every deploy. Do not edit by hand. Every class **backticked** on a page — a system page, a Reference page, the atlas, the introduction or the lecture map — and the pages that name it. Two things are outside it: a class named only inside a diagram, because the index reads backticked names and a mermaid label is not one, and the generated views, whose backticks are registry and packet ids rather than class names. 2739 names across 131 pages. A row is a simple name, not a class: a few names belong to more than one class (there are five `Main`s), and a few are library classes from Brigadier, DataFixerUpper or authlib. | class | pages | |---|---| | `AABB` | [math-and-primitives](../reference/math-and-primitives.md), [movement-and-collision](../systems/entities/movement-and-collision.md) | | `Abilities` | [entity-anatomy](../systems/entities/entity-anatomy.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md) | | `AboveRootPlacement` | [trees](../systems/worldgen/trees.md) | | `AbstractArrow` | [naming-drift](../reference/naming-drift.md), [non-living-damage](../reference/non-living-damage.md), [the-client-level](../systems/client/the-client-level.md), [damage-and-death](../systems/entities/damage-and-death.md), [enchantments](../systems/items/enchantments.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `AbstractBoat` | [the-client-level](../systems/client/the-client-level.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [authority](../systems/entities/authority.md), [input-to-movement](../systems/player/input-to-movement.md) | | `AbstractButton` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `AbstractClientPlayer` | [attributes](../systems/entities/attributes.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [player-anatomy](../systems/player/player-anatomy.md) | | `AbstractConsecutiveExecutor` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `AbstractContainerEventHandler` | [hierarchy](../maps/hierarchy.md) | | `AbstractContainerMenu` | [block-entities](../systems/blocks/block-entities.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [advancements](../systems/commands/advancements.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchanting](../systems/items/enchanting.md), [loot-tables](../systems/items/loot-tables.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `AbstractContainerScreen` | [hierarchy](../maps/hierarchy.md), [gui-and-screens](../systems/client/gui-and-screens.md), [the-client-loop](../systems/client/the-client-loop.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `AbstractCookingRecipe` | [recipes](../systems/items/recipes.md) | | `AbstractCraftingMenu` | [recipes](../systems/items/recipes.md) | | `AbstractDebugChart` | [hud](../systems/client/hud.md) | | `AbstractDeviceTracker` | [sound-engine](../systems/client/sound-engine.md) | | `AbstractEndPortalRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `AbstractFurnaceBlock` | [block-entities](../systems/blocks/block-entities.md) | | `AbstractFurnaceBlockEntity` | [block-entities](../systems/blocks/block-entities.md), [recipes](../systems/items/recipes.md) | | `AbstractFurnaceMenu` | [block-entities](../systems/blocks/block-entities.md), [recipes](../systems/items/recipes.md) | | `AbstractHurtingProjectile` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `AbstractMinecart` | [the-client-level](../systems/client/the-client-level.md) | | `AbstractScrollArea` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `AbstractSelectionList` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `AbstractSignRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `AbstractSoundInstance` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `AbstractTexture` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `AbstractTickableSoundInstance` | [sound-engine](../systems/client/sound-engine.md) | | `AbstractVillager` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `AbstractWidget` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `AbstractZombieRenderer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `AcaciaFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `AccountProfileKeyPairManager` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `AcquirePoi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [pathfinding](../systems/entities/pathfinding.md), [points-of-interest](../systems/world/points-of-interest.md) | | `Action` | [dialogs](../systems/commands/dialogs.md), [data-driven-types](../systems/foundations/data-driven-types.md), [text-components](../systems/foundations/text-components.md) | | `ActionButton` | [dialogs](../systems/commands/dialogs.md) | | `ActionTypes` | [dialogs](../systems/commands/dialogs.md) | | `ActiveProfiler` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `ActiveTextCollector` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `Activity` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [points-of-interest](../systems/world/points-of-interest.md) | | `ActivityData` | [glossary](../reference/glossary.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `AddValue` | [enchantments](../systems/items/enchantments.md) | | `Advancement` | [advancements](../systems/commands/advancements.md) | | `AdvancementCommands` | [advancements](../systems/commands/advancements.md) | | `AdvancementHolder` | [naming-drift](../reference/naming-drift.md), [advancements](../systems/commands/advancements.md) | | `AdvancementNode` | [naming-drift](../reference/naming-drift.md), [advancements](../systems/commands/advancements.md) | | `AdvancementProgress` | [advancements](../systems/commands/advancements.md) | | `AdvancementRequirements` | [advancements](../systems/commands/advancements.md) | | `AdvancementRewards` | [advancements](../systems/commands/advancements.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `AdvancementsScreen` | [advancements](../systems/commands/advancements.md) | | `AdvancementTab` | [advancements](../systems/commands/advancements.md) | | `AdvancementTabType` | [advancements](../systems/commands/advancements.md) | | `AdvancementTree` | [naming-drift](../reference/naming-drift.md), [advancements](../systems/commands/advancements.md) | | `AdvancementType` | [naming-drift](../reference/naming-drift.md), [advancements](../systems/commands/advancements.md) | | `AdvancementVisibilityEvaluator` | [advancements](../systems/commands/advancements.md) | | `AdvancementWidget` | [advancements](../systems/commands/advancements.md) | | `AdventureModePredicate` | [block-entities](../systems/blocks/block-entities.md) | | `AgeableMob` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `AgeableMobRenderer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `Allay` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [pathfinding](../systems/entities/pathfinding.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `AllayAi` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `AllOf` | [enchantments](../systems/items/enchantments.md) | | `AllOfCondition` | [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `AlterGroundDecorator` | [trees](../systems/worldgen/trees.md) | | `AlternativesEntry` | [loot-tables](../systems/items/loot-tables.md) | | `AmbientAdditionsSettings` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `AmbientMoodSettings` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `AmbientParticle` | [data-driven-types](../systems/foundations/data-driven-types.md), [particles](../systems/rendering/particles.md) | | `AmphibiousNodeEvaluator` | [pathfinding](../systems/entities/pathfinding.md) | | `AmphibiousPathNavigation` | [pathfinding](../systems/entities/pathfinding.md) | | `Animal` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `AnimationDefinition` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `AnimationState` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `AnvilMenu` | [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `AnyBlockInteractionTrigger` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ApplyBonusCount` | [enchantments](../systems/items/enchantments.md), [loot-tables](../systems/items/loot-tables.md) | | `ApplyEntityImpulse` | [enchantments](../systems/items/enchantments.md) | | `ApplyExhaustion` | [enchantments](../systems/items/enchantments.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `ApplyMobEffect` | [enchantments](../systems/items/enchantments.md) | | `ApplyStatusEffectsConsumeEffect` | [hunger-and-experience](../systems/player/hunger-and-experience.md), [status-effects](../systems/player/status-effects.md) | | `Aquifer` | [glossary](../reference/glossary.md), [blending](../systems/worldgen/blending.md), [terrain](../systems/worldgen/terrain.md) | | `AreaEffectCloud` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `ARGB` | [math-and-primitives](../reference/math-and-primitives.md) | | `ArgumentSignatures` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ArgumentType` | [glossary](../reference/glossary.md) | | `ArgumentTypeInfo` | [glossary](../reference/glossary.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ArgumentTypeInfos` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ArgumentUtils` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [permissions](../systems/commands/permissions.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `ArgumentVisitor` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ArmedEntityRenderState` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `ArmorModelSet` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `ArmorSlot` | [enchantments](../systems/items/enchantments.md) | | `ArmorStand` | [math-and-primitives](../reference/math-and-primitives.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ArrayVoxelShape` | [math-and-primitives](../reference/math-and-primitives.md) | | `AssignProfessionFromJobSite` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [points-of-interest](../systems/world/points-of-interest.md) | | `AtlasGlyphProvider` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `AtlasIds` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `AtlasManager` | [naming-drift](../reference/naming-drift.md), [resource-system](../systems/foundations/resource-system.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `AtlasSprite` | [text-components](../systems/foundations/text-components.md) | | `AtmosphericFogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `AttachedToLeavesDecorator` | [trees](../systems/worldgen/trees.md) | | `AttachedToLogsDecorator` | [trees](../systems/worldgen/trees.md) | | `AttachFace` | [math-and-primitives](../reference/math-and-primitives.md) | | `Attackable` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `AttackRange` | [data-components](../systems/foundations/data-components.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `Attribute` | [attributes](../systems/entities/attributes.md) | | `AttributeCommand` | [attributes](../systems/entities/attributes.md) | | `AttributeInstance` | [attributes](../systems/entities/attributes.md), [status-effects](../systems/player/status-effects.md) | | `AttributeMap` | [naming-drift](../reference/naming-drift.md), [attributes](../systems/entities/attributes.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `AttributeModifier` | [naming-drift](../reference/naming-drift.md), [attributes](../systems/entities/attributes.md), [enchantments](../systems/items/enchantments.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [biomes](../systems/worldgen/biomes.md) | | `AttributeRange` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `Attributes` | [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [attributes](../systems/entities/attributes.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [pathfinding](../systems/entities/pathfinding.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [status-effects](../systems/player/status-effects.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `AttributeSupplier` | [attributes](../systems/entities/attributes.md) | | `AttributeTrack` | [glossary](../reference/glossary.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `AttributeTrackSampler` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `AttributeType` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `AttributeTypes` | [data-driven-types](../systems/foundations/data-driven-types.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `AudioStream` | [sound-engine](../systems/client/sound-engine.md) | | `Avatar` | [glossary](../reference/glossary.md), [VI · Entities](../systems/entities/README.md), [attributes](../systems/entities/attributes.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [VIII · The player](../systems/player/README.md), [player-anatomy](../systems/player/player-anatomy.md) | | `AvatarRenderer` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [player-anatomy](../systems/player/player-anatomy.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `AvatarRenderState` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `AxeItem` | [data-components](../systems/foundations/data-components.md) | | `Axis` | [math-and-primitives](../reference/math-and-primitives.md) | | `AxisCycle` | [math-and-primitives](../reference/math-and-primitives.md) | | `Axolotl` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `BackendCreationException` | [blaze3d](../systems/rendering/blaze3d.md) | | `BackgroundMusic` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `BackUpIfTooClose` | [pathfinding](../systems/entities/pathfinding.md) | | `BadOmenMobEffect` | [points-of-interest](../systems/world/points-of-interest.md) | | `BakedGlyph` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `BakedQuad` | [naming-drift](../reference/naming-drift.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BakedSheetGlyph` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `BandwidthDebugChart` | [hud](../systems/client/hud.md) | | `BandwidthDebugMonitor` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `BannerRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BannerSpecialRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BaseContainerBlockEntity` | [loot-tables](../systems/items/loot-tables.md) | | `BaseEntityBlock` | [hierarchy](../maps/hierarchy.md), [block-entities](../systems/blocks/block-entities.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `BaseRailBlock` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `BaseSpawner` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `BeaconRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BeaconScreen` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `Beardifier` | [density-function-nodes](../reference/density-function-nodes.md), [density-functions](../systems/worldgen/density-functions.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md), [terrain](../systems/worldgen/terrain.md) | | `BedBlock` | [points-of-interest](../systems/world/points-of-interest.md) | | `BedPart` | [points-of-interest](../systems/world/points-of-interest.md) | | `BedRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BedRule` | [naming-drift](../reference/naming-drift.md) | | `Bee` | [biggest](../maps/biggest.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [pathfinding](../systems/entities/pathfinding.md), [points-of-interest](../systems/world/points-of-interest.md) | | `BeehiveBlockEntity` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [points-of-interest](../systems/world/points-of-interest.md) | | `BeehiveDecorator` | [trees](../systems/worldgen/trees.md) | | `Behavior` | [glossary](../reference/glossary.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `BehaviorBuilder` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `BehaviorControl` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `BelowZeroRetrogen` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [blending](../systems/worldgen/blending.md), [terrain](../systems/worldgen/terrain.md) | | `BendingTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `BinaryHeap` | [pathfinding](../systems/entities/pathfinding.md) | | `BindGroupLayout` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `BindGroupLayouts` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `Biome` | [lectures](../lectures.md), [naming-drift](../reference/naming-drift.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [server-level-tick](../systems/server/server-level-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [XII · World generation](../systems/worldgen/README.md), [biomes](../systems/worldgen/biomes.md), [blending](../systems/worldgen/blending.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `BiomeAmbientSoundsHandler` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `BiomeColors` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [biomes](../systems/worldgen/biomes.md) | | `BiomeFilter` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `BiomeGenerationSettings` | [naming-drift](../reference/naming-drift.md), [biomes](../systems/worldgen/biomes.md) | | `BiomeManager` | [math-and-primitives](../reference/math-and-primitives.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [biomes](../systems/worldgen/biomes.md) | | `BiomeSource` | [data-driven-types](../systems/foundations/data-driven-types.md), [biomes](../systems/worldgen/biomes.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `BiomeSpecialEffects` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [biomes](../systems/worldgen/biomes.md) | | `BiomeTags` | [tags](../systems/foundations/tags.md) | | `BitRandomSource` | [math-and-primitives](../reference/math-and-primitives.md) | | `BitSetDiscreteVoxelShape` | [math-and-primitives](../reference/math-and-primitives.md) | | `BitStorage` | [math-and-primitives](../reference/math-and-primitives.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `BlackholeTickAccess` | [the-client-level](../systems/client/the-client-level.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `BlackstoneReplaceProcessor` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `Blaze` | [pathfinding](../systems/entities/pathfinding.md) | | `BlendedNoise` | [density-function-nodes](../reference/density-function-nodes.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `Blender` | [density-function-nodes](../reference/density-function-nodes.md), [glossary](../reference/glossary.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [blending](../systems/worldgen/blending.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `BlendFunction` | [blaze3d](../systems/rendering/blaze3d.md) | | `BlendingData` | [glossary](../reference/glossary.md), [blending](../systems/worldgen/blending.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `BlindnessFogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `BlitRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `BlobFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `Block` | [fanin](../maps/fanin.md), [hierarchy](../maps/hierarchy.md), [block-update-flags](../reference/block-update-flags.md), [glossary](../reference/glossary.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [authority](../systems/entities/authority.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [particles](../systems/rendering/particles.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `BlockableEventLoop` | [glossary](../reference/glossary.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [sound-engine](../systems/client/sound-engine.md), [the-client-loop](../systems/client/the-client-loop.md), [resource-system](../systems/foundations/resource-system.md), [input-to-movement](../systems/player/input-to-movement.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [points-of-interest](../systems/world/points-of-interest.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `BlockAgeProcessor` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `BlockAndLightGetter` | [lighting](../systems/world/lighting.md) | | `BlockAttachedEntity` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `BlockBasedTestInstance` | [game-tests](../systems/commands/game-tests.md) | | `BlockBehaviour` | [fanin](../maps/fanin.md), [hierarchy](../maps/hierarchy.md), [block-update-flags](../reference/block-update-flags.md), [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md), [particles](../systems/rendering/particles.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [fluids](../systems/world/fluids.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `BlockBox` | [math-and-primitives](../reference/math-and-primitives.md) | | `BlockCollisions` | [math-and-primitives](../reference/math-and-primitives.md), [movement-and-collision](../systems/entities/movement-and-collision.md) | | `BlockColors` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BlockDataAccessor` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `BlockDataSource` | [text-components](../systems/foundations/text-components.md) | | `BlockDestructionProgress` | [block-breaking](../systems/blocks/block-breaking.md) | | `BlockEntity` | [block-update-flags](../reference/block-update-flags.md), [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `BlockEntityRenderDispatcher` | [resource-system](../systems/foundations/resource-system.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockEntityRenderer` | [glossary](../reference/glossary.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockEntityRenderers` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockEntityRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockEntityTicker` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `BlockEntityType` | [block-entities](../systems/blocks/block-entities.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockEntityTypes` | [block-entities](../systems/blocks/block-entities.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockEntityWithBoundingBoxRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockEventData` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `BlockGetter` | [movement-and-collision](../systems/entities/movement-and-collision.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `BlockHitResult` | [math-and-primitives](../reference/math-and-primitives.md), [block-interaction](../systems/blocks/block-interaction.md) | | `BlockIds` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `BlockIgnoreProcessor` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `BlockInput` | [block-update-flags](../reference/block-update-flags.md), [block-entities](../systems/blocks/block-entities.md) | | `BlockItem` | [naming-drift](../reference/naming-drift.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [data-components](../systems/foundations/data-components.md), [section-meshing](../systems/rendering/section-meshing.md) | | `BlockItemIds` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `BlockItemTagId` | [tags](../systems/foundations/tags.md) | | `BlockItemTags` | [tags](../systems/foundations/tags.md) | | `BlockLightEngine` | [lighting](../systems/world/lighting.md) | | `BlockListReport` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `BlockMarker` | [models-and-atlases](../systems/rendering/models-and-atlases.md), [particles](../systems/rendering/particles.md) | | `BlockMath` | [math-and-primitives](../reference/math-and-primitives.md) | | `BlockModel` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BlockModelFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `BlockModelGenerators` | [biggest](../maps/biggest.md) | | `BlockModelLighter` | [section-meshing](../systems/rendering/section-meshing.md) | | `BlockModelResolver` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BlockModelSet` | [naming-drift](../reference/naming-drift.md) | | `BlockPos` | [fanin](../maps/fanin.md), [math-and-primitives](../reference/math-and-primitives.md), [V · Blocks](../systems/blocks/README.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `BlockPosArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `BlockPositionSource` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `BlockPredicate` | [data-driven-types](../systems/foundations/data-driven-types.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `BlockPredicateType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `BlockRotProcessor` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `Blocks` | [biggest](../maps/biggest.md), [fanin](../maps/fanin.md), [packages](../maps/packages.md), [naming-drift](../reference/naming-drift.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [particles](../systems/rendering/particles.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [fluids](../systems/world/fluids.md), [lighting](../systems/world/lighting.md) | | `BlocksAttacks` | [damage-and-death](../systems/entities/damage-and-death.md), [data-components](../systems/foundations/data-components.md) | | `BlockSetType` | [block-interaction](../systems/blocks/block-interaction.md) | | `BlockState` | [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [the-frame](../systems/rendering/the-frame.md), [fluids](../systems/world/fluids.md) | | `BlockStateArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `BlockStateData` | [biggest](../maps/biggest.md) | | `BlockStateModel` | [naming-drift](../reference/naming-drift.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BlockStateModelDispatcher` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BlockStateModelLoader` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BlockStateModelPart` | [submit-phases](../reference/submit-phases.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BlockStateModelSet` | [naming-drift](../reference/naming-drift.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [section-meshing](../systems/rendering/section-meshing.md) | | `BlockStateModelWrapper` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BlockStateParser` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `BlockStatePredictionHandler` | [glossary](../reference/glossary.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [input-to-movement](../systems/player/input-to-movement.md) | | `BlockStateProperties` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [fluids](../systems/world/fluids.md), [trees](../systems/worldgen/trees.md) | | `BlockStateProvider` | [data-driven-types](../systems/foundations/data-driven-types.md), [trees](../systems/worldgen/trees.md) | | `BlockStateProviderType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `BlockTags` | [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [tags](../systems/foundations/tags.md), [enchanting](../systems/items/enchanting.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [points-of-interest](../systems/world/points-of-interest.md) | | `BlockTintCache` | [the-client-level](../systems/client/the-client-level.md), [biomes](../systems/worldgen/biomes.md) | | `BlockTypes` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `BlockUtil` | [math-and-primitives](../reference/math-and-primitives.md) | | `BodyRotationControl` | [pathfinding](../systems/entities/pathfinding.md) | | `Bogged` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `BonusLevelTableCondition` | [enchantments](../systems/items/enchantments.md), [loot-tables](../systems/items/loot-tables.md) | | `BooleanInput` | [dialogs](../systems/commands/dialogs.md) | | `BooleanModifier` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `BooleanOp` | [math-and-primitives](../reference/math-and-primitives.md) | | `BooleanProperty` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `Bootstrap` | [anatomy](../systems/anatomy/anatomy.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md) | | `BootstrapContext` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `BorderChangeListener` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `BorderStatus` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `BossHealthOverlay` | [hud-elements](../reference/hud-elements.md), [hud](../systems/client/hud.md) | | `BoundingBox` | [math-and-primitives](../reference/math-and-primitives.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `BowItem` | [using-an-item](../systems/items/using-an-item.md) | | `Brain` | [glossary](../reference/glossary.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [points-of-interest](../systems/world/points-of-interest.md) | | `BrandPayload` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `BreakingItemParticle` | [particles](../systems/rendering/particles.md) | | `Breeze` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `BrigadierExceptions` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `Brightness` | [math-and-primitives](../reference/math-and-primitives.md) | | `BrushableBlockEntity` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `BrushableBlockRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `BrushItem` | [using-an-item](../systems/items/using-an-item.md) | | `BubbleColumnAmbientSoundHandler` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `BubbleColumnBlock` | [fluids](../systems/world/fluids.md) | | `Bucketable` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `BucketItem` | [fluids](../systems/world/fluids.md) | | `BufferBuilder` | [blaze3d](../systems/rendering/blaze3d.md) | | `BuildContexts` | [naming-drift](../reference/naming-drift.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `BuiltInBlockModels` | [glossary](../reference/glossary.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `BuiltinDimensionTypes` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `BuiltInLootTables` | [naming-drift](../reference/naming-drift.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [loot-tables](../systems/items/loot-tables.md) | | `BuiltInPackSource` | [resource-system](../systems/foundations/resource-system.md) | | `BuiltInRegistries` | [fanin](../maps/fanin.md), [density-function-nodes](../reference/density-function-nodes.md), [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [advancements](../systems/commands/advancements.md), [dialogs](../systems/commands/dialogs.md), [permissions](../systems/commands/permissions.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [attributes](../systems/entities/attributes.md), [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [status-effects](../systems/player/status-effects.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [points-of-interest](../systems/world/points-of-interest.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [density-functions](../systems/worldgen/density-functions.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `BuiltinStructures` | [structure-placement](../systems/worldgen/structure-placement.md) | | `BuiltinStructureSets` | [structure-placement](../systems/worldgen/structure-placement.md) | | `BuiltinTestFunctions` | [game-tests](../systems/commands/game-tests.md) | | `BulkSectionAccess` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `BundleContents` | [data-components](../systems/foundations/data-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `BundleDelimiterPacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `BundleItem` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `BundlePacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `BundlerInfo` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `BuriedTreasurePieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `BushFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `Button` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ButtonListDialog` | [dialogs](../systems/commands/dialogs.md) | | `ByIdMap` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `ByteArrayTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ByteBufCodecs` | [fanin](../maps/fanin.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [text-components](../systems/foundations/text-components.md), [loot-tables](../systems/items/loot-tables.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ByteBufferBuilder` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `CacheableFunction` | [naming-drift](../reference/naming-drift.md) | | `CachedParseState` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `CakeBlock` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `CalibratedSculkSensorBlock` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `CalibratedSculkSensorBlockEntity` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `CallbackDeviceTracker` | [sound-engine](../systems/client/sound-engine.md) | | `CallFunction` | [functions-and-macros](../systems/commands/functions-and-macros.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `Camera` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [sound-engine](../systems/client/sound-engine.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [the-frame](../systems/rendering/the-frame.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `CameraRenderState` | [glossary](../reference/glossary.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [the-frame](../systems/rendering/the-frame.md) | | `CampfireBlock` | [recipes](../systems/items/recipes.md) | | `CampfireBlockEntity` | [recipes](../systems/items/recipes.md) | | `CampfireRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `CapeLayer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `CardinalLighting` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `CarverConfiguration` | [terrain](../systems/worldgen/terrain.md) | | `CarvingContext` | [terrain](../systems/worldgen/terrain.md) | | `CarvingMask` | [blending](../systems/worldgen/blending.md), [terrain](../systems/worldgen/terrain.md) | | `CatSpawner` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [points-of-interest](../systems/world/points-of-interest.md) | | `CauldronInteractions` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `CaveFeatures` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `ChainModifiers` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `ChangeItemDamage` | [enchantments](../systems/items/enchantments.md) | | `Channel` | [sound-engine](../systems/client/sound-engine.md) | | `ChannelAccess` | [sound-engine](../systems/client/sound-engine.md) | | `CharacterEvent` | [naming-drift](../reference/naming-drift.md), [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `ChargedProjectiles` | [items-and-stacks](../systems/items/items-and-stacks.md) | | `ChaseClient` | [threads](../reference/threads.md) | | `ChaseCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ChaseServer` | [threads](../reference/threads.md) | | `ChatAbilities` | [permissions](../systems/commands/permissions.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatComponent` | [hud-elements](../reference/hud-elements.md), [hud](../systems/client/hud.md) | | `ChatDecorator` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatFormatting` | [naming-drift](../reference/naming-drift.md), [text-components](../systems/foundations/text-components.md) | | `ChatListener` | [hud](../systems/client/hud.md), [text-components](../systems/foundations/text-components.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatReportContextBuilder` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatRestriction` | [permissions](../systems/commands/permissions.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatScreen` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatTrustLevel` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatType` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ChatVisiblity` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `Checkbox` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `CherryFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `CherryTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `ChestBlock` | [block-entities](../systems/blocks/block-entities.md), [containers-and-menus](../systems/items/containers-and-menus.md), [loot-tables](../systems/items/loot-tables.md) | | `ChestBlockEntity` | [block-entities](../systems/blocks/block-entities.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [containers-and-menus](../systems/items/containers-and-menus.md), [loot-tables](../systems/items/loot-tables.md) | | `ChestMenu` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ChestModel` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ChestRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ChestSpecialRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `Chicken` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `ChunkAccess` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [lighting](../systems/world/lighting.md), [blending](../systems/worldgen/blending.md), [terrain](../systems/worldgen/terrain.md) | | `ChunkBatchSizeCalculator` | [threads](../reference/threads.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ChunkDependencies` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `ChunkedSampleByteBuf` | [sound-engine](../systems/client/sound-engine.md) | | `ChunkGenerationTask` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `ChunkGenerator` | [level-data-and-rules](../reference/level-data-and-rules.md), [data-driven-types](../systems/foundations/data-driven-types.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [biomes](../systems/worldgen/biomes.md), [creating-a-world](../systems/worldgen/creating-a-world.md), [features-and-placement](../systems/worldgen/features-and-placement.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md), [terrain](../systems/worldgen/terrain.md) | | `ChunkGenerators` | [terrain](../systems/worldgen/terrain.md) | | `ChunkGeneratorStructureState` | [structure-placement](../systems/worldgen/structure-placement.md) | | `ChunkHolder` | [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ChunkLevel` | [glossary](../reference/glossary.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ChunkLoadCounter` | [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `ChunkMap` | [introduction](../introduction.md), [biggest](../maps/biggest.md), [packages](../maps/packages.md), [anatomy](../systems/anatomy/anatomy.md), [options](../systems/client/options.md), [attributes](../systems/entities/attributes.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [blending](../systems/worldgen/blending.md), [terrain](../systems/worldgen/terrain.md) | | `ChunkPos` | [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [IV · The world](../systems/world/README.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md) | | `ChunkPyramid` | [lectures](../lectures.md), [math-and-primitives](../reference/math-and-primitives.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [lighting](../systems/world/lighting.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ChunkScanAccess` | [chunk-storage](../systems/world/chunk-storage.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `ChunkSectionLayer` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [section-meshing](../systems/rendering/section-meshing.md) | | `ChunkSectionLayerGroup` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `ChunkSectionsToRender` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `ChunkSkyLightSources` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [lighting](../systems/world/lighting.md) | | `ChunkSource` | [pathfinding](../systems/entities/pathfinding.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ChunkStatus` | [lectures](../lectures.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [XII · World generation](../systems/worldgen/README.md), [biomes](../systems/worldgen/biomes.md), [blending](../systems/worldgen/blending.md), [density-functions](../systems/worldgen/density-functions.md), [features-and-placement](../systems/worldgen/features-and-placement.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md), [terrain](../systems/worldgen/terrain.md) | | `ChunkStatusTasks` | [block-entities](../systems/blocks/block-entities.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [blending](../systems/worldgen/blending.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `ChunkStep` | [glossary](../reference/glossary.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `ChunkTaskDispatcher` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [lighting](../systems/world/lighting.md) | | `ChunkTaskPriorityQueue` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `ChunkTracker` | [lighting](../systems/world/lighting.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ChunkTrackingView` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [players-and-sessions](../systems/server/players-and-sessions.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ChunkType` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md) | | `CipherBase` | [the-connection](../systems/networking/the-connection.md) | | `CipherDecoder` | [the-connection](../systems/networking/the-connection.md) | | `CipherEncoder` | [the-connection](../systems/networking/the-connection.md) | | `ClassTreeIdRegistry` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `Clearable` | [loot-tables](../systems/items/loot-tables.md) | | `ClearAllStatusEffectsConsumeEffect` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `ClickAction` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ClickEvent` | [dialogs](../systems/commands/dialogs.md), [text-components](../systems/foundations/text-components.md) | | `ClientAdvancements` | [advancements](../systems/commands/advancements.md) | | `ClientAvatarEntity` | [player-anatomy](../systems/player/player-anatomy.md) | | `ClientBootstrap` | [anatomy](../systems/anatomy/anatomy.md) | | `ClientboundAddEntityPacket` | [naming-drift](../reference/naming-drift.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundAnimatePacket` | [the-sword-swing](../systems/player/the-sword-swing.md) | | `ClientboundBlockChangedAckPacket` | [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundBlockDestructionPacket` | [block-breaking](../systems/blocks/block-breaking.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundBlockEntityDataPacket` | [block-entities](../systems/blocks/block-entities.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundBlockEventPacket` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md) | | `ClientboundBlockUpdatePacket` | [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md), [fluids](../systems/world/fluids.md) | | `ClientboundBundleDelimiterPacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundBundlePacket` | [synched-entity-data](../systems/entities/synched-entity-data.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundChangeDifficultyPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundChunkBatchFinishedPacket` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ClientboundChunkBatchStartPacket` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ClientboundChunksBiomesPacket` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [biomes](../systems/worldgen/biomes.md) | | `ClientboundClearDialogPacket` | [dialogs](../systems/commands/dialogs.md) | | `ClientboundCommandsPacket` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [permissions](../systems/commands/permissions.md) | | `ClientboundContainerClosePacket` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ClientboundContainerSetContentPacket` | [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [loot-tables](../systems/items/loot-tables.md) | | `ClientboundContainerSetDataPacket` | [block-entities](../systems/blocks/block-entities.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchanting](../systems/items/enchanting.md) | | `ClientboundContainerSetSlotPacket` | [naming-drift](../reference/naming-drift.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [recipes](../systems/items/recipes.md) | | `ClientboundCookieRequestPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundCooldownPacket` | [prediction-and-acks](../systems/client/prediction-and-acks.md), [using-an-item](../systems/items/using-an-item.md) | | `ClientboundCustomChatCompletionsPacket` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ClientboundCustomPayloadPacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundCustomQueryPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundDamageEventPacket` | [damage-and-death](../systems/entities/damage-and-death.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `ClientboundDebugBlockValuePacket` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `ClientboundDebugChunkValuePacket` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `ClientboundDebugEntityValuePacket` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `ClientboundDebugEventPacket` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `ClientboundDebugSamplePacket` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `ClientboundDeleteChatPacket` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ClientboundDisconnectPacket` | [text-components](../systems/foundations/text-components.md), [the-connection](../systems/networking/the-connection.md) | | `ClientboundDisguisedChatPacket` | [chat-and-signing](../systems/networking/chat-and-signing.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundEntityEventPacket` | [level-data-and-rules](../reference/level-data-and-rules.md), [permissions](../systems/commands/permissions.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [using-an-item](../systems/items/using-an-item.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ClientboundEntityPositionSyncPacket` | [naming-drift](../reference/naming-drift.md), [authority](../systems/entities/authority.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundExplodePacket` | [the-client-level](../systems/client/the-client-level.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [particles](../systems/rendering/particles.md) | | `ClientboundForgetLevelChunkPacket` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ClientboundGameEventPacket` | [level-data-and-rules](../reference/level-data-and-rules.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [player-anatomy](../systems/player/player-anatomy.md), [server-level-tick](../systems/server/server-level-tick.md) | | `ClientboundGameRuleValuesPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundGameTestHighlightPosPacket` | [game-tests](../systems/commands/game-tests.md) | | `ClientboundHelloPacket` | [naming-drift](../reference/naming-drift.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ClientboundHurtAnimationPacket` | [damage-and-death](../systems/entities/damage-and-death.md) | | `ClientboundInitializeBorderPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundKeepAlivePacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundLevelChunkPacketData` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `ClientboundLevelChunkWithLightPacket` | [naming-drift](../reference/naming-drift.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [lighting](../systems/world/lighting.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ClientboundLevelEventPacket` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [particles](../systems/rendering/particles.md) | | `ClientboundLevelParticlesPacket` | [particles](../systems/rendering/particles.md) | | `ClientboundLightUpdatePacket` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md), [lighting](../systems/world/lighting.md) | | `ClientboundLightUpdatePacketData` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [lighting](../systems/world/lighting.md) | | `ClientboundLoginCompressionPacket` | [naming-drift](../reference/naming-drift.md), [the-connection](../systems/networking/the-connection.md) | | `ClientboundLoginDisconnectPacket` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ClientboundLoginFinishedPacket` | [naming-drift](../reference/naming-drift.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundLoginPacket` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [protocol-phases](../systems/networking/protocol-phases.md), [player-anatomy](../systems/player/player-anatomy.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ClientboundMapItemDataPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundMountScreenOpenPacket` | [naming-drift](../reference/naming-drift.md), [gui-and-screens](../systems/client/gui-and-screens.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `ClientboundMoveEntityPacket` | [authority](../systems/entities/authority.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundMoveMinecartPacket` | [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundMoveVehiclePacket` | [authority](../systems/entities/authority.md), [input-to-movement](../systems/player/input-to-movement.md) | | `ClientboundOpenScreenPacket` | [gui-and-screens](../systems/client/gui-and-screens.md), [containers-and-menus](../systems/items/containers-and-menus.md), [loot-tables](../systems/items/loot-tables.md) | | `ClientboundPlaceGhostRecipePacket` | [recipes](../systems/items/recipes.md) | | `ClientboundPlayerAbilitiesPacket` | [player-anatomy](../systems/player/player-anatomy.md) | | `ClientboundPlayerChatPacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundPlayerCombatKillPacket` | [damage-and-death](../systems/entities/damage-and-death.md), [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundPlayerInfoRemovePacket` | [players-and-sessions](../systems/server/players-and-sessions.md) | | `ClientboundPlayerInfoUpdatePacket` | [player-anatomy](../systems/player/player-anatomy.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md) | | `ClientboundPlayerPositionPacket` | [input-to-movement](../systems/player/input-to-movement.md) | | `ClientboundPlayerRotationPacket` | [input-to-movement](../systems/player/input-to-movement.md) | | `ClientboundPongResponsePacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundProjectilePowerPacket` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundRecipeBookAddPacket` | [recipes](../systems/items/recipes.md) | | `ClientboundRegistryDataPacket` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundRemoveEntitiesPacket` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `ClientboundRemoveMobEffectPacket` | [synched-entity-data](../systems/entities/synched-entity-data.md), [status-effects](../systems/player/status-effects.md) | | `ClientboundResetChatPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundResetScorePacket` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ClientboundResourcePackPopPacket` | [resource-system](../systems/foundations/resource-system.md) | | `ClientboundResourcePackPushPacket` | [naming-drift](../reference/naming-drift.md), [resource-system](../systems/foundations/resource-system.md) | | `ClientboundRespawnPacket` | [player-anatomy](../systems/player/player-anatomy.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ClientboundRotateHeadPacket` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `ClientboundSectionBlocksUpdatePacket` | [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md), [fluids](../systems/world/fluids.md) | | `ClientboundSelectKnownPacks` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundServerDataPacket` | [text-components](../systems/foundations/text-components.md) | | `ClientboundServerLinksPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundSetBorderCenterPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundSetBorderLerpSizePacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundSetBorderSizePacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundSetBorderWarningDelayPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundSetBorderWarningDistancePacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundSetChunkCacheCenterPacket` | [naming-drift](../reference/naming-drift.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ClientboundSetChunkCacheRadiusPacket` | [naming-drift](../reference/naming-drift.md), [options](../systems/client/options.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ClientboundSetCursorItemPacket` | [naming-drift](../reference/naming-drift.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `ClientboundSetDefaultSpawnPositionPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ClientboundSetDisplayObjectivePacket` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ClientboundSetEntityDataPacket` | [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `ClientboundSetEntityMotionPacket` | [damage-and-death](../systems/entities/damage-and-death.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundSetEquipmentPacket` | [synched-entity-data](../systems/entities/synched-entity-data.md), [data-components](../systems/foundations/data-components.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundSetExperiencePacket` | [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `ClientboundSetHealthPacket` | [damage-and-death](../systems/entities/damage-and-death.md), [using-an-item](../systems/items/using-an-item.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `ClientboundSetHeldSlotPacket` | [naming-drift](../reference/naming-drift.md), [containers-and-menus](../systems/items/containers-and-menus.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ClientboundSetObjectivePacket` | [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ClientboundSetPlayerInventoryPacket` | [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ClientboundSetPlayerTeamPacket` | [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ClientboundSetScorePacket` | [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ClientboundSetSimulationDistancePacket` | [options](../systems/client/options.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ClientboundSetTimePacket` | [level-data-and-rules](../reference/level-data-and-rules.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `ClientboundShowDialogPacket` | [dialogs](../systems/commands/dialogs.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundSoundEntityPacket` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `ClientboundSoundPacket` | [block-interaction](../systems/blocks/block-interaction.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `ClientboundStartConfigurationPacket` | [protocol-phases](../systems/networking/protocol-phases.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ClientboundStatusResponsePacket` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundStopSoundPacket` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `ClientboundStoreCookiePacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundSystemChatPacket` | [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundTagQueryPacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ClientboundTakeItemEntityPacket` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `ClientboundTeleportEntityPacket` | [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ClientboundTickingStatePacket` | [server-tick](../systems/server/server-tick.md) | | `ClientboundTickingStepPacket` | [server-tick](../systems/server/server-tick.md) | | `ClientboundTransferPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundUpdateAttributesPacket` | [attributes](../systems/entities/attributes.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `ClientboundUpdateEnabledFeaturesPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientboundUpdateMobEffectPacket` | [synched-entity-data](../systems/entities/synched-entity-data.md), [status-effects](../systems/player/status-effects.md) | | `ClientboundUpdateRecipesPacket` | [resource-system](../systems/foundations/resource-system.md), [recipes](../systems/items/recipes.md) | | `ClientboundUpdateTagsPacket` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [enchantments](../systems/items/enchantments.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientChunkCache` | [naming-drift](../reference/naming-drift.md), [the-client-level](../systems/client/the-client-level.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [lighting](../systems/world/lighting.md) | | `ClientClockManager` | [the-client-level](../systems/client/the-client-level.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `ClientCommonPacketListenerImpl` | [biggest](../maps/biggest.md), [threads](../reference/threads.md), [the-client-loop](../systems/client/the-client-loop.md), [dialogs](../systems/commands/dialogs.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ClientConfigurationPacketListenerImpl` | [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [items-and-stacks](../systems/items/items-and-stacks.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientDebugSubscriber` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `ClientExplosionTracker` | [the-client-level](../systems/client/the-client-level.md), [particles](../systems/rendering/particles.md) | | `ClientHandshakePacketListenerImpl` | [anatomy](../systems/anatomy/anatomy.md), [options](../systems/client/options.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ClientInformation` | [options](../systems/client/options.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ClientInput` | [glossary](../reference/glossary.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `ClientIntent` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ClientIntentionPacket` | [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ClientItem` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ClientItemInfoLoader` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ClientLanguage` | [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `ClientLevel` | [lectures](../lectures.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [X · The client](../systems/client/README.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [the-client-level](../systems/client/the-client-level.md), [the-client-loop](../systems/client/the-client-loop.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md), [using-an-item](../systems/items/using-an-item.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [particles](../systems/rendering/particles.md), [section-meshing](../systems/rendering/section-meshing.md), [the-frame](../systems/rendering/the-frame.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [biomes](../systems/worldgen/biomes.md) | | `ClientMannequin` | [naming-drift](../reference/naming-drift.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ClientPacketListener` | [biggest](../maps/biggest.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [the-client-level](../systems/client/the-client-level.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [dialogs](../systems/commands/dialogs.md), [permissions](../systems/commands/permissions.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [text-components](../systems/foundations/text-components.md), [items-and-stacks](../systems/items/items-and-stacks.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [particles](../systems/rendering/particles.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [lighting](../systems/world/lighting.md) | | `ClientPackSource` | [resource-system](../systems/foundations/resource-system.md) | | `ClientRecipeBook` | [recipes](../systems/items/recipes.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ClientRecipeContainer` | [recipes](../systems/items/recipes.md) | | `ClientRegistryLayer` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `ClientShutdownWatchdog` | [threads](../reference/threads.md), [the-client-loop](../systems/client/the-client-loop.md), [the-window](../systems/rendering/the-window.md) | | `ClientSuggestionProvider` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md) | | `ClientTelemetryManager` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `ClientWaypointManager` | [hud](../systems/client/hud.md) | | `Climate` | [biomes](../systems/worldgen/biomes.md), [density-functions](../systems/worldgen/density-functions.md) | | `ClipBlockStateContext` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `ClipboardManager` | [the-window](../systems/rendering/the-window.md) | | `ClockNetworkState` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `ClockTimeMarker` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `ClockTimeMarkers` | [server-level-tick](../systems/server/server-level-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `CloneCommands` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `CloudRenderer` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `CloudStatus` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `CocoaDecorator` | [trees](../systems/worldgen/trees.md) | | `Codec` | [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `CodecModifier` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `CodepointMap` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `CollectFields` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `CollectingNeighborUpdater` | [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [server-level-tick](../systems/server/server-level-tick.md) | | `CollectionContentsPredicate` | [advancements](../systems/commands/advancements.md) | | `CollectionCountsPredicate` | [advancements](../systems/commands/advancements.md) | | `CollectionPredicate` | [advancements](../systems/commands/advancements.md) | | `CollectionTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `CollisionContext` | [math-and-primitives](../reference/math-and-primitives.md) | | `CollisionGetter` | [movement-and-collision](../systems/entities/movement-and-collision.md) | | `ColoredRectangleRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `ColorModifier` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `ColorResolver` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `ColorRGBA` | [math-and-primitives](../reference/math-and-primitives.md) | | `ColorTargetState` | [blaze3d](../systems/rendering/blaze3d.md) | | `CombatEntry` | [damage-and-death](../systems/entities/damage-and-death.md), [text-components](../systems/foundations/text-components.md) | | `CombatRules` | [damage-and-death](../systems/entities/damage-and-death.md) | | `CombatTracker` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [text-components](../systems/foundations/text-components.md) | | `CommandBuildContext` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `CommandDispatcher` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `CommandEncoder` | [glossary](../reference/glossary.md), [blaze3d](../systems/rendering/blaze3d.md) | | `CommandEncoderBackend` | [blaze3d](../systems/rendering/blaze3d.md) | | `CommandFunction` | [glossary](../reference/glossary.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [tags](../systems/foundations/tags.md) | | `CommandQueueEntry` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `CommandResultCallback` | [naming-drift](../reference/naming-drift.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `Commands` | [naming-drift](../reference/naming-drift.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [game-tests](../systems/commands/game-tests.md), [permissions](../systems/commands/permissions.md), [the-execution-engine](../systems/commands/the-execution-engine.md), [resource-system](../systems/foundations/resource-system.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `CommandSigningContext` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `CommandSource` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `CommandSourceStack` | [naming-drift](../reference/naming-drift.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [permissions](../systems/commands/permissions.md), [the-execution-engine](../systems/commands/the-execution-engine.md), [starting-a-server](../systems/server/starting-a-server.md) | | `CommandStorage` | [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [starting-a-server](../systems/server/starting-a-server.md) | | `CommandSuggestions` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `CommandSyntaxException` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [the-execution-engine](../systems/commands/the-execution-engine.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `CommandTemplate` | [dialogs](../systems/commands/dialogs.md) | | `CommonButtonData` | [dialogs](../systems/commands/dialogs.md) | | `CommonColors` | [math-and-primitives](../reference/math-and-primitives.md) | | `CommonComponents` | [text-components](../systems/foundations/text-components.md) | | `CommonDialogData` | [dialogs](../systems/commands/dialogs.md) | | `CommonListenerCookie` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [protocol-phases](../systems/networking/protocol-phases.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `CommonPlayerSpawnInfo` | [level-data-and-rules](../reference/level-data-and-rules.md), [player-anatomy](../systems/player/player-anatomy.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ComparatorBlock` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ComparatorBlockEntity` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `ComparatorMode` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md) | | `CompassItem` | [items-and-stacks](../systems/items/items-and-stacks.md) | | `CompiledSectionMesh` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [section-meshing](../systems/rendering/section-meshing.md) | | `Component` | [lectures](../lectures.md), [fanin](../maps/fanin.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [X · The client](../systems/client/README.md), [gui-and-screens](../systems/client/gui-and-screens.md), [text-and-fonts](../systems/client/text-and-fonts.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [text-components](../systems/foundations/text-components.md), [enchantments](../systems/items/enchantments.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [entity-rendering](../systems/rendering/entity-rendering.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ComponentArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [text-components](../systems/foundations/text-components.md) | | `ComponentCollector` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `ComponentContents` | [text-components](../systems/foundations/text-components.md) | | `ComponentPath` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ComponentPredicateParser` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ComponentRenderUtils` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `ComponentSerialization` | [naming-drift](../reference/naming-drift.md), [entity-selectors](../systems/commands/entity-selectors.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ComponentUtils` | [naming-drift](../reference/naming-drift.md), [text-components](../systems/foundations/text-components.md) | | `ComposableEntryContainer` | [loot-tables](../systems/items/loot-tables.md) | | `CompositeEntryBase` | [loot-tables](../systems/items/loot-tables.md) | | `CompositePackResources` | [resource-system](../systems/foundations/resource-system.md) | | `CompoundContainer` | [containers-and-menus](../systems/items/containers-and-menus.md), [loot-tables](../systems/items/loot-tables.md) | | `CompoundTag` | [glossary](../reference/glossary.md), [block-entities](../systems/blocks/block-entities.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `CompressionDecoder` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `CompressionEncoder` | [the-connection](../systems/networking/the-connection.md) | | `ConcentricRingsStructurePlacement` | [structure-placement](../systems/worldgen/structure-placement.md) | | `ConcurrentHolderGetter` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `ConditionalBlockModel` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ConditionalEffect` | [enchantments](../systems/items/enchantments.md) | | `ConditionalItemModel` | [naming-drift](../reference/naming-drift.md) | | `ConditionReference` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `Configuration` | [naming-drift](../reference/naming-drift.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `ConfigurationProtocols` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ConfigurationTask` | [protocol-phases](../systems/networking/protocol-phases.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ConfiguredFeature` | [data-driven-types](../systems/foundations/data-driven-types.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `ConfiguredWorldCarver` | [data-driven-types](../systems/foundations/data-driven-types.md), [terrain](../systems/worldgen/terrain.md) | | `ConfirmationDialog` | [dialogs](../systems/commands/dialogs.md) | | `ConfirmExperimentalFeaturesScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `Connection` | [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md) | | `ConnectionProtocol` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ConnectScreen` | [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ConsecutiveExecutor` | [anatomy](../systems/anatomy/anatomy.md), [resource-system](../systems/foundations/resource-system.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `ConsoleInput` | [starting-a-server](../systems/server/starting-a-server.md) | | `ConstantValue` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `Consumable` | [naming-drift](../reference/naming-drift.md), [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md), [using-an-item](../systems/items/using-an-item.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `ConsumableListener` | [using-an-item](../systems/items/using-an-item.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `Consumables` | [using-an-item](../systems/items/using-an-item.md) | | `ConsumeEffect` | [data-driven-types](../systems/foundations/data-driven-types.md), [using-an-item](../systems/items/using-an-item.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `ConsumeItemTrigger` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `Container` | [naming-drift](../reference/naming-drift.md), [block-entities](../systems/blocks/block-entities.md), [containers-and-menus](../systems/items/containers-and-menus.md), [loot-tables](../systems/items/loot-tables.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ContainerData` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ContainerEntity` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ContainerEventHandler` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ContainerHelper` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ContainerInput` | [naming-drift](../reference/naming-drift.md), [gui-and-screens](../systems/client/gui-and-screens.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `ContainerLevelAccess` | [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchanting](../systems/items/enchanting.md), [recipes](../systems/items/recipes.md) | | `ContainerListener` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ContainerObjectSelectionList` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `Containers` | [block-entities](../systems/blocks/block-entities.md) | | `ContainerSingleItem` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ContainerSynchronizer` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ContainerUser` | [entity-anatomy](../systems/entities/entity-anatomy.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ContextAwarePredicate` | [advancements](../systems/commands/advancements.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ContextChain` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ContextKey` | [naming-drift](../reference/naming-drift.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ContextKeySet` | [naming-drift](../reference/naming-drift.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ContextMap` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ContextualBar` | [hud-elements](../reference/hud-elements.md), [hud](../systems/client/hud.md) | | `ContinuationTask` | [functions-and-macros](../systems/commands/functions-and-macros.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `ContinuousProfiler` | [the-client-loop](../systems/client/the-client-loop.md) | | `Coordinates` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `CopperChestBlock` | [block-entities](../systems/blocks/block-entities.md) | | `CopperGolem` | [entity-anatomy](../systems/entities/entity-anatomy.md), [pathfinding](../systems/entities/pathfinding.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `CopperGolemStatueBlock` | [block-entities](../systems/blocks/block-entities.md) | | `CopperGolemStatueBlockEntity` | [block-entities](../systems/blocks/block-entities.md) | | `CopperGolemStatueSpecialRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `CopyComponentsFunction` | [data-components](../systems/foundations/data-components.md) | | `CountOnEveryLayerPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `CountPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `CrafterBlock` | [recipes](../systems/items/recipes.md) | | `CrafterSlot` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `CraftingContainer` | [recipes](../systems/items/recipes.md) | | `CraftingInput` | [recipes](../systems/items/recipes.md) | | `CraftingMenu` | [containers-and-menus](../systems/items/containers-and-menus.md), [recipes](../systems/items/recipes.md) | | `CraftingRecipe` | [recipes](../systems/items/recipes.md) | | `CrashReport` | [anatomy](../systems/anatomy/anatomy.md), [the-window](../systems/rendering/the-window.md), [starting-a-server](../systems/server/starting-a-server.md) | | `Creaking` | [pathfinding](../systems/entities/pathfinding.md) | | `CreakingHeartDecorator` | [trees](../systems/worldgen/trees.md) | | `CreateBuffetWorldScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `CreateFlatWorldScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `CreateWorldCallback` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `CreateWorldScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `CreativeModeInventoryScreen` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `CreativeModeTabs` | [biggest](../maps/biggest.md), [enchanting](../systems/items/enchanting.md) | | `Creeper` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [post-processing](../systems/rendering/post-processing.md) | | `CriteriaTriggers` | [naming-drift](../reference/naming-drift.md), [block-interaction](../systems/blocks/block-interaction.md), [advancements](../systems/commands/advancements.md), [containers-and-menus](../systems/items/containers-and-menus.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [the-spear](../systems/player/the-spear.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `Criterion` | [advancements](../systems/commands/advancements.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `CriterionTrigger` | [glossary](../reference/glossary.md), [advancements](../systems/commands/advancements.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `CriterionTriggerInstance` | [naming-drift](../reference/naming-drift.md), [advancements](../systems/commands/advancements.md) | | `CrossbowAttack` | [using-an-item](../systems/items/using-an-item.md) | | `CrossbowItem` | [enchantments](../systems/items/enchantments.md), [using-an-item](../systems/items/using-an-item.md) | | `CrossbowPull` | [enchantments](../systems/items/enchantments.md), [using-an-item](../systems/items/using-an-item.md) | | `CrossCollisionBlock` | [movement-and-collision](../systems/entities/movement-and-collision.md) | | `CrossFrameResourcePool` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `CrudeIncrementalIntIdentityHashBiMap` | [synched-entity-data](../systems/entities/synched-entity-data.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `Crypt` | [protocol-phases](../systems/networking/protocol-phases.md) | | `CubeDefinition` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `CubeDeformation` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `CubeListBuilder` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `CubeVoxelShape` | [math-and-primitives](../reference/math-and-primitives.md) | | `CubicSpline` | [density-function-nodes](../reference/density-function-nodes.md), [math-and-primitives](../reference/math-and-primitives.md) | | `CuboidFace` | [naming-drift](../reference/naming-drift.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `CuboidModel` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `CuboidModelElement` | [naming-drift](../reference/naming-drift.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `CuboidRotation` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `Cursor3D` | [math-and-primitives](../reference/math-and-primitives.md), [movement-and-collision](../systems/entities/movement-and-collision.md) | | `CursorType` | [the-window](../systems/rendering/the-window.md) | | `CursorTypes` | [the-window](../systems/rendering/the-window.md) | | `CustomAll` | [dialogs](../systems/commands/dialogs.md) | | `CustomBossEvents` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `CustomCommandExecutor` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `CustomData` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md) | | `CustomFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `CustomHeadLayer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `CustomModifierExecutor` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `CustomPacketPayload` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `CustomRecipe` | [recipes](../systems/items/recipes.md) | | `CustomSpawner` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `CycleButton` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `DamageCommand` | [damage-and-death](../systems/entities/damage-and-death.md) | | `DamageEffects` | [damage-and-death](../systems/entities/damage-and-death.md) | | `DamageEntity` | [enchantments](../systems/items/enchantments.md) | | `DamageScaling` | [damage-and-death](../systems/entities/damage-and-death.md) | | `DamageSource` | [damage-and-death](../systems/entities/damage-and-death.md), [text-components](../systems/foundations/text-components.md), [enchantments](../systems/items/enchantments.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `DamageSources` | [damage-and-death](../systems/entities/damage-and-death.md) | | `DamageType` | [damage-and-death](../systems/entities/damage-and-death.md), [text-components](../systems/foundations/text-components.md) | | `DamageTypes` | [damage-and-death](../systems/entities/damage-and-death.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-spear](../systems/player/the-spear.md) | | `DamageTypeTags` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [tags](../systems/foundations/tags.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `DarknessFogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `DarkOakFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `DarkOakTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `DataAccessor` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `DataCommands` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `DataComponentExactPredicate` | [data-components](../systems/foundations/data-components.md) | | `DataComponentGetter` | [entity-anatomy](../systems/entities/entity-anatomy.md), [data-components](../systems/foundations/data-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `DataComponentHolder` | [data-components](../systems/foundations/data-components.md) | | `DataComponentInitializers` | [data-components](../systems/foundations/data-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `DataComponentLookup` | [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `DataComponentMap` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `DataComponentMatchers` | [advancements](../systems/commands/advancements.md), [data-components](../systems/foundations/data-components.md) | | `DataComponentPatch` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [items-and-stacks](../systems/items/items-and-stacks.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `DataComponentPredicate` | [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `DataComponentPredicates` | [data-components](../systems/foundations/data-components.md) | | `DataComponents` | [fanin](../maps/fanin.md), [naming-drift](../reference/naming-drift.md), [V · Blocks](../systems/blocks/README.md), [block-breaking](../systems/blocks/block-breaking.md), [attributes](../systems/entities/attributes.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [data-components](../systems/foundations/data-components.md), [text-components](../systems/foundations/text-components.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [using-an-item](../systems/items/using-an-item.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `DataComponentType` | [Reference](../reference/README.md), [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [II · Foundations](../systems/foundations/README.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `DataFixer` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `DataFixers` | [biggest](../maps/biggest.md), [anatomy](../systems/anatomy/anatomy.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [starting-a-server](../systems/server/starting-a-server.md) | | `DataFixTypes` | [level-data-and-rules](../reference/level-data-and-rules.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [advancements](../systems/commands/advancements.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [players-and-sessions](../systems/server/players-and-sessions.md), [points-of-interest](../systems/world/points-of-interest.md) | | `DataLayer` | [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md) | | `DataLayerStorageMap` | [lighting](../systems/world/lighting.md) | | `DataPackConfig` | [resource-system](../systems/foundations/resource-system.md) | | `DatapackStructureReport` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `DataResult` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `DataSlot` | [containers-and-menus](../systems/items/containers-and-menus.md), [enchanting](../systems/items/enchanting.md) | | `DataSource` | [text-components](../systems/foundations/text-components.md) | | `DaylightDetectorBlockEntity` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md) | | `DeathMessageType` | [damage-and-death](../systems/entities/damage-and-death.md), [text-components](../systems/foundations/text-components.md) | | `DeathProtection` | [damage-and-death](../systems/entities/damage-and-death.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `DeathScreen` | [damage-and-death](../systems/entities/damage-and-death.md), [text-components](../systems/foundations/text-components.md) | | `DebugBeeInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugBrainDump` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugBreezeInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugCommand` | [functions-and-macros](../systems/commands/functions-and-macros.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `DebugConfigCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [dialogs](../systems/commands/dialogs.md), [protocol-phases](../systems/networking/protocol-phases.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `DebugEntityBlockIntersection` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugEntryLight` | [lighting](../systems/world/lighting.md) | | `DebugEntryParticleRenderStats` | [particles](../systems/rendering/particles.md) | | `DebugEntryPostEffect` | [post-processing](../systems/rendering/post-processing.md) | | `DebugGameEventInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugGameEventListenerInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugGoalInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugHiveInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugLevelSource` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [biomes](../systems/worldgen/biomes.md), [terrain](../systems/worldgen/terrain.md) | | `DebugMemoryUntracker` | [the-window](../systems/rendering/the-window.md) | | `DebugMobSpawningCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `DebugOptionsScreen` | [hud-elements](../reference/hud-elements.md) | | `DebugPathCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `DebugPathInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugPoiInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugRenderer` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugScreenDisplayer` | [hud](../systems/client/hud.md) | | `DebugScreenEntries` | [hud](../systems/client/hud.md) | | `DebugScreenEntry` | [hud](../systems/client/hud.md) | | `DebugScreenEntryList` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [hud](../systems/client/hud.md) | | `DebugScreenEntryStatus` | [hud](../systems/client/hud.md) | | `DebugScreenOverlay` | [threads](../reference/threads.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugScreenProfile` | [hud](../systems/client/hud.md) | | `DebugStructureInfo` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugSubscription` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugSubscriptions` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [pathfinding](../systems/entities/pathfinding.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [points-of-interest](../systems/world/points-of-interest.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `DebugValueAccess` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `DebugValueSource` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [entity-anatomy](../systems/entities/entity-anatomy.md) | | `DecoratedPotPatterns` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `DecoratedPotRecipe` | [recipes](../systems/items/recipes.md) | | `DecoratedPotSpecialRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `DedicatedPlayerList` | [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md) | | `DedicatedServer` | [level-data-and-rules](../reference/level-data-and-rules.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [block-interaction](../systems/blocks/block-interaction.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-storage](../systems/world/chunk-storage.md) | | `DedicatedServerProperties` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [block-interaction](../systems/blocks/block-interaction.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-storage](../systems/world/chunk-storage.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `DedicatedServerSettings` | [starting-a-server](../systems/server/starting-a-server.md) | | `DefaultAttributes` | [attributes](../systems/entities/attributes.md) | | `DefaultBlockInteractionTrigger` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `DefaultedMappedRegistry` | [entity-anatomy](../systems/entities/entity-anatomy.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `DefaultedRegistry` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `DefaultPlayerSkin` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `DefaultRedstoneWireEvaluator` | [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `DefaultVertexFormat` | [blaze3d](../systems/rendering/blaze3d.md) | | `DelegatingOps` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `DeltaTracker` | [introduction](../introduction.md), [naming-drift](../reference/naming-drift.md), [anatomy](../systems/anatomy/anatomy.md), [the-client-loop](../systems/client/the-client-loop.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [the-frame](../systems/rendering/the-frame.md) | | `Density` | [density-functions](../systems/worldgen/density-functions.md) | | `DensityFunction` | [density-function-nodes](../reference/density-function-nodes.md), [data-driven-types](../systems/foundations/data-driven-types.md), [XII · World generation](../systems/worldgen/README.md), [density-functions](../systems/worldgen/density-functions.md) | | `DensityFunctions` | [biggest](../maps/biggest.md), [density-function-nodes](../reference/density-function-nodes.md), [naming-drift](../reference/naming-drift.md), [data-driven-types](../systems/foundations/data-driven-types.md), [blending](../systems/worldgen/blending.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `DependencySorter` | [tags](../systems/foundations/tags.md) | | `DepthStencilState` | [blaze3d](../systems/rendering/blaze3d.md) | | `DerivedLevelData` | [level-data-and-rules](../reference/level-data-and-rules.md), [starting-a-server](../systems/server/starting-a-server.md) | | `DesertPyramidPiece` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `DestructionQueue` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `DeviceFeatures` | [blaze3d](../systems/rendering/blaze3d.md) | | `DeviceInfo` | [blaze3d](../systems/rendering/blaze3d.md) | | `DeviceLimits` | [blaze3d](../systems/rendering/blaze3d.md) | | `DeviceList` | [sound-engine](../systems/client/sound-engine.md) | | `DeviceType` | [blaze3d](../systems/rendering/blaze3d.md) | | `Dialog` | [dialogs](../systems/commands/dialogs.md), [data-driven-types](../systems/foundations/data-driven-types.md), [text-components](../systems/foundations/text-components.md) | | `DialogAction` | [dialogs](../systems/commands/dialogs.md) | | `DialogBody` | [dialogs](../systems/commands/dialogs.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `DialogBodyHandlers` | [dialogs](../systems/commands/dialogs.md) | | `DialogConnectionAccess` | [dialogs](../systems/commands/dialogs.md) | | `DialogControlSet` | [dialogs](../systems/commands/dialogs.md) | | `DialogListDialog` | [dialogs](../systems/commands/dialogs.md) | | `Dialogs` | [dialogs](../systems/commands/dialogs.md) | | `DialogScreen` | [dialogs](../systems/commands/dialogs.md) | | `DialogScreens` | [gui-and-screens](../systems/client/gui-and-screens.md), [dialogs](../systems/commands/dialogs.md) | | `DialogTags` | [dialogs](../systems/commands/dialogs.md) | | `DialogTypes` | [dialogs](../systems/commands/dialogs.md) | | `Dictionary` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `Difficulty` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `DifficultyInstance` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `DimensionDefaults` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `DimensionType` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md), [chunk-storage](../systems/world/chunk-storage.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [lighting](../systems/world/lighting.md) | | `DiodeBlock` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `Direction` | [fanin](../maps/fanin.md), [math-and-primitives](../reference/math-and-primitives.md), [submit-phases](../reference/submit-phases.md), [V · Blocks](../systems/blocks/README.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `Direction8` | [math-and-primitives](../reference/math-and-primitives.md), [blending](../systems/worldgen/blending.md) | | `DirectionalBlock` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md) | | `DirectoryLister` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `DirectoryLock` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md) | | `DirectoryValidator` | [resource-system](../systems/foundations/resource-system.md) | | `DiscardedPayload` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `DiscardedQueryPayload` | [protocol-phases](../systems/networking/protocol-phases.md) | | `DisconnectionDetails` | [the-connection](../systems/networking/the-connection.md) | | `DiscreteVoxelShape` | [math-and-primitives](../reference/math-and-primitives.md) | | `DispenserBlock` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `Display` | [non-living-damage](../reference/non-living-damage.md), [the-client-level](../systems/client/the-client-level.md), [damage-and-death](../systems/entities/damage-and-death.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `DisplayData` | [the-window](../systems/rendering/the-window.md) | | `DisplayInfo` | [advancements](../systems/commands/advancements.md) | | `DisplaySlot` | [hud-elements](../reference/hud-elements.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `DistanceManager` | [block-entities](../systems/blocks/block-entities.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `DistanceTrigger` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `DoorBlock` | [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `DownloadCacheCleaner` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [resource-system](../systems/foundations/resource-system.md) | | `DownloadedPackSource` | [resource-system](../systems/foundations/resource-system.md) | | `DownloadQueue` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [resource-system](../systems/foundations/resource-system.md) | | `DrawableGizmoPrimitives` | [submit-phases](../reference/submit-phases.md) | | `DriedGhastBlock` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `DropperBlock` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `DryFoliageColor` | [biomes](../systems/worldgen/biomes.md) | | `DryFoliageColorReloadListener` | [resource-system](../systems/foundations/resource-system.md) | | `DSL` | [fanin](../maps/fanin.md) | | `DyeColor` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `DyeRecipe` | [recipes](../systems/items/recipes.md) | | `DynamicGameEventListener` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `DynamicGraphMinFixedPoint` | [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `DynamicLoot` | [loot-tables](../systems/items/loot-tables.md) | | `DynamicOps` | [glossary](../reference/glossary.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `DynamicTexture` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `DynamicUniforms` | [blaze3d](../systems/rendering/blaze3d.md) | | `DynamicUniformStorage` | [blaze3d](../systems/rendering/blaze3d.md) | | `EasingType` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `EditBox` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `EditWorldScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `EffectGlyph` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `EmoteCommands` | [entity-selectors](../systems/commands/entity-selectors.md) | | `EmptyLevelChunk` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `EmptyLootItem` | [loot-tables](../systems/items/loot-tables.md) | | `EmptyPoolElement` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `Enchantable` | [data-components](../systems/foundations/data-components.md), [enchanting](../systems/items/enchanting.md) | | `EnchantCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [enchanting](../systems/items/enchanting.md) | | `EnchantedCountIncreaseFunction` | [naming-drift](../reference/naming-drift.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [loot-tables](../systems/items/loot-tables.md) | | `EnchantedItemInUse` | [enchantments](../systems/items/enchantments.md) | | `EnchantingTableBlock` | [enchanting](../systems/items/enchanting.md) | | `Enchantment` | [naming-drift](../reference/naming-drift.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md) | | `EnchantmentActiveCheck` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `EnchantmentAttributeEffect` | [attributes](../systems/entities/attributes.md), [enchantments](../systems/items/enchantments.md) | | `EnchantmentEffectComponents` | [naming-drift](../reference/naming-drift.md), [attributes](../systems/entities/attributes.md), [damage-and-death](../systems/entities/damage-and-death.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `EnchantmentEntityEffect` | [data-driven-types](../systems/foundations/data-driven-types.md), [enchantments](../systems/items/enchantments.md) | | `EnchantmentHelper` | [Reference](../reference/README.md), [attributes](../systems/entities/attributes.md), [damage-and-death](../systems/entities/damage-and-death.md), [data-components](../systems/foundations/data-components.md), [VII · Items and inventories](../systems/items/README.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [using-an-item](../systems/items/using-an-item.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `EnchantmentInstance` | [data-components](../systems/foundations/data-components.md), [enchantments](../systems/items/enchantments.md) | | `EnchantmentLevelProvider` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `EnchantmentLocationBasedEffect` | [data-driven-types](../systems/foundations/data-driven-types.md), [enchantments](../systems/items/enchantments.md) | | `EnchantmentMenu` | [data-components](../systems/foundations/data-components.md), [enchanting](../systems/items/enchanting.md) | | `EnchantmentNames` | [enchanting](../systems/items/enchanting.md) | | `EnchantmentProvider` | [data-driven-types](../systems/foundations/data-driven-types.md), [enchanting](../systems/items/enchanting.md) | | `Enchantments` | [block-breaking](../systems/blocks/block-breaking.md), [enchantments](../systems/items/enchantments.md) | | `EnchantmentsByCost` | [enchanting](../systems/items/enchanting.md) | | `EnchantmentsByCostWithDifficulty` | [enchanting](../systems/items/enchanting.md) | | `EnchantmentScreen` | [enchanting](../systems/items/enchanting.md) | | `EnchantmentTags` | [tags](../systems/foundations/tags.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md) | | `EnchantmentTarget` | [enchantments](../systems/items/enchantments.md) | | `EnchantmentValueEffect` | [data-driven-types](../systems/foundations/data-driven-types.md), [enchantments](../systems/items/enchantments.md) | | `EnchantRandomlyFunction` | [enchanting](../systems/items/enchanting.md), [loot-tables](../systems/items/loot-tables.md) | | `EnchantWithLevelsFunction` | [enchanting](../systems/items/enchanting.md), [loot-tables](../systems/items/loot-tables.md) | | `EncoderCache` | [data-components](../systems/foundations/data-components.md) | | `EndCityPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `EndCrystal` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `EnderDragon` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `EnderDragonFight` | [level-data-and-rules](../reference/level-data-and-rules.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [lighting](../systems/world/lighting.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `EnderDragonPart` | [non-living-damage](../reference/non-living-damage.md), [entity-selectors](../systems/commands/entity-selectors.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [chunk-storage](../systems/world/chunk-storage.md) | | `EnderMan` | [post-processing](../systems/rendering/post-processing.md) | | `EndFlashState` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `EndPodiumFeature` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `EndTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `Enemy` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `Entity` | [biggest](../maps/biggest.md), [fanin](../maps/fanin.md), [hierarchy](../maps/hierarchy.md), [glossary](../reference/glossary.md), [hud-elements](../reference/hud-elements.md), [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [non-living-damage](../reference/non-living-damage.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [the-client-level](../systems/client/the-client-level.md), [entity-selectors](../systems/commands/entity-selectors.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [pathfinding](../systems/entities/pathfinding.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [tags](../systems/foundations/tags.md), [text-components](../systems/foundations/text-components.md), [enchantments](../systems/items/enchantments.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-storage](../systems/world/chunk-storage.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `EntityAccess` | [entity-anatomy](../systems/entities/entity-anatomy.md), [chunk-storage](../systems/world/chunk-storage.md) | | `EntityAnchorArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `EntityArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md) | | `EntityAttachment` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `EntityAttachments` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `EntityBlock` | [block-entities](../systems/blocks/block-entities.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `EntityBoundSoundInstance` | [sound-engine](../systems/client/sound-engine.md) | | `EntityCollisionContext` | [math-and-primitives](../reference/math-and-primitives.md) | | `EntityDataAccessor` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `EntityDataSerializer` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `EntityDataSerializers` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [text-components](../systems/foundations/text-components.md) | | `EntityDataSource` | [text-components](../systems/foundations/text-components.md) | | `EntityDimensions` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `EntityEquipment` | [items-and-stacks](../systems/items/items-and-stacks.md), [player-anatomy](../systems/player/player-anatomy.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `EntityEvent` | [synched-entity-data](../systems/entities/synched-entity-data.md), [using-an-item](../systems/items/using-an-item.md) | | `EntityFluidInteraction` | [naming-drift](../reference/naming-drift.md), [movement-and-collision](../systems/entities/movement-and-collision.md) | | `EntityGetter` | [movement-and-collision](../systems/entities/movement-and-collision.md) | | `EntityHitResult` | [math-and-primitives](../reference/math-and-primitives.md) | | `EntityInLevelCallback` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `EntityLookup` | [entity-selectors](../systems/commands/entity-selectors.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `EntityModel` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `EntityModelSet` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `EntityPositionSource` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `EntityPredicate` | [advancements](../systems/commands/advancements.md), [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `EntityReference` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `EntityRenderDispatcher` | [synched-entity-data](../systems/entities/synched-entity-data.md), [resource-system](../systems/foundations/resource-system.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [particles](../systems/rendering/particles.md) | | `EntityRenderer` | [naming-drift](../reference/naming-drift.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `EntityRenderState` | [submit-phases](../reference/submit-phases.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [particles](../systems/rendering/particles.md) | | `EntitySection` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `EntitySectionStorage` | [entity-selectors](../systems/commands/entity-selectors.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `EntitySelector` | [glossary](../reference/glossary.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md), [permissions](../systems/commands/permissions.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [text-components](../systems/foundations/text-components.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `EntitySelectorOptions` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `EntitySelectorParser` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md), [permissions](../systems/commands/permissions.md), [text-components](../systems/foundations/text-components.md) | | `EntitySpawnReason` | [naming-drift](../reference/naming-drift.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `EntitySpawnRequest` | [naming-drift](../reference/naming-drift.md), [entity-anatomy](../systems/entities/entity-anatomy.md) | | `EntityStorage` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [chunk-storage](../systems/world/chunk-storage.md) | | `EntitySubPredicate` | [advancements](../systems/commands/advancements.md) | | `EntityTickList` | [the-client-level](../systems/client/the-client-level.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `EntityType` | [fanin](../maps/fanin.md), [hierarchy](../maps/hierarchy.md), [VI · Entities](../systems/entities/README.md), [attributes](../systems/entities/attributes.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [text-components](../systems/foundations/text-components.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [entity-rendering](../systems/rendering/entity-rendering.md), [chunk-storage](../systems/world/chunk-storage.md) | | `EntityTypeIds` | [naming-drift](../reference/naming-drift.md), [entity-anatomy](../systems/entities/entity-anatomy.md) | | `EntityTypes` | [naming-drift](../reference/naming-drift.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `EntityTypeTags` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [tags](../systems/foundations/tags.md) | | `EntityTypeTest` | [entity-selectors](../systems/commands/entity-selectors.md) | | `EntryAction` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `EntryGroup` | [loot-tables](../systems/items/loot-tables.md) | | `EnumProperty` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `EnvironmentAttribute` | [naming-drift](../reference/naming-drift.md), [the-client-level](../systems/client/the-client-level.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md) | | `EnvironmentAttributeCheck` | [data-driven-types](../systems/foundations/data-driven-types.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `EnvironmentAttributeLayer` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `EnvironmentAttributeMap` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [biomes](../systems/worldgen/biomes.md) | | `EnvironmentAttributeProbe` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [the-frame](../systems/rendering/the-frame.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [biomes](../systems/worldgen/biomes.md) | | `EnvironmentAttributeReader` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md) | | `EnvironmentAttributes` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [particles](../systems/rendering/particles.md), [server-level-tick](../systems/server/server-level-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md), [points-of-interest](../systems/world/points-of-interest.md) | | `EnvironmentAttributeSystem` | [naming-drift](../reference/naming-drift.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [server-level-tick](../systems/server/server-level-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [biomes](../systems/worldgen/biomes.md) | | `EnvironmentAttributeValue` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `EnvironmentScanPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `EqualSpacingLayout` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `EquipmentAssetManager` | [resource-system](../systems/foundations/resource-system.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `EquipmentClientInfo` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `EquipmentLayerRenderer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `EquipmentSlot` | [enchantments](../systems/items/enchantments.md), [player-anatomy](../systems/player/player-anatomy.md) | | `EquipmentSlotGroup` | [enchantments](../systems/items/enchantments.md) | | `EquipmentUser` | [entity-anatomy](../systems/entities/entity-anatomy.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `Equippable` | [hud-elements](../reference/hud-elements.md), [damage-and-death](../systems/entities/damage-and-death.md), [data-components](../systems/foundations/data-components.md) | | `ErrorCollector` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `EuclideanGameEventListenerRegistry` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `Eula` | [starting-a-server](../systems/server/starting-a-server.md) | | `EventLoopGroupHolder` | [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [the-connection](../systems/networking/the-connection.md) | | `EvokerFangs` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `ExecuteCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [the-execution-engine](../systems/commands/the-execution-engine.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ExecutionCommandSource` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `ExecutionContext` | [naming-drift](../reference/naming-drift.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `ExperienceBar` | [hud-elements](../reference/hud-elements.md), [hud](../systems/client/hud.md) | | `ExperienceCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ExperienceOrb` | [non-living-damage](../reference/non-living-damage.md), [the-client-level](../systems/client/the-client-level.md), [damage-and-death](../systems/entities/damage-and-death.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `ExperimentalRedstoneWireEvaluator` | [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `ExperimentsScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `ExplodeEffect` | [damage-and-death](../systems/entities/damage-and-death.md) | | `ExplorationMapFunction` | [structure-placement](../systems/worldgen/structure-placement.md) | | `ExplosionParticleInfo` | [particles](../systems/rendering/particles.md) | | `ExtraCodecs` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [text-components](../systems/foundations/text-components.md) | | `EyeOfEnder` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `EyesLayer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `FaceBakery` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `FallbackResourceManager` | [resource-system](../systems/foundations/resource-system.md) | | `FallenTreeFeature` | [trees](../systems/worldgen/trees.md) | | `FallingBlockEntity` | [non-living-damage](../reference/non-living-damage.md), [the-client-level](../systems/client/the-client-level.md), [damage-and-death](../systems/entities/damage-and-death.md), [server-level-tick](../systems/server/server-level-tick.md) | | `FallingBlockRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `FallLocation` | [damage-and-death](../systems/entities/damage-and-death.md) | | `FallthroughTask` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `FancyFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `FancyTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `Feature` | [naming-drift](../reference/naming-drift.md), [data-driven-types](../systems/foundations/data-driven-types.md), [creating-a-world](../systems/worldgen/creating-a-world.md), [features-and-placement](../systems/worldgen/features-and-placement.md), [trees](../systems/worldgen/trees.md) | | `FeatureConfiguration` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `FeatureElement` | [hierarchy](../maps/hierarchy.md), [block-interaction](../systems/blocks/block-interaction.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `FeatureFlag` | [glossary](../reference/glossary.md) | | `FeatureFlags` | [signal-and-dust](../systems/blocks/signal-and-dust.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `FeatureFlagSet` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md) | | `FeatureFlagsMetadataSection` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md) | | `FeatureFlagUniverse` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `FeaturePoolElement` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `FeatureRenderDispatcher` | [naming-drift](../reference/naming-drift.md), [submit-phases](../reference/submit-phases.md), [blaze3d](../systems/rendering/blaze3d.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [post-processing](../systems/rendering/post-processing.md), [the-frame](../systems/rendering/the-frame.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `FeatureSize` | [data-driven-types](../systems/foundations/data-driven-types.md), [trees](../systems/worldgen/trees.md) | | `FeatureSizeType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `FeatureSorter` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `FeatureTags` | [tags](../systems/foundations/tags.md) | | `FieldSelector` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `FileFixerProgressScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `FileFixerUpper` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `FilePackResources` | [resource-system](../systems/foundations/resource-system.md) | | `FileToIdConverter` | [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md) | | `FillBiomeCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `FilterMask` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `FiniteAudioStream` | [sound-engine](../systems/client/sound-engine.md) | | `FireBlock` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `FishingHook` | [the-client-level](../systems/client/the-client-level.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `FixedFormat` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `FixedPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `FlameFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `FlatLevelGeneratorPreset` | [glossary](../reference/glossary.md) | | `FlatLevelGeneratorPresets` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `FlatLevelGeneratorSettings` | [glossary](../reference/glossary.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `FlatLevelSource` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [biomes](../systems/worldgen/biomes.md), [creating-a-world](../systems/worldgen/creating-a-world.md), [terrain](../systems/worldgen/terrain.md) | | `FloatModifier` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `FloatProvider` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `FloatProviders` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `FloatSampleSource` | [sound-engine](../systems/client/sound-engine.md) | | `FloatWithAlpha` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `FlowingFluid` | [glossary](../reference/glossary.md), [fluids](../systems/world/fluids.md) | | `Fluid` | [server-level-tick](../systems/server/server-level-tick.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `FluidModel` | [naming-drift](../reference/naming-drift.md), [section-meshing](../systems/rendering/section-meshing.md) | | `FluidRenderer` | [naming-drift](../reference/naming-drift.md), [section-meshing](../systems/rendering/section-meshing.md) | | `Fluids` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `FluidState` | [glossary](../reference/glossary.md), [V · Blocks](../systems/blocks/README.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [tags](../systems/foundations/tags.md), [server-level-tick](../systems/server/server-level-tick.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `FluidStateModelSet` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `FluidTags` | [block-breaking](../systems/blocks/block-breaking.md), [tags](../systems/foundations/tags.md), [fluids](../systems/world/fluids.md) | | `FlyingPathNavigation` | [pathfinding](../systems/entities/pathfinding.md) | | `FlyNodeEvaluator` | [pathfinding](../systems/entities/pathfinding.md) | | `FocusNavigationEvent` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `FogData` | [naming-drift](../reference/naming-drift.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `FogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `FogRenderer` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `FogType` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `FolderRepositorySource` | [resource-system](../systems/foundations/resource-system.md) | | `FoliageColor` | [biomes](../systems/worldgen/biomes.md) | | `FoliageColorReloadListener` | [resource-system](../systems/foundations/resource-system.md) | | `FoliagePlacer` | [data-driven-types](../systems/foundations/data-driven-types.md), [trees](../systems/worldgen/trees.md) | | `FoliagePlacerType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `Font` | [naming-drift](../reference/naming-drift.md), [submit-phases](../reference/submit-phases.md), [text-and-fonts](../systems/client/text-and-fonts.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `FontDescription` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `FontManager` | [text-and-fonts](../systems/client/text-and-fonts.md), [resource-system](../systems/foundations/resource-system.md) | | `FontSet` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `FontTexture` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `FoodConstants` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `FoodData` | [naming-drift](../reference/naming-drift.md), [VIII · The player](../systems/player/README.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `FoodProperties` | [naming-drift](../reference/naming-drift.md), [using-an-item](../systems/items/using-an-item.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `Foods` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `ForbiddenSymlinkInfo` | [resource-system](../systems/foundations/resource-system.md) | | `ForkingTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `FormattedBidiReorder` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `FormattedCharSequence` | [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `FormattedCharSink` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `FormattedText` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `Fox` | [biggest](../maps/biggest.md), [hierarchy](../maps/hierarchy.md), [pathfinding](../systems/entities/pathfinding.md) | | `FpsDebugChart` | [hud](../systems/client/hud.md) | | `Frame` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `FrameGraphBuilder` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `FrameLayout` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `FramePass` | [post-processing](../systems/rendering/post-processing.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `FramerateLimiter` | [the-client-loop](../systems/client/the-client-loop.md) | | `FramerateLimitTracker` | [the-client-loop](../systems/client/the-client-loop.md), [the-frame](../systems/rendering/the-frame.md), [the-window](../systems/rendering/the-window.md) | | `FriendlyByteBuf` | [biggest](../maps/biggest.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `Frog` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [pathfinding](../systems/entities/pathfinding.md) | | `FrogAi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `FrontAndTop` | [math-and-primitives](../reference/math-and-primitives.md) | | `Frustum` | [particles](../systems/rendering/particles.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `FuelValues` | [block-entities](../systems/blocks/block-entities.md) | | `FullChunkStatus` | [block-entities](../systems/blocks/block-entities.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `FunctionArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [functions-and-macros](../systems/commands/functions-and-macros.md) | | `FunctionCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [the-execution-engine](../systems/commands/the-execution-engine.md) | | `FunctionGameTestInstance` | [game-tests](../systems/commands/game-tests.md) | | `FunctionInstantiationException` | [functions-and-macros](../systems/commands/functions-and-macros.md) | | `FurnaceMenu` | [block-entities](../systems/blocks/block-entities.md) | | `FurnaceResultSlot` | [block-entities](../systems/blocks/block-entities.md) | | `FutureChain` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `GameConfig` | [anatomy](../systems/anatomy/anatomy.md), [the-client-loop](../systems/client/the-client-loop.md) | | `GameEvent` | [non-living-damage](../reference/non-living-damage.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [authority](../systems/entities/authority.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [using-an-item](../systems/items/using-an-item.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `GameEventDispatcher` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `GameEventListener` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `GameEventListenerRegistry` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `GameEventTags` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `GameMasterBlock` | [block-breaking](../systems/blocks/block-breaking.md) | | `GameModeCommand` | [permissions](../systems/commands/permissions.md) | | `GameModeSwitcherScreen` | [permissions](../systems/commands/permissions.md) | | `GamePacketTypes` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `GameProfile` | [players-and-sessions](../systems/server/players-and-sessions.md) | | `GameProfileArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `GameProtocols` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `GameRenderer` | [hud-elements](../reference/hud-elements.md), [math-and-primitives](../reference/math-and-primitives.md), [hud](../systems/client/hud.md), [sound-engine](../systems/client/sound-engine.md), [the-client-loop](../systems/client/the-client-loop.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [post-processing](../systems/rendering/post-processing.md), [the-frame](../systems/rendering/the-frame.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `GameRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md), [post-processing](../systems/rendering/post-processing.md), [the-frame](../systems/rendering/the-frame.md) | | `GameRule` | [hierarchy](../maps/hierarchy.md), [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `GameRuleCategory` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md) | | `GameRuleCommand` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `GameRuleMap` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `GameRuleRegistryFix` | [naming-drift](../reference/naming-drift.md), [starting-a-server](../systems/server/starting-a-server.md) | | `GameRules` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [non-living-damage](../reference/non-living-damage.md), [block-breaking](../systems/blocks/block-breaking.md), [advancements](../systems/commands/advancements.md), [the-execution-engine](../systems/commands/the-execution-engine.md), [damage-and-death](../systems/entities/damage-and-death.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [text-components](../systems/foundations/text-components.md), [recipes](../systems/items/recipes.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [input-to-movement](../systems/player/input-to-movement.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `GameRuleType` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md) | | `GameRuleTypeVisitor` | [naming-drift](../reference/naming-drift.md) | | `GameTestBatch` | [game-tests](../systems/commands/game-tests.md) | | `GameTestBatchFactory` | [game-tests](../systems/commands/game-tests.md) | | `GameTestBlockHighlightRenderer` | [game-tests](../systems/commands/game-tests.md) | | `GameTestEnvironments` | [game-tests](../systems/commands/game-tests.md) | | `GameTestHelper` | [game-tests](../systems/commands/game-tests.md) | | `GameTestInfo` | [game-tests](../systems/commands/game-tests.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `GameTestInstance` | [naming-drift](../reference/naming-drift.md), [game-tests](../systems/commands/game-tests.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `GameTestInstances` | [game-tests](../systems/commands/game-tests.md) | | `GameTestMainUtil` | [game-tests](../systems/commands/game-tests.md) | | `GameTestRunner` | [game-tests](../systems/commands/game-tests.md) | | `GameTestSequence` | [game-tests](../systems/commands/game-tests.md) | | `GameTestServer` | [anatomy](../systems/anatomy/anatomy.md), [game-tests](../systems/commands/game-tests.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [chunk-storage](../systems/world/chunk-storage.md) | | `GameTestTicker` | [server-tick](../systems/server/server-tick.md) | | `GameType` | [level-data-and-rules](../reference/level-data-and-rules.md), [player-anatomy](../systems/player/player-anatomy.md) | | `GateBehavior` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `GaussianSampler` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [biomes](../systems/worldgen/biomes.md) | | `GeneratingChunkMap` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `GenerationChunkHolder` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `GenerationStep` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `GenericMessageScreen` | [how-a-server-dies](../systems/server/how-a-server-dies.md) | | `GenericThread` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md) | | `Ghast` | [entity-anatomy](../systems/entities/entity-anatomy.md), [pathfinding](../systems/entities/pathfinding.md) | | `GiantTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `GiveCommand` | [naming-drift](../reference/naming-drift.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `GizmoCollector` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `GizmoFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `Gizmos` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `GlBackend` | [anatomy](../systems/anatomy/anatomy.md) | | `GlBuffer` | [blaze3d](../systems/rendering/blaze3d.md) | | `GlCommandEncoder` | [blaze3d](../systems/rendering/blaze3d.md) | | `GlDevice` | [blaze3d](../systems/rendering/blaze3d.md) | | `GlHeuristics` | [blaze3d](../systems/rendering/blaze3d.md) | | `GlobalPalette` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `GlobalPos` | [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [points-of-interest](../systems/world/points-of-interest.md) | | `GlobalSettingsUniform` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `GlobalTestReporter` | [game-tests](../systems/commands/game-tests.md) | | `GlRenderPass` | [blaze3d](../systems/rendering/blaze3d.md) | | `GlslCompiler` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [blaze3d](../systems/rendering/blaze3d.md) | | `GlslPreprocessor` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `GlStateManager` | [blaze3d](../systems/rendering/blaze3d.md) | | `GLX` | [the-window](../systems/rendering/the-window.md) | | `GlyphBitmap` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `GlyphProvider` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `GlyphProviderDefinition` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `GlyphProviderType` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `GlyphRenderState` | [text-and-fonts](../systems/client/text-and-fonts.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GlyphRenderTypes` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `GlyphSource` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `GlyphStitcher` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `Goal` | [hierarchy](../maps/hierarchy.md), [glossary](../reference/glossary.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `GoalSelector` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [entity-anatomy](../systems/entities/entity-anatomy.md) | | `Goat` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `GolemSensor` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `GoToPotentialJobSite` | [points-of-interest](../systems/world/points-of-interest.md) | | `GpuBackend` | [anatomy](../systems/anatomy/anatomy.md), [blaze3d](../systems/rendering/blaze3d.md), [the-window](../systems/rendering/the-window.md) | | `GpuBuffer` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `GpuBufferSlice` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `GpuDevice` | [lectures](../lectures.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [anatomy](../systems/anatomy/anatomy.md), [XI · Rendering](../systems/rendering/README.md), [blaze3d](../systems/rendering/blaze3d.md), [the-window](../systems/rendering/the-window.md) | | `GpuDeviceBackend` | [glossary](../reference/glossary.md), [blaze3d](../systems/rendering/blaze3d.md) | | `GpuFence` | [blaze3d](../systems/rendering/blaze3d.md) | | `GpuFormat` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `GpuOutOfMemoryException` | [blaze3d](../systems/rendering/blaze3d.md) | | `GpuSampler` | [blaze3d](../systems/rendering/blaze3d.md) | | `GpuSurface` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md), [the-frame](../systems/rendering/the-frame.md), [the-window](../systems/rendering/the-window.md) | | `GpuSurfaceBackend` | [blaze3d](../systems/rendering/blaze3d.md) | | `GpuTexture` | [blaze3d](../systems/rendering/blaze3d.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `GpuTextureView` | [blaze3d](../systems/rendering/blaze3d.md) | | `GpuWarnlistManager` | [resource-system](../systems/foundations/resource-system.md) | | `GraphicsPreset` | [post-processing](../systems/rendering/post-processing.md) | | `GraphicsResourceAllocator` | [blaze3d](../systems/rendering/blaze3d.md) | | `GrassBlock` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [biomes](../systems/worldgen/biomes.md) | | `GrassColor` | [biomes](../systems/worldgen/biomes.md) | | `GrassColorReloadListener` | [resource-system](../systems/foundations/resource-system.md) | | `GravityProcessor` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `GridLayout` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `GrindstoneMenu` | [enchanting](../systems/items/enchanting.md) | | `GroundPathNavigation` | [pathfinding](../systems/entities/pathfinding.md) | | `GroupSlotSource` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `Gui` | [biggest](../maps/biggest.md), [glossary](../reference/glossary.md), [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [gui-and-screens](../systems/client/gui-and-screens.md), [hud](../systems/client/hud.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [the-client-loop](../systems/client/the-client-loop.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md), [resource-system](../systems/foundations/resource-system.md), [how-a-server-dies](../systems/server/how-a-server-dies.md) | | `GuiBannerResultRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiBookModelRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiElementRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiEntityRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiEventListener` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `GuiGraphicsExtractor` | [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [hud](../systems/client/hud.md), [text-and-fonts](../systems/client/text-and-fonts.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md), [items-and-stacks](../systems/items/items-and-stacks.md), [post-processing](../systems/rendering/post-processing.md) | | `GuiItemAtlas` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiItemRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiProfilerChartRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiRenderer` | [text-and-fonts](../systems/client/text-and-fonts.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md), [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md), [the-frame](../systems/rendering/the-frame.md) | | `GuiRenderState` | [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [hud](../systems/client/hud.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiSkinRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `GuiTextRenderState` | [text-and-fonts](../systems/client/text-and-fonts.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `Half` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `HandlerNames` | [the-connection](../systems/networking/the-connection.md) | | `HandshakeProtocols` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `HangingSignBlockEntity` | [block-entities](../systems/blocks/block-entities.md) | | `HappyGhast` | [pathfinding](../systems/entities/pathfinding.md) | | `HarvestFarmland` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `HasCustomInventoryScreen` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `HashedPatchMap` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [containers-and-menus](../systems/items/containers-and-menus.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `HashedStack` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `HashMapPalette` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `HashOps` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `HeaderAndFooterLayout` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `Heightmap` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [creating-a-world](../systems/worldgen/creating-a-world.md), [terrain](../systems/worldgen/terrain.md) | | `HeightmapPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `HeightProvider` | [data-driven-types](../systems/foundations/data-driven-types.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `HeightProviderType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `HeightRangePlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `HiddenByteBuf` | [the-connection](../systems/networking/the-connection.md) | | `HintsAndWorkarounds` | [blaze3d](../systems/rendering/blaze3d.md) | | `HitResult` | [math-and-primitives](../reference/math-and-primitives.md) | | `HoeItem` | [data-components](../systems/foundations/data-components.md) | | `Hoglin` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `Holder` | [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [II · Foundations](../systems/foundations/README.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [text-components](../systems/foundations/text-components.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [items-and-stacks](../systems/items/items-and-stacks.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `HolderGetter` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `HolderLookup` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `HolderOwner` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `HolderSet` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [recipes](../systems/items/recipes.md), [terrain](../systems/worldgen/terrain.md) | | `HolderSetCodec` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `HorizontalDirectionalBlock` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md) | | `Horse` | [damage-and-death](../systems/entities/damage-and-death.md) | | `HoverEvent` | [text-components](../systems/foundations/text-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `Hud` | [biggest](../maps/biggest.md), [glossary](../reference/glossary.md), [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [gui-and-screens](../systems/client/gui-and-screens.md), [hud](../systems/client/hud.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [resource-system](../systems/foundations/resource-system.md), [items-and-stacks](../systems/items/items-and-stacks.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `HumanoidArmorLayer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `HumanoidMobRenderer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `HumanoidModel` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `HumanoidRenderState` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `HurtByTargetGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `IconSet` | [the-window](../systems/rendering/the-window.md) | | `IdDispatchCodec` | [naming-drift](../reference/naming-drift.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `Identifier` | [introduction](../introduction.md), [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [hud](../systems/client/hud.md), [sound-engine](../systems/client/sound-engine.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [entity-selectors](../systems/commands/entity-selectors.md), [permissions](../systems/commands/permissions.md), [attributes](../systems/entities/attributes.md), [II · Foundations](../systems/foundations/README.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [text-components](../systems/foundations/text-components.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [blaze3d](../systems/rendering/blaze3d.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [post-processing](../systems/rendering/post-processing.md) | | `IdentifierArgument` | [naming-drift](../reference/naming-drift.md) | | `IdentifierException` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `IdMap` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `IdMapper` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `IglooPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `Ignite` | [enchantments](../systems/items/enchantments.md) | | `ImbueRecipe` | [recipes](../systems/items/recipes.md) | | `ImpossibleTrigger` | [advancements](../systems/commands/advancements.md) | | `ImposterProtoChunk` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ImprovedNoise` | [density-functions](../systems/worldgen/density-functions.md) | | `InactiveProfiler` | [the-client-loop](../systems/client/the-client-loop.md) | | `InclusiveRange` | [math-and-primitives](../reference/math-and-primitives.md) | | `IndexMerger` | [math-and-primitives](../reference/math-and-primitives.md) | | `IndexType` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `Ingredient` | [naming-drift](../reference/naming-drift.md), [recipes](../systems/items/recipes.md) | | `Input` | [input-and-keybinds](../systems/client/input-and-keybinds.md), [dialogs](../systems/commands/dialogs.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md) | | `InputConstants` | [input-and-keybinds](../systems/client/input-and-keybinds.md), [the-window](../systems/rendering/the-window.md) | | `InputControl` | [dialogs](../systems/commands/dialogs.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `InputControlHandlers` | [dialogs](../systems/commands/dialogs.md) | | `InputQuirks` | [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `InputType` | [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `InputWithModifiers` | [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `InsideBlockEffectApplier` | [movement-and-collision](../systems/entities/movement-and-collision.md) | | `InsideBlockEffectType` | [movement-and-collision](../systems/entities/movement-and-collision.md) | | `InSquarePlacement` | [naming-drift](../reference/naming-drift.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `InstantiatedFunction` | [functions-and-macros](../systems/commands/functions-and-macros.md) | | `IntArrayTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `IntegerModifier` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `IntegerProperty` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `IntegratedServer` | [introduction](../introduction.md), [anatomy](../systems/anatomy/anatomy.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [options](../systems/client/options.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-storage](../systems/world/chunk-storage.md) | | `Interaction` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [the-spear](../systems/player/the-spear.md) | | `InteractionHand` | [block-interaction](../systems/blocks/block-interaction.md) | | `InteractionResult` | [naming-drift](../reference/naming-drift.md), [block-interaction](../systems/blocks/block-interaction.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [using-an-item](../systems/items/using-an-item.md) | | `InteractWithDoor` | [block-interaction](../systems/blocks/block-interaction.md) | | `IntermediaryShaderModule` | [blaze3d](../systems/rendering/blaze3d.md) | | `InterpolationHandler` | [naming-drift](../reference/naming-drift.md), [the-client-level](../systems/client/the-client-level.md), [authority](../systems/entities/authority.md), [movement-and-collision](../systems/entities/movement-and-collision.md) | | `IntProvider` | [data-driven-types](../systems/foundations/data-driven-types.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `IntProviders` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `Inventory` | [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [containers-and-menus](../systems/items/containers-and-menus.md), [items-and-stacks](../systems/items/items-and-stacks.md), [recipes](../systems/items/recipes.md), [player-anatomy](../systems/player/player-anatomy.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `InventoryChangeTrigger` | [advancements](../systems/commands/advancements.md) | | `InventoryMenu` | [gui-and-screens](../systems/client/gui-and-screens.md), [containers-and-menus](../systems/items/containers-and-menus.md), [recipes](../systems/items/recipes.md) | | `InventoryScreen` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `InvertableSetOptionState` | [entity-selectors](../systems/commands/entity-selectors.md) | | `InWorldGameRulesScreen` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `IoSupplier` | [resource-system](../systems/foundations/resource-system.md) | | `IOWorker` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [chunk-storage](../systems/world/chunk-storage.md), [blending](../systems/worldgen/blending.md) | | `IsolatedCall` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `IsXmas` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `Item` | [hierarchy](../maps/hierarchy.md), [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [attributes](../systems/entities/attributes.md), [II · Foundations](../systems/foundations/README.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [VII · Items and inventories](../systems/items/README.md), [items-and-stacks](../systems/items/items-and-stacks.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `ItemArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ItemAttributeModifiers` | [attributes](../systems/entities/attributes.md) | | `ItemBody` | [dialogs](../systems/commands/dialogs.md) | | `ItemClusterRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ItemCombinerMenu` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ItemCombinerScreen` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ItemCommands` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ItemContainerContents` | [data-components](../systems/foundations/data-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `ItemCooldowns` | [block-interaction](../systems/blocks/block-interaction.md), [using-an-item](../systems/items/using-an-item.md) | | `ItemEnchantments` | [data-components](../systems/foundations/data-components.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `ItemEntity` | [hierarchy](../maps/hierarchy.md), [non-living-damage](../reference/non-living-damage.md), [block-breaking](../systems/blocks/block-breaking.md), [the-client-level](../systems/client/the-client-level.md), [damage-and-death](../systems/entities/damage-and-death.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [VII · Items and inventories](../systems/items/README.md), [items-and-stacks](../systems/items/items-and-stacks.md), [the-sword-swing](../systems/player/the-sword-swing.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `ItemEntityRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ItemFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `ItemFrame` | [non-living-damage](../reference/non-living-damage.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [damage-and-death](../systems/entities/damage-and-death.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `ItemIds` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `ItemInHandLayer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `ItemInHandRenderer` | [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [using-an-item](../systems/items/using-an-item.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `ItemInput` | [naming-drift](../reference/naming-drift.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `ItemInstance` | [data-components](../systems/foundations/data-components.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `ItemLike` | [hierarchy](../maps/hierarchy.md) | | `ItemLore` | [data-components](../systems/foundations/data-components.md) | | `ItemModel` | [naming-drift](../reference/naming-drift.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ItemModelGenerator` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ItemModelResolver` | [naming-drift](../reference/naming-drift.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ItemModels` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ItemOwner` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `ItemParser` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ItemParticleOption` | [items-and-stacks](../systems/items/items-and-stacks.md) | | `ItemPickupParticle` | [particles](../systems/rendering/particles.md) | | `ItemPredicate` | [advancements](../systems/commands/advancements.md), [data-components](../systems/foundations/data-components.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ItemPredicateArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `Items` | [biggest](../maps/biggest.md), [hierarchy](../maps/hierarchy.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [the-spear](../systems/player/the-spear.md) | | `ItemStack` | [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [non-living-damage](../reference/non-living-damage.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [attributes](../systems/entities/attributes.md), [damage-and-death](../systems/entities/damage-and-death.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [II · Foundations](../systems/foundations/README.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md), [tags](../systems/foundations/tags.md), [VII · Items and inventories](../systems/items/README.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ItemStackRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ItemStackTemplate` | [data-components](../systems/foundations/data-components.md), [text-components](../systems/foundations/text-components.md), [items-and-stacks](../systems/items/items-and-stacks.md), [recipes](../systems/items/recipes.md) | | `ItemStackWithSlot` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ItemSteerable` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `ItemTags` | [tags](../systems/foundations/tags.md), [the-sword-swing](../systems/player/the-sword-swing.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `ItemTintSource` | [naming-drift](../reference/naming-drift.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ItemUseAnimation` | [naming-drift](../reference/naming-drift.md), [using-an-item](../systems/items/using-an-item.md), [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `ItemUsedOnLocationTrigger` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `JigsawBlock` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `JigsawBlockEntity` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `JigsawJunction` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `JigsawPlacement` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `JigsawReplacementProcessor` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `JigsawStructure` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `JoinWorldTask` | [protocol-phases](../systems/networking/protocol-phases.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `JOrbisAudioStream` | [sound-engine](../systems/client/sound-engine.md) | | `JsonEventLog` | [resource-system](../systems/foundations/resource-system.md) | | `JsonOps` | [data-driven-types](../systems/foundations/data-driven-types.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `JsonRpc` | [threads](../reference/threads.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [starting-a-server](../systems/server/starting-a-server.md) | | `JsonRpcNotificationService` | [starting-a-server](../systems/server/starting-a-server.md) | | `JumpableVehicleBar` | [hud-elements](../reference/hud-elements.md), [hud](../systems/client/hud.md) | | `JumpControl` | [pathfinding](../systems/entities/pathfinding.md) | | `JungleTemplePiece` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `JUnitLikeTestReporter` | [game-tests](../systems/commands/game-tests.md) | | `JvmProfiler` | [server-tick](../systems/server/server-tick.md) | | `KeybindContents` | [text-components](../systems/foundations/text-components.md) | | `KeybindResolver` | [text-components](../systems/foundations/text-components.md) | | `KeyboardHandler` | [glossary](../reference/glossary.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [the-client-loop](../systems/client/the-client-loop.md), [permissions](../systems/commands/permissions.md), [resource-system](../systems/foundations/resource-system.md), [text-components](../systems/foundations/text-components.md), [input-to-movement](../systems/player/input-to-movement.md), [the-window](../systems/rendering/the-window.md) | | `KeyboardInput` | [input-and-keybinds](../systems/client/input-and-keybinds.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `KeyDispatchCodec` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `KeyEvent` | [naming-drift](../reference/naming-drift.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [input-to-movement](../systems/player/input-to-movement.md) | | `Keyframe` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `KeyframeAnimation` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `KeyframeTrack` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `KeyframeTrackSampler` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `KeyMapping` | [packages](../maps/packages.md), [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [text-components](../systems/foundations/text-components.md), [input-to-movement](../systems/player/input-to-movement.md), [the-window](../systems/rendering/the-window.md) | | `KineticWeapon` | [data-components](../systems/foundations/data-components.md), [enchantments](../systems/items/enchantments.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `KnownPack` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `KnownPacksManager` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `Language` | [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `LanguageManager` | [resource-system](../systems/foundations/resource-system.md), [text-components](../systems/foundations/text-components.md) | | `LanServerPinger` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md) | | `LastSeenMessages` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `LastSeenMessagesTracker` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `LastSeenMessagesValidator` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `LavaFluid` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LavaFogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `LavaSubmergedBlockProcessor` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `LayerDefinition` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `LayerDefinitions` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `LayeredRegistryAccess` | [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `LayeringTransform` | [blaze3d](../systems/rendering/blaze3d.md) | | `LayerLightEventListener` | [lighting](../systems/world/lighting.md) | | `LayerLightSectionStorage` | [lighting](../systems/world/lighting.md) | | `Layout` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `LayoutElement` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `LayoutSettings` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `Leashable` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `LeashFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `LeavesBlock` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `LeaveVineDecorator` | [trees](../systems/worldgen/trees.md) | | `LecternScreen` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `LegacyQueryHandler` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [the-connection](../systems/networking/the-connection.md) | | `LegacyRandomSource` | [math-and-primitives](../reference/math-and-primitives.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `LegacySinglePoolElement` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `LenientJsonParser` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `LerpFunction` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `LerpingBossEvent` | [hud](../systems/client/hud.md) | | `Level` | [lectures](../lectures.md), [fanin](../maps/fanin.md), [Reference](../reference/README.md), [block-update-flags](../reference/block-update-flags.md), [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [V · Blocks](../systems/blocks/README.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md), [X · The client](../systems/client/README.md), [the-client-level](../systems/client/the-client-level.md), [the-client-loop](../systems/client/the-client-loop.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [entity-selectors](../systems/commands/entity-selectors.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [pathfinding](../systems/entities/pathfinding.md), [containers-and-menus](../systems/items/containers-and-menus.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [using-an-item](../systems/items/using-an-item.md), [XI · Rendering](../systems/rendering/README.md), [entity-rendering](../systems/rendering/entity-rendering.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [particles](../systems/rendering/particles.md), [section-meshing](../systems/rendering/section-meshing.md), [III · The server](../systems/server/README.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LevelAccessor` | [block-interaction](../systems/blocks/block-interaction.md), [the-client-level](../systems/client/the-client-level.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LevelBasedPermissionSet` | [naming-drift](../reference/naming-drift.md), [entity-selectors](../systems/commands/entity-selectors.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [permissions](../systems/commands/permissions.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `LevelBasedValue` | [data-driven-types](../systems/foundations/data-driven-types.md), [enchantments](../systems/items/enchantments.md) | | `LevelCallback` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `LevelChunk` | [lectures](../lectures.md), [block-entities](../systems/blocks/block-entities.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [the-client-level](../systems/client/the-client-level.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [fluids](../systems/world/fluids.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `LevelChunkSection` | [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [biomes](../systems/worldgen/biomes.md) | | `LevelChunkTicks` | [server-level-tick](../systems/server/server-level-tick.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LevelData` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `LevelDebugSynchronizers` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [server-level-tick](../systems/server/server-level-tick.md), [points-of-interest](../systems/world/points-of-interest.md) | | `LeveledPriorityQueue` | [lighting](../systems/world/lighting.md) | | `LevelEntityGetterAdapter` | [entity-selectors](../systems/commands/entity-selectors.md) | | `LevelEvent` | [particles](../systems/rendering/particles.md) | | `LevelEventHandler` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `LevelExtractor` | [block-update-flags](../reference/block-update-flags.md), [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [options](../systems/client/options.md), [the-client-level](../systems/client/the-client-level.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [resource-system](../systems/foundations/resource-system.md), [XI · Rendering](../systems/rendering/README.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [particles](../systems/rendering/particles.md), [section-meshing](../systems/rendering/section-meshing.md), [the-frame](../systems/rendering/the-frame.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [lighting](../systems/world/lighting.md) | | `LevelHeightAccessor` | [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `LevelLightEngine` | [the-client-level](../systems/client/the-client-level.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md) | | `LevelLoadingScreen` | [hud-elements](../reference/hud-elements.md) | | `LevelLoadListener` | [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md) | | `LevelLoadProgressTracker` | [starting-a-server](../systems/server/starting-a-server.md) | | `LevelLoadTracker` | [starting-a-server](../systems/server/starting-a-server.md) | | `LevelReader` | [the-client-level](../systems/client/the-client-level.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [biomes](../systems/worldgen/biomes.md) | | `LevelRenderer` | [glossary](../reference/glossary.md), [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [submit-phases](../reference/submit-phases.md), [block-interaction](../systems/blocks/block-interaction.md), [the-client-level](../systems/client/the-client-level.md), [the-gui-render-tree](../systems/client/the-gui-render-tree.md), [blaze3d](../systems/rendering/blaze3d.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [post-processing](../systems/rendering/post-processing.md), [section-meshing](../systems/rendering/section-meshing.md), [the-frame](../systems/rendering/the-frame.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `LevelResource` | [level-data-and-rules](../reference/level-data-and-rules.md), [advancements](../systems/commands/advancements.md) | | `LevelSettings` | [level-data-and-rules](../reference/level-data-and-rules.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `LevelStem` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [starting-a-server](../systems/server/starting-a-server.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `LevelStorageSource` | [level-data-and-rules](../reference/level-data-and-rules.md), [resource-system](../systems/foundations/resource-system.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-storage](../systems/world/chunk-storage.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `LevelSummary` | [level-data-and-rules](../reference/level-data-and-rules.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [starting-a-server](../systems/server/starting-a-server.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `LevelTargetBundle` | [post-processing](../systems/rendering/post-processing.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `LevelTickAccess` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LevelTicks` | [the-client-level](../systems/client/the-client-level.md), [server-level-tick](../systems/server/server-level-tick.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LevelWriter` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [fluids](../systems/world/fluids.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `LeverBlock` | [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `Library` | [sound-engine](../systems/client/sound-engine.md) | | `Lifecycle` | [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `LightChunk` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [lighting](../systems/world/lighting.md) | | `LightChunkGetter` | [lighting](../systems/world/lighting.md) | | `LightCoordsUtil` | [naming-drift](../reference/naming-drift.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `LightEngine` | [the-client-level](../systems/client/the-client-level.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [lighting](../systems/world/lighting.md) | | `LightEventListener` | [lighting](../systems/world/lighting.md) | | `LightLayer` | [lighting](../systems/world/lighting.md) | | `Lightmap` | [introduction](../introduction.md), [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [the-frame](../systems/rendering/the-frame.md), [lighting](../systems/world/lighting.md) | | `LightmapRenderState` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `LightmapRenderStateExtractor` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `LightningBolt` | [hierarchy](../maps/hierarchy.md), [non-living-damage](../reference/non-living-damage.md), [the-client-level](../systems/client/the-client-level.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [damage-and-death](../systems/entities/damage-and-death.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `LightningRodBlock` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LinearCongruentialGenerator` | [math-and-primitives](../reference/math-and-primitives.md) | | `LinearLayout` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `LinearPalette` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `LinkFileSystem` | [resource-system](../systems/foundations/resource-system.md) | | `LinkFSPath` | [resource-system](../systems/foundations/resource-system.md) | | `LinkFSProvider` | [resource-system](../systems/foundations/resource-system.md) | | `LiquidBlock` | [glossary](../reference/glossary.md), [fluids](../systems/world/fluids.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `LiquidBlockContainer` | [fluids](../systems/world/fluids.md) | | `Listener` | [sound-engine](../systems/client/sound-engine.md) | | `ListenerTransform` | [sound-engine](../systems/client/sound-engine.md) | | `ListPoolElement` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `ListTag` | [glossary](../reference/glossary.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `LivingEntity` | [biggest](../maps/biggest.md), [fanin](../maps/fanin.md), [hierarchy](../maps/hierarchy.md), [Reference](../reference/README.md), [glossary](../reference/glossary.md), [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [non-living-damage](../reference/non-living-damage.md), [the-client-level](../systems/client/the-client-level.md), [entity-selectors](../systems/commands/entity-selectors.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [VI · Entities](../systems/entities/README.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [pathfinding](../systems/entities/pathfinding.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [text-components](../systems/foundations/text-components.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [using-an-item](../systems/items/using-an-item.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [VIII · The player](../systems/player/README.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [status-effects](../systems/player/status-effects.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [entity-rendering](../systems/rendering/entity-rendering.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [points-of-interest](../systems/world/points-of-interest.md) | | `LivingEntityRenderer` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `LivingEntityRenderState` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `Llama` | [pathfinding](../systems/entities/pathfinding.md) | | `LoadingChunkTracker` | [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `LoadingOverlay` | [anatomy](../systems/anatomy/anatomy.md), [gui-and-screens](../systems/client/gui-and-screens.md), [resource-system](../systems/foundations/resource-system.md) | | `LocalCoordinates` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `LocalFrameDecoder` | [the-connection](../systems/networking/the-connection.md) | | `LocalFrameEncoder` | [the-connection](../systems/networking/the-connection.md) | | `LocalMobCapCalculator` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md) | | `LocalPlayer` | [biggest](../maps/biggest.md), [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [block-interaction](../systems/blocks/block-interaction.md), [gui-and-screens](../systems/client/gui-and-screens.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [permissions](../systems/commands/permissions.md), [damage-and-death](../systems/entities/damage-and-death.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [text-components](../systems/foundations/text-components.md), [using-an-item](../systems/items/using-an-item.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `LocalSampleLogger` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `LocalTime` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `LocateCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [points-of-interest](../systems/world/points-of-interest.md) | | `LocationCheck` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LocationPredicate` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LocatorBar` | [hud-elements](../reference/hud-elements.md), [hud](../systems/client/hud.md) | | `LodestoneTracker` | [points-of-interest](../systems/world/points-of-interest.md) | | `LoggedChatMessage` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `LoggingLevelLoadListener` | [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md) | | `LoginProtocols` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `LogTestReporter` | [game-tests](../systems/commands/game-tests.md) | | `LongArrayTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `LookAtPlayerGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `LookControl` | [pathfinding](../systems/entities/pathfinding.md) | | `LoopingAudioStream` | [sound-engine](../systems/client/sound-engine.md) | | `LootCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LootContext` | [advancements](../systems/commands/advancements.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md), [loot-tables](../systems/items/loot-tables.md) | | `LootContextArg` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LootContextParams` | [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [damage-and-death](../systems/entities/damage-and-death.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchanting](../systems/items/enchanting.md), [loot-tables](../systems/items/loot-tables.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `LootContextParamSets` | [block-breaking](../systems/blocks/block-breaking.md), [entity-selectors](../systems/commands/entity-selectors.md), [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LootContextUser` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LootDataType` | [data-driven-types](../systems/foundations/data-driven-types.md), [tags](../systems/foundations/tags.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `LootItem` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `LootItemCondition` | [advancements](../systems/commands/advancements.md), [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md) | | `LootItemConditionalFunction` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `LootItemConditions` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LootItemEntityPropertyCondition` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LootItemFunction` | [data-driven-types](../systems/foundations/data-driven-types.md), [enchanting](../systems/items/enchanting.md), [loot-tables](../systems/items/loot-tables.md) | | `LootItemFunctions` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `LootItemRandomChanceCondition` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `LootItemRandomChanceWithEnchantedBonusCondition` | [loot-tables](../systems/items/loot-tables.md) | | `LootParams` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `LootPool` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `LootPoolEntries` | [loot-tables](../systems/items/loot-tables.md) | | `LootPoolEntryContainer` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `LootPoolSingletonContainer` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `LootTable` | [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `LpVec3` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `MacosUtil` | [the-window](../systems/rendering/the-window.md) | | `MacroFunction` | [naming-drift](../reference/naming-drift.md), [functions-and-macros](../systems/commands/functions-and-macros.md) | | `Main` | [the-client-loop](../systems/client/the-client-loop.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `MainTarget` | [blaze3d](../systems/rendering/blaze3d.md) | | `ManagementServer` | [threads](../reference/threads.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md) | | `MangroveRootPlacement` | [trees](../systems/worldgen/trees.md) | | `MangroveRootPlacer` | [trees](../systems/worldgen/trees.md) | | `Mannequin` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [player-anatomy](../systems/player/player-anatomy.md) | | `MapCodec` | [fanin](../maps/fanin.md), [dialogs](../systems/commands/dialogs.md), [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md) | | `MapId` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `MapIndex` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `MapItem` | [items-and-stacks](../systems/items/items-and-stacks.md) | | `MapItemSavedData` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `MappableRingBuffer` | [blaze3d](../systems/rendering/blaze3d.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [post-processing](../systems/rendering/post-processing.md) | | `MappedRegistry` | [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `Marker` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `MarsagliaPolarGaussian` | [math-and-primitives](../reference/math-and-primitives.md) | | `Material` | [naming-drift](../reference/naming-drift.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `MaterialBaker` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `MaterialRuleList` | [terrain](../systems/worldgen/terrain.md) | | `MegaJungleFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `MegaJungleTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `MegaPineFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `MemoryMap` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `MemoryModuleType` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [points-of-interest](../systems/world/points-of-interest.md) | | `MemoryReserve` | [starting-a-server](../systems/server/starting-a-server.md) | | `MemoryServerHandshakePacketListenerImpl` | [protocol-phases](../systems/networking/protocol-phases.md) | | `MemorySlot` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `MenuProvider` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `MenuScreens` | [gui-and-screens](../systems/client/gui-and-screens.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `MenuType` | [hierarchy](../maps/hierarchy.md), [gui-and-screens](../systems/client/gui-and-screens.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `MerchantOffers` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `MeshData` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `MeshDefinition` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `MessageArgument` | [glossary](../reference/glossary.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md), [text-components](../systems/foundations/text-components.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `MessageSignature` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `MessageSignatureCache` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `MetricCategory` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `MetricsRecorder` | [the-client-loop](../systems/client/the-client-loop.md), [server-tick](../systems/server/server-tick.md) | | `MinecartCollisionContext` | [math-and-primitives](../reference/math-and-primitives.md) | | `MinecartTNT` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `Minecraft` | [biggest](../maps/biggest.md), [fanin](../maps/fanin.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [X · The client](../systems/client/README.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [gui-and-screens](../systems/client/gui-and-screens.md), [hud](../systems/client/hud.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [options](../systems/client/options.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [sound-engine](../systems/client/sound-engine.md), [the-client-level](../systems/client/the-client-level.md), [the-client-loop](../systems/client/the-client-loop.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [resource-system](../systems/foundations/resource-system.md), [text-components](../systems/foundations/text-components.md), [using-an-item](../systems/items/using-an-item.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [XI · Rendering](../systems/rendering/README.md), [blaze3d](../systems/rendering/blaze3d.md), [entity-rendering](../systems/rendering/entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [particles](../systems/rendering/particles.md), [post-processing](../systems/rendering/post-processing.md), [the-frame](../systems/rendering/the-frame.md), [the-window](../systems/rendering/the-window.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [lighting](../systems/world/lighting.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `MinecraftServer` | [introduction](../introduction.md), [lectures](../lectures.md), [biggest](../maps/biggest.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [XIII · Commands and data packs](../systems/commands/README.md), [advancements](../systems/commands/advancements.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [dialogs](../systems/commands/dialogs.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [game-tests](../systems/commands/game-tests.md), [permissions](../systems/commands/permissions.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [text-components](../systems/foundations/text-components.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md), [recipes](../systems/items/recipes.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [input-to-movement](../systems/player/input-to-movement.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [biomes](../systems/worldgen/biomes.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `MinecraftServerGui` | [threads](../reference/threads.md) | | `MinecraftServerStateServiceImpl` | [how-a-server-dies](../systems/server/how-a-server-dies.md) | | `MineshaftPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `MineshaftStructure` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `MinMaxBounds` | [advancements](../systems/commands/advancements.md), [entity-selectors](../systems/commands/entity-selectors.md) | | `MipmapGenerator` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `MipmapStrategy` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `Mirror` | [math-and-primitives](../reference/math-and-primitives.md) | | `MiscOverworldFeatures` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `MissingTextureAtlasSprite` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `Mob` | [biggest](../maps/biggest.md), [hierarchy](../maps/hierarchy.md), [non-living-damage](../reference/non-living-damage.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [pathfinding](../systems/entities/pathfinding.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `MobCategory` | [attributes](../systems/entities/attributes.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md) | | `MobEffect` | [hierarchy](../maps/hierarchy.md), [naming-drift](../reference/naming-drift.md), [attributes](../systems/entities/attributes.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [status-effects](../systems/player/status-effects.md) | | `MobEffectCategory` | [status-effects](../systems/player/status-effects.md) | | `MobEffectFogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `MobEffectInstance` | [hud-elements](../reference/hud-elements.md), [status-effects](../systems/player/status-effects.md) | | `MobEffects` | [naming-drift](../reference/naming-drift.md), [attributes](../systems/entities/attributes.md), [damage-and-death](../systems/entities/damage-and-death.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [input-to-movement](../systems/player/input-to-movement.md), [status-effects](../systems/player/status-effects.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `MobEffectUtil` | [block-breaking](../systems/blocks/block-breaking.md), [status-effects](../systems/player/status-effects.md) | | `MobRenderer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `MobSpawnSettings` | [biomes](../systems/worldgen/biomes.md) | | `Model` | [submit-phases](../reference/submit-phases.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `ModelBakery` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ModelBlockRenderer` | [naming-drift](../reference/naming-drift.md), [submit-phases](../reference/submit-phases.md) | | `ModelDiscovery` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ModelFeatureRenderer` | [submit-phases](../reference/submit-phases.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `ModelGroupCollector` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ModelLayerLocation` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `ModelLayers` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `ModelManager` | [blocks-and-states](../systems/blocks/blocks-and-states.md), [resource-system](../systems/foundations/resource-system.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [section-meshing](../systems/rendering/section-meshing.md) | | `ModelPart` | [submit-phases](../reference/submit-phases.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `ModelProvider` | [biggest](../maps/biggest.md) | | `ModelState` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `Monitor` | [naming-drift](../reference/naming-drift.md), [the-window](../systems/rendering/the-window.md) | | `MonitoredLocalFrameDecoder` | [the-connection](../systems/networking/the-connection.md) | | `MonitorManager` | [naming-drift](../reference/naming-drift.md), [the-window](../systems/rendering/the-window.md) | | `Monster` | [attributes](../systems/entities/attributes.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `MonsterRoomFeature` | [loot-tables](../systems/items/loot-tables.md) | | `MoonPhase` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `MouseButtonEvent` | [naming-drift](../reference/naming-drift.md), [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `MouseHandler` | [packages](../maps/packages.md), [glossary](../reference/glossary.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [input-to-movement](../systems/player/input-to-movement.md), [the-window](../systems/rendering/the-window.md) | | `MoveControl` | [pathfinding](../systems/entities/pathfinding.md) | | `MoverType` | [authority](../systems/entities/authority.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [input-to-movement](../systems/player/input-to-movement.md) | | `MoveThroughVillageGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `MoveToTargetSink` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [pathfinding](../systems/entities/pathfinding.md) | | `MovingBlockFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `MovingBlockRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `MovingPistonBlock` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `MsgCommand` | [entity-selectors](../systems/commands/entity-selectors.md) | | `Mth` | [fanin](../maps/fanin.md), [packages](../maps/packages.md), [math-and-primitives](../reference/math-and-primitives.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `MultiActionDialog` | [dialogs](../systems/commands/dialogs.md) | | `MultiLineLabel` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `MultiNoiseBiomeSource` | [biomes](../systems/worldgen/biomes.md) | | `MultiNoiseBiomeSourceParameterList` | [naming-drift](../reference/naming-drift.md) | | `MultiPackResourceManager` | [resource-system](../systems/foundations/resource-system.md) | | `MultiPartModel` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `MultiPlayerGameMode` | [naming-drift](../reference/naming-drift.md), [anatomy](../systems/anatomy/anatomy.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [gui-and-screens](../systems/client/gui-and-screens.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchantments](../systems/items/enchantments.md), [using-an-item](../systems/items/using-an-item.md), [the-connection](../systems/networking/the-connection.md), [player-anatomy](../systems/player/player-anatomy.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [section-meshing](../systems/rendering/section-meshing.md), [fluids](../systems/world/fluids.md), [lighting](../systems/world/lighting.md) | | `MultipleTestTracker` | [game-tests](../systems/commands/game-tests.md) | | `MultiplyValue` | [enchantments](../systems/items/enchantments.md) | | `MultiVariant` | [naming-drift](../reference/naming-drift.md) | | `MushroomCow` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `MusicManager` | [sound-engine](../systems/client/sound-engine.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `MutableComponent` | [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `MyceliumBlock` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `Nameable` | [entity-selectors](../systems/commands/entity-selectors.md), [entity-anatomy](../systems/entities/entity-anatomy.md) | | `NameAndId` | [naming-drift](../reference/naming-drift.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `NamedRule` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `NameTagFeatureRenderer` | [submit-phases](../reference/submit-phases.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `NativeImage` | [XI · Rendering](../systems/rendering/README.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [the-window](../systems/rendering/the-window.md) | | `NativeLibrariesBootstrap` | [anatomy](../systems/anatomy/anatomy.md), [sound-engine](../systems/client/sound-engine.md), [the-window](../systems/rendering/the-window.md) | | `NaturalSpawner` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md), [biomes](../systems/worldgen/biomes.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `NbtAccounter` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `NbtContents` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [text-components](../systems/foundations/text-components.md) | | `NbtException` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `NbtIo` | [glossary](../reference/glossary.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [how-a-server-dies](../systems/server/how-a-server-dies.md) | | `NbtOps` | [glossary](../reference/glossary.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `NbtPathArgument` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [text-components](../systems/foundations/text-components.md) | | `NbtProvider` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `NbtProviders` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `NbtTagArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `NbtUtils` | [entity-selectors](../systems/commands/entity-selectors.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `NearestAttackableTargetGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `NearestBedSensor` | [points-of-interest](../systems/world/points-of-interest.md) | | `NeighborUpdater` | [block-update-flags](../reference/block-update-flags.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `NestedLootTable` | [naming-drift](../reference/naming-drift.md), [loot-tables](../systems/items/loot-tables.md) | | `NetherFortressPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `NetherFossilPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `NetherWorldCarver` | [terrain](../systems/worldgen/terrain.md) | | `NetworkRegistryLoadTask` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `NeutralMob` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `NewMinecartBehavior` | [input-to-movement](../systems/player/input-to-movement.md) | | `NoDataSpecialModelRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `Node` | [pathfinding](../systems/entities/pathfinding.md) | | `NodeEvaluator` | [glossary](../reference/glossary.md), [pathfinding](../systems/entities/pathfinding.md) | | `NoiseBasedChunkGenerator` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [biomes](../systems/worldgen/biomes.md), [blending](../systems/worldgen/blending.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `NoiseBasedCountPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `NoiseChunk` | [density-function-nodes](../reference/density-function-nodes.md), [biomes](../systems/worldgen/biomes.md), [blending](../systems/worldgen/blending.md), [density-functions](../systems/worldgen/density-functions.md), [structure-placement](../systems/worldgen/structure-placement.md), [terrain](../systems/worldgen/terrain.md) | | `NoiseGeneratorSettings` | [math-and-primitives](../reference/math-and-primitives.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [terrain](../systems/worldgen/terrain.md) | | `NoiseRouter` | [glossary](../reference/glossary.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `NoiseRouterData` | [biggest](../maps/biggest.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [blending](../systems/worldgen/blending.md), [density-functions](../systems/worldgen/density-functions.md) | | `Noises` | [density-functions](../systems/worldgen/density-functions.md) | | `NoiseSettings` | [terrain](../systems/worldgen/terrain.md) | | `NoiseThresholdCountPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `NoneFeatureConfiguration` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `NoRenderParticle` | [particles](../systems/rendering/particles.md) | | `NoRenderParticleGroup` | [particles](../systems/rendering/particles.md) | | `NormalCraftingRecipe` | [recipes](../systems/items/recipes.md) | | `NormalNoise` | [density-function-nodes](../reference/density-function-nodes.md), [density-functions](../systems/worldgen/density-functions.md) | | `NoteBlock` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `NoticeDialog` | [dialogs](../systems/commands/dialogs.md) | | `NotificationManager` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md) | | `NullOps` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `NumberFormat` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `NumberFormatType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `NumberFormatTypes` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `NumberProvider` | [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `NumberProviders` | [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `NumberRangeInput` | [dialogs](../systems/commands/dialogs.md) | | `NumericTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ObjectContents` | [text-components](../systems/foundations/text-components.md) | | `ObjectInfo` | [text-components](../systems/foundations/text-components.md) | | `Objective` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `ObjectiveArgument` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ObjectiveCriteria` | [glossary](../reference/glossary.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ObjectSelectionList` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ObserverBlock` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `OceanMonumentPieces` | [biggest](../maps/biggest.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `OceanMonumentStructure` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `OceanRuinPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `OctahedralGroup` | [math-and-primitives](../reference/math-and-primitives.md) | | `Octree` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `OldMinecartBehavior` | [input-to-movement](../systems/player/input-to-movement.md) | | `OldUsersConverter` | [starting-a-server](../systems/server/starting-a-server.md) | | `OminousBottleAmplifier` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `OminousItemSpawner` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `OptimizeWorldScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `OptionInstance` | [options](../systems/client/options.md), [the-client-loop](../systems/client/the-client-loop.md) | | `Options` | [biggest](../maps/biggest.md), [packages](../maps/packages.md), [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [anatomy](../systems/anatomy/anatomy.md), [options](../systems/client/options.md), [sound-engine](../systems/client/sound-engine.md), [the-client-level](../systems/client/the-client-level.md), [the-client-loop](../systems/client/the-client-loop.md), [resource-system](../systems/foundations/resource-system.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [input-to-movement](../systems/player/input-to-movement.md), [the-sword-swing](../systems/player/the-sword-swing.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [post-processing](../systems/rendering/post-processing.md), [the-window](../systems/rendering/the-window.md), [chunk-storage](../systems/world/chunk-storage.md) | | `OptionsList` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `OptionsRenderState` | [post-processing](../systems/rendering/post-processing.md) | | `OrderedSubmitNodeCollector` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `OreFeature` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `OreVeinifier` | [terrain](../systems/worldgen/terrain.md) | | `Orientation` | [signal-and-dust](../systems/blocks/signal-and-dust.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `OutgoingChatMessage` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `OutputTarget` | [blaze3d](../systems/rendering/blaze3d.md) | | `Overlay` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `OverlayMetadataSection` | [resource-system](../systems/foundations/resource-system.md) | | `OverlayTexture` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `OversizedItemRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `OverworldBiomeBuilder` | [biomes](../systems/worldgen/biomes.md) | | `OwnableEntity` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `Pack` | [resource-system](../systems/foundations/resource-system.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `PackCompatibility` | [resource-system](../systems/foundations/resource-system.md) | | `PackDetector` | [resource-system](../systems/foundations/resource-system.md) | | `Packet` | [fanin](../maps/fanin.md), [hierarchy](../maps/hierarchy.md), [threads](../reference/threads.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `PacketBundlePacker` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `PacketBundleUnpacker` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `PacketDecoder` | [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `PacketEncoder` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [text-components](../systems/foundations/text-components.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `PacketFlow` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `PacketListener` | [anatomy](../systems/anatomy/anatomy.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [server-tick](../systems/server/server-tick.md) | | `PacketProcessor` | [naming-drift](../reference/naming-drift.md), [anatomy](../systems/anatomy/anatomy.md), [the-client-loop](../systems/client/the-client-loop.md), [dialogs](../systems/commands/dialogs.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [containers-and-menus](../systems/items/containers-and-menus.md), [the-connection](../systems/networking/the-connection.md), [input-to-movement](../systems/player/input-to-movement.md), [the-sword-swing](../systems/player/the-sword-swing.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md) | | `PacketReport` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `PacketSendListener` | [text-components](../systems/foundations/text-components.md), [the-connection](../systems/networking/the-connection.md) | | `PacketType` | [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `PacketUtils` | [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [server-tick](../systems/server/server-tick.md) | | `PackFormat` | [resource-system](../systems/foundations/resource-system.md) | | `PackLocationInfo` | [resource-system](../systems/foundations/resource-system.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `PackMetadataSection` | [resource-system](../systems/foundations/resource-system.md) | | `PackRepository` | [resource-system](../systems/foundations/resource-system.md) | | `PackResources` | [resource-system](../systems/foundations/resource-system.md) | | `PackSelectionConfig` | [resource-system](../systems/foundations/resource-system.md) | | `PackSource` | [glossary](../reference/glossary.md), [resource-system](../systems/foundations/resource-system.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `PackType` | [resource-system](../systems/foundations/resource-system.md) | | `PaleMossDecorator` | [trees](../systems/worldgen/trees.md) | | `Palette` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `PalettedContainer` | [naming-drift](../reference/naming-drift.md), [section-meshing](../systems/rendering/section-meshing.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [biomes](../systems/worldgen/biomes.md) | | `PalettedContainerFactory` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `PalettedContainerRO` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `PalettedPermutations` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `PaletteResize` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `PanoramaRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `Parrot` | [tags](../systems/foundations/tags.md) | | `ParsedTemplate` | [dialogs](../systems/commands/dialogs.md) | | `ParserBasedArgument` | [naming-drift](../reference/naming-drift.md) | | `PartDefinition` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `Particle` | [particles](../systems/rendering/particles.md) | | `ParticleEngine` | [math-and-primitives](../reference/math-and-primitives.md), [the-client-level](../systems/client/the-client-level.md), [the-client-loop](../systems/client/the-client-loop.md), [particles](../systems/rendering/particles.md) | | `ParticleGroup` | [naming-drift](../reference/naming-drift.md), [particles](../systems/rendering/particles.md) | | `ParticleGroupRenderState` | [particles](../systems/rendering/particles.md) | | `ParticleLimit` | [naming-drift](../reference/naming-drift.md), [particles](../systems/rendering/particles.md) | | `ParticleOptions` | [data-driven-types](../systems/foundations/data-driven-types.md), [status-effects](../systems/player/status-effects.md) | | `ParticleRenderType` | [particles](../systems/rendering/particles.md) | | `ParticleResources` | [resource-system](../systems/foundations/resource-system.md), [particles](../systems/rendering/particles.md) | | `ParticlesRenderState` | [particles](../systems/rendering/particles.md) | | `ParticleType` | [data-driven-types](../systems/foundations/data-driven-types.md), [particles](../systems/rendering/particles.md) | | `ParticleTypes` | [non-living-damage](../reference/non-living-damage.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `PartPose` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `PatchedDataComponentMap` | [naming-drift](../reference/naming-drift.md), [data-components](../systems/foundations/data-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `Path` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [pathfinding](../systems/entities/pathfinding.md), [points-of-interest](../systems/world/points-of-interest.md) | | `PathFinder` | [glossary](../reference/glossary.md), [pathfinding](../systems/entities/pathfinding.md) | | `PathfinderMob` | [hierarchy](../maps/hierarchy.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `PathfindingContext` | [pathfinding](../systems/entities/pathfinding.md) | | `PathNavigation` | [block-interaction](../systems/blocks/block-interaction.md), [attributes](../systems/entities/attributes.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [pathfinding](../systems/entities/pathfinding.md), [points-of-interest](../systems/world/points-of-interest.md) | | `PathNavigationRegion` | [pathfinding](../systems/entities/pathfinding.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `PathPackResources` | [resource-system](../systems/foundations/resource-system.md) | | `PathType` | [naming-drift](../reference/naming-drift.md), [pathfinding](../systems/entities/pathfinding.md) | | `PathTypeCache` | [pathfinding](../systems/entities/pathfinding.md) | | `PatrolSpawner` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [points-of-interest](../systems/world/points-of-interest.md) | | `PerfCommand` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `PeriodicNotificationManager` | [X · The client](../systems/client/README.md), [resource-system](../systems/foundations/resource-system.md) | | `PerlinNoise` | [density-functions](../systems/worldgen/density-functions.md) | | `PerlinSimplexNoise` | [density-functions](../systems/worldgen/density-functions.md) | | `Permission` | [glossary](../reference/glossary.md), [permissions](../systems/commands/permissions.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `PermissionCheck` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [permissions](../systems/commands/permissions.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `PermissionCheckTypes` | [permissions](../systems/commands/permissions.md) | | `PermissionLevel` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [permissions](../systems/commands/permissions.md) | | `PermissionProviderCheck` | [naming-drift](../reference/naming-drift.md), [permissions](../systems/commands/permissions.md) | | `Permissions` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [entity-selectors](../systems/commands/entity-selectors.md), [permissions](../systems/commands/permissions.md), [text-components](../systems/foundations/text-components.md), [player-anatomy](../systems/player/player-anatomy.md) | | `PermissionSet` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [permissions](../systems/commands/permissions.md), [player-anatomy](../systems/player/player-anatomy.md) | | `PermissionSetSupplier` | [naming-drift](../reference/naming-drift.md), [permissions](../systems/commands/permissions.md) | | `PermissionSetUnion` | [functions-and-macros](../systems/commands/functions-and-macros.md), [permissions](../systems/commands/permissions.md) | | `PermissionTypes` | [permissions](../systems/commands/permissions.md) | | `PersistentEntitySectionManager` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-storage](../systems/world/chunk-storage.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `Phantom` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `PhantomSpawner` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [starting-a-server](../systems/server/starting-a-server.md) | | `PictureInPictureRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `PieceGenerator` | [structure-placement](../systems/worldgen/structure-placement.md) | | `PieceGeneratorSupplier` | [structure-placement](../systems/worldgen/structure-placement.md) | | `PiecesContainer` | [glossary](../reference/glossary.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `PiercingWeapon` | [data-components](../systems/foundations/data-components.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `Piglin` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [pathfinding](../systems/entities/pathfinding.md), [the-spear](../systems/player/the-spear.md) | | `PiglinAi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `PineFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `PingDebugChart` | [hud](../systems/client/hud.md) | | `PingDebugMonitor` | [threads](../reference/threads.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `PistonBaseBlock` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [server-level-tick](../systems/server/server-level-tick.md) | | `PistonHeadBlock` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `PistonHeadRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `PistonHeadRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `PistonMovingBlockEntity` | [block-entities](../systems/blocks/block-entities.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `PistonStructureResolver` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `PlaceCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `PlacedFeature` | [data-driven-types](../systems/foundations/data-driven-types.md), [features-and-placement](../systems/worldgen/features-and-placement.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `PlacementContext` | [XII · World generation](../systems/worldgen/README.md) | | `PlacementFilter` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `PlacementInfo` | [naming-drift](../reference/naming-drift.md), [recipes](../systems/items/recipes.md) | | `PlacementModifier` | [data-driven-types](../systems/foundations/data-driven-types.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `PlacementModifierType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `PlaceOnGroundDecorator` | [trees](../systems/worldgen/trees.md) | | `PlaceRecipeHelper` | [recipes](../systems/items/recipes.md) | | `PlainMessage` | [dialogs](../systems/commands/dialogs.md) | | `PlainTextContents` | [naming-drift](../reference/naming-drift.md), [text-components](../systems/foundations/text-components.md) | | `PlainTextFunction` | [functions-and-macros](../systems/commands/functions-and-macros.md) | | `Player` | [biggest](../maps/biggest.md), [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [non-living-damage](../reference/non-living-damage.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [gui-and-screens](../systems/client/gui-and-screens.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [VI · Entities](../systems/entities/README.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [text-components](../systems/foundations/text-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [VIII · The player](../systems/player/README.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [server-level-tick](../systems/server/server-level-tick.md), [chunk-storage](../systems/world/chunk-storage.md) | | `PlayerAdvancements` | [naming-drift](../reference/naming-drift.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [advancements](../systems/commands/advancements.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `PlayerChatMessage` | [text-components](../systems/foundations/text-components.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `PlayerChunkSender` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md), [lighting](../systems/world/lighting.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `PlayerDataStorage` | [level-data-and-rules](../reference/level-data-and-rules.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md) | | `PlayerEnderChestContainer` | [player-anatomy](../systems/player/player-anatomy.md) | | `PlayerEquipment` | [player-anatomy](../systems/player/player-anatomy.md) | | `PlayerGlyphProvider` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `PlayerInfo` | [chat-and-signing](../systems/networking/chat-and-signing.md), [player-anatomy](../systems/player/player-anatomy.md) | | `PlayerList` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [advancements](../systems/commands/advancements.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [entity-selectors](../systems/commands/entity-selectors.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [text-components](../systems/foundations/text-components.md), [recipes](../systems/items/recipes.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [protocol-phases](../systems/networking/protocol-phases.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `PlayerModel` | [player-anatomy](../systems/player/player-anatomy.md) | | `PlayerPredicate` | [advancements](../systems/commands/advancements.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `PlayerRideableJumping` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `PlayerScoreEntry` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `PlayerSkinRenderCache` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `PlayerSpawnFinder` | [protocol-phases](../systems/networking/protocol-phases.md), [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [biomes](../systems/worldgen/biomes.md) | | `PlayerSprite` | [text-components](../systems/foundations/text-components.md) | | `PlayerTabOverlay` | [hud-elements](../reference/hud-elements.md) | | `PlayerTeam` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [text-components](../systems/foundations/text-components.md) | | `PlayerTrigger` | [advancements](../systems/commands/advancements.md) | | `PlaySoundCommand` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `PlaySoundConsumeEffect` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `PlaySoundEffect` | [enchantments](../systems/items/enchantments.md) | | `PoiCompetitorScan` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [points-of-interest](../systems/world/points-of-interest.md) | | `PoiManager` | [glossary](../reference/glossary.md), [VI · Entities](../systems/entities/README.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [server-tick](../systems/server/server-tick.md), [chunk-storage](../systems/world/chunk-storage.md), [points-of-interest](../systems/world/points-of-interest.md) | | `PoiRecord` | [points-of-interest](../systems/world/points-of-interest.md) | | `PoiSection` | [points-of-interest](../systems/world/points-of-interest.md) | | `PoiType` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [points-of-interest](../systems/world/points-of-interest.md) | | `PoiTypes` | [points-of-interest](../systems/world/points-of-interest.md) | | `PoiTypeTags` | [tags](../systems/foundations/tags.md), [points-of-interest](../systems/world/points-of-interest.md) | | `PolygonMode` | [blaze3d](../systems/rendering/blaze3d.md) | | `PoolAliasBinding` | [data-driven-types](../systems/foundations/data-driven-types.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `PoolAliasLookup` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `PoolElementStructurePiece` | [glossary](../reference/glossary.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `Pools` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `PortalForcer` | [points-of-interest](../systems/world/points-of-interest.md) | | `Pose` | [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `PoseStack` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md), [blaze3d](../systems/rendering/blaze3d.md), [entity-rendering](../systems/rendering/entity-rendering.md) | | `Position` | [math-and-primitives](../reference/math-and-primitives.md) | | `PositionalRandomFactory` | [math-and-primitives](../reference/math-and-primitives.md) | | `PositionCollisionContext` | [math-and-primitives](../reference/math-and-primitives.md) | | `PositionMoveRotation` | [the-client-level](../systems/client/the-client-level.md), [input-to-movement](../systems/player/input-to-movement.md) | | `PositionSource` | [data-driven-types](../systems/foundations/data-driven-types.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `PositionSourceType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `PosRuleTest` | [data-driven-types](../systems/foundations/data-driven-types.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `PosRuleTestType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `PostChain` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [post-processing](../systems/rendering/post-processing.md) | | `PostChainConfig` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [post-processing](../systems/rendering/post-processing.md) | | `PostPass` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [post-processing](../systems/rendering/post-processing.md) | | `PostPlacementProcessor` | [structure-placement](../systems/worldgen/structure-placement.md) | | `PostSpawnProcessor` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `PotentialCalculator` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `PotentSulfurBlock` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `Potion` | [hierarchy](../maps/hierarchy.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `PotionContents` | [hunger-and-experience](../systems/player/hunger-and-experience.md), [status-effects](../systems/player/status-effects.md) | | `PowderedSnowFogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `PredictiveAction` | [prediction-and-acks](../systems/client/prediction-and-acks.md) | | `PreeditEvent` | [naming-drift](../reference/naming-drift.md), [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `PreferredGraphicsApi` | [anatomy](../systems/anatomy/anatomy.md), [blaze3d](../systems/rendering/blaze3d.md) | | `PreparableReloadListener` | [glossary](../reference/glossary.md), [resource-system](../systems/foundations/resource-system.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `PreparedRenderType` | [blaze3d](../systems/rendering/blaze3d.md) | | `PrepareSpawnTask` | [protocol-phases](../systems/networking/protocol-phases.md), [player-anatomy](../systems/player/player-anatomy.md), [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `PresetEditor` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `PresetFlatWorldScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `PrimaryLevelData` | [level-data-and-rules](../reference/level-data-and-rules.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `PrimedTnt` | [non-living-damage](../reference/non-living-damage.md), [the-client-level](../systems/client/the-client-level.md), [damage-and-death](../systems/entities/damage-and-death.md) | | `PrimitiveTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `PrimitiveTopology` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `PriorityConsecutiveExecutor` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [chunk-storage](../systems/world/chunk-storage.md) | | `ProblemReporter` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ProcessorRule` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `ProfiledReloadInstance` | [resource-system](../systems/foundations/resource-system.md) | | `ProfilePublicKey` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `Profiler` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `ProfilerFiller` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [server-tick](../systems/server/server-tick.md) | | `ProfilerPieChart` | [hud](../systems/client/hud.md) | | `Projectile` | [hierarchy](../maps/hierarchy.md), [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [using-an-item](../systems/items/using-an-item.md) | | `ProjectileUtil` | [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `ProjectileWeaponItem` | [enchantments](../systems/items/enchantments.md), [using-an-item](../systems/items/using-an-item.md) | | `Projection` | [post-processing](../systems/rendering/post-processing.md) | | `ProjectionMatrixBuffer` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `Property` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `ProtoChunk` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ProtoChunkTicks` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ProtocolCodecBuilder` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ProtocolInfo` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ProtocolInfoBuilder` | [naming-drift](../reference/naming-drift.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ProtocolSwapHandler` | [the-connection](../systems/networking/the-connection.md) | | `PunchTreeTutorialStepInstance` | [tags](../systems/foundations/tags.md) | | `PushReaction` | [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md) | | `QuadCollection` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `QuadParticleFeatureRenderer` | [submit-phases](../reference/submit-phases.md), [particles](../systems/rendering/particles.md) | | `QuadParticleGroup` | [particles](../systems/rendering/particles.md) | | `QuadParticleRenderState` | [particles](../systems/rendering/particles.md) | | `QuartPos` | [glossary](../reference/glossary.md), [math-and-primitives](../reference/math-and-primitives.md) | | `QueryThreadGs4` | [threads](../reference/threads.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md) | | `Rabbit` | [pathfinding](../systems/entities/pathfinding.md) | | `Raid` | [points-of-interest](../systems/world/points-of-interest.md) | | `RaidCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `Raids` | [level-data-and-rules](../reference/level-data-and-rules.md), [points-of-interest](../systems/world/points-of-interest.md) | | `RandomBooleanSelectorFeature` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `RandomizableContainer` | [data-driven-types](../systems/foundations/data-driven-types.md), [containers-and-menus](../systems/items/containers-and-menus.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `RandomizableContainerBlockEntity` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `RandomLookAroundGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `RandomOffsetPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `RandomSelectorFeature` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `RandomSequence` | [math-and-primitives](../reference/math-and-primitives.md) | | `RandomSequences` | [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `RandomSource` | [fanin](../maps/fanin.md), [packages](../maps/packages.md), [math-and-primitives](../reference/math-and-primitives.md), [entity-selectors](../systems/commands/entity-selectors.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `RandomSpreadFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `RandomSpreadStructurePlacement` | [structure-placement](../systems/worldgen/structure-placement.md) | | `RandomSpreadType` | [structure-placement](../systems/worldgen/structure-placement.md) | | `RandomState` | [math-and-primitives](../reference/math-and-primitives.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [biomes](../systems/worldgen/biomes.md), [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `RandomSupport` | [math-and-primitives](../reference/math-and-primitives.md) | | `RangedAttribute` | [attributes](../systems/entities/attributes.md) | | `RangedBowAttackGoal` | [pathfinding](../systems/entities/pathfinding.md) | | `RangedCrossbowAttackGoal` | [using-an-item](../systems/items/using-an-item.md) | | `RangeSelectItemModel` | [naming-drift](../reference/naming-drift.md) | | `RateKickingConnection` | [the-connection](../systems/networking/the-connection.md) | | `Ravager` | [damage-and-death](../systems/entities/damage-and-death.md) | | `RconClient` | [threads](../reference/threads.md) | | `RconConsoleSource` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `RconThread` | [threads](../reference/threads.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md) | | `ReadOnlyScoreInfo` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `RealmsConnect` | [protocol-phases](../systems/networking/protocol-phases.md) | | `RealmsScreen` | [hierarchy](../maps/hierarchy.md) | | `Recipe` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md) | | `RecipeAccess` | [recipes](../systems/items/recipes.md) | | `RecipeBook` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `RecipeBookCategories` | [recipes](../systems/items/recipes.md) | | `RecipeBookCategory` | [recipes](../systems/items/recipes.md) | | `RecipeBookComponent` | [recipes](../systems/items/recipes.md) | | `RecipeBookMenu` | [recipes](../systems/items/recipes.md) | | `RecipeBookSettings` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `RecipeBookType` | [recipes](../systems/items/recipes.md) | | `RecipeCache` | [recipes](../systems/items/recipes.md) | | `RecipeCollection` | [recipes](../systems/items/recipes.md) | | `RecipeCraftingHolder` | [recipes](../systems/items/recipes.md) | | `RecipeDisplay` | [glossary](../reference/glossary.md), [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `RecipeDisplayEntry` | [naming-drift](../reference/naming-drift.md), [recipes](../systems/items/recipes.md) | | `RecipeDisplayId` | [glossary](../reference/glossary.md), [recipes](../systems/items/recipes.md) | | `RecipeHolder` | [recipes](../systems/items/recipes.md) | | `RecipeInput` | [recipes](../systems/items/recipes.md) | | `RecipeManager` | [block-entities](../systems/blocks/block-entities.md), [data-driven-types](../systems/foundations/data-driven-types.md), [resource-system](../systems/foundations/resource-system.md), [recipes](../systems/items/recipes.md) | | `RecipeMap` | [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md) | | `RecipePropertySet` | [recipes](../systems/items/recipes.md) | | `RecipeSerializer` | [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md) | | `RecipeSerializers` | [recipes](../systems/items/recipes.md) | | `RecipeType` | [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md) | | `RecipeUnlockedTrigger` | [advancements](../systems/commands/advancements.md) | | `RecordCodecBuilder` | [fanin](../maps/fanin.md) | | `RedStoneOreBlock` | [prediction-and-acks](../systems/client/prediction-and-acks.md) | | `RedstoneSide` | [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `RedstoneTorchBlock` | [signal-and-dust](../systems/blocks/signal-and-dust.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `RedStoneWireBlock` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `RedstoneWireEvaluator` | [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `ReentrantBlockableEventLoop` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [the-client-loop](../systems/client/the-client-loop.md), [server-tick](../systems/server/server-tick.md), [points-of-interest](../systems/world/points-of-interest.md) | | `RegionBitmap` | [chunk-storage](../systems/world/chunk-storage.md) | | `RegionFile` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [chunk-storage](../systems/world/chunk-storage.md) | | `RegionFileStorage` | [chunk-storage](../systems/world/chunk-storage.md) | | `RegionFileVersion` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-storage](../systems/world/chunk-storage.md) | | `RegionStorageInfo` | [chunk-storage](../systems/world/chunk-storage.md) | | `RegistrationInfo` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `Registries` | [fanin](../maps/fanin.md), [density-function-nodes](../reference/density-function-nodes.md), [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [V · Blocks](../systems/blocks/README.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [game-tests](../systems/commands/game-tests.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [attributes](../systems/entities/attributes.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [recipes](../systems/items/recipes.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [creating-a-world](../systems/worldgen/creating-a-world.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `Registry` | [entity-anatomy](../systems/entities/entity-anatomy.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `RegistryAccess` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `RegistryCodecs` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `RegistryDataCollector` | [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [items-and-stacks](../systems/items/items-and-stacks.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `RegistryDataLoader` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md), [protocol-phases](../systems/networking/protocol-phases.md), [starting-a-server](../systems/server/starting-a-server.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `RegistryFileCodec` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [density-functions](../systems/worldgen/density-functions.md) | | `RegistryFixedCodec` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `RegistryFriendlyByteBuf` | [fanin](../maps/fanin.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `RegistryLayer` | [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `RegistryLoadTask` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `RegistryOps` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [text-components](../systems/foundations/text-components.md) | | `RegistrySetBuilder` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `RegistrySynchronization` | [data-components](../systems/foundations/data-components.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `RegistryValidator` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `Relative` | [input-to-movement](../systems/player/input-to-movement.md) | | `ReloadableResourceManager` | [anatomy](../systems/anatomy/anatomy.md), [resource-system](../systems/foundations/resource-system.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `ReloadableServerRegistries` | [naming-drift](../reference/naming-drift.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `ReloadableServerResources` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [data-components](../systems/foundations/data-components.md), [data-driven-types](../systems/foundations/data-driven-types.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [items-and-stacks](../systems/items/items-and-stacks.md), [starting-a-server](../systems/server/starting-a-server.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `ReloadCommand` | [resource-system](../systems/foundations/resource-system.md) | | `ReloadInstance` | [anatomy](../systems/anatomy/anatomy.md), [resource-system](../systems/foundations/resource-system.md) | | `RemoteChatSession` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `RemoteFriendListUpdateHandler` | [X · The client](../systems/client/README.md) | | `RemotePlayer` | [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [player-anatomy](../systems/player/player-anatomy.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `RemoteSampleLogger` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `RemoteSlot` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md) | | `RemoveBinomial` | [enchantments](../systems/items/enchantments.md) | | `RemoveStatusEffectsConsumeEffect` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `RenderBuffers` | [section-meshing](../systems/rendering/section-meshing.md), [the-frame](../systems/rendering/the-frame.md) | | `RenderLayer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `RenderLayerParent` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `RenderPass` | [glossary](../reference/glossary.md), [submit-phases](../reference/submit-phases.md), [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `RenderPassBackend` | [blaze3d](../systems/rendering/blaze3d.md) | | `RenderPipeline` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `RenderPipelines` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [particles](../systems/rendering/particles.md), [post-processing](../systems/rendering/post-processing.md) | | `RenderRegionCache` | [section-meshing](../systems/rendering/section-meshing.md) | | `RenderSectionRegion` | [the-client-level](../systems/client/the-client-level.md), [section-meshing](../systems/rendering/section-meshing.md) | | `RenderSetup` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `RenderShape` | [naming-drift](../reference/naming-drift.md) | | `RenderSystem` | [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [input-and-keybinds](../systems/client/input-and-keybinds.md), [the-client-loop](../systems/client/the-client-loop.md), [blaze3d](../systems/rendering/blaze3d.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [post-processing](../systems/rendering/post-processing.md) | | `RenderTarget` | [blaze3d](../systems/rendering/blaze3d.md) | | `RenderType` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [submit-phases](../reference/submit-phases.md), [blaze3d](../systems/rendering/blaze3d.md), [entity-rendering](../systems/rendering/entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `RenderTypeFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `RenderTypes` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md) | | `Repairable` | [data-components](../systems/foundations/data-components.md) | | `RepairItemRecipe` | [enchanting](../systems/items/enchanting.md) | | `RepeaterBlock` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `RepeatingPlacement` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `ReportedException` | [the-client-loop](../systems/client/the-client-loop.md), [containers-and-menus](../systems/items/containers-and-menus.md), [the-connection](../systems/networking/the-connection.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md) | | `ReportGameListener` | [game-tests](../systems/commands/game-tests.md) | | `ReportingContext` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `ReportType` | [how-a-server-dies](../systems/server/how-a-server-dies.md) | | `RepositorySource` | [resource-system](../systems/foundations/resource-system.md) | | `ResetRaidStatus` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `ResolutionContext` | [naming-drift](../reference/naming-drift.md), [entity-selectors](../systems/commands/entity-selectors.md), [text-components](../systems/foundations/text-components.md) | | `ResolvableProfile` | [text-components](../systems/foundations/text-components.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ResolvedModel` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `Resource` | [resource-system](../systems/foundations/resource-system.md) | | `ResourceFilterSection` | [resource-system](../systems/foundations/resource-system.md) | | `ResourceKey` | [fanin](../maps/fanin.md), [level-data-and-rules](../reference/level-data-and-rules.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [advancements](../systems/commands/advancements.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `ResourceLoadStateTracker` | [resource-system](../systems/foundations/resource-system.md) | | `ResourceManager` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md) | | `ResourceManagerRegistryLoadTask` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `ResourceManagerReloadListener` | [resource-system](../systems/foundations/resource-system.md) | | `ResourceMetadata` | [resource-system](../systems/foundations/resource-system.md) | | `ResourceOrIdArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ResourceOrTagArgument` | [tags](../systems/foundations/tags.md) | | `ResourceOrTagKeyArgument` | [tags](../systems/foundations/tags.md) | | `ResourceSelectorArgument` | [naming-drift](../reference/naming-drift.md), [tags](../systems/foundations/tags.md) | | `ResultContainer` | [recipes](../systems/items/recipes.md) | | `ResultSlot` | [containers-and-menus](../systems/items/containers-and-menus.md), [recipes](../systems/items/recipes.md) | | `ReturnCommand` | [the-execution-engine](../systems/commands/the-execution-engine.md) | | `RootCommandNode` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `RootPlacer` | [data-driven-types](../systems/foundations/data-driven-types.md), [trees](../systems/worldgen/trees.md) | | `RootPlacerType` | [data-driven-types](../systems/foundations/data-driven-types.md), [trees](../systems/worldgen/trees.md) | | `RotatingSectionStorage` | [section-meshing](../systems/rendering/section-meshing.md) | | `Rotation` | [math-and-primitives](../reference/math-and-primitives.md) | | `Rotations` | [math-and-primitives](../reference/math-and-primitives.md) | | `RuinedPortalPiece` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `RuleBlockEntityModifier` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `RuleBlockEntityModifierType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `RuleProcessor` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `RuleTest` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `RuleTestType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `RunFunction` | [functions-and-macros](../systems/commands/functions-and-macros.md) | | `RunningOnDifferentThreadException` | [the-connection](../systems/networking/the-connection.md), [server-tick](../systems/server/server-tick.md) | | `RunOne` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `SampleLogger` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [server-tick](../systems/server/server-tick.md) | | `SamplerCache` | [blaze3d](../systems/rendering/blaze3d.md) | | `SampleStorage` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `SaplingBlock` | [features-and-placement](../systems/worldgen/features-and-placement.md), [trees](../systems/worldgen/trees.md) | | `SavedData` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `SavedDataStorage` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-storage](../systems/world/chunk-storage.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `SavedDataType` | [level-data-and-rules](../reference/level-data-and-rules.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `SavedTick` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ScaleExponentially` | [enchantments](../systems/items/enchantments.md) | | `ScatteredFeaturePiece` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `ScheduleCommand` | [functions-and-macros](../systems/commands/functions-and-macros.md) | | `ScheduledTick` | [server-level-tick](../systems/server/server-level-tick.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ScheduledTickAccess` | [the-client-level](../systems/client/the-client-level.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `Schema` | [fanin](../maps/fanin.md) | | `Scope` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `Score` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `ScoreAccess` | [glossary](../reference/glossary.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `Scoreboard` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ScoreboardCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ScoreboardNameProvider` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `ScoreboardNameProviders` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `ScoreboardSaveData` | [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ScoreContents` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [text-components](../systems/foundations/text-components.md) | | `ScoreHolder` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [text-components](../systems/foundations/text-components.md) | | `ScoreHolderArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `Screen` | [hierarchy](../maps/hierarchy.md), [hud-elements](../reference/hud-elements.md), [naming-drift](../reference/naming-drift.md), [gui-and-screens](../systems/client/gui-and-screens.md), [the-client-loop](../systems/client/the-client-loop.md), [permissions](../systems/commands/permissions.md), [post-processing](../systems/rendering/post-processing.md) | | `ScreenAxis` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ScreenDirection` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ScreenEffectRenderer` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `ScreenNarrationCollector` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ScreenPosition` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ScreenRectangle` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `Screenshot` | [text-components](../systems/foundations/text-components.md), [the-window](../systems/rendering/the-window.md) | | `ScrollWheelHandler` | [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `SculkCatalystBlockEntity` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `SculkSensorBlock` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `SculkSensorBlockEntity` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `SculkSensorPhase` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `SculkShriekerBlock` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `SculkShriekerBlockEntity` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `SecondaryPoiSensor` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `SectionBufferBuilderPack` | [section-meshing](../systems/rendering/section-meshing.md) | | `SectionBufferBuilderPool` | [section-meshing](../systems/rendering/section-meshing.md) | | `SectionCompiler` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [section-meshing](../systems/rendering/section-meshing.md) | | `SectionCopy` | [section-meshing](../systems/rendering/section-meshing.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `SectionOcclusionGraph` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `SectionPos` | [math-and-primitives](../reference/math-and-primitives.md), [IV · The world](../systems/world/README.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [lighting](../systems/world/lighting.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `SectionRenderDispatcher` | [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [the-client-loop](../systems/client/the-client-loop.md), [blaze3d](../systems/rendering/blaze3d.md), [section-meshing](../systems/rendering/section-meshing.md) | | `SectionStorage` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [points-of-interest](../systems/world/points-of-interest.md) | | `SectionTaskDynamicQueue` | [section-meshing](../systems/rendering/section-meshing.md) | | `SectionTracker` | [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md) | | `SectionUpdateTracker` | [section-meshing](../systems/rendering/section-meshing.md), [lighting](../systems/world/lighting.md) | | `SeedCommand` | [permissions](../systems/commands/permissions.md) | | `SeededContainerLoot` | [loot-tables](../systems/items/loot-tables.md) | | `SelectableRecipe` | [recipes](../systems/items/recipes.md) | | `SelectItemModel` | [naming-drift](../reference/naming-drift.md) | | `SelectorContents` | [text-components](../systems/foundations/text-components.md) | | `SelectWorldScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `Sensing` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `Sensor` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `SequenceFeature` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `SequenceFunction` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `SequentialEntry` | [loot-tables](../systems/items/loot-tables.md) | | `SerializableChunkData` | [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md), [points-of-interest](../systems/world/points-of-interest.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [blending](../systems/worldgen/blending.md) | | `SerializableTickContainer` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ServerActivityMonitor` | [server-tick](../systems/server/server-tick.md) | | `ServerAdvancementManager` | [advancements](../systems/commands/advancements.md), [data-driven-types](../systems/foundations/data-driven-types.md), [resource-system](../systems/foundations/resource-system.md) | | `ServerboundAcceptTeleportationPacket` | [input-to-movement](../systems/player/input-to-movement.md) | | `ServerboundAttackPacket` | [naming-drift](../reference/naming-drift.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `ServerboundChangeGameModePacket` | [input-and-keybinds](../systems/client/input-and-keybinds.md) | | `ServerboundChatAckPacket` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ServerboundChatCommandPacket` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ServerboundChatCommandSignedPacket` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ServerboundChatPacket` | [text-components](../systems/foundations/text-components.md) | | `ServerboundChatSessionUpdatePacket` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `ServerboundChunkBatchReceivedPacket` | [threads](../reference/threads.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [players-and-sessions](../systems/server/players-and-sessions.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ServerboundClientCommandPacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ServerboundClientInformationPacket` | [options](../systems/client/options.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundClientTickEndPacket` | [the-client-loop](../systems/client/the-client-loop.md), [input-to-movement](../systems/player/input-to-movement.md) | | `ServerboundCommandSuggestionPacket` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `ServerboundConfigurationAcknowledgedPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundContainerButtonClickPacket` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ServerboundContainerClickPacket` | [containers-and-menus](../systems/items/containers-and-menus.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ServerboundContainerClosePacket` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `ServerboundContainerSlotStateChangedPacket` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ServerboundCookieResponsePacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundCustomClickActionPacket` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ServerboundCustomPayloadPacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ServerboundDebugSubscriptionRequestPacket` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `ServerboundFinishConfigurationPacket` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundHelloPacket` | [naming-drift](../reference/naming-drift.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundInteractPacket` | [naming-drift](../reference/naming-drift.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `ServerboundJigsawGeneratePacket` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `ServerboundKeepAlivePacket` | [threads](../reference/threads.md) | | `ServerboundKeyPacket` | [naming-drift](../reference/naming-drift.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundLoginAcknowledgedPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundMovePlayerPacket` | [authority](../systems/entities/authority.md), [input-to-movement](../systems/player/input-to-movement.md) | | `ServerboundMoveVehiclePacket` | [the-client-level](../systems/client/the-client-level.md), [authority](../systems/entities/authority.md), [input-to-movement](../systems/player/input-to-movement.md) | | `ServerboundPingRequestPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundPlaceRecipePacket` | [recipes](../systems/items/recipes.md) | | `ServerboundPlayerAbilitiesPacket` | [player-anatomy](../systems/player/player-anatomy.md) | | `ServerboundPlayerActionPacket` | [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [using-an-item](../systems/items/using-an-item.md), [the-spear](../systems/player/the-spear.md) | | `ServerboundPlayerCommandPacket` | [input-to-movement](../systems/player/input-to-movement.md) | | `ServerboundPlayerInputPacket` | [naming-drift](../reference/naming-drift.md), [input-to-movement](../systems/player/input-to-movement.md) | | `ServerboundPlayerLoadedPacket` | [players-and-sessions](../systems/server/players-and-sessions.md) | | `ServerboundResourcePackPacket` | [resource-system](../systems/foundations/resource-system.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ServerboundSeenAdvancementsPacket` | [advancements](../systems/commands/advancements.md) | | `ServerboundSelectBundleItemPacket` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `ServerboundSelectKnownPacks` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `ServerboundSetCarriedItemPacket` | [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [containers-and-menus](../systems/items/containers-and-menus.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ServerboundSetCreativeModeSlotPacket` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [containers-and-menus](../systems/items/containers-and-menus.md), [items-and-stacks](../systems/items/items-and-stacks.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ServerboundSetGameRulePacket` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `ServerboundSetJigsawBlockPacket` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `ServerboundStatusRequestPacket` | [protocol-phases](../systems/networking/protocol-phases.md) | | `ServerboundSwingPacket` | [block-interaction](../systems/blocks/block-interaction.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `ServerboundUseItemOnPacket` | [block-interaction](../systems/blocks/block-interaction.md), [prediction-and-acks](../systems/client/prediction-and-acks.md) | | `ServerboundUseItemPacket` | [block-interaction](../systems/blocks/block-interaction.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [using-an-item](../systems/items/using-an-item.md) | | `ServerChunkCache` | [level-data-and-rules](../reference/level-data-and-rules.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [block-entities](../systems/blocks/block-entities.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [input-to-movement](../systems/player/input-to-movement.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [lighting](../systems/world/lighting.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ServerClockManager` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `ServerCommonPacketListenerImpl` | [biggest](../maps/biggest.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [resource-system](../systems/foundations/resource-system.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md) | | `ServerConfigurationPacketListenerImpl` | [protocol-phases](../systems/networking/protocol-phases.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ServerConnectionListener` | [anatomy](../systems/anatomy/anatomy.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md) | | `ServerDebugSubscribers` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [pathfinding](../systems/entities/pathfinding.md), [server-tick](../systems/server/server-tick.md) | | `ServerEntity` | [attributes](../systems/entities/attributes.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [server-level-tick](../systems/server/server-level-tick.md) | | `ServerFunctionLibrary` | [functions-and-macros](../systems/commands/functions-and-macros.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md) | | `ServerFunctionManager` | [functions-and-macros](../systems/commands/functions-and-macros.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [server-tick](../systems/server/server-tick.md) | | `ServerGamePacketListenerImpl` | [biggest](../maps/biggest.md), [level-data-and-rules](../reference/level-data-and-rules.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [XIII · Commands and data packs](../systems/commands/README.md), [advancements](../systems/commands/advancements.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [permissions](../systems/commands/permissions.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [data-components](../systems/foundations/data-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchantments](../systems/items/enchantments.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [protocol-phases](../systems/networking/protocol-phases.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-tick](../systems/server/server-tick.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ServerHandshakePacketListenerImpl` | [threads](../reference/threads.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ServerItemCooldowns` | [using-an-item](../systems/items/using-an-item.md) | | `ServerLevel` | [biggest](../maps/biggest.md), [fanin](../maps/fanin.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [naming-drift](../reference/naming-drift.md), [threads](../reference/threads.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [entity-selectors](../systems/commands/entity-selectors.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [the-execution-engine](../systems/commands/the-execution-engine.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [pathfinding](../systems/entities/pathfinding.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [player-anatomy](../systems/player/player-anatomy.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [particles](../systems/rendering/particles.md), [III · The server](../systems/server/README.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-anatomy](../systems/world/chunk-anatomy.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [fluids](../systems/world/fluids.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md), [points-of-interest](../systems/world/points-of-interest.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [tickets-and-loading](../systems/world/tickets-and-loading.md), [features-and-placement](../systems/worldgen/features-and-placement.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `ServerLevelAccessor` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `ServerLevelData` | [level-data-and-rules](../reference/level-data-and-rules.md), [starting-a-server](../systems/server/starting-a-server.md) | | `ServerLinks` | [starting-a-server](../systems/server/starting-a-server.md) | | `ServerLinksDialog` | [dialogs](../systems/commands/dialogs.md) | | `ServerLoginPacketListenerImpl` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ServerOpListEntry` | [naming-drift](../reference/naming-drift.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ServerPackCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [resource-system](../systems/foundations/resource-system.md) | | `ServerPacketListener` | [server-tick](../systems/server/server-tick.md) | | `ServerPackManager` | [resource-system](../systems/foundations/resource-system.md) | | `ServerPacksSource` | [resource-system](../systems/foundations/resource-system.md), [starting-a-server](../systems/server/starting-a-server.md) | | `ServerPlaceRecipe` | [recipes](../systems/items/recipes.md) | | `ServerPlayer` | [lectures](../lectures.md), [biggest](../maps/biggest.md), [packages](../maps/packages.md), [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [block-breaking](../systems/blocks/block-breaking.md), [block-entities](../systems/blocks/block-entities.md), [block-interaction](../systems/blocks/block-interaction.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [XIII · Commands and data packs](../systems/commands/README.md), [advancements](../systems/commands/advancements.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [dialogs](../systems/commands/dialogs.md), [entity-selectors](../systems/commands/entity-selectors.md), [permissions](../systems/commands/permissions.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md), [text-components](../systems/foundations/text-components.md), [containers-and-menus](../systems/items/containers-and-menus.md), [enchanting](../systems/items/enchanting.md), [enchantments](../systems/items/enchantments.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md), [IX · Networking](../systems/networking/README.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [protocol-phases](../systems/networking/protocol-phases.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [VIII · The player](../systems/player/README.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [input-to-movement](../systems/player/input-to-movement.md), [player-anatomy](../systems/player/player-anatomy.md), [status-effects](../systems/player/status-effects.md), [the-spear](../systems/player/the-spear.md), [the-sword-swing](../systems/player/the-sword-swing.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ServerPlayerGameMode` | [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [prediction-and-acks](../systems/client/prediction-and-acks.md), [items-and-stacks](../systems/items/items-and-stacks.md), [loot-tables](../systems/items/loot-tables.md), [using-an-item](../systems/items/using-an-item.md), [player-anatomy](../systems/player/player-anatomy.md), [the-two-phase-tick](../systems/player/the-two-phase-tick.md) | | `ServerRecipeBook` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [recipes](../systems/items/recipes.md), [player-anatomy](../systems/player/player-anatomy.md) | | `ServerResourcePackConfigurationTask` | [resource-system](../systems/foundations/resource-system.md) | | `ServerScoreboard` | [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `ServerStatsCounter` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md) | | `ServerStatus` | [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md) | | `ServerStatusPacketListenerImpl` | [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ServerStatusPinger` | [entity-selectors](../systems/commands/entity-selectors.md), [text-components](../systems/foundations/text-components.md), [protocol-phases](../systems/networking/protocol-phases.md), [the-connection](../systems/networking/the-connection.md) | | `ServerTextFilter` | [starting-a-server](../systems/server/starting-a-server.md) | | `ServerTickRateManager` | [anatomy](../systems/anatomy/anatomy.md), [server-tick](../systems/server/server-tick.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `ServerWatchdog` | [glossary](../reference/glossary.md), [threads](../reference/threads.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [starting-a-server](../systems/server/starting-a-server.md) | | `ServerWaypointManager` | [level-data-and-rules](../reference/level-data-and-rules.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [attributes](../systems/entities/attributes.md) | | `Services` | [starting-a-server](../systems/server/starting-a-server.md) | | `SetAttributesFunction` | [attributes](../systems/entities/attributes.md) | | `SetEnchantmentsFunction` | [enchanting](../systems/items/enchanting.md) | | `SetHiddenState` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `SetItemCountFunction` | [naming-drift](../reference/naming-drift.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `SetOnceOptionState` | [entity-selectors](../systems/commands/entity-selectors.md) | | `Settings` | [starting-a-server](../systems/server/starting-a-server.md) | | `SetValue` | [enchantments](../systems/items/enchantments.md) | | `SetWalkTargetFromBlockMemory` | [points-of-interest](../systems/world/points-of-interest.md) | | `ShaderDefines` | [blaze3d](../systems/rendering/blaze3d.md) | | `ShaderManager` | [resource-system](../systems/foundations/resource-system.md), [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `ShaderSource` | [blaze3d](../systems/rendering/blaze3d.md) | | `ShadowFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `ShapedRecipe` | [naming-drift](../reference/naming-drift.md), [recipes](../systems/items/recipes.md) | | `ShapedRecipePattern` | [recipes](../systems/items/recipes.md) | | `ShapelessRecipe` | [recipes](../systems/items/recipes.md) | | `ShapeOutlineFeatureRenderer` | [submit-phases](../reference/submit-phases.md) | | `Shapes` | [math-and-primitives](../reference/math-and-primitives.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [pathfinding](../systems/entities/pathfinding.md), [fluids](../systems/world/fluids.md) | | `SharedConstants` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [block-breaking](../systems/blocks/block-breaking.md), [debugging-the-running-game](../systems/client/debugging-the-running-game.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [dialogs](../systems/commands/dialogs.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [the-connection](../systems/networking/the-connection.md), [starting-a-server](../systems/server/starting-a-server.md), [blending](../systems/worldgen/blending.md), [creating-a-world](../systems/worldgen/creating-a-world.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [terrain](../systems/worldgen/terrain.md) | | `Shearable` | [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `ShearsItem` | [block-breaking](../systems/blocks/block-breaking.md) | | `Sheep` | [VI · Entities](../systems/entities/README.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [data-components](../systems/foundations/data-components.md) | | `SheepRenderer` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `SheepRenderState` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `SheepWoolLayer` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `SheepWoolUndercoatLayer` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `ShelfRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ShieldItem` | [damage-and-death](../systems/entities/damage-and-death.md) | | `ShipwreckPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `ShovelItem` | [data-components](../systems/foundations/data-components.md) | | `Shulker` | [the-client-level](../systems/client/the-client-level.md), [movement-and-collision](../systems/entities/movement-and-collision.md) | | `ShulkerBoxBlock` | [loot-tables](../systems/items/loot-tables.md) | | `ShulkerBoxBlockEntity` | [block-entities](../systems/blocks/block-entities.md) | | `ShulkerBoxSpecialRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ShulkerBullet` | [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `SignableCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `SignalGetter` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [pistons-and-block-events](../systems/blocks/pistons-and-block-events.md), [signal-and-dust](../systems/blocks/signal-and-dust.md) | | `SignBlockEntity` | [block-entities](../systems/blocks/block-entities.md), [dialogs](../systems/commands/dialogs.md), [permissions](../systems/commands/permissions.md) | | `SignedArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `SignedMessageBody` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `SignedMessageChain` | [naming-drift](../reference/naming-drift.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `SignedMessageLink` | [naming-drift](../reference/naming-drift.md), [chat-and-signing](../systems/networking/chat-and-signing.md) | | `SignedMessageValidator` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `SignRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SignText` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SilentInitException` | [the-client-loop](../systems/client/the-client-loop.md) | | `SimpleBitStorage` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `SimpleContainer` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `SimpleCriterionTrigger` | [advancements](../systems/commands/advancements.md) | | `SimpleDialog` | [dialogs](../systems/commands/dialogs.md) | | `SimpleFeatureRenderPhase` | [submit-phases](../reference/submit-phases.md) | | `SimpleJsonResourceReloadListener` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [resource-system](../systems/foundations/resource-system.md) | | `SimpleModelWrapper` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SimplePreparableReloadListener` | [resource-system](../systems/foundations/resource-system.md), [recipes](../systems/items/recipes.md), [post-processing](../systems/rendering/post-processing.md) | | `SimpleRandomSelectorFeature` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `SimpleRegionStorage` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [chunk-storage](../systems/world/chunk-storage.md), [blending](../systems/worldgen/blending.md) | | `SimpleReloadInstance` | [resource-system](../systems/foundations/resource-system.md) | | `SimpleSoundInstance` | [sound-engine](../systems/client/sound-engine.md) | | `SimpleUnboundProtocol` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `SimpleWaterloggedBlock` | [fluids](../systems/world/fluids.md) | | `SimplexNoise` | [density-functions](../systems/worldgen/density-functions.md) | | `SimulationChunkTracker` | [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `SingleEnchantment` | [enchanting](../systems/items/enchanting.md) | | `SingleFile` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SingleOptionInput` | [dialogs](../systems/commands/dialogs.md) | | `SinglePieceStructure` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `SinglePoolElement` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `SingleQuadParticle` | [naming-drift](../reference/naming-drift.md), [submit-phases](../reference/submit-phases.md), [particles](../systems/rendering/particles.md) | | `SingleSpriteSource` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `SingleThreadedRandomSource` | [math-and-primitives](../reference/math-and-primitives.md) | | `SingleTickProfiler` | [the-client-loop](../systems/client/the-client-loop.md), [server-tick](../systems/server/server-tick.md) | | `SingletonArgumentInfo` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `SingleValuePalette` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `SingleVariant` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SkinManager` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `SkinTextureDownloader` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `SkipPacketDecoderException` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `SkipPacketEncoderException` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `SkipPacketException` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `SkullBlockRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SkyLightEngine` | [lighting](../systems/world/lighting.md) | | `SkyLightSectionStorage` | [lighting](../systems/world/lighting.md) | | `SkyRenderer` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `SkyRenderState` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `SleepInBed` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [points-of-interest](../systems/world/points-of-interest.md) | | `SleepStatus` | [server-level-tick](../systems/server/server-level-tick.md) | | `SliceShape` | [math-and-primitives](../reference/math-and-primitives.md) | | `Slot` | [containers-and-menus](../systems/items/containers-and-menus.md) | | `SlotDisplay` | [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `SlotDisplayContext` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [recipes](../systems/items/recipes.md) | | `SlotLoot` | [data-driven-types](../systems/foundations/data-driven-types.md), [loot-tables](../systems/items/loot-tables.md) | | `SlotProvider` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `SlotSource` | [data-driven-types](../systems/foundations/data-driven-types.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `SlotSources` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `SmithingMenu` | [recipes](../systems/items/recipes.md) | | `SmoothDouble` | [input-to-movement](../systems/player/input-to-movement.md) | | `SnbtDatafixer` | [anatomy](../systems/anatomy/anatomy.md) | | `SnbtGrammar` | [naming-drift](../reference/naming-drift.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `SnbtOperations` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `SnowGolem` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `Sound` | [sound-engine](../systems/client/sound-engine.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `SoundBuffer` | [sound-engine](../systems/client/sound-engine.md) | | `SoundBufferLibrary` | [sound-engine](../systems/client/sound-engine.md) | | `SoundEngine` | [threads](../reference/threads.md), [sound-engine](../systems/client/sound-engine.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `SoundEngineExecutor` | [threads](../reference/threads.md), [sound-engine](../systems/client/sound-engine.md) | | `SoundEvent` | [fanin](../maps/fanin.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `SoundEventListener` | [sound-engine](../systems/client/sound-engine.md) | | `SoundEventRegistration` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `SoundEvents` | [biggest](../maps/biggest.md), [fanin](../maps/fanin.md), [non-living-damage](../reference/non-living-damage.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `SoundInstance` | [sound-engine](../systems/client/sound-engine.md) | | `SoundManager` | [sound-engine](../systems/client/sound-engine.md), [the-client-loop](../systems/client/the-client-loop.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md), [resource-system](../systems/foundations/resource-system.md) | | `SoundPreviewHandler` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `SoundSource` | [sound-engine](../systems/client/sound-engine.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `SoundType` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `SourceFilter` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SpacerElement` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `SpatialAttributeInterpolator` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [biomes](../systems/worldgen/biomes.md) | | `SpatialLongSet` | [lighting](../systems/world/lighting.md) | | `SpawnArmorTrimsCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `SpawnCondition` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `SpawnEggItem` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `SpawnerBlockEntity` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `SpawnerRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SpawnParticlesEffect` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `SpawnPlacements` | [glossary](../reference/glossary.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `SpawnPlacementType` | [naming-drift](../reference/naming-drift.md) | | `SpawnPlacementTypes` | [naming-drift](../reference/naming-drift.md) | | `SpawnPrioritySelectors` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `SpearAnimations` | [the-spear](../systems/player/the-spear.md) | | `SpearApproach` | [the-spear](../systems/player/the-spear.md) | | `SpearAttack` | [the-spear](../systems/player/the-spear.md) | | `SpearRetreat` | [the-spear](../systems/player/the-spear.md) | | `SpearUseGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [the-spear](../systems/player/the-spear.md) | | `SpecialBlockModelWrapper` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SpecialDates` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SpecialModelRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SpecialModelRenderers` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SpecialModelWrapper` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `SpectatorGui` | [hud-elements](../reference/hud-elements.md) | | `SpeleothemBlock` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `Spider` | [post-processing](../systems/rendering/post-processing.md) | | `SplashManager` | [resource-system](../systems/foundations/resource-system.md) | | `SpriteContents` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SpriteId` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SpriteLoader` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SpriteSet` | [particles](../systems/rendering/particles.md) | | `SpriteSourceList` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SpriteSources` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `SpruceFoliagePlacer` | [trees](../systems/worldgen/trees.md) | | `SpvSampler` | [blaze3d](../systems/rendering/blaze3d.md) | | `SpvUniformBuffer` | [blaze3d](../systems/rendering/blaze3d.md) | | `SpyglassItem` | [using-an-item](../systems/items/using-an-item.md) | | `StackedContents` | [recipes](../systems/items/recipes.md) | | `StackedItemContents` | [recipes](../systems/items/recipes.md) | | `StagedVertexBuffer` | [submit-phases](../reference/submit-phases.md), [blaze3d](../systems/rendering/blaze3d.md), [the-frame](../systems/rendering/the-frame.md) | | `StagingBuffer` | [blaze3d](../systems/rendering/blaze3d.md), [section-meshing](../systems/rendering/section-meshing.md) | | `StairBlock` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `StairsShape` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `Stat` | [glossary](../reference/glossary.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `StateDefinition` | [glossary](../reference/glossary.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [fluids](../systems/world/fluids.md) | | `StateHolder` | [V · Blocks](../systems/blocks/README.md), [block-interaction](../systems/blocks/block-interaction.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [fluids](../systems/world/fluids.md) | | `StaticCache2D` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md) | | `Stats` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [block-breaking](../systems/blocks/block-breaking.md), [block-interaction](../systems/blocks/block-interaction.md), [damage-and-death](../systems/entities/damage-and-death.md), [items-and-stacks](../systems/items/items-and-stacks.md), [recipes](../systems/items/recipes.md), [using-an-item](../systems/items/using-an-item.md) | | `StatType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `StatusProtocols` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `Std140Builder` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `Std140SizeCalculator` | [blaze3d](../systems/rendering/blaze3d.md) | | `Stitcher` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `StitcherException` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `StonecutterMenu` | [recipes](../systems/items/recipes.md) | | `StonecutterRecipe` | [recipes](../systems/items/recipes.md) | | `StopCommand` | [how-a-server-dies](../systems/server/how-a-server-dies.md) | | `Stopwatches` | [level-data-and-rules](../reference/level-data-and-rules.md), [starting-a-server](../systems/server/starting-a-server.md) | | `StorageDataAccessor` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `StorageDataSource` | [text-components](../systems/foundations/text-components.md) | | `StraightTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `Strategy` | [naming-drift](../reference/naming-drift.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `StreamCodec` | [fanin](../maps/fanin.md), [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [synched-entity-data](../systems/entities/synched-entity-data.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-driven-types](../systems/foundations/data-driven-types.md), [recipes](../systems/items/recipes.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `StreamDecoder` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `StreamEncoder` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `StreamMemberEncoder` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `StreamTagVisitor` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `StrictJsonParser` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `Strider` | [pathfinding](../systems/entities/pathfinding.md) | | `StringDecomposer` | [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `StringReader` | [advancements](../systems/commands/advancements.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `StringSplitter` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `StringTag` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `StringTemplate` | [dialogs](../systems/commands/dialogs.md), [functions-and-macros](../systems/commands/functions-and-macros.md) | | `StringUtil` | [chat-and-signing](../systems/networking/chat-and-signing.md) | | `StrollAroundPoi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `StrollToPoi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `StrollToPoiList` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `StrongholdPieces` | [biggest](../maps/biggest.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `StrongholdStructure` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `Structure` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [data-driven-types](../systems/foundations/data-driven-types.md), [biomes](../systems/worldgen/biomes.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `StructureAccess` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `StructureCheck` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `StructureCheckResult` | [structure-placement](../systems/worldgen/structure-placement.md) | | `StructureGridSpawner` | [game-tests](../systems/commands/game-tests.md) | | `StructureManager` | [structure-placement](../systems/worldgen/structure-placement.md) | | `StructurePiece` | [loot-tables](../systems/items/loot-tables.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `StructurePieceAccessor` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `StructurePiecesBuilder` | [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `StructurePieceSerializationContext` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `StructurePieceType` | [glossary](../reference/glossary.md), [data-driven-types](../systems/foundations/data-driven-types.md) | | `StructurePlacement` | [data-driven-types](../systems/foundations/data-driven-types.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `StructurePlacementType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `StructurePlaceSettings` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `StructurePoolElement` | [data-driven-types](../systems/foundations/data-driven-types.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `StructurePoolElementType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `StructureProcessor` | [data-driven-types](../systems/foundations/data-driven-types.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `StructureProcessorList` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `StructureSet` | [structure-placement](../systems/worldgen/structure-placement.md) | | `StructureStart` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `StructureTags` | [tags](../systems/foundations/tags.md) | | `StructureTemplate` | [block-entities](../systems/blocks/block-entities.md), [hand-built-structures](../systems/worldgen/hand-built-structures.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md), [trees](../systems/worldgen/trees.md) | | `StructureTemplateManager` | [resource-system](../systems/foundations/resource-system.md), [starting-a-server](../systems/server/starting-a-server.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `StructureTemplatePool` | [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `StructureType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `StructureUtils` | [game-tests](../systems/commands/game-tests.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `Style` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `StyleArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `SubmitNodeCollection` | [submit-phases](../reference/submit-phases.md), [text-and-fonts](../systems/client/text-and-fonts.md), [entity-rendering](../systems/rendering/entity-rendering.md), [post-processing](../systems/rendering/post-processing.md) | | `SubmitNodeCollector` | [naming-drift](../reference/naming-drift.md), [submit-phases](../reference/submit-phases.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [entity-rendering](../systems/rendering/entity-rendering.md), [the-frame](../systems/rendering/the-frame.md) | | `SubmitNodeStorage` | [glossary](../reference/glossary.md), [naming-drift](../reference/naming-drift.md), [entity-rendering](../systems/rendering/entity-rendering.md), [the-frame](../systems/rendering/the-frame.md) | | `SubStringSource` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `SubtitleOverlay` | [hud-elements](../reference/hud-elements.md), [sound-engine](../systems/client/sound-engine.md) | | `SuggestionProviders` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `SuggestionSupplier` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `SulfurCube` | [synched-entity-data](../systems/entities/synched-entity-data.md) | | `SummonCommand` | [entity-anatomy](../systems/entities/entity-anatomy.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `SummonEntityEffect` | [enchantments](../systems/items/enchantments.md) | | `SuppressedExceptionCollector` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md) | | `SurfaceRuleData` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `SurfaceRules` | [data-driven-types](../systems/foundations/data-driven-types.md), [terrain](../systems/worldgen/terrain.md) | | `SurfaceSystem` | [density-functions](../systems/worldgen/density-functions.md), [terrain](../systems/worldgen/terrain.md) | | `SuspiciousStewEffects` | [hunger-and-experience](../systems/player/hunger-and-experience.md), [status-effects](../systems/player/status-effects.md) | | `SwampHutPiece` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `SweetBerryBushBlock` | [authority](../systems/entities/authority.md) | | `SwimNodeEvaluator` | [pathfinding](../systems/entities/pathfinding.md) | | `SwingAnimation` | [data-components](../systems/foundations/data-components.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `SwingAnimationType` | [the-spear](../systems/player/the-spear.md) | | `SwizzleArgument` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `SyncedDataHolder` | [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `SynchedEntityData` | [entity-anatomy](../systems/entities/entity-anatomy.md), [synched-entity-data](../systems/entities/synched-entity-data.md) | | `SynchronizeRegistriesTask` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `TabOrderedElement` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `Tag` | [glossary](../reference/glossary.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [text-components](../systems/foundations/text-components.md) | | `TagBuilder` | [tags](../systems/foundations/tags.md) | | `TagEntry` | [tags](../systems/foundations/tags.md), [loot-tables](../systems/items/loot-tables.md) | | `TagFile` | [tags](../systems/foundations/tags.md) | | `TagKey` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [tags](../systems/foundations/tags.md) | | `TagLoader` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [starting-a-server](../systems/server/starting-a-server.md) | | `TagNetworkSerialization` | [tags](../systems/foundations/tags.md) | | `TagParser` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `TagsProvider` | [tags](../systems/foundations/tags.md) | | `TagType` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `TagValueInput` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `TagValueOutput` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `Target` | [pathfinding](../systems/entities/pathfinding.md) | | `TargetBlock` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `TargetedConditionalEffect` | [enchantments](../systems/items/enchantments.md) | | `Targeting` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `TargetingConditions` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `TaskScheduler` | [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `Team` | [scoreboard-and-data](../systems/commands/scoreboard-and-data.md), [text-components](../systems/foundations/text-components.md) | | `TeamColor` | [naming-drift](../reference/naming-drift.md), [scoreboard-and-data](../systems/commands/scoreboard-and-data.md) | | `TeamColorArgument` | [naming-drift](../reference/naming-drift.md) | | `TeamCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `TeamMsgCommand` | [entity-selectors](../systems/commands/entity-selectors.md) | | `TelemetryEventType` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `TelemetryProperty` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `TeleportRandomlyConsumeEffect` | [hunger-and-experience](../systems/player/hunger-and-experience.md) | | `TeleportTransition` | [players-and-sessions](../systems/server/players-and-sessions.md) | | `TemplateStructurePiece` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `TemptGoal` | [pathfinding](../systems/entities/pathfinding.md) | | `Term` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `TerrainAdjustment` | [structure-placement](../systems/worldgen/structure-placement.md) | | `TerrainParticle` | [models-and-atlases](../systems/rendering/models-and-atlases.md), [particles](../systems/rendering/particles.md) | | `TerrainProvider` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `TestBlock` | [game-tests](../systems/commands/game-tests.md) | | `TestBlockEditScreen` | [game-tests](../systems/commands/game-tests.md) | | `TestBlockMode` | [game-tests](../systems/commands/game-tests.md) | | `TestData` | [naming-drift](../reference/naming-drift.md), [game-tests](../systems/commands/game-tests.md) | | `TestEnvironmentDefinition` | [naming-drift](../reference/naming-drift.md), [functions-and-macros](../systems/commands/functions-and-macros.md), [game-tests](../systems/commands/game-tests.md), [data-driven-types](../systems/foundations/data-driven-types.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `TestFinder` | [naming-drift](../reference/naming-drift.md) | | `TestFunctionLoader` | [naming-drift](../reference/naming-drift.md) | | `TestInstanceBlock` | [naming-drift](../reference/naming-drift.md) | | `TestInstanceBlockEditScreen` | [game-tests](../systems/commands/game-tests.md) | | `TestInstanceBlockEntity` | [naming-drift](../reference/naming-drift.md), [game-tests](../systems/commands/game-tests.md) | | `TestInstanceRenderer` | [game-tests](../systems/commands/game-tests.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `TextColor` | [naming-drift](../reference/naming-drift.md), [text-components](../systems/foundations/text-components.md) | | `TextComponentTagVisitor` | [text-components](../systems/foundations/text-components.md) | | `TextFeatureRenderer` | [submit-phases](../reference/submit-phases.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `TextInput` | [dialogs](../systems/commands/dialogs.md) | | `TextInputManager` | [the-client-loop](../systems/client/the-client-loop.md), [the-window](../systems/rendering/the-window.md) | | `TextRenderable` | [text-and-fonts](../systems/client/text-and-fonts.md) | | `TextureAtlas` | [blaze3d](../systems/rendering/blaze3d.md), [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `TextureManager` | [the-client-loop](../systems/client/the-client-loop.md), [resource-system](../systems/foundations/resource-system.md), [models-and-atlases](../systems/rendering/models-and-atlases.md), [the-window](../systems/rendering/the-window.md) | | `TextureSlots` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `TextureTarget` | [blaze3d](../systems/rendering/blaze3d.md), [post-processing](../systems/rendering/post-processing.md) | | `TextureTransform` | [blaze3d](../systems/rendering/blaze3d.md) | | `TheEndBiomeSource` | [biomes](../systems/worldgen/biomes.md) | | `TheEndGatewayRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `ThreadedLevelLightEngine` | [how-a-server-dies](../systems/server/how-a-server-dies.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [lighting](../systems/world/lighting.md) | | `ThreadingDetector` | [math-and-primitives](../reference/math-and-primitives.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `ThreadSafeLegacyRandomSource` | [math-and-primitives](../reference/math-and-primitives.md) | | `ThreeLayersFeatureSize` | [trees](../systems/worldgen/trees.md) | | `ThrottlingChunkTaskDispatcher` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `ThrownTrident` | [enchantments](../systems/items/enchantments.md) | | `TickablePacketListener` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [the-connection](../systems/networking/the-connection.md), [server-tick](../systems/server/server-tick.md) | | `TickableSoundInstance` | [sound-engine](../systems/client/sound-engine.md) | | `TickableTexture` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `TickAccess` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `TickCommand` | [server-tick](../systems/server/server-tick.md) | | `TickContainerAccess` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `Ticket` | [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `TicketStorage` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [what-the-client-is-told](../systems/networking/what-the-client-is-told.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-level-tick](../systems/server/server-level-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `TicketType` | [glossary](../reference/glossary.md), [protocol-phases](../systems/networking/protocol-phases.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [players-and-sessions](../systems/server/players-and-sessions.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `TickingBlockEntity` | [block-entities](../systems/blocks/block-entities.md), [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `TickPriority` | [diodes-and-observers](../systems/blocks/diodes-and-observers.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `TickRateManager` | [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [block-entities](../systems/blocks/block-entities.md), [the-client-loop](../systems/client/the-client-loop.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [input-to-movement](../systems/player/input-to-movement.md), [block-entity-rendering](../systems/rendering/block-entity-rendering.md), [the-frame](../systems/rendering/the-frame.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `TickTask` | [server-tick](../systems/server/server-tick.md) | | `TickThrottler` | [chat-and-signing](../systems/networking/chat-and-signing.md), [input-to-movement](../systems/player/input-to-movement.md) | | `TiledBlitRenderState` | [the-gui-render-tree](../systems/client/the-gui-render-tree.md) | | `TimeCommand` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `Timeline` | [naming-drift](../reference/naming-drift.md), [VI · Entities](../systems/entities/README.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `Timelines` | [naming-drift](../reference/naming-drift.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [points-of-interest](../systems/world/points-of-interest.md) | | `TimelineTags` | [tags](../systems/foundations/tags.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `TimerQueue` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [server-level-tick](../systems/server/server-level-tick.md), [server-tick](../systems/server/server-tick.md) | | `TlsfAllocator` | [section-meshing](../systems/rendering/section-meshing.md) | | `ToggleKeyMapping` | [input-and-keybinds](../systems/client/input-and-keybinds.md), [input-to-movement](../systems/player/input-to-movement.md) | | `Tool` | [block-breaking](../systems/blocks/block-breaking.md), [data-components](../systems/foundations/data-components.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `ToolMaterial` | [block-breaking](../systems/blocks/block-breaking.md) | | `Tooltip` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `TooltipDisplay` | [data-components](../systems/foundations/data-components.md) | | `TpsDebugChart` | [hud](../systems/client/hud.md) | | `TpsDebugDimensions` | [server-tick](../systems/server/server-tick.md) | | `TraceableEntity` | [entity-anatomy](../systems/entities/entity-anatomy.md) | | `TrackingDebugSynchronizer` | [debugging-the-running-game](../systems/client/debugging-the-running-game.md) | | `TrackingEmitter` | [particles](../systems/rendering/particles.md) | | `TracyZoneFiller` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md) | | `TradeSet` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [loot-tables](../systems/items/loot-tables.md) | | `Transformation` | [math-and-primitives](../reference/math-and-primitives.md) | | `TransientBlockAllocator` | [blaze3d](../systems/rendering/blaze3d.md) | | `TransientCraftingContainer` | [containers-and-menus](../systems/items/containers-and-menus.md), [recipes](../systems/items/recipes.md) | | `TransientEntitySectionManager` | [the-client-level](../systems/client/the-client-level.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `TransientMemory` | [blaze3d](../systems/rendering/blaze3d.md) | | `TranslatableContents` | [text-and-fonts](../systems/client/text-and-fonts.md), [text-components](../systems/foundations/text-components.md) | | `TranslatableFormatException` | [text-components](../systems/foundations/text-components.md) | | `TranslucencyPointOfView` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `TranslucentFeatureRenderPhase` | [submit-phases](../reference/submit-phases.md) | | `TransmuteRecipe` | [recipes](../systems/items/recipes.md) | | `TreeConfiguration` | [naming-drift](../reference/naming-drift.md), [trees](../systems/worldgen/trees.md) | | `TreeDecorator` | [data-driven-types](../systems/foundations/data-driven-types.md), [trees](../systems/worldgen/trees.md) | | `TreeDecoratorType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `TreeFeature` | [trees](../systems/worldgen/trees.md) | | `TreeFeatures` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [trees](../systems/worldgen/trees.md) | | `TreeGrower` | [naming-drift](../reference/naming-drift.md), [trees](../systems/worldgen/trees.md) | | `TreeNodePosition` | [advancements](../systems/commands/advancements.md) | | `TrialSpawner` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `TrialSpawnerStateData` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `TridentItem` | [enchantments](../systems/items/enchantments.md), [using-an-item](../systems/items/using-an-item.md) | | `TridentSpecialRenderer` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `TriggerCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `TripWireBlock` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `TrunkPlacer` | [data-driven-types](../systems/foundations/data-driven-types.md), [tags](../systems/foundations/tags.md), [trees](../systems/worldgen/trees.md) | | `TrunkPlacerType` | [data-driven-types](../systems/foundations/data-driven-types.md) | | `TrunkVineDecorator` | [trees](../systems/worldgen/trees.md) | | `TryFindWaterGoal` | [pathfinding](../systems/entities/pathfinding.md) | | `Tutorial` | [the-client-loop](../systems/client/the-client-loop.md) | | `TwoLayersFeatureSize` | [trees](../systems/worldgen/trees.md) | | `TypedDataComponent` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md) | | `TypedEntityData` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [data-components](../systems/foundations/data-components.md) | | `TypedInstance` | [entity-anatomy](../systems/entities/entity-anatomy.md), [tags](../systems/foundations/tags.md), [items-and-stacks](../systems/items/items-and-stacks.md) | | `UberGpuBuffer` | [naming-drift](../reference/naming-drift.md), [blaze3d](../systems/rendering/blaze3d.md), [section-meshing](../systems/rendering/section-meshing.md) | | `UiLightmap` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `UnbakedCuboidGeometry` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `UnbakedGeometry` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `UnbakedGlyph` | [naming-drift](../reference/naming-drift.md), [text-and-fonts](../systems/client/text-and-fonts.md) | | `UnboundEntryAction` | [functions-and-macros](../systems/commands/functions-and-macros.md) | | `UnboundProtocol` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `UnconfiguredPipelineHandler` | [the-connection](../systems/networking/the-connection.md) | | `UndeadRenderState` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `UnderwaterAmbientSoundHandler` | [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `Uniform` | [post-processing](../systems/rendering/post-processing.md) | | `UniformGenerator` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `UniformValue` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [post-processing](../systems/rendering/post-processing.md) | | `Unit` | [math-and-primitives](../reference/math-and-primitives.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md), [enchantments](../systems/items/enchantments.md) | | `Unstitcher` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `UpdateActivityFromSchedule` | [points-of-interest](../systems/world/points-of-interest.md) | | `UpgradeData` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `UpwardsBranchingTrunkPlacer` | [trees](../systems/worldgen/trees.md) | | `UseBonemeal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `UseCooldown` | [using-an-item](../systems/items/using-an-item.md) | | `UseDuration` | [using-an-item](../systems/items/using-an-item.md) | | `UseEffects` | [using-an-item](../systems/items/using-an-item.md), [hunger-and-experience](../systems/player/hunger-and-experience.md), [the-spear](../systems/player/the-spear.md) | | `UseOnContext` | [blocks-and-states](../systems/blocks/blocks-and-states.md) | | `UseRemainder` | [items-and-stacks](../systems/items/items-and-stacks.md) | | `UserNameToIdResolver` | [players-and-sessions](../systems/server/players-and-sessions.md) | | `Utf8String` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `Util` | [packages](../maps/packages.md), [level-data-and-rules](../reference/level-data-and-rules.md), [math-and-primitives](../reference/math-and-primitives.md), [threads](../reference/threads.md), [anatomy](../systems/anatomy/anatomy.md), [sound-engine](../systems/client/sound-engine.md), [the-client-loop](../systems/client/the-client-loop.md), [entity-selectors](../systems/commands/entity-selectors.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [recipes](../systems/items/recipes.md), [chat-and-signing](../systems/networking/chat-and-signing.md), [section-meshing](../systems/rendering/section-meshing.md), [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md), [how-a-server-dies](../systems/server/how-a-server-dies.md), [server-tick](../systems/server/server-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md), [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `UUIDUtil` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [protocol-phases](../systems/networking/protocol-phases.md) | | `Validatable` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchantments](../systems/items/enchantments.md) | | `ValidateNearbyPoi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [points-of-interest](../systems/world/points-of-interest.md) | | `ValidationContext` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ValidationContextSource` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `ValueInput` | [block-entities](../systems/blocks/block-entities.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ValueInputContextHelper` | [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `ValueOutput` | [naming-drift](../reference/naming-drift.md), [codecs-nbt-json](../systems/foundations/codecs-nbt-json.md) | | `VanillaEnchantmentProviders` | [enchanting](../systems/items/enchanting.md) | | `VanillaPackResources` | [resource-system](../systems/foundations/resource-system.md) | | `VanillaRegistries` | [what-this-book-skips](../systems/anatomy/what-this-book-skips.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `VariantSelector` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `VarInt` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `Varint21FrameDecoder` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md), [the-connection](../systems/networking/the-connection.md) | | `VarLong` | [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `VaultBlockEntity` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md) | | `VaultRenderState` | [block-entity-rendering](../systems/rendering/block-entity-rendering.md) | | `Vec2` | [math-and-primitives](../reference/math-and-primitives.md), [input-to-movement](../systems/player/input-to-movement.md) | | `Vec3` | [fanin](../maps/fanin.md), [math-and-primitives](../reference/math-and-primitives.md), [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [packets-and-stream-codecs](../systems/networking/packets-and-stream-codecs.md) | | `Vec3Argument` | [glossary](../reference/glossary.md), [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `Vec3i` | [math-and-primitives](../reference/math-and-primitives.md) | | `VecDeltaCodec` | [what-the-client-is-told](../systems/networking/what-the-client-is-told.md) | | `VehicleEntity` | [hierarchy](../maps/hierarchy.md), [non-living-damage](../reference/non-living-damage.md), [damage-and-death](../systems/entities/damage-and-death.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `VersionCommand` | [permissions](../systems/commands/permissions.md) | | `VertexConsumer` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `VertexFormat` | [blaze3d](../systems/rendering/blaze3d.md) | | `VertexFormatElement` | [blaze3d](../systems/rendering/blaze3d.md) | | `VerticalAnchor` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `Vex` | [pathfinding](../systems/entities/pathfinding.md) | | `VibrationParticleOption` | [data-driven-types](../systems/foundations/data-driven-types.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `VibrationSelector` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `VibrationSystem` | [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `VideoMode` | [naming-drift](../reference/naming-drift.md), [the-window](../systems/rendering/the-window.md) | | `VideoSettingsScreen` | [the-window](../systems/rendering/the-window.md) | | `ViewArea` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `Villager` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [pathfinding](../systems/entities/pathfinding.md), [data-components](../systems/foundations/data-components.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [points-of-interest](../systems/world/points-of-interest.md) | | `VillagerCalmDown` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `VillagerGoalPackages` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [points-of-interest](../systems/world/points-of-interest.md) | | `VillagerMakeLove` | [points-of-interest](../systems/world/points-of-interest.md) | | `VillagerProfession` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `VillagerTrade` | [contexts-and-predicates](../systems/items/contexts-and-predicates.md), [enchanting](../systems/items/enchanting.md) | | `VillageSiege` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [points-of-interest](../systems/world/points-of-interest.md) | | `VisGraph` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `Visibility` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [tickets-and-loading](../systems/world/tickets-and-loading.md) | | `VisibilitySet` | [visibility-and-the-frame-graph](../systems/rendering/visibility-and-the-frame-graph.md) | | `VoxelShape` | [math-and-primitives](../reference/math-and-primitives.md), [submit-phases](../reference/submit-phases.md), [movement-and-collision](../systems/entities/movement-and-collision.md), [jigsaw-and-templates](../systems/worldgen/jigsaw-and-templates.md) | | `VulkanBackend` | [anatomy](../systems/anatomy/anatomy.md), [blaze3d](../systems/rendering/blaze3d.md) | | `VulkanCommandEncoder` | [blaze3d](../systems/rendering/blaze3d.md) | | `VulkanGpuSurface` | [blaze3d](../systems/rendering/blaze3d.md) | | `VulkanRenderPass` | [blaze3d](../systems/rendering/blaze3d.md) | | `WaitingForResponseScreen` | [dialogs](../systems/commands/dialogs.md) | | `WakeUp` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [points-of-interest](../systems/world/points-of-interest.md) | | `WalkNodeEvaluator` | [pathfinding](../systems/entities/pathfinding.md) | | `WallClimberNavigation` | [pathfinding](../systems/entities/pathfinding.md) | | `WanderingTraderData` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `WanderingTraderSpawner` | [entity-lifecycle](../systems/entities/entity-lifecycle.md), [server-level-tick](../systems/server/server-level-tick.md), [starting-a-server](../systems/server/starting-a-server.md), [points-of-interest](../systems/world/points-of-interest.md) | | `Warden` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [damage-and-death](../systems/entities/damage-and-death.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `WardenAi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [game-events-and-vibrations](../systems/world/game-events-and-vibrations.md) | | `WardenSpawnTrackerCommand` | [brigadier-and-commands](../systems/commands/brigadier-and-commands.md) | | `WaterAvoidingRandomStrollGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `WaterBoundPathNavigation` | [pathfinding](../systems/entities/pathfinding.md) | | `WaterFluid` | [fluids](../systems/world/fluids.md) | | `WaterFogEnvironment` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `WaterloggedTransparentBlock` | [fluids](../systems/world/fluids.md) | | `WaypointStyleManager` | [hud](../systems/client/hud.md), [resource-system](../systems/foundations/resource-system.md) | | `Weapon` | [data-components](../systems/foundations/data-components.md), [the-sword-swing](../systems/player/the-sword-swing.md) | | `WeatherAttributes` | [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `WeatherData` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [server-level-tick](../systems/server/server-level-tick.md) | | `WeatherEffectRenderer` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `WeatherRenderState` | [lightmap-fog-and-sky](../systems/rendering/lightmap-fog-and-sky.md) | | `WeighedSoundEvents` | [sound-engine](../systems/client/sound-engine.md), [what-makes-a-sound](../systems/client/what-makes-a-sound.md) | | `WeightedList` | [particles](../systems/rendering/particles.md) | | `WeightedRandomSelectorFeature` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `WeightedVariants` | [models-and-atlases](../systems/rendering/models-and-atlases.md) | | `WidgetTooltipHolder` | [gui-and-screens](../systems/client/gui-and-screens.md) | | `Window` | [anatomy](../systems/anatomy/anatomy.md), [the-client-loop](../systems/client/the-client-loop.md), [the-window](../systems/rendering/the-window.md), [how-a-server-dies](../systems/server/how-a-server-dies.md) | | `WindowEventHandler` | [the-window](../systems/rendering/the-window.md) | | `WingsLayer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `WitherBoss` | [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `Wolf` | [damage-and-death](../systems/entities/damage-and-death.md), [data-components](../systems/foundations/data-components.md) | | `WoodlandMansionPieces` | [hand-built-structures](../systems/worldgen/hand-built-structures.md) | | `WorkAtComposter` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `WorkAtPoi` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `WorldBorder` | [level-data-and-rules](../reference/level-data-and-rules.md), [server-level-tick](../systems/server/server-level-tick.md) | | `WorldCarver` | [data-driven-types](../systems/foundations/data-driven-types.md), [blending](../systems/worldgen/blending.md), [terrain](../systems/worldgen/terrain.md) | | `WorldClock` | [level-data-and-rules](../reference/level-data-and-rules.md), [naming-drift](../reference/naming-drift.md), [server-level-tick](../systems/server/server-level-tick.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `WorldClocks` | [level-data-and-rules](../reference/level-data-and-rules.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md) | | `WorldCreationContext` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldCreationGameRulesScreen` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldCreationUiState` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldData` | [level-data-and-rules](../reference/level-data-and-rules.md), [starting-a-server](../systems/server/starting-a-server.md) | | `WorldDataConfiguration` | [level-data-and-rules](../reference/level-data-and-rules.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md) | | `WorldDimensions` | [level-data-and-rules](../reference/level-data-and-rules.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldGenContext` | [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [chunk-storage](../systems/world/chunk-storage.md) | | `WorldGenerationContext` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `WorldGenLevel` | [features-and-placement](../systems/worldgen/features-and-placement.md) | | `WorldgenRandom` | [math-and-primitives](../reference/math-and-primitives.md), [features-and-placement](../systems/worldgen/features-and-placement.md), [structure-placement](../systems/worldgen/structure-placement.md) | | `WorldGenRegion` | [block-update-flags](../reference/block-update-flags.md), [blocks-and-states](../systems/blocks/blocks-and-states.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [environment-attributes-and-timelines](../systems/world/environment-attributes-and-timelines.md), [points-of-interest](../systems/world/points-of-interest.md), [scheduled-ticks](../systems/world/scheduled-ticks.md), [blending](../systems/worldgen/blending.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `WorldGenSettings` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [starting-a-server](../systems/server/starting-a-server.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldGenTickAccess` | [scheduled-ticks](../systems/world/scheduled-ticks.md) | | `WorldLoader` | [glossary](../reference/glossary.md), [data-driven-types](../systems/foundations/data-driven-types.md), [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md), [resource-system](../systems/foundations/resource-system.md), [tags](../systems/foundations/tags.md), [starting-a-server](../systems/server/starting-a-server.md), [XII · World generation](../systems/worldgen/README.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldOpenFlows` | [creating-a-world](../systems/worldgen/creating-a-world.md), [features-and-placement](../systems/worldgen/features-and-placement.md) | | `WorldOptions` | [glossary](../reference/glossary.md), [level-data-and-rules](../reference/level-data-and-rules.md), [chunk-generation-pipeline](../systems/world/chunk-generation-pipeline.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldOptionsScreen` | [permissions](../systems/commands/permissions.md) | | `WorldPreset` | [glossary](../reference/glossary.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldPresets` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldSelectionList` | [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldSessionTelemetryManager` | [advancements](../systems/commands/advancements.md) | | `WorldStem` | [glossary](../reference/glossary.md), [starting-a-server](../systems/server/starting-a-server.md), [creating-a-world](../systems/worldgen/creating-a-world.md) | | `WorldUpgrader` | [starting-a-server](../systems/server/starting-a-server.md) | | `WrappedGoal` | [glossary](../reference/glossary.md), [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `WritableLevelData` | [level-data-and-rules](../reference/level-data-and-rules.md) | | `WritableRegistry` | [identifiers-and-registries](../systems/foundations/identifiers-and-registries.md) | | `WrittenBookContent` | [text-components](../systems/foundations/text-components.md) | | `XoroshiroRandomSource` | [math-and-primitives](../reference/math-and-primitives.md) | | `YieldJobSite` | [points-of-interest](../systems/world/points-of-interest.md) | | `ZeroBitStorage` | [chunk-anatomy](../systems/world/chunk-anatomy.md) | | `Zoglin` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `Zombie` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md), [attributes](../systems/entities/attributes.md), [authority](../systems/entities/authority.md), [entity-lifecycle](../systems/entities/entity-lifecycle.md) | | `ZombieAttackGoal` | [ai-goals-and-brains](../systems/entities/ai-goals-and-brains.md) | | `ZombieRenderer` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `ZombieRenderState` | [entity-rendering](../systems/rendering/entity-rendering.md) | | `ZombifiedPiglin` | [pathfinding](../systems/entities/pathfinding.md) | --- # The lecture map The video series is an *ordering* over the system pages, which are the stable content. Each part's landing page lists its lectures in watching order and says in a line what each one is; this page assembles the thirteen orders and states the dependencies between them — where a later part is assumed by an earlier one, and what to watch first because of it. It was drafted in pass 3, one part per session, and is confirmed by the owner, who reads every part with the decompile open, before anything is recorded. Nothing in [Reference](reference/README.md) is watched, and nothing in the [maps](maps/README.md) is: the maps are looked at once, the reference pages are looked up, and a lecture links into them wherever a viewer would pause the video to read a table. Because the subject here is the order, nothing below describes a lecture. What each one is about is said once, on its part's landing page; what is here is what the order needs — the shape of each part, the lectures that must be watched together or in a fixed sequence, and the three that are worth taking out of turn: *environment attributes and timelines* before Part III, *contexts and predicates* before Part XIII's advancements, and *the client loop* before Part XI. The one rule that holds everywhere: **each part assumes only the parts before it**, and where that is not true the section says so and names the page to watch early. [The dependencies between parts](#the-dependencies-between-parts), at the end, draws the whole graph. ## I · Anatomy 1. [Anatomy](systems/anatomy/anatomy.md) — watched first, because every later diagram's lanes assume it. 2. [What this book skips](systems/anatomy/what-this-book-skips.md) — second and not last, for the reason the closing paragraph of this page gives. ## II · Foundations Part II is a stack, watched bottom-up; each page is the machinery the one above it assumes. 1. [Codecs, NBT and JSON](systems/foundations/codecs-nbt-json.md) 2. [Identifiers and registries](systems/foundations/identifiers-and-registries.md) 3. [The resource system](systems/foundations/resource-system.md) 4. [Tags](systems/foundations/tags.md) 5. [Data components](systems/foundations/data-components.md) 6. [Text components](systems/foundations/text-components.md) 7. [The data-driven type pattern](systems/foundations/data-driven-types.md) [Math and primitives](reference/math-and-primitives.md) is not a lecture; it is Reference, and the parts link into it. ## III · The server Part III is a line into a loop and out again: the loop first, because it is what the rest of the book lives inside, and the beginning and the end last, because they are only interesting once you know what they start and stop. 1. [The server tick](systems/server/server-tick.md) — with the next, the most load-bearing pair in the book after *Anatomy*: seven later parts assume one lecture or the other. 2. [The level tick](systems/server/server-level-tick.md) — watched immediately after, and never apart from it. 3. [Players and sessions](systems/server/players-and-sessions.md) 4. [Starting a server](systems/server/starting-a-server.md) 5. [How a server dies](systems/server/how-a-server-dies.md) Watch [environment attributes and timelines](systems/world/environment-attributes-and-timelines.md) (Part IV) before *the level tick* if you want its opening to mean anything. It costs nothing to take out of order — it depends on nothing but registries and codecs — and Part III is the earliest of the six other parts that lean on it. ## IV · The world Part IV is a conveyor: five pages that hand a chunk along a line, and five more about the world the line delivers. The conveyor is lectures 2 to 6 and must be watched in that order — nothing later in the chain can be watched first. Lecture 1 is off the line on purpose, and the last four can be watched in any order once the vocabulary page is done. 1. [Environment attributes and timelines](systems/world/environment-attributes-and-timelines.md) — first, for the reason Part III already gave: it depends on nothing but registries and codecs, and nine pages in six other parts depend on it. 2. [Chunk anatomy](systems/world/chunk-anatomy.md) — the vocabulary page. Everything below assumes it, and so does Part V. 3. [Tickets and loading](systems/world/tickets-and-loading.md) 4. [The chunk generation pipeline](systems/world/chunk-generation-pipeline.md) — Part XII is the cargo on this conveyor and cannot be watched before it. 5. [Lighting](systems/world/lighting.md) — self-contained. 6. [Chunk storage](systems/world/chunk-storage.md) 7. [Scheduled ticks](systems/world/scheduled-ticks.md) — Part V's redstone lecture assumes this one, and so does the next. 8. [Fluids](systems/world/fluids.md) 9. [Game events and vibrations](systems/world/game-events-and-vibrations.md) 10. [Points of interest](systems/world/points-of-interest.md) — Part VI owns the brain; this owns the index it reads. [Level data and rules](reference/level-data-and-rules.md) is not a lecture; it is Reference, and this part and Part III both link into it. ## V · Blocks Part V is a hub and six spokes, and the hub is watched first because the other six all reach back into the same figure in it: what `Level.setBlock` and `LevelChunk.setBlockState` do once the section has been written. Two of the six are one lecture in two halves. 1. [Blocks and states](systems/blocks/blocks-and-states.md) 2. [Block interaction](systems/blocks/block-interaction.md) 3. [Block breaking](systems/blocks/block-breaking.md) — the same lecture's other half. Watch it immediately after, and never apart from it. 4. [Block entities](systems/blocks/block-entities.md) — self-contained. 5. [Signal and dust](systems/blocks/signal-and-dust.md) — assumes [scheduled ticks](systems/world/scheduled-ticks.md) only lightly. 6. [Pistons and block events](systems/blocks/pistons-and-block-events.md) 7. [Diodes and the observer](systems/blocks/diodes-and-observers.md) — assumes [scheduled ticks](systems/world/scheduled-ticks.md) properly: a repeater's delay is an entry in that queue. Part V's two click lectures are the applications of [prediction and acknowledgement](systems/client/prediction-and-acks.md) in Part X, and that page's own scenario needs this part's vocabulary. The dependency is circular and this book cuts it here: both click pages open with the same statement of the contract, and the machinery waits for Part X. ## VI · Entities Part VI is a ladder, and the second rung carries the rest of it. Nothing in the part can be reordered without breaking something, and the one page most often skipped — *authority* — is the one three later parts link back to. 1. [Entity anatomy](systems/entities/entity-anatomy.md) — the vocabulary the other eight lectures use. 2. [Authority](systems/entities/authority.md) — short, and Parts VIII, IX and X all assume it rather than re-deriving it. 3. [Entity lifecycle](systems/entities/entity-lifecycle.md) — assumes [tickets and loading](systems/world/tickets-and-loading.md) for what *entity-ticking* means. 4. [Synched entity data](systems/entities/synched-entity-data.md) — the first of the two channels that describe an entity. 5. [Attributes](systems/entities/attributes.md) — the second. Watch it after *synched entity data*, because the contrast between the two is the lesson. 6. [Movement and collision](systems/entities/movement-and-collision.md) — needs *authority* in front of it, and [blocks and states](systems/blocks/blocks-and-states.md) for what a collision shape is. 7. [AI: goals and brains](systems/entities/ai-goals-and-brains.md) — assumes [environment attributes and timelines](systems/world/environment-attributes-and-timelines.md) for the schedule and [points of interest](systems/world/points-of-interest.md) for the bed. 8. [Pathfinding](systems/entities/pathfinding.md) — the other half of the same lecture, and watchable on its own once *goals and brains* has said where the wanted position comes from. 9. [Damage and death](systems/entities/damage-and-death.md) Part VI must precede Part VIII, which is the player half of nearly every page here, and it should precede Parts IX and X, both of which lean on *authority*. It assumes Part IV for what makes an entity tick at all and Part V for what it collides with. ## VII · Items and inventories Part VII is two tiers. The first three lectures are the vocabulary and are watched in order; the last five are three engines that hand each other nothing, so they can be watched in any order — though the two pairs below want to stay together, and *contexts and predicates* leans on no stack at all and could come first. 1. [Items and stacks](systems/items/items-and-stacks.md) — assumes [data components](systems/foundations/data-components.md) completely. 2. [Using an item](systems/items/using-an-item.md) 3. [Containers and menus](systems/items/containers-and-menus.md) — needs [the level tick](systems/server/server-level-tick.md) for when a broadcast happens. 4. [Recipes](systems/items/recipes.md) 5. [Enchantments](systems/items/enchantments.md) 6. [Enchanting](systems/items/enchanting.md) — watch it directly after *enchantments*. 7. [Contexts and predicates](systems/items/contexts-and-predicates.md) — the one page in this part that Part XIII needs. 8. [Loot tables](systems/items/loot-tables.md) Part VII assumes Part II for components, codecs and the reload, Part III for where in a tick a packet is drained and a broadcast lands, and Part V for how a chest gets opened. It must precede Part VIII, which is the player's own inventory and the swing that spends an item's durability. *Contexts and predicates* is a prerequisite of Part XIII's `/execute if predicate` and of the advancement system, and is the one lecture here a viewer coming for commands should watch out of order. ## VIII · The player Part VIII is a trunk and four branches: two lectures on what a player is and when it runs, then five more on what a player does, in four independent groups. Only one group has an internal order — the spear is the sword swing's sequel. 1. [Player anatomy](systems/player/player-anatomy.md) — the vocabulary lecture. 2. [The two-phase tick](systems/player/the-two-phase-tick.md) — watch it immediately after *player anatomy*. 3. [Input to movement](systems/player/input-to-movement.md) 4. [The sword swing](systems/player/the-sword-swing.md) 5. [The spear](systems/player/the-spear.md) 6. [Hunger and experience](systems/player/hunger-and-experience.md) 7. [Status effects](systems/player/status-effects.md) Part VIII assumes Part VI above everything — [authority](systems/entities/authority.md) in particular, which is where the whole part's premise is stated — and Part III for the tick phases the two-phase tick lives between. It assumes Part VII for the inventory and for [using an item](systems/items/using-an-item.md), which is the machinery the spear's charge runs on. Nothing later in the book is needed to watch it, but Part IX and Part X both come back to it. ## IX · Networking Part IX is one wire and three passengers. The first two lectures are one lecture in two halves and should be watched together; the last three are unrelated systems that ride the wire, and each has a different shape. 1. [The connection](systems/networking/the-connection.md) 2. [Packets and stream codecs](systems/networking/packets-and-stream-codecs.md) — the second half of the same lecture. 3. [Protocol phases](systems/networking/protocol-phases.md) 4. [What the client is told](systems/networking/what-the-client-is-told.md) 5. [Chat and signing](systems/networking/chat-and-signing.md) Part IX assumes Part III for the tick phases its traffic is timed against, and Part I's [anatomy](systems/anatomy/anatomy.md) for the two loops — the client drains packets once per *frame*, and that single fact explains most of what looks like network jitter. It assumes Part II for codecs and for `Component`, and Part VI's [authority](systems/entities/authority.md) for the premise under lecture four. It is a prerequisite of Part X, which is the same wire watched from the receiving end. ## X · The client Part X is a hub and its spokes, and the spokes are cadences rather than stages. Nothing here hands off to anything; every page after the first answers "when in the client's one loop does *this* happen". Watch the hub first and then take the rest in any order that suits — except the two pairs noted below. 1. [The client loop](systems/client/the-client-loop.md) — the hub, and the page every other page in the part leans on. 2. [The client level](systems/client/the-client-level.md) 3. [Prediction and acknowledgement](systems/client/prediction-and-acks.md) 4. [Input and keybinds](systems/client/input-and-keybinds.md) 5. [Options](systems/client/options.md) 6. [GUI and screens](systems/client/gui-and-screens.md) 7. [The GUI render tree](systems/client/the-gui-render-tree.md) 8. [Text and fonts](systems/client/text-and-fonts.md) 9. [The HUD](systems/client/hud.md) 10. [Sound: the engine](systems/client/sound-engine.md) 11. [What makes a sound happen](systems/client/what-makes-a-sound.md) 12. [Debugging the running game](systems/client/debugging-the-running-game.md) Six to nine are the part's one internal pipeline — a screen records, the tree sorts and batches, the text becomes glyphs — and are watched consecutively. Two and three are the other pair: the ledger lives on `ClientLevel` and is reached through four of its methods. Part X assumes [Part IX](systems/networking/README.md), which is the same wire watched from the sending end, and Part I's [anatomy](systems/anatomy/anatomy.md) for the two loops. It assumes Part VI's [authority](systems/entities/authority.md) as the premise under lectures two and three, and **Part V before lecture three** — Part V's landing page rules that its two click lectures come first, because they are the ledger's two applications. Lecture eight assumes Part II's [text components](systems/foundations/text-components.md). Lecture one is a prerequisite of Part XI, which begins where it ends, at the acquired surface. ## XI · Rendering *A substrate under a pipeline.* Two lectures are what the renderer stands on and have no trace through the world; the rest are one frame in the order it happens. The part opens on the frame itself because it is the shortest way to see the whole shape at once — and because a viewer who has watched one frame end to end has a reason to care what a `GpuDevice` is. 1. [The frame](systems/rendering/the-frame.md) 2. [The window](systems/rendering/the-window.md) 3. [Blaze3D](systems/rendering/blaze3d.md) 4. [Visibility and the frame graph](systems/rendering/visibility-and-the-frame-graph.md) 5. [Section meshing](systems/rendering/section-meshing.md) 6. [Models and atlases](systems/rendering/models-and-atlases.md) 7. [Entity rendering](systems/rendering/entity-rendering.md) 8. [Block-entity rendering](systems/rendering/block-entity-rendering.md) 9. [Lightmap, fog and sky](systems/rendering/lightmap-fog-and-sky.md) 10. [Particles](systems/rendering/particles.md) 11. [Post-processing](systems/rendering/post-processing.md) Four and five are a pair — two pages that were one, and still one journey seen from its two ends — and so are seven and eight, the second written as the differences from the first. One to three can also be watched one, three, two. Part XI assumes two pages of Part X: the [client loop](systems/client/the-client-loop.md), which is what says when a frame happens, and [the client level](systems/client/the-client-level.md), for what the thing being drawn actually is. Lecture six assumes Part II's [resource system](systems/foundations/resource-system.md); lecture nine assumes Part IV's [environment attributes and timelines](systems/world/environment-attributes-and-timelines.md), which owns the system this part only consumes. ## XII · World generation A substrate, a pipeline, and a wing — and the wing runs first while being watched last. The ten lectures below run against the chunk status ladder rather than along it: a structure is *decided* two statuses before the biomes and terrain it will stand in exist, and writes its blocks four statuses later, so keeping the three structure lectures together at the end costs one forward reference and buys a whole arc in one place. 1. [Density functions](systems/worldgen/density-functions.md) 2. [Biomes](systems/worldgen/biomes.md) 3. [Terrain](systems/worldgen/terrain.md) 4. [Blending at the old-chunk border](systems/worldgen/blending.md) 5. [Features and placement](systems/worldgen/features-and-placement.md) 6. [Trees](systems/worldgen/trees.md) 7. [Structure placement](systems/worldgen/structure-placement.md) 8. [Jigsaw and templates](systems/worldgen/jigsaw-and-templates.md) 9. [Hand-built structures](systems/worldgen/hand-built-structures.md) 10. [Creating a world](systems/worldgen/creating-a-world.md) Two comes before three: `ChunkPyramid` makes `ChunkStatus.BIOMES` a requirement of both `ChunkStatus.NOISE` and `ChunkStatus.SURFACE`, and the surface pass reads the biome. Four needs both, and reaches one status forward into five and six's. Seven comes before eight and nine, which are alternatives to each other rather than a sequence. Ten is the object the other nine read, told last because it is a tree of everything they explain. Part XII assumes Part IV's [chunk generation pipeline](systems/world/chunk-generation-pipeline.md), and hard: it is the only page that says when any of this runs and what the twelve statuses are, and eight of the ten lectures here name one. It also assumes Part IV's [chunk anatomy](systems/world/chunk-anatomy.md) for what is being written into and Part IV's [environment attributes and timelines](systems/world/environment-attributes-and-timelines.md) for lecture two, where `Biome` has been hollowed out into one layer of a modifier stack; and three Part II lectures — codecs, registries and [the data-driven type pattern](systems/foundations/data-driven-types.md), whose fifty-six instances this part owns twenty-six of — because worldgen is the most thoroughly data-driven system in the game. ## XIII · Commands and data packs A stack of three floors, and the dependency runs strictly one way: *parse*, then *execute*, then the four systems built on both. The last four are peers rather than a sequence — watch them in any order, or only the ones you care about — but neither of the first two floors is optional for any of them. 1. [Brigadier and commands](systems/commands/brigadier-and-commands.md) 2. [Permissions](systems/commands/permissions.md) 3. [Entity selectors](systems/commands/entity-selectors.md) 4. [The execution engine](systems/commands/the-execution-engine.md) 5. [Functions and macros](systems/commands/functions-and-macros.md) 6. [Advancements](systems/commands/advancements.md) 7. [Scores, teams and stored data](systems/commands/scoreboard-and-data.md) 8. [Dialogs](systems/commands/dialogs.md) 9. [Game tests](systems/commands/game-tests.md) Two and four are the pair that most changes how a viewer reads everything else, and two is the one an existing mod author most needs; three sits between them because it needs the atom two defines and hands its fan-out to four. Six, seven, eight and nine each assume one through five and nothing else in this part. Part XIII assumes Part III's [server tick](systems/server/server-tick.md) twice over — command functions run near the top of `MinecraftServer.tickChildren`, before any level ticks, and the connection phase that calls `ServerPlayer.doTick` runs after the levels, which is what puts a periodic advancement trigger one tick behind the packet that should have carried it. It assumes Part II's [codecs](systems/foundations/codecs-nbt-json.md) and [the data-driven type pattern](systems/foundations/data-driven-types.md), of which dialogs and game tests are the two clearest instances; Part IX's [connection](systems/networking/the-connection.md) for the Netty/server thread boundary the command packets cross two different ways; and, for advancements alone, Part VII's [contexts and predicates](systems/items/contexts-and-predicates.md), because a trigger's conditions are loot conditions. ## The dependencies between parts Every arrow below is a *before you start* entry on a landing page: the part at the tail is one the part at the head assumes, and not optionally. The two dependencies every part shares are drawn as boxes but not as edges, because their arrows would reach almost every node — Part I's [anatomy](systems/anatomy/anatomy.md), for the threads every diagram's lanes are on, and Part II's [codecs](systems/foundations/codecs-nbt-json.md) and [registries](systems/foundations/identifiers-and-registries.md), assumed wherever something is written to disk, sent on the wire or looked up by name. Read a solid arrow as *watch before*. ```mermaid flowchart TB P1["I · Anatomy"] P2["II · Foundations"] P3["III · The server"] P4["IV · The world"] P5["V · Blocks"] P6["VI · Entities"] P7["VII · Items and inventories"] P8["VIII · The player"] P9["IX · Networking"] P10["X · The client"] P11["XI · Rendering"] P12["XII · World generation"] P13["XIII · Commands and data packs"] P1 --> P2 --> P3 P3 --> P4 P3 --> P5 P3 --> P6 P3 --> P7 P3 --> P8 P3 --> P9 P3 --> P13 P4 --> P5 P4 --> P6 P4 --> P11 P4 --> P12 P5 --> P6 P5 --> P7 P5 --> P10 P6 --> P8 P6 --> P9 P6 --> P10 P7 --> P8 P7 --> P13 P9 --> P10 P9 --> P13 P10 --> P11 P4 -. "tickets and loading, environment attributes" .-> P3 P10 -. "prediction and acknowledgement, cut at Part V" .-> P5 ``` The graph is a line with two knots in it, and the sidebar order is a valid walk through it: no solid arrow points at an earlier part. The two dashed arrows are the places where it does not hold, and each is cut on purpose rather than solved by reordering. **Part III assumes two pages of Part IV.** [The level tick](systems/server/server-level-tick.md) uses *entity-ticking* and *block-ticking* range, which [tickets and loading](systems/world/tickets-and-loading.md) owns, and its first statement about the day-night cycle rests on [environment attributes and timelines](systems/world/environment-attributes-and-timelines.md). The first is cut by definition — the level tick defines both ranges in two sentences before it uses them — and the second by order: the environment page depends on nothing but registries and codecs, so it is the first page of Part IV in the sidebar, in that part's watch order and in the list above, and it is the one lecture worth watching before its part. **Part V and Part X assume each other.** The two click lectures in Part V are the applications of [prediction and acknowledgement](systems/client/prediction-and-acks.md), and that page's own scenario is a block placed against a wall, which needs Part V's vocabulary. The cut is at Part V: both click pages open with the same four-sentence statement of the ledger's contract, which is all either needs, and the machinery waits for Part X, so the whole of Parts V and VI is watched before that one Part X lecture. Part VI hands something forward to a *different* Part X page: [the client level](systems/client/the-client-level.md) opens by saying it is not an authority either, which needs [authority](systems/entities/authority.md) behind it. Ten pages carry most of the graph — nine rows below, because the two server ticks are one dependency in two lectures. The membership rule is mechanical: **a page that two or more landing pages name under *before you start***, less the three every part assumes, which are the boxes the figure draws without edges. A viewer who has watched these ten can take the parts they belong to in almost any order; a viewer who skips one of them will find a later part's first surprise unexplained. | the page | its part | the parts whose landing pages assume it | |---|---|---| | [The server tick](systems/server/server-tick.md) and [the level tick](systems/server/server-level-tick.md) | III | IV, V, VI, VII, VIII, IX, XIII — seven of the eight later parts that run on the Server thread, for *which phase* something ran in | | [Environment attributes and timelines](systems/world/environment-attributes-and-timelines.md) | IV | III, VI, XI, XII — the clock, the schedule, and the colour of the sky | | [Chunk anatomy](systems/world/chunk-anatomy.md) | IV | V, VI, XII — a block state's home, a ticking entity's chunk, and what terrain is written into | | [Authority](systems/entities/authority.md) | VI | VIII, IX, X — the premise under every page about a player, and under *what the client is told* | | [The resource system](systems/foundations/resource-system.md) | II | III, VII, XI — the staged load and its barrier: a server's own data at startup, where recipes and loot tables come from, and the reload the atlases are built by | | [The connection](systems/networking/the-connection.md) | IX | X, XIII — the thread boundary every packet crosses | | [Tickets and loading](systems/world/tickets-and-loading.md) | IV | III, VI — what *entity-ticking* means | | [The data-driven type pattern](systems/foundations/data-driven-types.md) | II | XII, XIII — the *type* field in a data-pack file and the registry it dispatches on; these two parts own most of its instances | | [Text components](systems/foundations/text-components.md) | II | IX, X — what a chat message and a screen's label are before anything draws them | Three more pages are a single part's dependency, and each is named in that part's *before you start* rather than here: [blocks and states](systems/blocks/blocks-and-states.md) before Part VI, [contexts and predicates](systems/items/contexts-and-predicates.md) before Part XIII's advancements, and [the client loop](systems/client/the-client-loop.md) before Part XI. The last two are the ones a viewer coming for that part alone most often has to fetch from elsewhere in the book. Watched straight through, the sidebar order still needs one departure from itself, and it is now as small as it can be: *environment attributes and timelines* is the first lecture of Part IV and wants watching before Part III's second. A viewer coming for one part rather than the whole book takes that part's *before you start* list as the order. [What this book skips](systems/anatomy/what-this-book-skips.md) is the second lecture and not the last: it is the only page that states the series' boundary, and a boundary is drawn before the investment, not after. Part XIII's game tests are the closing lecture because they are the game's own answer to the question the whole book has been asking — how do you know what it does — and because nothing later depends on them.