Files
RustNotMC/docs/rust-parity.md
SashegDev 874b6f20c2 Клиент: начальный импорт кодовой базы
Основа — Horizon (MIT). История апстрима не переносится: этот репозиторий ведёт свою.
2026-10-29 22:04:28 +03:00

1588 lines
104 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Horizon Rust rewrite status
The behavior target is the repository's pinned **Minecraft Java Edition 26.3**.
One-to-one vanilla gameplay parity is the acceptance criterion. It has **not**
been reached, but the Rust game is now playable: the [native client](native-client.md)
has a title screen and world management. It generates vanilla terrain with
decoration and streams, lights and renders it. Survival play works: mining,
placing, crafting, chests and furnaces, items, mobs, combat, hunger, health,
sounds, and Anvil saves.
The Rust workspace implements core arithmetic/random streams, world data and
resident sections, scheduled tick ordering, geometry, contained fluid states,
noise/density evaluation, biome and terrain generation kernels, material rules,
and parts of feature placement and decoration. Movement/fluid interactions,
registered attributes, hunger storage/ticks, synchronized player health and
absorption, damage reduction, outer hurt gates, blocking-item damage, item
durability and item-use start/tick/release/completion have scoped native comparisons.
Shared inventory/equipment storage now binds item-use flags and lifecycle, while
item templates, remainders, checked bundle weights and cooldown expiry have
native comparisons. Item completion composes stack copies with remainder and
cooldown effects and server inventory delivery. Consumable start/completion now
composes food storage, native random streams and eating particles, with exact
sound parameters and effect ordering checked against both pinned JARs. Consume
effect bodies now cover status application/removal/clearing, sounds and random
teleport attempts. Shared status instances implement hidden-chain updates,
server/client countdown and client blending with native comparisons. Entity
effect storage now binds consumable status changes and the effect tick loop,
including expiration, periodic refresh and client particle selection. Registered
effect behavior, notification side effects and full entity/world lifecycle
bindings remain unfinished. All pinned block-survival
families now have native differential coverage. Full simulation callbacks,
complete chunk generation, entity behavior, networking, rendering and playable
integration remain unfinished. The linked subsystem documents and hashed reports
record the exact tested scope and remaining gaps.
The `horizon-assets` crate now decodes pinned cuboid models, with all 2,883 bundled
block-model JSON files and 2,457 synthetic cases compared against the actual
client parser. [Decoder scope and validation](native-client.md#validation)
also include parent/texture resolution over 5,648 roots and cuboid baking over
7,100 roots with identity blockstate transforms. Blockstate resource decoding now
matches all 1,288 bundled definitions and 1,351 synthetic cases; registry
binding now also matches native selection across registered and special
item-frame states. Weighted selection and multipart random draw consumption
also match native traces. Transformed baking now checks every bundled
model/rotation/UV-lock tuple and all XYZ quarter-turn combinations against native
geometry by exact float bits. The native renderer now draws its controlled
resident chunks from registry-bound, selected and transformed models, including binary
cutouts, multipart connections, weighted stone variants and stepped lantern
animation. All 16 scene selections match native model collection. These are not
yet simulated gameplay blocks. Resident chunk meshing now preserves weighted
variants and UV orientation when greedily merging matching cube faces, and uses
native-checked neighbor visibility. Full block-catalog material support, lighting
and chunk streaming remain unfinished.
[Neighbor face visibility](face-visibility.md) now matches both reference JARs
across 1,441,816 state pairs, including all pinned suppression-rule families.
Animation metadata, frame sequences and tick-state selection now have native
differential coverage, including all bundled animated texture metadata. GPU
interpolation and game-loop synchronization remain unfinished.
## Completion criteria and playable builds
The final deliverable is the complete Horizon Rust game with one-to-one parity
with pinned Java Edition 26.3, plus a playable **Linux** build that the user can
download and test. The target platform is a Linux x86-64 desktop with an NVIDIA
GPU (Vulkan) on Wayland. macOS and Windows are out of scope. The build must include a packaged native
executable, required runtime resources, launch instructions, artifact checksums,
and the tested OS/CPU/GPU configuration.
The build is complete only after its packaged Rust game is launched and tested on the target machine:
create/load a world, render terrain, use keyboard/mouse controls, move and interact,
break/place blocks, use inventory/crafting, save, quit and reload. These launch
checks accompany the full gameplay-parity suite; they do not replace it. Record
failures and test limits. Compiler success, library comparisons
and the existing Java packs do not establish a playable Rust build.
Optimize FPS, frame pacing, memory use and render distance using matched scenes,
worlds, settings and hardware after behavior is validated. Package and test on
the target machine before declaring the rewrite complete. Commit and push
verified development checkpoints regularly.
## Implemented foundation
`crates/horizon-core` is a dependency-free Rust library with no unsafe code.
It uses small integer position values, exclusively owned RNG state, and packed
64-bit storage. These choices avoid Java object overhead in this slice; no
whole-game memory, FPS, or render-distance gain has been measured.
| Area | Implemented and checked operations | Remaining scope |
| --- | --- | --- |
| Positions | Block, section, and chunk packing/unpacking; wrapping block offsets; block-to-section/chunk conversion; section-relative packet positions; block-state storage indices; region coordinates/local indices; bounded inclusive/directional block-box iteration | Remaining iterators, world bounds and entity/world integration |
| Simulation geometry | Selected Vec3 operations, quantized trig/rotations, normalized AABB bounds and transforms, finite block traversal/swept visitation, ray faces, immutable cube/array voxel grids, greedy boxes, collision distances, column heights, closest points, lazy floor height, box quantization and Boolean/optimized joins; movement resolution over supplied shapes, step candidates and step-up decisions/query bounds | Shape rotations/faces/edges, entity-context shape selection, full entity movement and side effects |
| Legacy random stream | Seeding/reseeding; int, bounded/inclusive/exclusive int, long, boolean, float, double; triangular float/double draws; fork; consume count; positional factories and Java UTF-16 string hashes | Gaussian sampling, threading-error behavior and remaining world-generation call sites |
| Xoroshiro random stream | 64-bit seed expansion; explicit 128-bit state including all-zero fallback; the same selected random operations as above; positional factories and MD5-derived seeds with Java UTF-8 surrogate handling | Gaussian sampling, serialization and remaining world-generation call sites |
| Noise generation | 26.3 Perlin/simplex/smeared Perlin; permutation construction and coordinate wrap; Perlin derivatives; cached/scalar density-volume accumulation; nested/flattened stacks; normal-noise octave weights and normalization; modern and legacy Nether seeding | Serialized noise definitions, density graphs, complete chunk generation, biome/feature/structure integration |
| Density evaluation | Compiled constants, clamped/repeating/mirrored gradients, unshifted and XZ/XYZ shifted noise; offset-noise variants, old blended noise, outer End islands and distance-to-point; all eleven unary operators, six binary operators, powers, rounding, constant-operand specializations, min/max, clamp, range choice, interval selection and linear interpolation; separate scalar/bulk paths, nested scratch buffers, context-bound blend alpha/offset/structure samplers, absent/empty blend-density wrappers, context-owned scalar/volume caches, coordinate slices, coarse-grid interpolation, surface searches and nested cubic splines | Codecs, structure-density producers, nonempty old-world blending and terrain integration |
| Density graph compilation | Immutable definitions for constants, gradients, arithmetic/selection operations, powers, rounding, distance-to-point, slices, caches, interpolation, surface search, nested splines, outer End islands and normal/offset/blended noise; context fields, blend-density wrappers, direct/named reference holders; range/axis metadata, constant and disjoint-range specialization, uniform-axis rewriting, cache-ID allocation/deduplication, memoized roots, spline coordinate sharing/validation, world-seed lookup and failure state | Codecs, concrete world-field producers and complete terrain integration |
| Density range analysis | Checked interval constructors, hulls, arithmetic/min/max, reciprocal, abs/square/sign, powers/logarithms, clamp, monotonic endpoint mapping, finite/infinite interpolation, predicates, equality and Java hashes; conservative spline range propagation; integration with supported graph definitions | Propagation through remaining graph definitions |
| Packed palette-index storage | Widths 1–32; allocation; imported raw words; reads; writes returning old values; unpack; fill; padding/tail preservation; invalid-write rejection | Remaining network container integration |
| World data | Named/network NBT, modified UTF-8, mixed lists; gzip/zlib/uncompressed/lz4-java codecs; region exports including external chunks; block/biome saved palettes | Complete save lifecycle, data fixing, concurrent saves and recovery |
| Resident block sections | Pinned block-state registry import, defaults, numeric IDs, uniform sections, packed palette growth, freed-slot reuse and compaction; cached collision shape lookup bound to the same registry | Block behavior, entity-context collision shapes, light and full chunk-lifecycle integration |
| Heightmaps | Six type predicates and flags, packed heights, updates/removal rescans, shared priming, raw imports and length-mismatch fallback; all pinned states and mutation fixtures compared | Block air/fluid behavior and chunk-lifecycle integration |
| Scheduled ticks | Chunk queues, level collection/drain ordering, duplicate suppression, seven priorities, saved delays, chunk eligibility, callback mutations, area removal/copying, saved block/fluid NBT | Actual block/fluid callbacks, neighbor updates, tick-time and chunk-lifecycle integration |
Packet-relative positions use X/Z/Y ordering; block-state storage indices use
Y/Z/X ordering. Negative coordinate conversion must floor rather than truncate.
The legacy RNG must retain Java's signed-int arithmetic and rejection rules;
the xoroshiro RNG has a different bounded-int algorithm and bit selection.
Positional factories preserve signed coordinate hashing and each stream's distinct
seed derivation. The hash API accepts UTF-16 units so malformed surrogate sequences
retain Java's replacement behavior. Gaussian sampling is still absent: a private
probe found bit differences between Java `Math.log` and the platform logarithm,
which must be resolved before its values and cached-draw behavior can be claimed.
Storage entries never cross a word boundary. Vanilla's fill operation also
writes padding and unused tail bits; imported words retain their original bits
when a slot is changed. These details are covered by the differential corpus.
Rust APIs return typed errors instead of Java exceptions. Storage lengths and
indices use `usize`; raw decoded values use `u32` bit patterns. Writes reject
values above the nonnegative Java-int domain, including at width 32. Test output
casts raw imported width-32 reads back to signed Java ints for comparison.
Negative lengths and the complete Java object/API surface are not supported.
## Reproduce validation
The core/world libraries require Rust 1.85 or newer; the native client's locked
dependencies require Rust 1.90 or newer. Use current stable Rust, Python 3.10 or
newer, and JDK 25 for the complete workspace. From the checkout:
```sh
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
python3 tools/test_rust_parity.py --prepare
python3 tools/test_world_parity.py
python3 tools/test_tick_parity.py
python3 tools/test_geometry_parity.py
python3 tools/test_collision_parity.py
python3 tools/test_noise_parity.py
python3 tools/test_density_parity.py
python3 tools/test_worldgen_parity.py
python3 tools/test_heightmap_parity.py
python3 tools/test_terrain_parity.py
python3 tools/test_structure_density_parity.py
python3 tools/test_material_surface_parity.py
python3 tools/test_material_condition_parity.py
python3 tools/test_material_stage_parity.py
python3 tools/test_biome_zoom_parity.py
python3 tools/test_biome_chunk_parity.py
python3 tools/test_biome_generation_parity.py
python3 tools/test_biome_source_parity.py
python3 tools/test_anchor_parity.py
python3 tools/test_biome_temperature_parity.py
python3 tools/test_float_log_parity.py
python3 tools/test_raid_parity.py
```
`--prepare` regenerates the local reference artifacts from the existing lock;
omit it after preparation to run without downloads. The runner verifies the
official manifest and both game downloads, the server's inner game JAR, and each
server dependency against pinned/bundled hashes. The original Java test adapter
calls vanilla classes directly; it does not substitute a Java reimplementation
of the Rust algorithms. The same corpus runs against client and server code.
The fixtures need no display, account, assets, or world generation.
The deterministic corpus contains coordinate extremes, all 4,096 local section
positions, 8,192 random coordinate/offset queries, boundary and random signed
seeds, mixed RNG operations, fork/reseed/consume checks, invalid bounds, and
all 32 storage widths at boundary and 4,096-entry sizes. It also imports words
containing all set bits and compares the raw padding and fill results. The
current run produces **1,634,520 matching output lines per reference JAR**.
It includes 4,400 positional-factory commands and 5,000 random-helper commands,
with overflowing integer intervals, hash padding boundaries and malformed UTF-16.
This count includes repeated checks and raw entries; it is not a count of
distinct vanilla behaviors or a percentage of game coverage.
Artifacts remain gitignored under `.local/rust-parity/`: corpus, Rust output,
client/server output, compiled oracle, and `report.json`. The report records
source/input/output hashes, reference identities, tool versions, and matched
line counts. A prior success report is removed before a new run can fail.
CI runs the same comparison in addition to offline Rust and Python checks.
The checked-in [scoped validation report](../benchmarks/rust-core-2026-10-05/scoped-parity.json)
contains only metadata and digests from the local run, not game code or artifacts.
The additional [world-data suite](world-data.md) checks NBT and region
interoperability, saved palettes, all 35,723 registered block-state IDs, and
resident sections after repeated mutation. The corresponding
[scoped report](../benchmarks/rust-world-2026-10-05/scoped-parity.json)
records client/server reference hashes and the tested source hashes.
The [scheduled-tick suite](scheduled-ticks.md) adds 25,815 commands including
2,096 tick invocations, producing 97,776 matching output lines per reference.
It also verifies block/fluid tick save codecs and a persisted region chunk.
The [geometry and cached-collision suites](collision-geometry.md) check numerical
boundaries and runtime block-state shape data, with separate
[geometry](../benchmarks/rust-geometry-2026-10-05/scoped-parity.json) and
[cached-collision](../benchmarks/rust-collision-2026-10-05/scoped-parity.json)
metadata reports. All differential runners reject changed source hashes during a run before publishing
a success report. Historic reports describe their own recorded source versions.
The [noise suite](noise-generation.md) checks base sampling, derivative and bulk
arithmetic, nested/flattened stacks, normal-noise normalization and both modern
and legacy Nether initialization. Its [scoped report](../benchmarks/rust-noise-2026-10-05/scoped-parity.json)
records the tested sources and both reference artifacts.
The [density suite](density-functions.md) checks compiled scalar and bulk samplers,
including gradients, nested shifted noise, arithmetic, caching, axis slicing and
coarse-grid interpolation, surface searches and nested splines with range analysis;
it also compares the initial graph compiler with vanilla's compiler. Its
[scoped report](../benchmarks/rust-density-2026-10-05/scoped-parity.json) records
**15,524,627 matching output lines per reference JAR**. That suite constructs graphs through its transport. Serialized registry
comparisons are covered separately below; generated terrain remains pending.
The [float-log report](../benchmarks/rust-float-log-2026-10-05/scoped-parity.json)
records exhaustive compiled scalar comparisons for all **4,294,967,296 float bit
patterns** against both JARs on the local Linux/JDK runtime, with canonical NaN
results. Double-precision logarithms and other-platform math remain separate
validation work.
The [world-generation codec suite](world-generation.md) checks every bundled
normal-noise JSON definition and all 55 bundled density definitions against
vanilla's actual registry-aware codecs. It compares decoded octave metadata,
graph ranges and axes, raw and optimized compilation, cache allocation and
seeded scalar and strided bulk sampling. Its
[scoped report](../benchmarks/rust-worldgen-2026-10-06/scoped-parity.json) records
**961,693 matching output lines per reference JAR**. All seven bundled eight-field
routers and their climate samplers are also
compared, including quart-coordinate wrapping and float/long quantization.
Climate search-tree construction and warm/fresh ties are checked across the
bounded API. Scalar and prepared-volume multi-noise biome resolvers are implemented
using private imported built-in parameter tables and compared against vanilla
scalar and bulk biome resolvers. Climate parameter codecs, vertical terrain bounds and the global fluid picker/
disabled-aquifer path are also compared. Noise-based aquifer configuration and
fluid-pressure selection are implemented, with differential checks for seeded
decisions, sampling order, caches and failure recovery. Tag codecs, stacked resource
replacement and bounded dependency resolution are compared across all 300 bundled
block tags and synthetic missing-reference/cycle fixtures. Complete terrain
integration remains pending.
The [heightmap suite](heightmaps.md) records **1,671,668 matching output lines per
reference JAR** across all 35,723 block states and 100 chunk fixtures. Rust resolves
tag membership; block air/fluid facts are private, independently verified imports.
Actual vanilla heightmaps compare predicates, mutations, packed words, priming and
selected ordered block reads. Chunk-lifecycle integration remains pending.
The [noise-fill suite](terrain-filling.md) now compares **7,950,873 output lines per
reference JAR** across 114 fixtures and 200 fills. Final density, aquifers, resident
sections, world-generation heightmaps and fluid postprocessing are integrated for
this stage. The surface stage has a separate comparison; biome, feature and
structure stages remain pending.
The [structure-density sampler](structure-density.md) matches **1,360,095 output
lines per reference JAR**, including ordered rigid-piece/junction contributions
and scalar/bulk clipping. Live structure discovery and placement remain pending.
The [vertical-anchor suite](vertical-anchors.md) matches **181,632 output lines per
reference JAR**, including four anchor codecs, runtime overflow, nested exclusive
fields and generation bounds.
The [material-surface primitives](material-surfaces.md) match **122,988 output
lines per reference JAR** for seeded terracotta bands, band lookup and surface
noise queries. Subsequent suites cover rule evaluation and surface application.
The [numeric material-condition suite](material-conditions.md) matches **5,627,426
output lines per reference JAR** for eleven condition forms and their column/block
cache transitions, including codecs, all eight bundled condition definitions and
block/sequence/conditional/terracotta-band rule composition.
The chunk surface comparison below integrates these rules.
The [biome-temperature suite](biome-temperatures.md) matches **836,659 output lines
per reference JAR** for temperature modifiers, precipitation decisions and ordered
cache behavior, including thread isolation and climate fields from all 67 bundled
biome definitions. Full weather placement remains pending.
The [surface-extension suite](material-surfaces.md#biome-specific-column-extensions)
matches **233,100 output lines per reference JAR** for badlands pillars and
frozen-ocean icebergs, including ordered column reads/writes and both random modes.
The [bundled material-rule suite](material-rule-libraries.md) matches **678,174
output lines per reference JAR**, including 665,712 rule applications across all
42 bundled definitions and the pinned biome, noise and density registries.
The [chunk surface stage](material-surfaces.md#chunk-surface-stage) matches
**6,733,382 output lines per reference JAR** across 79 fixtures, 42 terrain fills
and 106 surface builds, followed by 158 carving-mask passes, including 21,757,952 block values, heightmaps, fluid
postprocessing and ordered biome queries. It tests all seven bundled terrain
settings through both stages with shared density contexts and vanilla fuzzy
biome lookup against 378 generated neighbor biome chunks (400,896 stored cells).
Preset builders and complete generated worlds remain pending.
The [biome-lookup suite](biome-lookup.md) matches **256,933 output lines per
reference JAR** for seed obfuscation, eight-corner fuzzy selection, direct quart
queries and source replacement. Seed hashes also match an independent SHA-256
implementation. [Chunk biome storage](biome-lookup.md#chunk-biome-storage) matches
**478,340 lines per JAR** across 160 fills, including section-atomic failure
behavior and clamped reads. [Chunk biome generation](biome-lookup.md#chunk-biome-generation)
matches **383,072 lines per JAR** across 322 fills, including both imported
multi-noise presets and fixed/checkerboard/End sources through all seven terrain
routers. Preset builders and complete world generation remain pending.
The [fixed/checkerboard/End source suite](biome-lookup.md#fixed-checkerboard-and-end-sources)
matches **1,306,096 lines per JAR**, including 1,147,984 biome queries and ordered
End erosion sampling. [Registry-bound source codecs](biome-lookup.md#registry-bound-source-codecs)
match **363,121 lines per JAR** across 40,481 inputs, including canonical encoding,
possible-biome sets and resolved queries. Chunk generation and surface integration
share the resolved-source API. Inline biome definitions and complete registry
lifecycle behavior remain pending.
The [carving-mask suite](carving.md) matches **45,742 lines per JAR** across
28 fixtures, including packed column ranges, coordinate wrapping and mutations
retained after failing traversal callbacks. Mask application is included in the
surface-stage suite. [Shared carving geometry](carving.md#shared-shape-primitives)
matches **106,699 lines per JAR**, including 690 ellipsoids and 5,536 reachability
queries. [Seeded cave/canyon paths](carving.md#seeded-cave-and-canyon-paths) match
**882,005** and **1,094,517 lines per JAR**, respectively. The combined stage suite
also covers 42 procedural carving passes, including neighboring biome selection
and ordinal carver seeds. Full-world integration remains pending.
The [feature-placement primitives](feature-placement.md) match **1,268,600 lines per
JAR** across seventeen modifier kinds, including callback failures and shared random
state. [Modifier-chain execution](feature-placement.md#modifier-chains) matches
**548,461 lines per JAR**, including feature callback failures and reused pending
buffers. [Initial block-writing features](feature-placement.md#initial-block-writing-features)
match **1,820,614 lines per JAR** for the void platform, End islands and End arrival
platform. Remaining feature algorithms and complete world integration remain pending.
The [scalar value-provider suite](value-providers.md) matches **429,920 lines per
JAR** across 504 definitions and 5,040 seeded fixtures, including exact random
stream consumption. [Anchored height providers](value-providers.md#anchored-height-providers)
match **507,660 lines per JAR**, including all four anchors and overflowing contexts.
Weighted lists include zero weights, nested choices and maximum totals. Gaussian
distributions and provider encoding remain pending.
The [block-state JSON codec suite](block-state-codecs.md) matches **221,261 output
lines per reference JAR**, covering every registered state and compact/full
encoding plus malformed-property fallback. Block gameplay remains pending.
## Work required for a playable rewrite
| Subsystem | Current Rust status | Required parity evidence |
| --- | --- | --- |
| Registries, block states, items and data components | Block-state IDs, property descriptors and defaults imported and checked; items/components absent | Registry lifecycle, remaining registries, data components and behavior |
| World/chunk storage, NBT and region files | Bounded NBT, four codecs, region export, saved and resident section palettes | Generated-save integration, entity/POI integration, upgrades, live saves and crash recovery |
| Terrain, biomes, structures and lighting | Seeded noise/density graphs, bundled registry and router loading, climate sampling/search and imported-table multi-noise biome resolution, aquifer decisions, noise-stage filling and surface-stage application implemented; complete terrain generation absent | Remaining terrain/biome codecs and world fields, seeded generated-chunk comparisons, biome/feature/structure placement, chunk lifecycle and light propagation |
| Tick scheduling, blocks, fluids and redstone | Scheduled queues and saved ticks, block survival families, fluid geometry/flow and entity immersion kernels checked; complete live block/fluid/redstone simulation absent | Actual block/fluid state transitions, neighbor updates, world integration and end-to-end ordered traces |
| Entities, AI, movement and combat | Collision, movement/fluid kernels, registered attributes, damage reduction, hurt gates and immunity predicates checked; complete live entity simulation absent | Context-dependent collision/physics and tick traces, spawning, pathfinding, damage, equipment and lifecycle |
| Survival, inventory, crafting and progression | Hunger ticks/storage and synchronized health/absorption implemented and checked; inventory/crafting/progression absent | Recipes, item transitions, hunger/health, effects, experience and advancement behavior |
| Dimensions, portals, weather and game rules | Not implemented | Cross-dimension state, transitions, time/weather and rule-dependent behavior |
| Multiplayer, protocol, authentication and commands | Not implemented | Packet/codec fixtures, vanilla interoperability, reconnects and command behavior |
| Rendering, resources, sound, UI and input | Native inspection client with selected block models, neighbor visibility, greedy meshing and a free camera; gameplay rendering/audio/UI incomplete | Resource compatibility, render/culling correctness, controls and playable interaction tests |
| Performance and platform support | Registered-attribute allocation reduction measured on Linux; whole-game performance and playable platform packages pending | Repeated matched gameplay workloads, frame/tick distributions, memory and physical GPU tests |
The next simulation milestone is integrating the checked scheduler and
registry-backed sections with actual block behavior and neighbor updates. World
generation now has noise-stage filling, aquifers and surface-stage application;
biome assignment and remaining generation stages need work,
followed by complete seeded-world comparisons. The inspection renderer needs
complete material/lighting support, chunk streaming and a simulated world.
Movement, health and combat kernels need shared live player/entity state and
world services. Scoped comparisons alone cannot establish complete gameplay parity. Performance tuning must preserve the
validated simulation and use measured workloads; changing render distance,
simulation distance or terrain detail invalidates an equal-workload speed claim.
No finite corpus proves every vanilla behavior. Future milestones need broader
version-specific differential suites and playable integration checks. The
project will report implemented/validated scope rather than label a partial
port as 100% parity.
The [pre-light chunk-stage comparison](terrain-filling.md#block-writes-across-generation-stages)
adds **8,090,090 matching lines per JAR** for stage-dependent block mutation and
heightmap selection, including insertion/removal of every pinned state. This does
not establish the complete chunk lifecycle or region integration.
The [resident feature-region comparison](feature-placement.md#resident-feature-regions)
adds **28,922,634 matching lines per JAR** for basic features, configured fill layers, replacements and scattered ore sharing
modifier chains and resident neighboring chunks, including live heightmap updates.
World-region side effects and complete feature generation remain pending.
The [inline feature-graph comparison](feature-placement.md#inline-feature-composition)
adds **1,146,057 matching lines per JAR** for nested sequences, overlays and random selectors,
including short-circuit results, shared randomness and partial mutations.
The [named feature-resource comparison](feature-placement.md#named-feature-resources)
adds **2,528,704 matching lines per JAR** for registry-aware feature/placed-feature
holders, ordered tags and subsequent nested execution. Complete registry loading,
reloads and remaining feature algorithms are still pending.
The [configured fill-layer comparison](feature-placement.md#configured-fill-layers)
adds **1,305,274 matching lines per JAR** for block-state binding and ordered
column placement, including decoding every pinned block state. Complete feature
generation and gameplay remain pending.
The [replacement-rule comparison](feature-placement.md#replacement-rules) adds
**1,890,716 matching lines per JAR** for all ten native predicate kinds, bound
state/tag identities and ordered random evaluation. Complete ore and structure
generation remain pending.
The [single-block replacement comparison](feature-placement.md#single-block-replacement)
adds **303,678 matching lines per JAR** for ordered rule-driven block mutations,
rejected writes, failures and subsequent random state.
The [scattered-ore comparison](feature-placement.md#scattered-ore) adds
**758,484 matching lines per JAR** for attempt offsets, ordered air-exposure checks,
rule evaluation and block mutations. Ordinary ore veins and complete biome
decoration remain pending.
The [bulk-section comparison](feature-placement.md#bulk-section-access) adds
**959,640 matching lines per JAR** for cached raw section access, every block state,
all-air reads and unchanged heightmaps. This supplies storage behavior needed by
ordinary ore generation; vein geometry and concurrent ownership remain pending.
The [biome feature-order comparison](feature-placement.md#biome-feature-ordering)
adds **411,751 matching lines per JAR** for identity-based ordering, cycles and
step index mappings, including every bundled biome's feature lists. This does
not yet execute complete chunk decoration.
The [world-generation random-adapter comparison](feature-placement.md#world-generation-random-streams)
adds **238,800 matching lines per JAR** for native bit sampling, counters, forks,
nested wrappers and decoration/feature seeding. Gaussian sampling and complete
chunk decoration remain pending.
The [biome-placement comparison](feature-placement.md#biome-placement-filtering)
adds **121,423 matching lines per JAR** for top-feature contexts, native generation
settings membership, query order, nested ordinary placers and partial failures.
Complete biome sampling integration and chunk decoration remain pending.
The [decoration scheduler comparison](feature-placement.md#decoration-feature-scheduling)
adds **337,398 matching lines per JAR** through the native chunk-decoration entry
point, with structures disabled and traced feature sinks. It covers resident
biome selection, global ordering, per-feature seeds and failure/progress state;
complete feature algorithms and chunk lifecycle remain pending.
The [resident decoration comparison](feature-placement.md#decoration-over-resident-chunks)
adds **36,173,951 matching lines per JAR** for native chunk decoration combining
biome storage/zoom, generation-stage guards, scheduling, implemented feature
graphs, block writes and live heightmaps. It compares complete final resident
sections; structures, lighting and the full generation lifecycle remain pending.
The [block-state provider comparison](feature-placement.md#block-state-providers)
adds **257,299,719 matching lines per JAR** for ten state-provider kinds, including
all eight bundled provider definitions. It includes every state under six
directions, property transforms, nested draw order and empty selections. Rule-based
selection includes twelve predicate kinds, ordered conditions, empty results and
query failures. Named holder references are resolved through immutable resource
snapshots. Additional predicates, complete registry loading and complete feature placement remain pending.
The [contained-fluid comparison](world-data.md#contained-fluid-states) adds
**243,331 matching lines per JAR** for fluid-state mapping, liquid-block conversion
and surface/camera height queries. It covers every pinned block state and query
failures; fluid spreading, entity interactions and rendering remain pending.
## Block entity removal, drops and copper golems
`LevelChunk.setBlockState` runs `BlockEntity.preRemoveSideEffects` when a
block with a block entity is replaced by another block (`LevelAccess::remove_block_entity`,
`level/removal.rs`): containers spill every slot through `Containers.dropItemStack`
(chests, barrels, copper chests, hoppers, dispensers, droppers, crafters,
furnaces, brewing stands, chiseled bookshelves, shelves, decorated pots and
campfires, the last four from the block entities kept as loaded), jukeboxes pop
their record, lecterns their book, and shulker boxes keep their contents for the
dropped box. Copper chests and statues keep their block entity across
weathering, and `UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS` skips the spill.
`Block.dropResources` draws its loot and pop spots when the behaviour calls it,
with ore, sculk and spawner experience, and the item entities and orbs the
behaviour makes join the world before it returns, in the same tick. Player
mining, explosions and `/setblock`/`/fill destroy` use the same path in
vanilla's order.
`tools/test_block_removal_parity.py` replaces, destroys and removes every block
entity holder with random contents (4,000 cases) against `VanillaDispenserOracle`
running vanilla's `setBlock`/`destroyBlock`/`removeBlock`, and compares block
changes, contents left, item entities and orbs, events, sounds and the level
random: all cases match. The block update oracle replays the same removal, so
the block update, random tick and redstone harnesses cover it too.
The copper golem is a species: its model, weathered textures and glowing eyes,
keyframe animations (compared by the entity model harness), sounds, weathering,
waxing and scraping, statues, saved `weather_state` and `next_weather_age`, death
loot (entity death loot harness), the poppy iron golems give it, and building it
from a pumpkin on a copper block (the copper block becomes the matching copper
chest). The dispenser harness builds, shears and equips copper golems against
vanilla. Its chest-sorting AI (`CopperGolemAi` with
`TransportItemsBetweenContainers`) still runs as goals that follow vanilla's
behaviour without its brain: porting it onto the brain machinery of
`mobs/villager_brain.rs` and comparing it in the mob AI harness, which needs
container contents in the probe's world, is still to do.
## Overlay feature composition
The Rust overlay feature executes every child placed feature in order and
accumulates success, sharing the placer and random stream across siblings.
Named holders and placed-feature tags use the same catalog resolution as other
compositions. Empty named tags produce false; empty direct lists fail decoding.
The expanded [inline graph comparison](../benchmarks/rust-feature-graph-2026-10-06/scoped-parity.json)
matches **1,146,057 output lines per reference JAR**, and the expanded
[named catalog comparison](../benchmarks/rust-feature-catalog-2026-10-06/scoped-parity.json)
matches **2,528,704 lines per reference JAR**. These include partial failures,
rejected writes, nested modifiers, repeated calls, random-stream tails and both
Java random modes. This verifies scoped composition behavior; complete chunk
generation and gameplay remain pending. Workspace tests and all-target Clippy
also pass for this implementation.
## Block-pile placement
Rust block piles preserve radius and sparse-candidate random draws, air checks,
dirt-path acceptance, state-aware FULL upper support, provider fallback reads,
and update flags 260. Previously read support states are evaluated without an
extra world read. The [native comparison](../benchmarks/rust-block-pile-2026-10-06/scoped-parity.json)
matches **11,995,254 lines per reference JAR**, covering every candidate and
supporting registry state, partial failures, rejected writes, repeated placement,
and overlay/modifier composition. Workspace tests and all-target Clippy pass.
Ordinary piles visit at most 98 cells. Rust preserves wrapped native box traversal
but limits pathological iteration to 2^20 cells; negative-count overflow fixtures
compare failure-terminated prefixes. Dynamic block entities are absent in this
feature fixture, and resident-world block updates remain pending.
## Disk replacement and postprocessing
Disk features now decode bounded radii and half-heights, traverse circular
columns in native order, apply optional providers, and mark up to two non-air
blocks above each replacement run. Failed target predicates end a run; empty
optional providers preserve it. Postprocessing requests resolve the containing
resident chunk and retain duplicate marks and prior effects after a failure.
The [disk comparison](../benchmarks/rust-disk-2026-10-06/scoped-parity.json)
matches **2,816,586 output lines per reference JAR** across **76,409 fixtures**.
Every candidate state and every possible above-state is covered, alongside
random providers, rejected writes, partial failures, wrapped coordinates and
composed placement. Chunk-resolution and native ProtoChunk mark calls are
traced; later chunk postprocessing execution is outside this comparison.
Workspace tests, all-target Clippy and all 149 Python tests pass.
## Block-blob placement
Block blobs descend until their support predicate accepts the block below, then
write three overlapping randomized shapes with flags 3. Radius calculations
retain native float precision while position distances use double precision;
even the last shape consumes its three origin-offset draws.
The [block-blob comparison](../benchmarks/rust-block-blob-2026-10-06/scoped-parity.json)
matches **2,387,914 lines per reference JAR** over **77,686 fixtures**, including
every candidate/supporting state, repeated calls, early returns, failed queries,
rejected writes, partial failures and overlay/modifier composition. Workspace
tests and all-target Clippy pass. Rust bounds pathological traversal work;
resident-world update callbacks and full gameplay remain pending.
## Fluid-state JSON codecs
Fluid states now decode and encode native object-form identifiers and properties.
Missing properties retain the pinned defaults (`falling=true`, flowing level 1);
invalid property values are ignored, while unknown fluid identifiers fail.
Integer levels share the Java-compatible decimal parser with block properties.
The expanded [fluid comparison](../benchmarks/rust-fluid-2026-10-06/scoped-parity.json)
matches **243,331 lines per reference JAR**, including all 37 fluid states,
2,432 codec fixtures, encoding, legacy block conversion, and the existing
contained-fluid/height tests. Workspace tests and all-target Clippy pass.
Fluid spreading, tick execution and entity interaction remain pending.
## Spring features and fluid tick requests
Spring features preserve above/below/origin gating, the separate west/east/north/
south/below rock and air-count passes, fluid-to-block conversion, writes with
flags 2, and immediate fluid tick requests after accepted or rejected writes.
Fluid tick execution is still separate simulation work.
The [spring comparison](../benchmarks/rust-spring-2026-10-06/scoped-parity.json)
matches **3,649,087 lines per reference JAR** across **172,141 fixtures**. It
covers every state in the gating and neighbor roles, all five-neighbor air/rock/
other combinations and count pairs, all 37 fluid states, malformed fields, tags,
wrapped positions, partial failures and nested composition. Workspace tests and
all-target Clippy pass. Full fluid spreading and world updates remain pending.
## Vine feature attachment
Vine features require an air state at the origin and choose the first acceptable
attachment in Up/North/South/West/East order. They evaluate actual support and
collision shapes and return success after the write request, including when the
world rejects that write. Feature and world random streams are unchanged.
The [vine comparison](../benchmarks/rust-vines-2026-10-06/scoped-parity.json)
matches **4,315,682 lines per reference JAR** across **224,743 fixtures**, covering
every origin state and every state on each candidate face, support combinations,
wrapped positions, partial failures and composed placement. Workspace tests and
all-target Clippy pass. Vine growth, block updates and complete gameplay remain
pending; this fixture uses absent dynamic block entities.
## Full-face occlusion
Block property bindings now cache six full-face occlusion results per state,
sharing calculations for identical imported shapes. These results use native
occlusion rules independently of collision or support geometry. Unknown state
IDs produce an error.
The expanded [block-property comparison](../benchmarks/rust-block-properties-2026-10-06/scoped-parity.json)
matches **197,180 lines per reference JAR**, including all 35,723 states and
18,096 synthetic shape/flag combinations. Workspace tests and all-target Clippy
pass. Lighting and renderer integration remain separate work.
## Underwater magma and probability decoding
Underwater magma now performs native water-column scans, including the upper
scan before the lower scan and repeated boundary reads. Each cube position draws
a probability value before testing block identity and the five required full
occlusion faces. Rejected writes still count as successful placements.
The [underwater magma comparison](../benchmarks/rust-underwater-magma-2026-10-06/scoped-parity.json)
matches **6,523,072 lines per reference JAR** over **253,252 fixtures**. Both RNG
modes cover the complete radius-64 cube, consuming **2,146,689 draws** with zero
placement probability. Every state in the origin/floor/neighbor roles, scan
limits, partial failures, wrapped radius-zero columns and composition are tested.
The native comparison exposed signed-zero codec behavior: bounded probabilities
reject `-0.0`, including negative values that round to float zero. Magma and
pillar decoding now preserve this. The expanded pillar gate passes **921,111
lines per JAR** over **41,746 fixtures**. Workspace tests and all-target Clippy
pass. World updates and magma/fluid gameplay remain pending.
## Blue ice generation
Blue ice features now preserve world sea-level gating, water and packed-ice
seeding checks, all 200 spreading attempts, signed vertical radius adjustment,
random draw order and six-direction adjacency. World sea level can differ from
the generation context's sea level. Rejected writes preserve native success and
subsequent attempts observe the actual mutable world.
The [blue-ice comparison](../benchmarks/rust-blue-ice-2026-10-06/scoped-parity.json)
matches **8,954,666 lines per reference JAR** across **79,944 fixtures**, covering
every origin/candidate state through failure-terminated prefixes, full spreading
runs, all packed-ice neighbor masks, extreme sea levels, wrapped positions,
partial failures and composition. Workspace tests, all-target Clippy and all
149 Python tests pass. Ice gameplay and complete world generation remain pending.
## Spike geometry and roots
Spike features now implement descending air search, support predicates, randomized
height and radius, rare elevated spikes, float taper calculations and independent
upper/lower replacement tests. Roots retain the native fixed Y=50 boundary,
corner-specific random lengths, gaps, and exact-state replacement exception.
The [spike comparison](../benchmarks/rust-spike-2026-10-06/scoped-parity.json)
matches **8,859,761 lines per reference JAR** over **113,127 fixtures**. Every
output/origin/candidate state is covered by failure-terminated prefixes; complete
runs include 1,024 seeds per RNG, replacement and support predicates, repeated
mutations, rejected writes, partial failures and composition. Workspace tests
and all-target Clippy pass. Complete world generation and block updates remain
pending. Pathological descent/root work is bounded to 2^20 visits.
## End exit podium construction
End podium features reproduce active/inactive construction, the native distance
cutoffs and box traversal, central bedrock pillar and ordered wall-torch facing.
Active replacement reads block identity, requests destruction with drops and no
responsible entity, then writes the replacement even if destruction was rejected.
Matching blocks skip both requests.
The [End podium comparison](../benchmarks/rust-end-podium-2026-10-06/scoped-parity.json)
matches **3,309,219 lines per reference JAR** over **38,207 fixtures**. All states
reach active replacement prefixes; full runs cover representative blocks, repeated
construction, rejected destruction/writes, partial failures, composition and
nonoverflow extreme coordinates. Workspace tests, all-target Clippy and all
149 Python tests pass. The fixture controls destruction success and air
replacement; loot, block entities, updates, portal travel and dragon gameplay
remain pending. Pathological overflow-box work is bounded to 2^20 visits.
## Replacement blobs
The `netherrack_replace_blobs` feature now clamps the search origin to world
height, descends to a matching block, samples three independent radii and visits
clipped Manhattan shells in native order. Target matching uses block identity
regardless of state properties; rejected writes still produce success.
The [replacement-blob comparison](../benchmarks/rust-replace-blobs-2026-10-06/scoped-parity.json)
matches **5,538,034 lines per reference JAR** over **109,003 fixtures**, including
every target/output state, all candidate states through failure-terminated
prefixes, every constant radius, six provider families, both RNGs, height
boundaries, wrapped X/Z coordinates, partial failures and composition. Workspace
tests and all-target Clippy pass. World updates and full generation remain
pending; pathological descent is bounded to 2^20 visits.
## Bamboo and podzol patches
Bamboo features preserve the initial air gate, real survival checks and native
attempt-success behavior when survival fails. Podzol patches use ordered surface
height requests and the native replacement tag. Stalks preserve random draws and
leaf states, including the first cap overwriting the stopping position when at
least three stalk positions were attempted.
The [bamboo comparison](../benchmarks/rust-bamboo-2026-10-06/scoped-parity.json)
matches **3,485,027 lines per reference JAR** over **144,452 fixtures**. Every
origin/support/podzol state has a failure-terminated prefix; every obstacle state
has a complete no-podzol run. Tests include both RNGs, signed-zero decoding,
obstruction heights, rejected writes, repeated mutations, wrapped coordinates
and composition. Workspace tests and all-target Clippy pass. Fixture surface
heights are controlled; dynamic height updates, bamboo growth and full gameplay
remain pending. Pathological wrapped patch loops have a 2^20 work limit.
## Nether delta patches
Delta features now preserve the double-precision rim chance, conditional rim
offset draws, independent patch radius draws and Manhattan traversal. Placement
checks block identity and ordered air/nonair neighbors, writes the rim, then
checks shifted contents against the mutated world. A zero rim component still
shifts contents even when it disables the rim write.
The [delta comparison](../benchmarks/rust-delta-2026-10-06/scoped-parity.json)
matches **13,418,994 lines per reference JAR** across **324,269 fixtures**,
covering every origin/content/rim state and all six neighbor roles, every air
mask, radius limits, six provider families, both RNGs, repeated mutations,
rejected writes, partial failures, wrapped positions and composition. Workspace
tests, all-target Clippy and all 149 Python tests pass. The fixture supplies a
flat ground/air boundary with mutable overrides; fluid behavior, world updates
and complete generation remain pending.
## Biome freezing and snow eligibility
Registry-bound weather predicates now use the native biome temperature cache,
precipitation selection, build-height and block-light gates. Freezing distinguishes
source water from flowing water, requires a liquid block and optionally checks
water neighbors in west/east/north/south order. Snow accepts air or existing snow
and evaluates the default snow state's actual survival rules.
The [biome-weather comparison](../benchmarks/rust-biome-weather-2026-10-06/scoped-parity.json)
matches **4,071,594 lines per reference JAR** across **326,057 fixtures**,
covering all states in block/fluid/snow/support/neighbor roles, neighbor masks,
float thresholds and nonfinite temperatures, both temperature modifiers, light
and height boundaries, precipitation flags, repeated queries and partial failures.
Workspace tests and all-target Clippy pass. Native fixture temperature caches
are removed after each independent case to avoid retaining hundreds of thousands
of biomes; repeated queries within a case keep their cache. Dynamic block entities
are absent. Actual weather ticks, lighting propagation and world updates remain
pending.
## Frozen top layers and snowy ground
The `freeze_top_layer` feature now visits its 16×16 footprint in native column
order, queries motion-blocking height and retains the selected biome for both
weather checks. Ice is attempted before snow eligibility. Snow attempts are
followed by snowy ground-property updates even if the snow write was rejected.
The [snow/freezing feature comparison](../benchmarks/rust-snow-freeze-2026-10-06/scoped-parity.json)
matches **7,764,082 lines per reference JAR** over **108,339 fixtures**. Every
ground/candidate/fluid state has a failure-terminated prefix; full runs cover
climate thresholds and NaN, both modifiers, precipitation, light gates, repeated
mutations, rejected writes, partial failures, extreme heights/sea levels, wrapped
coordinates and composition. Workspace tests and all-target Clippy pass. The
fixture uses controlled heights and one biome; lighting propagation, dynamic
biome selection, world updates and full generation remain pending.
## Generation-stage tick containers
Proto-chunk tick containers now keep the first type/position request in insertion
order. Newly scheduled ticks retain priority with zero delay; loaded entries
retain their saved delay. Generation tick access routes requests to the selected
container and reports no currently executing ticks or aggregate count. Snapshots
can be promoted into the existing level tick queue.
The [generation-tick comparison](../benchmarks/rust-generation-ticks-2026-10-06/scoped-parity.json)
matches **101,784 lines per reference JAR**, covering duplicates, saved entries,
snapshot independence, mixed proto/level routing, lookup failures, promotion,
priority boundaries and integer/time extremes. Workspace tests, all-target Clippy
and all 149 Python tests pass. Rust exposes packed entries as an immutable
borrowed slice; mutating Java's returned list is outside this API. Feature-world
scheduling integration and block/fluid tick execution remain pending.
## Resident generation tick queues
Resident terrain chunks now own separate block and fluid proto-tick queues.
Feature-world scheduling normalizes block states to block identity, routes to the
chunk containing the original position and advances the region's tick order even
for duplicate requests or missing chunks. Time and order use Java wrapping rules.
The [resident queue comparison](../benchmarks/rust-feature-region-ticks-2026-10-06/scoped-parity.json)
matches **28,985,883 lines per reference JAR** across **192 fixtures**, with nine
resident chunks each, **2,688 requested feature placements** and **14,784 direct
tick requests**. Both references agree on final sections, heightmaps and saved
ticks. Coverage includes spring scheduling, duplicate block identities, separate
fluid identities, missing chunks, out-of-height positions and time/order overflow.
Workspace tests and all-target Clippy pass. This connects generation-stage queues;
block/fluid tick execution, complete chunk persistence and full world-generation
effects remain pending.
## Resident block and fluid queries
Feature regions can bind an immutable snapshot of resolved block/fluid tags and
contained fluid states. The snapshot shares compiled tables with configured
features and rejects bindings from a different block registry. Feature access
forwards fluid, water-tag and block-tag queries against current resident storage.
Missing tags have no members; missing chunks remain query errors.
The [resident query comparison](../benchmarks/rust-feature-region-queries-2026-10-06/scoped-parity.json)
matches **29,312,766 lines per reference JAR**, retaining the 192 multi-chunk
feature/tick fixtures and adding insertion/removal probes for **all 35,723 block
states**. Each probe checks fluid identity, amount, falling/source/full flags,
water membership and all 300 bundled block tags plus an absent tag. Negative
coordinates, chunk edges, outside-height reads and missing chunks are included.
Workspace tests and all-target Clippy pass; a separate regression covers foreign
registries, binding lifetime and a custom water tag. These queries do not execute
fluid spreading, block behavior or lighting.
## End gateway structures and exit configuration
The `end_gateway` feature now places its air, bedrock and gateway blocks in native
box order. A configured exit triggers a block-entity lookup even when the gateway
write was rejected. Missing or unrelated entities are no-ops; a gateway receives
its exit/exact fields before its dirty notification.
The [gateway comparison](../benchmarks/rust-end-gateway-2026-10-06/scoped-parity.json)
matches **754,877 lines per reference JAR** across **5,820 fixtures**, including
every world-call failure boundary, rejected writes, repeated updates, both random
streams, placement composition, codec edge cases and bounded prefixes of wrapped
coordinate boxes. The reference invokes the actual gateway entity setter and
compares its fields after partial failures. Workspace tests, all-target Clippy and
all 149 Python tests pass. The Python checks also caught and repaired a shared
fixture dependency: decoration retains its six-feature corpus independently of
region spring/tick/query probes; regenerated inputs are byte-identical to their
validated corpora. Gateway entity creation/removal, dirty propagation, exit search
and teleportation remain pending.
## Bonus chest placement and loot assignment
Bonus chest generation now independently shuffles the chunk's X and Z columns,
searches motion-blocking-no-leaves heights and checks air or empty collision.
After a chest write it looks up a randomizable container, consuming a loot seed
only when one exists, then checks torch survival in native horizontal order.
Rejected writes retain the subsequent entity and torch operations.
The [bonus-chest comparison](../benchmarks/rust-bonus-chest-2026-10-06/scoped-parity.json)
matches **1,505,542 lines per reference JAR** across **42,396 fixtures**. All
35,723 states participate in collision prefixes; complete searches cover both
random streams, missing/unrelated/chest entities, sole viable columns, exhausted
searches, repeated mutation, rejected writes, partial failures, extreme positions
and placement composition. Actual native chest loot-table and seed fields are
compared. Workspace tests and all-target Clippy pass. Heights and entity existence
are controlled by the fixture; inventory contents, loot generation, automatic
entity lifecycle and complete spawn generation remain pending.
## Recursive chorus generation
Chorus generation now checks its air origin and tagged support, builds and
refreshes connection states, branches recursively within the native depth and
distance bounds, and places age-five terminal flowers. Clearance checks exclude
the parent branch, and rejected writes retain native subsequent reads and draws.
The [chorus comparison](../benchmarks/rust-chorus-plant-2026-10-06/scoped-parity.json)
matches **8,083,367 lines per reference JAR** across **254,296 fixtures**. Every
state participates in origin/support gates and five neighbor-connection prefixes.
Full growth covers 1,024 seeds per random generator, early failure boundaries,
rejected writes, repeated mutation, random obstacles, wrapped coordinates and
feature composition. Workspace tests and all-target Clippy pass. Neighbor-update
execution, random tick growth, drops, rendering and complete End generation remain
pending.
## Small pointed formations
The `speleothem` feature now chooses its growth direction from ceiling/floor
blocks, spreads a base patch, selects one- or two-block height, rechecks its base
and writes waterlogged tip/frustum states. Configured base and pointed states
select block identities; placement uses their defaults. Unsupported pointed-block
properties fail during placement after the preceding patch operations.
The [speleothem comparison](../benchmarks/rust-speleothem-2026-10-06/scoped-parity.json)
matches **2,752,289 lines per reference JAR** across **145,473 fixtures**, including
all states in support, taller-growth and contained-fluid roles; optional float
defaults and boundaries; both random streams; rejected writes; partial failures;
repeated mutation; wrapped positions; and composition. Workspace tests and
all-target Clippy pass. Neighbor updates, falling behavior, fluid simulation,
larger clusters and complete cave generation remain pending.
## Generation-time destruction on resident chunks
Feature access now routes destruction through its supplied write callback using
air and update flags `3`. Air is an immediate false result. This follows
`WorldGenRegion.destroyBlock`, whose generation-time path does not evaluate drops;
live-level destruction remains separate work.
The [resident destruction comparison](../benchmarks/rust-feature-region-destroy-2026-10-06/scoped-parity.json)
matches **29,502,523 lines per reference JAR** across **192 fixtures**, adding
active/inactive End podiums to the resident feature corpus. The reference invokes
the actual WorldGenRegion method through a subclass that transports reads/writes
to the existing chunk fixture. Final sections, heightmaps, ticks, tag/fluid probes,
rejected writes and partial failures still agree. Workspace tests, all-target
Clippy and all 149 Python tests pass. Write-zone policy, POIs, block entities,
postprocessing execution, lighting and live-world effects remain pending.
## Generation write radii
Feature access can now apply the generation step's horizontal write radius before
calling the mutation policy. Ordinary writes and destruction share the guard;
queries and tick scheduling retain their independent routing. The predicate uses
Java wrapping subtraction and absolute value, including integer-minimum cases.
The [write-zone comparison](../benchmarks/rust-feature-region-write-zone-2026-10-06/scoped-parity.json)
matches **29,231,123 lines per reference JAR**. It adds **20,125 direct zone cases**
for both chunk and block coordinates, then exercises unrestricted, zero-radius,
shifted-center and negative-radius policies across the 192 resident fixtures.
Vanilla's actual public/private write-zone methods provide the reference. Workspace
tests, all-target Clippy and both focused fixture tests pass. The owning world
still supplies generation-step metadata and upgrading-world vertical restrictions;
full WorldGenRegion effects remain pending.
## Proto-chunk block-entity storage
Rust now stores pending NBT and caller-owned active block-entity payloads by full
block position. Active insertion removes pending data, later pending insertion
cannot overwrite an active entry, and removal clears both maps. Saving uses the
active serializer when present and otherwise returns a pending snapshot. Changing
pending coordinate fields retains the existing map key. Coordinate resolution
matches NumericTag integer narrowing and floating-point flooring before saturating
conversion, then relocates mismatched X/Z into the owning chunk.
The [block-entity storage comparison](../benchmarks/rust-block-entity-storage-2026-10-06/scoped-parity.json)
matches **78,251 lines per reference JAR** across **28,511 commands**, using actual
native ProtoChunks and chest entities. Cases cover replacement, removal, position
unions, save selection, missing/nonnumeric coordinates, infinities/NaNs, integer
extremes and positions that alias when packed. Four focused Rust tests and
all-target Clippy pass. Collection order is normalized; Rust exposes borrowed or
owned data instead of unrestricted Java aliases. Entity construction from NBT,
complete serializers, active entity behavior and resident chunk lifecycle
integration remain pending.
## Upgrading-world write heights
Feature access can now enforce the upgrading center chunk's generation height
before invoking its write callback. Below-zero proto-chunk upgrades use Y = -64
through -1, inclusive. Ordinary generation has no vertical policy gate; storage
bounds remain separate. Reads and tick scheduling keep their existing behavior,
and generation-time destruction shares the write guard.
The [upgrade-height comparison](../benchmarks/rust-feature-region-upgrade-height-2026-10-06/scoped-parity.json)
matches **29,247,433 lines per reference JAR**, including **10,098 height cases**,
20,125 horizontal zone cases and all 192 resident fixtures. The reference calls
actual `WorldGenRegion.ensureCanWrite` with a native ProtoChunk before and after
installing below-zero upgrade data. Direct write probes cover boundary and extreme
heights, ordinary/upgrading modes and horizontal rejection. Five focused Rust
region tests, all-target Clippy and both Python fixture tests passed for that
checkpoint. Subsequent work below adds resident upgrade metadata, cleanup,
bedrock transformations and live center-chunk write policy. Automatic upgrade
activation, biome retention and the complete generation lifecycle remain pending.
## Chunk-status identities and metadata
Rust now represents all ten pinned chunk statuses, their ordering and parents,
proto/level chunk classification, structure-distance constant, and selected
heightmaps. Existing pre-light chunks use these definitions for their names and
heightmap groups. Direct name lookup retains vanilla's `empty` fallback; NBT
codec lookup rejects unknown names and writes canonical namespaced identifiers.
The [chunk-status comparison](../benchmarks/rust-chunk-status-2026-10-06/scoped-parity.json)
matches **4,232 lines per reference JAR** across all 100 ordered status pairs and
**2,065 codec inputs**. Inputs include qualified/unqualified names, empty
namespaces, obsolete and malformed names, random strings and nonstring NBT.
World-crate tests and all-target Clippy pass. Codec error text is normalized.
Status identities do not execute generation tasks: scheduling, lighting,
post-light lifecycle transitions and the complete gameplay runtime remain pending.
## Below-zero upgrade data and resident transformations
Resident chunks now hold optional below-zero upgrade data. Installation alone
keeps the data even if the current stage already exceeds its target; assigning a
persisted stage at or beyond that target clears it. Upgrading chunks expose the
-64 through -1 generation interval independently of their storage bounds.
The Rust upgrade codec validates nonempty target statuses, accepts vanilla's
lenient optional bedrock-hole mask, normalizes trailing zero words and omits an
empty mask when saving. Hole queries wrap local X/Z coordinates. Resident
transformations replace bedrock in the old Y = 0 through 4 band with default
deepslate and clear marked columns through the chunk's current generation height.
The [upgrade-data comparison](../benchmarks/rust-retrogen-data-2026-10-06/scoped-parity.json)
matches **20,626 lines per reference JAR** across **5,114 codec inputs**, including
canonical output, all 256 column bits, out-of-footprint bits, signed numeric
streams, malformed optional fields and random masks. Codec error text is normalized.
The [resident upgrade comparison](../benchmarks/rust-retrogen-chunk-2026-10-06/scoped-parity.json)
matches **3,028,057 lines per reference JAR** across **138 fixtures**. It checks all
nine nonempty targets against the six pre-light stages, repeated/backward stage
assignments, complete stored sections and heightmap words after replacement and
masking, partial vertical overlap, empty sections and changed generation bounds.
World-crate tests and all-target Clippy pass. Save-dirty notifications, post-light
transitions, biome retention, structure restrictions, automatic generation
scheduling and full saved-world/gameplay integration remain pending.
## Live upgrade permissions and retained biome status
Feature access can bind a resident center chunk and read its upgrade state before
every write. Successful callbacks that complete, remove or reinstall an upgrade
affect the next write through the same access object. The destination chunk's
upgrade state does not select this region-wide height gate. Explicit fixed-height
policies remain available for callers that need a supplied interval.
Resident chunks also expose vanilla's highest generated status: the later of
their current persisted stage and retained upgrade target. Region biome reads use
this status, allowing access to old biomes while below-zero regeneration is still
at an earlier stage. Removing upgrade metadata restores the persisted-stage gate.
The [live region comparison](../benchmarks/rust-live-upgrade-region-2026-10-06/scoped-parity.json)
matches **29,248,297 lines per reference JAR**, extending the 192 resident fixtures
with **54 lifecycle cases**. It compares native write permission, highest status
and actual ProtoChunk biome-read availability across all six pre-light stages and
nine nonempty upgrade targets. The [retained-status comparison](../benchmarks/rust-retained-upgrade-status-2026-10-06/scoped-parity.json)
also matches **3,028,057 lines per JAR** across 138 upgrade fixtures. Seven focused
region tests, all-target Clippy and the region corpus test pass. These checks do
not yet cover the biome resolver's cave-biome retention, structure restrictions,
automatic scheduling or complete saved-world integration.
## Retained biomes during below-zero generation
The [biome-retention implementation](biome-lookup.md#below-zero-biome-retention)
now preserves the pinned three cave-biome candidates and otherwise reads the old
column at quart Y = 0. The resolver captures upgrade activation at construction;
candidate failures precede fallback reads. Section fills preserve native commit
ordering and partial-failure behavior while allowing access to old biome storage.
The [retention/fill comparison](../benchmarks/rust-retrogen-biome-fill-2026-10-06/scoped-parity.json)
matches **593,152 lines per JAR** across 740 fills and 2,800 direct queries. The
[generation integration](../benchmarks/rust-retrogen-biome-generation-2026-10-06/scoped-parity.json)
matches **834,080 lines per JAR** across 396 fixtures and 610 fills, including 144
upgrading chunks alongside the ordinary-generation cases. It executes actual
native `ChunkGenerator.doCreateBiomes`, including retained-status old biome reads,
native below-zero generation bounds, repeated fills and all biome-source kinds.
This closes the resolver gap described in the preceding checkpoint. Nonempty
blending, structure restrictions, automatic generation scheduling and complete
saved-world/gameplay integration remain pending.
## Active status-effect storage and notifications
Rust now composes shared status instances with native-order map storage for
entity effect addition, forced replacement, removal, clearing and blend lookup.
It preserves eligibility checks, source identity, hidden-chain updates, blend
transfer, callback order, and mutations made while callbacks are running. Clearing
copies the map before removing live entries and reports effects in the copied
map's iteration order.
The [active-effect comparison](../benchmarks/rust-active-effects-2026-10-07/scoped-parity.json)
matches **535,063 lines per reference JAR** across **1,266 fixtures**, using actual
vanilla entity storage methods, status instances and JDK maps. Native holder hash
and identity-hash values are captured separately for each reference process.
World-library and fixture Clippy checks pass. Eligibility policy, attribute and
packet notifications, and registered effect behavior remain controlled in these
checks. Consumable integration, the entity effect tick loop and complete gameplay
remain pending; this is scoped evidence, not full-game parity.
## Consumable effects bound to entity storage
`StoredConsume` now binds concrete apply/remove/clear consume effects to the
entity's shared active-effect map. Consumable additions use a null source and
ordinary status-instance copies, then execute the entity's eligibility, update
and notification sequence. Sound and RNG services receive access to live storage
so callback mutations affect subsequent operations.
The [combined comparison](../benchmarks/rust-stored-consume-2026-10-07/scoped-parity.json)
matches **1,214,124 lines per reference JAR**, executing actual native consume
effects and inherited entity storage together. It covers both random generators,
probability boundaries, duplicate inputs, hidden chains, colliding holder keys,
map-copy removal order, callback mutations and failure prefixes. World-library
and fixture Clippy checks pass. Eligibility policy, registered effect behavior,
attribute/packet notifications and sound delivery remain controlled. Named
holder-set binding, the full player consumption lifecycle and complete gameplay
remain pending.
## Entity effect tick loop and client particles
The Rust entity effect loop now iterates shared active storage, removes expired
effects before notifying observers, reports hidden-effect restoration, and
refreshes surviving effects on vanilla's 600-tick duration boundary. It uses the
actual level class distinction. Server iteration catches concurrent-modification
failures from map iteration or callbacks; the initial level query and client
iteration preserve their propagating failure behavior. Value replacement alone
does not invalidate iteration, and callbacks continue using captured instances.
Client ticks advance each instance's duration and blend, then read synchronized
particle/ambience data, apply the invisibility-dependent random interval, and
select particles from the captured list. Position queries and the destination
level preserve native evaluation order.
The [effect-loop comparison](../benchmarks/rust-active-effect-tick-2026-10-07/scoped-parity.json)
matches **1,732,406 client-JAR lines** and **1,732,849 server-JAR lines** across
**4,036 fixtures**. Each reference exports its own holder identity hashes; those
hashes can change iteration order and subsequent callback paths between JVMs.
The checks execute inherited native entity and instance methods, native maps,
synchronized data and both RNG implementations. Library and fixture Clippy pass.
Registered effect bodies, attribute/packet notification side effects and random
position methods are still controlled. Complete entity ticking, gameplay and
playable platform builds remain pending.
## Pending-attribute set storage
Rust now implements the default fastutil object set used by vanilla attribute
update and synchronization queues. It preserves null membership, retained key
identity, native slot order, growth/shrink behavior and iterator removal across
wrapped clusters. Iterators read the live slot array and retain their original
remaining count; external mutations have native failure prefixes rather than
HashMap-style concurrent-modification checks.
The [set comparison](../benchmarks/rust-object-open-set-2026-10-07/scoped-parity.json)
matches **611,998 lines per reference classpath** against the pinned fastutil
8.5.18 library. Library and fixture Clippy pass. The gate covers default set
operations and live iteration; alternate constructors, bulk methods, and external
structural mutation followed by iterator removal remain outside its scope.
Binding this storage to attribute refresh and the complete entity lifecycle is
the next step; this collection comparison does not establish gameplay parity.
## Effect eligibility and entity notification bodies
Effect eligibility now follows vanilla's ordered immunity-tag branches. Entity
effect callbacks mark synchronization dirty, retain captured effect/map handles,
apply or remove modifiers, refresh pending attributes and construct passenger
update/removal packets in native order. New effect storage starts dirty, matching
the entity constructor. Attribute refresh reads its captured live fastutil set
and clears that same set only after every callback succeeds. Passenger lists
remain captured while later connection fields and packet data are read at their
native evaluation points.
The [notification comparison](../benchmarks/rust-effect-notifications-2026-10-07/scoped-parity.json)
matches **1,667,616 lines per reference JAR**, exercising inherited native
eligibility, notification, refresh and passenger methods together with effect
storage. The fixtures use actual status instances, pending sets, immutable
passenger lists and packet constructors. Library and fixture Clippy pass.
Modifier bodies, attribute-update consequences and packet transport are still
controlled; their full bindings and complete gameplay remain unfinished.
## Attribute-update consequences and health storage
Pending attribute refresh now dispatches native max-health, max-absorption,
scale and waypoint-range consequences. Lowering a health limit compares against
the first captured maximum, then uses the ordinary setter and its fresh limit
lookup. Health synchronization retains its selected data object through later
callbacks. Waypoint changes capture the server manager before reading the
actor's direct attribute-map field, and track only a strictly positive range.
The [combined attribute-update comparison](../benchmarks/rust-attribute-updates-2026-10-07/scoped-parity.json)
matches **3,111,429 lines per reference JAR**, including the notification corpus,
finite/nonfinite limits, signed zero, dirty health callbacks, map/manager changes
and failure prefixes. Actual native effect notifications, attribute-update
methods, health setters and synchronized Float storage execute together. Library
and fixture Clippy pass. Attribute values, dimension rebuilds and waypoint-manager
internals remain controlled. Registered modifier bodies, full player integration
and complete gameplay/platform builds are still unfinished.
## Effect-modifier map storage
The default fastutil object map now preserves native slot order, nullable keys
and values, growth/shrink behavior and live indexed entry handles. Entry
iterators invalidate their current handle on removal; older handles continue
reading the live arrays at their stored indexes. Callback traversal captures the
key array but reads values from the current array, including after a rehash.
Writes through a stale entry into an empty slot do not make that key present.
The [map comparison](../benchmarks/rust-object-open-map-2026-10-07/scoped-parity.json)
matches **1,142,630 lines per reference classpath** against pinned fastutil
8.5.18. Library and fixture Clippy pass. The gate includes collision clusters,
equal-key identity retention, entry mutation/invalidation and traversal callback
failure prefixes. Alternate constructors, custom default return values,
compute/merge/bulk APIs and external structural mutation combined with iterator
removal remain outside this scope. Effect-modifier and full gameplay integration
remain unfinished.
## Effect attribute templates and permanent modifiers
Effect modifier definitions now scale their amount with vanilla's wrapping
amplifier addition and create a fresh modifier identity on each application.
Application looks up the attribute instance, removes the current definition's
modifier, then reads the indexed definition again before creating and adding a
permanent modifier. This preserves callback changes between those operations.
Missing attribute instances skip template access, and failures retain preceding
map and attribute writes.
The [modifier comparison](../benchmarks/rust-effect-attribute-modifiers-2026-10-07/scoped-parity.json)
matches **1,421,730 lines per reference JAR** across **3,317 fixtures**. It executes
native effect modifier methods with real attribute instances, including cached
values, permanent membership, dirty observers, nonfinite arithmetic, identity,
map rehashes and callback failures. Library and fixture Clippy pass. The fixture
asserts actual holder hashes when constructing collision cases. Attribute-map
lookup and dirty callback receivers remain controlled. Registered effect
definitions, pending queue ownership, entity bindings and complete gameplay/
platform builds remain unfinished.
## Attribute-map creation and live queues
Effect modifiers now have a concrete attribute-map storage path with separate
instance lookup, update and synchronization collections. Lazy creation copies
the supplier before inserting the instance into the lookup map. Its dirty
callback adds the update entry before checking synchronization eligibility, so
a failed creation can leave an instance queued without a lookup entry. Later
lookups can create a second instance while the first remains queued. Successful
creation uses an ordinary map insertion after the callback, preserving callback
replacement, resize and alias behavior.
The [stored-attribute comparison](../benchmarks/rust-stored-effect-attributes-2026-10-07/scoped-parity.json)
matches **4,543,569 lines per reference JAR**. It combines actual native effect
modifier methods, attribute-map lookup, supplier creation, attribute-instance
replacement and permanent modifiers with real pending sets. Native instance
identity hashes are captured separately for each JVM and replayed in Rust.
Library and fixture Clippy pass. Basic-attribute default/synchronization methods
remain controlled callbacks; the fixture covers queue and lookup mutation but
not arbitrary recursive instance mutation. Registered definitions, entity
notification binding, bulk map methods and complete gameplay/platform builds
remain unfinished.
## Entity notifications bound to stored attributes
The stored-attribute adapter now connects effect modifier dispatch to a captured
attribute map, supplier creation and instance operations. Entity effect
notifications and pending refresh execute over the same concrete lookup and
update/synchronization sets, preserving queued instance identity after lookup
aliasing or removal. Refresh clears the pending update set only after every
attribute callback succeeds.
The [combined notification comparison](../benchmarks/rust-stored-effect-notifications-2026-10-07/scoped-parity.json)
matches **21,117,051 lines per reference JAR**. It includes the full previous
stored-attribute corpus plus inherited native entity notification and private
refresh methods, client guards, dirty state, optional reapplication, empty
removal and callback failure/queue mutation. Library and both affected fixture
Clippy checks pass. Native JVM instance hashes are replayed separately per
reference. Attribute-update consequences and basic-attribute default/syncability
methods remain controlled; passenger lists are empty here, with packet behavior
covered separately. Registered attribute/effect definitions, combined health
and dimension consequences, complete entity gameplay and platform builds remain
unfinished.
## Stored attribute queries and base reset
Attribute-map queries now select the existing instance or supplier prototype
without creating an instance. Computed values preserve the selected object's
cache, and modifier queries distinguish missing cached modifiers from supplier
lookup failures. Base reset checks supplier membership before looking up the
current instance, then retains that instance across the supplier base lookup.
Resetting a supported attribute with no instance leaves it uncreated.
The [query comparison](../benchmarks/rust-attribute-map-queries-2026-10-07/scoped-parity.json)
matches **34,531,715 lines per reference JAR**. It includes the full stored
notification corpus, actual native map query/reset methods, mutable supplier
prototypes, reference-sharing modifiers, alias/null lookup entries, prototype
and instance cache state, nonfinite arithmetic and dirty callback failures.
Library and fixture Clippy pass. Present supplier prototypes are non-null;
custom virtual prototype methods, registered ranged-attribute integration and
combined health/dimension consequences remain outside this gate. Complete
gameplay and playable platform builds remain unfinished.
## Live registered attribute component
Registered entity attributes now have a reusable component combining immutable
catalog defaults with lazy instances, effect modifiers and native-order lookup,
update and synchronization collections. Instance handles retain their identity
independently of pending queue membership. Queries and base resets leave absent
instances uncreated; effect application uses the same stored instance and queues
as direct edits.
The [live attribute comparison](../benchmarks/rust-live-attributes-2026-10-07/scoped-parity.json)
matches **8,350,018 lines per reference JAR**. Client and server exports agree on
all **40 attributes and 93 entity suppliers**. The gate includes the previous
entity-attribute corpus, range boundaries, null lookups, modifier identities and
caches, nonfinite values, wrapping amplifiers and independent/bulk queue
acknowledgements. It replays each JVM's actual holder and instance hashes to
compare collection order. Library and fixture Clippy pass.
This component assumes immutable registered defaults with no modifiers; the
exporter verifies that assumption. Mutable custom suppliers and their internal
caches use the separate generic storage interfaces. Combined entity health and
dimension consequences, full gameplay, save/network codecs and the playable native
Linux build remain unfinished.
The production health binding now accepts the live component through computed
attribute queries. Healing, health and base absorption limits, synchronized
health storage and hunger regeneration therefore share its stored values and
modifier engine. The [health integration comparison](../benchmarks/rust-live-attribute-health-2026-10-07/scoped-parity.json)
matches **666,082 lines per reference JAR for each storage backend**, including
all 93 entity suppliers and observer failure prefixes. All `horizon-world`
targets compile, and library/affected fixture Clippy checks pass. This borrowed
binding keeps its selected attribute map fixed; entity virtual dispatch,
attribute refresh, player absorption synchronization and starvation damage
delivery still require their complete runtime integration.
## Entity refresh over live registered attributes
The live attribute bridge now connects virtual entity queries and pending
attribute refresh to registered storage and attribute-update dispatch. Refresh
retains its selected map through callbacks and clears that map's pending set
only on success. Health and absorption consequences read actual computed limits;
waypoint updates retain their manager before reading the direct attribute-map
field. Scale changes dispatch dimension refresh.
The [combined live update comparison](../benchmarks/rust-live-attribute-updates-2026-10-07/scoped-parity.json)
matches **2,836,190 client-reference lines and 2,836,234 server-reference lines**.
It executes actual native notifications, modifier loops, registered attribute
queries, private refresh, health/absorption update bodies and synchronized Float
storage. Cases cover all registered attributes/default suppliers, selected and
captured map/data handles, callbacks changing limits or queues, null pending
members, client guards, empty removals and failures late in large refreshes.
Each reference uses its own JVM identity hashes, so callback-mutation paths can
produce different trace lengths between runs. Library and fixture Clippy pass.
Dimension rebuilding and waypoint-manager internals remain controlled, and
passenger lists are empty in this combined fixture. Full active-effect lifecycle
and particle/invisibility synchronization, player absorption storage and complete
gameplay/platform integration remain unfinished.
## Registered effect presentation synchronization
Effect synchronization now builds the visible particle list in active-map order,
updates invisibility and ambient state, and preserves the server/client glowing
branches and shared flags. It retains each selected synchronized-data object and
clears effect dirtiness only after both visibility and glowing updates succeed.
Removing the particle list leaves the ambient field unchanged, matching vanilla.
The registry exporter verifies all **40 effect factories**: **34** create fresh
colored options, while **six** return constant simple particles. Colored options
use native object identity, so equal colors in newly created lists still trigger
updates. Constant options retain identity. Synchronized storage now supports
shared list values as well as scalar fields, preserving callback and dirty-flag
ordering.
The [effect synchronization comparison](../benchmarks/rust-effect-sync-2026-10-07/scoped-parity.json)
matches **2,754,744 lines per reference JAR**, including repeated updates,
ambient/visible combinations, callback map changes, captured data objects,
reentrant writes/packing and failure prefixes. The
[byte](../benchmarks/rust-effect-sync-2026-10-07/synced-bytes-regression.json) and
[float](../benchmarks/rust-effect-sync-2026-10-07/synced-floats-regression.json)
storage regressions also pass against both JARs. Library and affected fixture
Clippy checks pass, and all world targets compile.
The registered factories have no world access; the exporter verifies their
inherited implementation and captured arguments. This permits an immutable map
traversal during particle creation. Custom impure factories remain outside this
binding. Rendering, transport, full entity lifecycle integration and playable
Linux gameplay remain unfinished. In this pinned version,
`LivingEntity.updateDataBeforeSync` invokes dirty effect synchronization; it is a
separate phase from `tickEffects`.
## Scoreboards, teams and subtitles
`tools/test_scoreboard_parity.py` runs vanilla's own `Scoreboard` (what
`ServerScoreboard` extends) and `horizon_client::scoreboard` on 500 scripted
cases, about 81,000 operations: objectives with every kind of criteria (custom,
team kill, and statistics in every spelling `ObjectiveCriteria.byName` accepts,
which resolve to their canonical names), display names, render types,
auto-updated displays and number formats; scores set, added, read, locked,
given displays and formats, reset, and changed through `forAllObjectives` as the
gameplay criteria change them; display slots; teams with every option, joined,
left, removed and emptied by `Scoreboard.entityRemoved`. Every result must
match, as must each collection in the order vanilla iterates it (objectives,
holders and teams are fastutil open hash maps, each team's members a
`HashSet`; `fastutil_order` keeps the same tables), the
`ScoreboardSaveData.Packed` codec output that `data/minecraft/scoreboard.dat`
holds (fields equal to the codec defaults are left out, components are written
as `ComponentSerialization` writes them), and the data after each side loads
the other's NBT. Only each holder's scores, which vanilla keeps in an
identity-hash map, are compared as a set. `tools/test_save_roundtrip_parity.py`
also builds a scoreboard with every saved field on a vanilla server and requires
the game to save it back unchanged.
`tools/test_team_rules_parity.py` runs vanilla's `Entity.doTeamsAllowDamage`
(friendly fire), `isAlliedTo`, `EntitySelector.pushableBy` (collision rules),
`Entity.isInvisibleTo` (seeing invisible team mates) and
`LivingEntityRenderer.shouldShowName` (name tag visibility, with a stand-in
client) on stand-in entities over 6,000 combinations of two teams' options and
the entities' teams; the game uses the same functions. Mobs on the player's team
never target the player, a creeper's blast spares its team mates when friendly
fire is off, mobs push each other only as their collision rules allow, name tags
show the team's prefix, suffix and colour and the score of the objective
displayed below names, and a team that hides death messages keeps the player's
own death message out of chat. The criteria that follow the player (health,
food, armor, xp, level, air) update as `ServerPlayer.doTick` does, and deaths,
kills, team kills and statistics update their objectives.
`tools/test_subtitle_parity.py` runs vanilla's `SubtitleOverlay` with a
stand-in client on 1,500 scripted cases of sounds reported by the sound engine
(subtitles from sounds.json, positions, ranges, looping) at controlled times,
and frames that render it for a listener transform, the subtitle options (shown
or not, notification display time, text background opacity) and a GUI size.
Every frame's fills and texts, with their pose, must match the overlay in
`hud_overlays`: which captions show and in what order, merging of repeats,
expiry, the fade by age, the direction arrows and the box width. The overlay
listens to the sound engine from the frame that renders it with the option on
until one renders with it off, as vanilla's does.
Not covered yet: the player's air supply (Horizon does not track drowning, so
the `air` criteria stays at 300); displays auto-updated with the player's name
carry the team formatting but not the click, hover and insertion of
`Player.getDisplayName`; there is no attack sweep for allies to be spared from;
scores below other players' names need the multiplayer scoreboard packets,
which the client does not handle yet.
## Loom, crafter and recipe book
The loom, the crafter and the recipe book run on code checked against
vanilla's own classes, and the game uses that code:
- **Menus.** `horizon_client::menu` runs `LoomMenu` (selectable patterns,
`clickMenuButton`, the six-layer limit, quick moves, the result's `onTake`)
and the server's `CrafterMenu` over the block entity (the result following
the grid, `setSlotState`, a filled disabled slot enabling itself) besides the
other menus. `tools/test_menu_click_parity.py` compares 8,000 random click
sequences over 19 menus, with pattern button and slot state packets, with
vanilla's menu classes. The game's loom and crafter screens click through
`Game::menu_session` on the same engine.
- **Crafter block.** `tools/test_crafter_parity.py` runs `CrafterBlock.dispenseFrom`
and `CrafterBlockEntity.serverTick` over the block update oracle's level:
4,000 crafts of datapack recipes (remainders and special recipes included)
with disabled slots and containers, other crafters or air in front, comparing
block changes, container contents, the animation ticks, triggered flag,
comparator signal, shot items, level events and the level random.
- **Recipe placement.** `horizon_client::menu::place` ports `ServerPlaceRecipe`
with `StackedContents`' recipe picker for the inventory, crafting table and
furnaces. Vanilla counts the available items in a hash map ordered by
identity hash codes, an order that changes between runs and decides which
of several fitting items fills an ingredient; `tools/test_place_recipe_parity.py`
hands the oracle's order to the Rust side and compares 6,000 placements.
- **The book's contents.** `recipe_book::ClientBook` keeps the displays as
`ClientRecipeBook`'s `HashMap` of display ids does (on the verified Java
`HashMap` port), whose order is the order of the buttons;
`tools/test_recipe_book_view_parity.py` runs the client's `ClientRecipeBook`,
`RecipeCollection` selection and the tab and page logic over 1,200 cases.
- **The player's book.** `recipe_book::Settings` with a `RecipeIndex` is
`ServerRecipeBook`: awards (advancements, `/recipe`, crafting) never learn
special recipes and count displays, and loading drops unknown recipes;
`tools/test_recipe_book_parity.py` compares 1,500 operation sequences and the
saved compound.
- **Screens.** `screen_layout` holds `LoomScreen.extractBackground` and
`CrafterScreen`'s decorations as draw calls the game paints;
`tools/test_screen_layout_parity.py` runs vanilla's screens on stand-in
menus and compares 1,600 call lists.
- **The book's drawing.** `recipe_book_screen` lays out
`RecipeBookComponent`'s background, search `EditBox`, tab buttons, filter
`CycleButton`, the page of `RecipeButton`s with its number and arrows, the
`OverlayRecipeComponent`, the tooltips and `GhostSlots` as GUI calls with
their poses (squeeze animations, the overlay's 3/8 scale items); the game
paints them. `tools/test_recipe_book_render_parity.py` builds vanilla's
widgets for 600 states (`VanillaRecipeBookScreenOracle`) and compares every
call. Ingredients cycle in their `HolderSet` order (tags in `TagLoader` order).
- **The book's text search.** `recipe_book_view` also searches: the oracle
builds `SessionSearchTrees`' `FullTextSearchTree` over the tooltip lines and
ids of every result a display can show, and `item_tooltip` writes those
lines (attribute modifiers, smithing template, potion, dyed, pot, rocket and
disc lines; the names of potions, templates and banner patterns). Displays
with several results (map copies, tipped arrows, dyed armour) keep them in
`Entry::variants`.
- **The ghost recipe.** `Book::ghost` is the one place that fills a recipe's
ingredients into the grid or furnace slots (`PlaceRecipeHelper`'s centring,
the first slots for shapeless recipes, every fuel for an empty fuel slot, in
the registry's component-lookup order); `tools/test_ghost_recipe_parity.py`
runs `fillGhostRecipe` against the real menus for all 2,698 recipe and menu
pairs.
- **The loom's banner preview.** `item_render::gui::draw_banner_result` draws
`GuiBannerResultRenderer`'s flag model and layers in the 20x40 picture in
picture, lit with `ITEMS_FLAT`; the block entity render harness compares its
quads under the picture-in-picture pose.
Not compared yet: furnaces awarding their recipes and experience when their
output is taken, and the limited crafting game rule. Tooltips in general lack
the lines `item_tooltip` writes for recipe results (the game's hover tooltips
do not show attribute modifiers yet).
`horizon_client::hotbar` reads the creative toolbars of an older `hotbar.nbt`
through the `HOTBAR` data fixers (`EmptyItemInHotbarFix`, every item stack
fixer): `tools/test_hotbar_parity.py` loads generated files of 34 versions with
the real `HotbarManager`. The toolbar screen (save and load keys) is not wired
to it yet.
## Player list and world border rendering
`horizon_client::tab_list` lays out vanilla's `PlayerTabOverlay` as draw calls:
the listed players in `PLAYER_COMPARATOR` order (higher list order first,
spectators last, team name, profile name ignoring case), at most 80 of them in
columns of 20 rows; heads with the default skin for the UUID, flipped for
Dinnerbone and Grumm in the level, only in online mode; team prefix, suffix and
colour; italic, translucent spectator names (italic at the component root);
ping bars; the `list` objective as scores in their number format (the score's,
the objective's, or yellow) or as hearts with `HealthState`'s change blink and
the `hp` text when they no longer fit; and the header and footer wrapped by
`Font::split_lines`, which now follows `StringSplitter.splitLines`, with legacy
`§` codes applied by `Text::resolve_legacy`. `Hud.extractTabList`'s rule hides
the list in single player unless a `list` objective is set.
`tools/test_tab_list_parity.py` runs vanilla's own `extractRenderState` frame
after frame (`VanillaTabListOracle`: stand-in connection, level and options,
real `Scoreboard`) against `examples/probe/tab_list_fixture.rs` and compares every fill,
styled text run, sprite and face blit (as `hud_layout::GuiDraw` calls, painted
like the rest of the HUD): **1,000 cases, 3,476 frames and 844,758
calls match**. The game draws the same layout while the player-list key is
held, from the local player and scoreboard in single player and from
`PlayerInfoUpdate`, the tab list header and footer, `onlineMode` and the
objective, score, display-slot and team packets on a server (checked live
against the 26.3 server jar). Real skins are not downloaded: every head uses
its default skin. Horizon has no "chat background only" option, so the row
background is vanilla's default.
The client `WorldBorder` wraps `horizon_core::geometry::border` (checked by
`test_world_border_parity.py`) and `horizon_client::border_render` ports
`WorldBorderRenderer`: `extract`'s fade (`(1 - distance / render distance)^4`)
and status colour (growing, shrinking, stationary), the wall buffer that is only
rebuilt when the border moves, the model offset and the texture scroll of
`prepareDynamicTransforms`, and `draw`'s walls within the render distance,
nearest first. `WorldBorder::warning_strength` feeds `hud_overlays::vignette`
the border's warning strength at the camera. `tools/test_world_border_render_parity.py` drives vanilla's border,
renderer and vignette (`VanillaWorldBorderRenderOracle`, with a stand-in GPU
device, dynamic-uniform writer and render pass) against
`examples/probe/world_border_render_fixture.rs`: **1,500 cases and 16,859 frames match
bit for bit** (8,513 with the wall drawn). The game draws the wall with
`RenderPipelines.WORLD_BORDER`'s overlay blend and the mipmapped forcefield
texture, and follows the server's border packets. One border is kept for all
dimensions.
## Resource pack screen and chat input
`horizon_client::pack_screen` is `PackSelectionScreen` as a state machine: the
header, the two `TransferableSelectionList`s (rows, header entries, scroll,
selection outline, focus, the hover overlay and its arrows, incompatible packs'
tint and text), the search box filtering both lists, the footer buttons and
their tooltip, and the `ConfirmScreen` an incompatible pack opens, all as GUI
calls (`screen_draw`, which also ports `StringWidget`, `MultiLineTextWidget`,
`Button`, `EditBox` with its scrolling, and `StringSplitter`'s line splitting
with legacy formatting codes, whose split remainder takes the style found at
the break). `tools/test_pack_screen_parity.py` drives a real
`PackSelectionScreen` (`VanillaPackScreenOracle`) through 3,000 random sessions
of clicks, typing and the mouse wheel and compares every call.
`horizon_client::chat::ChatScreen` ports `ChatScreen`, its `EditBox` and
`CommandSuggestions`: argument colouring, usage lines and parse errors, the
suggestion list (sorting, hover, Tab and Shift+Tab, clicks), history and the
input's editing keys. `tools/test_chat_screen_parity.py` runs a real
`ChatScreen` (`VanillaChatScreenOracle`) on the client command tree an operator
receives (`Commands.sendCommands`, `ClientboundCommandsPacket`), whose custom
suggestions the server's dispatcher answers as
`handleCustomCommandSuggestions` does, over 3,000 sessions, and compares the
input's state and every call. Not compared: the mouse wheel over the
suggestion list and clipboard keys.
`horizon_client::creative` is the creative inventory. Its tabs come from
`creative-inventory.json`, which `VanillaCreativeTabsExporter` writes from
vanilla's own `CreativeModeTabs.tryRebuildTabContents` over the vanilla data
pack's registries: every stack with its components, and for each stack of the
search tab the creative tooltip lines and item tags that the client's search
trees index. The search ports `refreshSearchResults` and `SessionSearchTrees`
(substring matches over tooltip lines; after a colon the item id's namespace,
then its path or a line; tags after `#`), and the scrollbar follows
`mouseClicked`/`mouseDragged`/`mouseScrolled`. `tools/test_creative_parity.py`
compares every shown tab's stacks, the search tab and 6,793 searches (fixed
queries, then every stack's item path and tooltip lines and every tag, whole
and halved) with the vanilla client's own search trees
(`VanillaCreativeSearchOracle`). Not compared: the operator tab (off by
default, `operatorItemsTab`) and the saved hotbars tab.
## Pillager patrols and raids
`horizon_client::patrol` ports `PatrolSpawner` and `horizon_client::raid` ports
`Raid` and `Raids`; the game runs both through them. `tools/test_raid_parity.py`
runs vanilla's own classes in `VanillaRaidOracle`, on a stand-in `ServerLevel`
built from boxes of blocks and light, a biome, village sections, village points
of interest, unloaded and non-ticking chunks, the overworld clock, local
difficulty inputs and one player, and compares every case with
`raid_fixture` line by line. The last run matched **1,900 cases at zero
differences**: 1,000 patrol, 400 raid and 500 codec cases, with 474 patrol
pillagers, 1,364 raid waves, 11,399 raiders, 149 victories and 21 losses.
- **Patrols**: the countdown, `isBrightOutside`, the one in five draw, the
random player, spectators, the village distance, `hasChunksAt`, the
`can_pillager_patrol_spawn` attribute (mushroom fields and the early game
timeline), the group size from the local difficulty, each member's
`MOTION_BLOCKING_NO_LEAVES` height, empty spawn block and spawn rules, the
leader's banner and patrol target, and the level random's next draw.
- **Raids**: `createOrExtendRaid` (spectators, the `raids` rule,
`can_start_raid`, the mean of the village points of interest, the omen
absorbed, the trigger stat), the pre-raid countdown and its cached spawn
position, `findRandomSpawnPos` with Mth's sine table, wave composition by
difficulty and the bonus wave, leaders and riders, `addWaveMob`,
`removeFromRaid` and `updateBossbar` (progress bits with Java's `clamp`, the
title, visibility, the player in the bar), the horn's seed and position,
`updateRaiders` (far, gone, removed and idle raiders), joining, loaded and
hurt raiders, victory with Hero of the Village, loss, the celebration,
peaceful, the `raids` rule, unloaded chunks, `/raid setomen` and `stop`, and
the 48,000 tick timeout.
- **raids.dat**: `Raids.CODEC` with `resultOrPartial` over generated and
mutated data: numbers of any NBT type converted as Java does, partial
results (`Util.fixedSize` streams, lists with bad elements, a non-list
`raids`), raids dropped on missing fields, a fresh `Raids` when `next_id` or
`tick` is missing, the heroes in `HashSet` order and the raids in the order
of fastutil's entry-set stream (the tick iterates the map the other way).
Each dimension keeps its own `dimensions/<id>/data/minecraft/raids.dat`.
Scope and remaining gaps: `finalizeSpawn` runs with a fixed random in the
oracle, so its own draws are not compared, and raid buffs and patrol targets
come from unseeded entity randoms. The omens act inside the effect tick
(`BadOmenMobEffect`, `RaidOmenMobEffect`), including the
`ConcurrentModificationException` vanilla swallows when Raid Omen is first
added; this has unit tests but no oracle, since the effect map's order follows
identity hashes. Raider goals (pathfinding to the raid, celebrating, fetching
the banner, villagers' raid activities) and ravager riding are not compared:
they belong to the mob AI harness. The game's village distance comes from the
points of interest its villagers claim rather than a `PoiManager`.