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

38 KiB
Raw Blame History

Feature placement

Horizon implements eighteen Java Edition 26.3 placement modifiers: count, square spread, three-axis offset, height range, rarity filtering, random-chance filtering, fixed positions, random selection among modifiers, heightmap positions, surface-relative thresholds, water-depth filtering, both noise-based count forms, cuboid placement, count-on-every-layer scans, predicate filtering, vertical environment scans and biome filtering.

Placement preserves callback order, duplicate positions, wrapping coordinates, provider sampling and random draw consumption. Fixed positions are filtered by the origin's chunk X/Z. Random-choice nesting is bounded to depth 256 and 65,536 modifier nodes; lists are bounded to 8,192 entries. Consumer failures retain previous emissions and consumed draws. Probability-range decoding distinguishes negative zero from positive zero, matching the native codec.

Run python3 tools/test_placement_parity.py. The local report records 1,268,600 matching lines per reference JAR, across 520 definitions and 21,840 seeded fixtures, with 70,080 requested modification calls and 17,280 direct count queries. Actual PlacementModifier.CODEC and modifier callbacks supply the reference results. The corpus tests both random modes, relative-height overflow, nested choices, malformed fields, repeated calls, consumers that draw randomness and failures before or after earlier emissions.

This suite uses an actual placement context backed by a world proxy with traced synthetic heightmaps. Additional block predicates, full feature registries, registry integration, Gaussian providers, encoding and complete generated worlds remain pending.

Modifier chains

FeaturePlacer collects a modifier's outputs before processing its descendants, preserving sibling order through a reusable pending stack. Actual feature callbacks share the modifier random stream. Boolean results accumulate without skipping later placements. An empty chain calls the feature directly. Errors retain pending and modified-position buffers, matching vanilla when the same placer is used again.

Run python3 tools/test_feature_placement_parity.py. The chain report records 548,461 matching lines per JAR. Actual native FeaturePlacer executes native modifiers and a synthetic feature sink, comparing emitted positions, feature results, consumed randomness and repeated calls after failures. This tests the execution machinery; the separate feature suite below connects it to initial block-writing algorithms.

Rust bounds chains to 1,024 modifiers and per-call work/pending buffers to 2^20. Unit tests reject oversized chains before feature calls or random draws and stop multiplicative count expansion after bounded partial progress.

Heightmap queries

PlacementContext supplies generation bounds, the world minimum and heightmap queries. These are separate: the world minimum can differ from the generator's minimum. Heightmap placement emits only above the world minimum. Surface-relative thresholds use 64-bit sums; water-depth subtraction retains Java integer wrapping. Queries and emitted positions retain native order, and query errors stop the current operation without drawing randomness or emitting a replacement position.

Both suites include ordinary/extreme synthetic heights, all six map types, distinct world/generator minima, default/reversed threshold ranges and injected query failures. This validates how modifiers consume heightmaps; connecting feature generation to complete world regions remains pending.

Noise-based counts

Noise-based and noise-threshold counts share vanilla's fixed biome-information simplex noise. They preserve the float noise result before double arithmetic, ceiling/narrowing behavior and strict threshold comparison. They consume no caller random draws. repetitions exposes counts without expanding position lists.

Primitive comparisons include extreme ratios, factors, offsets and thresholds, including count-only queries that can return enormous counts safely. Chain tests exercise ordinary noise counts together with other modifiers and feature callbacks. Emitting repeating modifiers remains bounded to 4,096 repetitions in Rust.

Cuboid placement samples Y size, then independent X and Z sizes, before emitting positions in X/Y/Z order. Its extents are inclusive. Edge and interior controls are independent, and coordinates wrap. The suites cover all four control combinations, provider variation, maximum decoded extents, consumer failures and cuboids embedded in modifier chains.

Layered placement

Count-on-every-layer placement samples its count provider at every loop-condition check, including the final check. Each attempt draws X/Z offsets, queries the motion-blocking heightmap and scans down for the requested surface layer. Air states and exact water/lava blocks count as empty. Bedrock is excluded as ground; other blocks, including bubble columns, are potential ground. Emissions and count sampling interleave, sharing the caller's random stream.

The suites compare traced synthetic strata containing these block kinds, random count providers, repeated passes and injected height/block/consumer failures. LayerBlock is a query classification supplied by the world; complete block behavior and region-backed feature generation remain pending. A shared 65,536-step budget bounds scans, including when world queries supply extreme heights.

Initial block-writing features

BasicFeature implements the void start platform, End islands and the End arrival platform. The world supplies block reads and mutations; required block states bind through its registry. The algorithms preserve write order, update flags, wrapped coordinates, ignored false mutation results and partial effects when a world operation fails. End-island radius sampling retains native draw order.

Run python3 tools/test_basic_feature_parity.py. The feature report records 1,820,614 matching lines per JAR. Actual feature codecs and native place methods operate on a mutable world proxy. Both random modes, multiple seeds, nearby/far chunks, extreme coordinates, rejected writes, thrown failures and repeated placements are compared, including complete accumulated stored states. Native setBlockAndUpdate executes its actual default method, exposing its flags. The suite also composes these features with modifier chains, sharing the mutable world and random stream and reusing placer buffers after failures. place_with_context gives feature callbacks the same world used by modifiers.

This covers feature algorithms against a supplied mutation interface. Actual chunk mutation, neighbor updates, lighting, entities/drops, complete feature registries and generated worlds remain pending. The End-platform generation path does not request the separate block-destruction/drop behavior used by portal recreation.

Predicate filters and environment scans

Logical predicates (true, not, all_of, any_of) preserve ordered short-circuiting, including empty-list identities. Inside-world-bounds predicates apply wrapping offsets before checking the world's inclusive height bounds. Their offset codec retains Java's wrapping absolute-value behavior for the minimum signed integer. These five predicate kinds also work inside filters and vertical scans; state-dependent predicates remain pending.

Environment scans check the initial allowed condition before the target, then move one block at a time. They stop immediately outside build height. A failed allowed condition after a move still permits the final target test at that position. The codec limits scans to 1–32 steps; shared work and nesting budgets also bound directly constructed predicates and scans.

Both placement suites compare actual native codecs and predicate execution, traced world-bound queries, short-circuiting, wrapped offsets, invalid directions, step limits and query failures. Unit tests exercise oversized trees and scans that would otherwise traverse billions of positions.

Resident feature regions

TerrainRegion routes block reads/writes by signed chunk coordinates and lazily primes missing heightmaps. It requires consistent vertical bounds and registry identity across resident chunks. Its placement context supplies generation bounds, world bounds, heightmaps and layer classifications from those same chunks. TerrainFeatureAccess shares that region with feature algorithms and forwards all writes and flags to a caller-supplied mutation callback.

Run python3 tools/test_feature_region_parity.py. The region report records 28,922,634 matching lines per JAR across 192 fixtures and 2,304 requested placements. Actual native features and FeaturePlacer operate over a world proxy backed by nine ProtoChunks per fixture. The comparison includes both heightmap groups, count/square/heightmap/layered chains, repeated writes, missing neighbors, rejected writes, failures, update flags, random state, final raw sections and packed heightmaps.

The region corpus also runs a composed sequence of configured fill layers over the same live chunks: water, horizontal logs, air and an out-of-height layer. This connects block-state decoding, nested feature execution, shared placement and chunk heightmap updates; the standalone fill-layer suite below covers integer-property parsing and per-column query ordering in more detail. Single-block replacement and scattered ore also execute against those resident chunks, sharing the updated state, heightmaps, failure policy and random stream.

The reference proxy supplies storage mutation. This verifies the connection between features, placement queries and resident chunks; actual region write zones, POIs, block entities, postprocessing, lighting and the full feature registry remain pending. Unit tests check mixed-registry/bounds rejection, negative chunk routing, missing-neighbor errors and mutation-callback flag/return handling.

Inline feature composition

FeatureDefinition and PlacedFeatureDefinition compose the initial block-writing features with no-op, sequence, boolean choice, uniform choice, chance-based choice, weighted-choice and overlay features. Overlays execute every child and accumulate success. Sequences stop on the first false child. Selectors return the chosen child's result directly. Child modifiers and features share world state and randomness; each sequence uses one fresh placer across its children. Native single-object holder-set shorthand is accepted for sequence/uniform lists.

Run python3 tools/test_feature_graph_parity.py. The graph report records 1,146,057 matching lines per JAR for actual native feature codecs and nested placement. Cases include empty/malformed lists, singleton and nested choices, zero/one probabilities, negative zero, false child results, partial writes, world failures, repeated calls and top-level modifier chains.

Decode depth, node count and list size are bounded. Feature/placed-feature visits and nested modifier-chain execution share a runtime budget. Unit tests cover deep decoding, large trees and modifier expansion that reaches no leaf features. Weighted choices retain zero-weight children and return false without random draws when their total weight is zero. Positive totals preserve the existing bounded weighted draw algorithm. OptionalWeightedList keeps this behavior separate from the nonempty weighted lists required by integer/height providers. The graph suite covers empty/zero lists, integer narrowing, invalid/overflowing weights and nested choices; both provider suites also pass after the shared implementation change. The resource resolver below adds named holders and tags. Encoders and the full feature registry remain pending.

Named feature resources

FeatureCatalog stores an immutable resource snapshot and resolves requested feature or placed-feature roots. Named children, compact singleton holder sets, mixed named/inline lists and ordered placed-feature tags expand into executable trees. Resolution preserves numeric JSON fields, validates unselected children and bounds dependency depth and expanded nodes. Duplicate canonical resource names, missing dependencies and cycles return errors.

Run python3 tools/test_feature_catalog_parity.py. The catalog report records 2,528,704 matching lines per JAR against actual registry-aware holder codecs, registered feature snapshots and native TagLoader. It compares ten named features, fifteen placed features, five tag files, nested/duplicate references, optional/required missing tags, namespace shorthand and subsequent execution. Configured fill layers, single-block replacements and scattered ore bind block-state metadata through named roots, nested tags and modifier chains. Block-set predicates execute through filters, ordered volumes and environment scans against the mutable world, including repeated placement after earlier writes and flowing-water replacement that changes later fluid-predicate results.

Empty named tags retain a native distinction from empty inline lists: a sequence can resolve an empty tag and succeed, an overlay returns false, while a uniform selector resolves it and then fails its zero-bound random choice. The inline list codecs reject empty lists during decoding. These cases and their random-state effects are compared.

The resource API validates requested reachable roots, allowing unused unported resources to remain in the snapshot. Complete datapack loading, live reloads, registry ownership/identity semantics and all feature algorithms remain pending.

Configured fill layers

FeatureBlockBindings binds block-state JSON against the pinned registry and its integer-property metadata. FillLayerFeature visits 16 by 16 columns in native X/Z order, queries the world minimum for each column and replaces only air states. It preserves wrapping coordinates, write flags, ignored false write results and early termination on world errors. Placement consumes no random draws.

Run python3 tools/test_fill_layer_parity.py. The fill-layer report records 1,305,274 matching lines per JAR, including codec inputs for all 35,723 pinned states, compact/full states, integer properties, invalid-property defaults, height bounds, extreme coordinates, rejected writes and injected query, read and write failures. Both random implementations retain matching subsequent state. This comparison uses observable mutable storage; complete region effects and full generated worlds remain pending.

Replacement rules

RuleTest implements all ten pinned replacement-rule kinds: constant true, negation, all/any, block identity, exact block state, block tags, inclusive height range and the two random block/state matches. Logical rules preserve ordering and short-circuiting. Random rules draw only after matching identity, including at zero/one probability; their codecs allow probabilities outside that interval.

Run python3 tools/test_rule_test_parity.py. The rule report records 1,890,716 matching lines per JAR, covering every block/state identity, all 300 bundled block tags, absent and empty-path tags, malformed inputs, reversed height ranges, nested logic and subsequent draws from both random streams. Decode depth, node count and list size are bounded, including branching inputs.

Bindings share immutable sets of state IDs for blocks and resolved tags. Native tag identifiers permit an empty path, which remains distinct from a missing field. These predicates do not establish complete ore generation or structure-processor behavior.

Single-block replacement

ReplaceBlockFeature applies ordered rule/state pairs at an origin. It re-reads the current state for each candidate and stops at the first match, regardless of whether the write returns true. Empty or unmatched target lists still succeed; read/write errors stop evaluation with the preceding mutations and RNG state intact. Target decoding shares the graph's node budget when nested in a feature.

Run python3 tools/test_replace_block_parity.py. The replacement report records 303,678 matching lines per JAR for actual native codecs and placement, including rule ordering, shared random state, update flags, rejected writes, partial failures, repeated mutations and invalid unselected targets.

Scattered ore

ScatteredOreFeature samples the native attempt count, consumes six float draws for each offset (including zero spread), rounds with Java's tie behavior and captures the candidate state once per attempt. Matching target rules then apply the configured air-exposure probability and ordered directional neighbor checks. The first accepted target writes with flag 2; a false write result is ignored.

Run python3 tools/test_scattered_ore_parity.py. The scattered-ore report records 758,484 matching lines per JAR across 11,544 fixtures and 23,088 requested placements. Cases include size/discard codec boundaries, empty targets, solid/mixed worlds, each of the six air-neighbor directions, wrapping positions, partial failures, rejected writes and both shared random streams.

Ordinary ore veins use a separate bulk-section mutation path and remain pending. A private Linux/JDK 25 probe also found 4,935 bit differences among 2,581,112 double sine/cosine results for sampled ore angles when using Rust platform math. Those intermediates are not yet validated for vein geometry; no exact double-trig parity is claimed. This scoped feature comparison does not establish biome decoration or full generated-world parity.

Bulk section access

TerrainBulkAccess exclusively borrows resident chunks and caches sections by vanilla's packed section key. Raw reads retain cave/void air even when normal chunk reads collapse an all-air section to AIR. Outside-height reads return AIR before resolving a chunk. Raw writes maintain section occupancy but leave chunk heightmaps untouched; region write permissions remain the caller's responsibility.

Run python3 tools/test_bulk_section_parity.py. The bulk report records 959,640 matching lines per JAR against actual BulkSectionAccess and resident ProtoChunks. It covers every state, primed/lazy heightmaps, missing chunks, vertical boundaries, packed-coordinate aliases, cache reset and complete final section/heightmap data. The exclusive Rust borrow replaces Java's acquire/release locks within this resident-region API; concurrent section ownership remains pending.

Biome feature ordering

build_features_per_step combines ordered biome feature lists using placed-feature object identities. It assigns encounter indices, orders graph traversal by step and index, detects cycles and reverses DFS completion order to produce each step's feature list and identity-to-index mapping. Equal feature payloads remain separate when their identities differ. Traversal uses an explicit stack and bounded input work; tests include a 50,000-feature dependency chain.

Run python3 tools/test_feature_sort_parity.py. The ordering report records 411,751 matching lines per JAR across 3,871 fixtures. Native FeatureSorter receives identity-distinct dummy payloads for synthetic graphs and the feature lists from all 67 bundled biomes, including shuffled/subset combinations. Cycles, duplicate references, cross-step reuse, empty steps and index lookups are compared. Cycle diagnostic minimization/text and complete decoration remain pending.

World-generation random streams

WorldgenRandom applies vanilla's legacy bit-sampling interface to legacy or xoroshiro sources. Longs and doubles retain their multi-draw assembly, bounded integers retain legacy rejection and each bit request increments a wrapping count. Reseeding preserves the counter. Forks and positional factories delegate to the underlying source and return its normal draw behavior through a transparent variant.

Run python3 tools/test_worldgen_random_parity.py. The random-adapter report records 238,800 matching lines per JAR for 119,100 operations in 300 fixtures. It includes one to three nested wrappers, arbitrary signed bit-width shifts, failed bounds, parent/child streams, positional UTF-16 hashes and decoration, feature, large-feature, salted and slime-chunk seeds with int/long overflow. Gaussian sampling, concurrent use and complete chunk decoration remain pending.

Biome placement filtering

Biome requires the invocation's top placed feature before querying the biome and its generation settings. place_with_biome_check establishes that context for the current modifier chain; a nested ordinary placer starts without it. Biome resolution and membership are supplied by the world. Native membership treats structurally equal placed-feature copies as equal, while feature ordering uses object identities.

Run python3 tools/test_biome_placement_parity.py. The biome-filter report records 121,423 matching lines per JAR for 27,456 requested placements across 9,152 fixtures. Actual BiomeFilter and FeaturePlacer compare missing roots, equal copies, different features, ordered queries, nested ordinary placement, injected failures and repeated calls with retained buffers. Both random sources are tested directly and through WorldgenRandom. Complete registry membership, biome sampling integration and chunk decoration remain pending.

Decoration feature scheduling

FeatureDecorationPlan owns immutable biome generation lists and their globally ordered step features. It reads the surrounding nine chunks in X-first order, intersects their resident biomes with the generator's possible-biome set and executes the sorted union of global feature indices for each step. Each root receives its decoration/feature seed and a shared placer. False results do not skip subsequent roots. Errors retain previous work and the current-feature marker; successful completion clears the marker.

Run python3 tools/test_decoration_parity.py. The decoration report records 337,398 matching lines per JAR across 2,188 fixtures and 4,376 requested decoration calls. Actual ChunkGenerator.applyBiomeDecoration runs with structures disabled, resident ProtoChunk biome palettes and traced feature sinks. All 67 bundled biome feature lists participate. Sparse selections, excluded biomes, step gaps, cycles, coordinate overflow, biome-check modifiers, false returns and query/placement/progress failures are compared. Rust bounds extreme signed chunk neighborhoods and input work explicitly. Structure decoration, real feature payloads in this suite and full chunk lifecycle remain pending.

Decoration over resident chunks

BiomeRegion connects quart biome sections to seeded block-position biome zoom. TerrainRegion::biome_at checks the selected chunk's generation stage before reading its biome. Premature reads fail even if raw palette values already exist; decoration's initial palette collection can still inspect those raw values.

Run python3 tools/test_decoration_region_parity.py. The resident decoration report records 36,173,951 matching lines per JAR for 240 fixtures and 480 requested decoration calls. Java's actual decoration entry point places three basic features, configured fill-layer sequences, replacements and scattered ore into nine ProtoChunks. Uniform/mixed biome palettes, biome zoom, global scheduling, feature seeds, biome filters, named biome-set and volume predicates, live heightmaps, repeated decoration and rejected or failed writes execute together. Every final raw block and heightmap word is compared, including EMPTY, BIOMES and TERRAIN stage behavior. Workspace tests, Clippy and 137 Python tests passed after this integration.

The world transport supplies storage mutation. Actual WorldGenRegion write zones, retrogen, POIs, block entities, postprocessing, lighting, structures and the complete noise-to-decoration lifecycle remain pending.

Block-state providers

BlockStateProvider supports simple states, weighted states, random blocks, rotation, property copying, randomized integer properties, three noise-based providers and rule-based selection. Direct decoding preserves the exclusive choice between a full state object and a typed provider. Random block choices retain holder/tag order and duplicates. Empty choices return no optional state, while ordinary sampling reads the existing world state. Rotation chooses a direction before sampling its nested provider, changes only compatible axis/facing properties and preserves other properties. Fixed directions consume no direction draw. Property copying preserves only compatible property families. Integer sampling leaves states without the named integer property unchanged without drawing a value; invalid sampled values fail after consuming the draw. Shared compact state layouts avoid rebuilding property maps during these transforms.

Noise providers use independently seeded legacy noise and preserve the native float/double coordinate conversions. Threshold selection preserves conditional random draws. Dual noise computes the selected candidate directly: the other candidates are pure noise queries without random or world side effects. Rule-based providers evaluate conditions in order, continue after empty optional results and sample their fallback only after all rules are exhausted. Current conditions cover true, not, all-of, any-of, world bounds, height ranges, block tags volume matching and registry-bound block/fluid/biome sets. Height-range predicates reconstruct generation bounds per evaluation; volume matching visits X, Z, then Y and stops on the first failure. World adapters supply tag membership and fallible generation-bound queries. The height-range comparison currently exercises ordinary LevelAccessor bounds; the WorldGenLevel server/generator lookup branch is not covered by this fixture.

Interrupted runs can be resumed with python3 tools/test_state_provider_parity.py --resume. The runner verifies source, environment, fixture and compiled-artifact hashes before reusing completed stages. A changed output is regenerated; changed inputs require a fresh run without --resume. Checkpoints stay under .local, and a parity report is written only after both native comparisons pass.

Run python3 tools/test_state_provider_parity.py. The provider report records 257,299,719 matching lines per JAR, with 356,935 codec inputs and 79,473,574 requested samples across ten provider kinds. Every pinned state is decoded and tested under all six fixed rotations; random/nested rotations, both sampling methods, empty tags, weighted boundaries, fallback query failures and four random-stream combinations are compared. Every target state is also tested against representatives of all compatible property values, including cross-block copies and integer-property boundaries. The native harness verifies that the pinned property-family signatures are unambiguous; Rust validates the Cartesian state tables before binding their transitions. Noise cases include all eight bundled provider definitions, coordinate boundaries, extreme scales, variety selection and malformed codec inputs. Rule cases cover ordered empty results, fallbacks, nested rules, bound-query failures and wrapping coordinates. Block-tag cases cover every tag against all block defaults and the two bundled rule-based definitions against every state. Volume comparisons include exact read order and early exits, while Rust-only tests bound pathological nested evaluation work. Workspace tests, Clippy and 137 Python tests passed at this stage.

The unobstructed predicate validates its optional integer-vector offset but ignores it during evaluation, matching the pinned Java implementation. The comparison checks exact translated unit-block bounds, a null excluded entity, true/false world results, nested volumes and query failures.

BiomePredicateBindings resolves biome names and holder tags to IDs in the world's owning registry snapshot. The comparison covers every pinned biome and every bundled biome tag, empty sets, missing names, query failures and nested volume/short-circuit conditions. Decoration shares the same BiomeId identity.

FluidStateTable derives fluid identity, amount, falling/source flags and own height from the pinned block descriptors. The harness compares every state, including falling source water in all eight waterlogged copper grates. Fluid predicates compare all five fluid kinds, holder lists and every bundled fluid tag against every block state. This covers contained fluid state, not spreading or entity interactions.

StateProviderCatalog resolves named holder references from an immutable resource snapshot. Holder, direct and typed entry points retain their distinct decoding semantics, including ambiguous state/provider objects. The comparison registers all eight bundled definitions plus nested fixture resources and checks missing names, nested references and later random draws. Unused resources are not evaluated; Rust tests reject reachable cycles, duplicate canonical names and excessive expansion. Full datapack registry loading/reloading remains separate.

Recursive provider decoding is bounded to depth 128 and shares a 65,536-node budget with rules, predicates and integer providers; holder/rule lists are bounded to 8,192 entries. Additional predicates, complete registry loading, block survival and complete feature placement remain pending.

Solid and replaceable predicates now bind immutable property tables to the same block registry as provider and feature decoding. Their offsets, state reads, short-circuit composition, empty optional-child fallthrough and read failures are compared across the entire state registry. Together with the earlier kinds, 14 block-predicate kinds are covered in the state-provider report. Full workspace tests, warning-free Clippy and 139 Python tests passed at this integration snapshot. Face support and block survival still need implementation.

The has-sturdy-face predicate now decodes native directions and bounded offsets, then delegates the ordered state/support query to its placement world. The state-provider comparison covers all 35,524 cached states, six directions, read failures and composed predicates, bringing the scoped corpus to 15 predicate kinds. Dynamic state support is not established by this integration report; unavailable world behavior remains an explicit query error. Workspace tests, warning-free Clippy and 140 Python tests passed at this snapshot.

Unified block-support dispatch now extends that predicate corpus to all 35,723 states, including the 199 dynamic states with absent block entities and injected block-entity query failures. The updated provider report covers the combined path. Shulker animation snapshots are covered separately by the dynamic support suite; active moving-piston shape generation and block-entity lifecycle remain outside this integration snapshot. Workspace tests, Clippy and 141 Python tests passed.

The would_survive predicate now decodes a candidate block state and bounded offset, then invokes survival at the translated position without placing it. Both provider and feature bindings use the shared block-state codec; placement chains forward the survival query. The refreshed provider report records 257,299,719 matching lines per JAR, 356,935 inputs, 79,473,574 requested samples and 16 predicate kinds. Its survival snapshot now covers all 51 pinned survival families, 1,286 definitions and 35,723 states. Behavior representatives run against the entire neighbor-state registry, with malformed definitions, wrapped offsets, brightness thresholds, short-circuit conditions, optional-child fallthrough and query failures. Dynamic support and empty-context collision use absent block entities in this integration corpus; animated shulker and active-piston snapshots have separate geometry comparisons. This establishes the recorded provider/predicate integration, while full block placement, updates and gameplay remain pending. Workspace tests, warning-free Clippy and the 147-test Python suite passed.

Simple-block placement

SimpleBlockFeature combines holder-aware state providers, candidate survival, precompiled placement transitions and optional scheduled ticks. Ordinary writes retain native flags and ignore a rejected write's boolean result. Double plants first check the block above for air or matching replaceable fluid, then copy waterlogging independently into each half. Pale moss carpet derives side states from support/collision queries, consumes the world's separate random stream for its topper and recomputes lower sides after the upper write. Scheduled ticks use the block actually stored after placement, including rejected writes.

FeatureBlockBindings::with_state_providers binds these operations and provider resources to the same registry. FeatureDefinition and FeatureCatalog compose simple blocks with named holders, selectors, sequences and placement modifiers. Provider decoding shares the enclosing graph's bounded work/depth budget; compiled placement tables are shared by immutable reference-counted handles.

Run python3 tools/test_simple_block_parity.py against the pinned JARs. The simple-block report records 9,613,872 matching output lines per JAR across 492,554 fixtures. It covers every candidate state, double-plant obstructions, waterlogging, moss attachment/topper combinations, ordered reads/writes, both random streams, rejected writes, partial failures, wrapped coordinates and nested composition. Workspace tests and all-target Clippy passed. Native chunk updates, scheduled tick execution and complete playable integration remain pending.

Block columns

BlockColumnFeature samples all layer heights before checking space. Its first obstruction query is one step beyond the origin. It removes excess height from the base or tip in native order, then evaluates providers while writing the remaining layers from the original position. Empty optional providers use their ordinary fallback read; rejected writes still advance the position. A completely zero-height sample returns false, while obstruction that trims every layer to zero still returns true.

The block-column report records 1,204,081 matching lines per JAR over 64,120 fixtures. Run python3 tools/test_block_column_parity.py to compare all candidate descriptors, six directions, both truncation priorities, provider and predicate composition, wrapped coordinates, codec bounds and partial mutations. This uses the same feature/catalog integration and mutable-world harness as simple-block placement. Layer/decode limits and a million-operation runtime budget reject pathological workloads explicitly; complete resident-world integration remains pending.

Vertical pillars

The single-block pillar feature checks its replacement predicate, consumes its continuation draw, and then checks build height before evaluating and writing each provider state. Its cap runs one step opposite the stopping position, including when no pillar block was written. Rejected writes still advance the pillar, and a cap's false result does not make the pillar return false. Named caps and nested pillars use the same feature catalog and graph work budget.

Run python3 tools/test_pillar_parity.py. The pillar report records 921,111 matching lines per JAR across 41,746 fixtures, including every candidate descriptor, both vertical directions, continuation boundaries, signed-zero probability rejection, replacement conditions, cap composition, failures, rejected writes and random stream tails. Workspace tests and all-target Clippy passed. Resident world updates and complete generation/gameplay remain unfinished.

Neighbor-based spreading

The random-neighbor-spread feature evaluates and writes its origin provider before sampling attempts or testing replacement. Each attempt samples X, Y and Z offsets in order, checks replacement, then visits neighbors in native direction order. Exactly one accepted neighbor allows placement; finding a second stops the scan. Rejected writes preserve the same subsequent queries and random consumption.

The neighbor-spread report records 2,650,856 matching lines per JAR across 79,580 fixtures. Run python3 tools/test_neighbor_spread_parity.py to compare all candidate and neighbor states, all direction masks, bounded integer providers, named/empty holder sets, mutable repeated attempts, partial failures and nested feature/modifier calls. Workspace tests and all-target Clippy passed. These feature algorithms still require complete chunk mutation, neighbor updates and gameplay integration.

Additional verified feature algorithms

The executable feature graph also includes overlay composition, block piles, disks, block blobs, springs, vines and underwater magma. Their version-specific reports and remaining integration limits are recorded in Rust rewrite status. These comparisons cover ordered world requests; complete world updates and playable gameplay remain pending.