Files
horizon-airdef/docs/native-client.md
T

286 lines
16 KiB
Markdown
Raw 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 native development client
`horizon-client` opens a native Rust window and renders a controlled block scene
stored in Horizon's resident chunks. Every occupied cell selects models from its
registry state, including the terrain and sixteen featured placements. It loads
block definitions, cuboid models, sprites and occlusion grids from pinned Java
Edition 26.3 resources. Model selection uses the default block position seed;
neighbor visibility uses the validated Rust face rules across chunk boundaries.
The camera remains a free inspection tool.
Eligible opaque unit-cube faces use greedy rectangles only when texture layer,
UV rotation/mirroring and shading match. Each rectangle retains its signed UV
basis, so weighted texture variation survives merging. Other baked geometry and
binary-alpha cutouts use the separate model-face instance pipeline. Geometry
with nonintegral UV-lock rounding also stays on that path.
This checkpoint has no player simulation, vanilla world generation, block
interaction, inventory, sound, networking or world save/load. It does not meet
the [playable-build acceptance criteria](rust-parity.md#completion-criteria-and-playable-builds).
The controlled chunk scene now uses blockstate dispatch, weighted variants,
multipart conditions and transformed baking. Full block-catalog material support,
fractional transparency, tinting, lighting, resource packs, chunk streaming and
full visual parity remain unfinished.
The resource gallery sits beside the pyramid and includes oak stairs, a slab,
an anvil, a torch, a lantern, connected fences and iron bars, a cobweb, a wall
grindstone, a ladder, a sideways amethyst cluster and four weighted stone
placements. Registry state properties select the models: the stairs are an
upside-down inner corner, the slab is a top slab and the fence gate is open.
These are resident chunk states; simulation callbacks remain unfinished. Binary-alpha cutouts are rendered;
the lantern's three frames advance every eight inspection ticks at 20 ticks/s.
The animation metadata codec and frame selection now use the shared
`horizon-assets` implementation checked against the pinned client. GPU
interpolation and synchronization with the eventual game loop remain unfinished.
Sprites currently require matching square frame dimensions.
## Run from a checkout
Use current stable Rust (the locked client dependencies require Rust 1.90 or
newer), Python 3.10+ and JDK 25. A native desktop display and compatible graphics
driver are required. Resource preparation verifies the pinned game artifacts,
exports the registry using the reference server and property schemas using the
reference client, and extracts selected client
resources into ignored `.local/horizon-client/26.3/`.
```sh
python3 tools/prepare_native_client.py --prepare
cargo +stable run --locked -p horizon-client
```
Java is used during preparation. The native executable itself runs without a
JVM. Prepared resources stay private; do not commit or publish the extracted game
files. Final distributable builds still need resource provisioning and packaging.
Use `--resources PATH` for a prepared resource directory. The executable also
looks for a sibling `resources/` directory before the checkout default. Use
`--radius N` to select 1–16 chunks around the origin; this is a finite test scene,
not a render-distance or world-streaming implementation.
| Control | Action |
| --- | --- |
| WASD | Move horizontally |
| Space / either Shift | Rise / descend |
| Either Ctrl | Boost inspection speed |
| Arrow keys | Rotate camera |
| Left click | Capture mouse for camera rotation |
| Escape | Release mouse |
| F3 | Toggle FPS/chunk/mesh diagnostics in the window title |
| Ctrl+Q or window close | Exit |
Controls bind physical keyboard positions. Losing focus clears held keys and
releases the mouse. `--trace-input` logs keyboard, mouse-button and focus events
for diagnosis. On headless Linux use `vscreen run ./target/debug/horizon-client`,
then `vscreen shot`. The `wtype` tool's default generated keymap does not preserve
physical key positions; typing the character W can report physical Escape.
## Validation
The separate `horizon-assets` crate decodes the pinned 26.3 cuboid model format.
Its native comparison covers all **2,883 block-model JSON files** in the client
JAR plus **2,457 synthetic cases**: element bounds, optional geometry, nullable
children, face UVs and quarter turns, single-axis/Euler rotations, texture
references and translucency materials, light emission, display transforms and
inheritance markers. Floating-point fields compare by their exact bits. The
comparison also covers primitive coercion, integer truncation/wraparound and
selected strict-reader numeric/syntax limits. The numeric scanner also preserves
vanilla's rejection when its wrapping integer accumulator hits zero before
another digit, including in ignored fields. Twenty newly exposed acceptance
mismatches were corrected; the complete comparison now covers 5,340 models.
The [resolution](../benchmarks/rust-resource-number-scanner-2026-10-06/model-resolution.json),
[baking](../benchmarks/rust-resource-number-scanner-2026-10-06/model-baking.json) and
[animation](../benchmarks/rust-resource-number-scanner-2026-10-06/sprite-animation.json)
comparisons also pass with the shared scanner correction.
```sh
python3 tools/test_cuboid_model_parity.py
```
The [archived decoder result](../benchmarks/rust-resource-number-scanner-2026-10-06/model-decoding.json)
records source, fixture and reference hashes. The oracle uses actual client
classes; those rendering APIs are absent from the server. The decoder, parent
resolver and baker now feed the development gallery. Blockstate selection, transformed
models and UV locking have the separate checks described below. Rendered
appearance still needs its own checks.
`ModelLibrary` resolves parent geometry, ambient occlusion, GUI lighting and
each display context. It shares inherited elements rather than cloning them.
Texture aliases bind after child overrides are applied; successful and failed
alias walks are cached. Missing resources use the native missing-model cube;
parent cycles invalidate their descendants. The
[resolution comparison](../benchmarks/rust-model-resolution-2026-10-06/scoped-parity.json)
matches **5,648 requested roots across 439 graphs**, including the full bundled
block-model catalog, missing parents, 702 cyclic roots, 1,024-model parent chains,
512-slot alias chains, explicit empty geometry and identity/null transforms.
It calls native `ModelDiscovery`, `ResolvedModel` and `TextureSlots` APIs.
```sh
python3 tools/test_model_resolution_parity.py
cargo +stable test --locked -p horizon-assets
```
Cuboid baking now produces positions in block units, normalized unit-atlas UVs,
face winding and facing, material references, tint indices, shade overrides and
light-emission metadata. It supports single-axis and Euler element rotations
with the pinned rescaling rule. The
[baking comparison](../benchmarks/rust-model-baking-2026-10-06/scoped-parity.json)
matches **7,100 roots across 440 graphs**, using native `FaceBakery` with a real
sprite and the default JOML math options. All bundled block models bake without
errors. Synthetic coverage includes reversed/degenerate bounds, signed zero,
arbitrary rotations, rescaling, null geometry children and parent cycles.
Position, UV and matrix fields compare by exact float bits for these fixtures.
```sh
python3 tools/test_model_baking_parity.py
```
That comparison uses the identity blockstate transform. Atlas packing,
transparency classification, actual tint/light evaluation and GPU rendering
remain separate from the geometry result. Other operating systems still need
their native validation runs.
`BlockModelTransform` now prepares all 64 XYZ quarter-turn combinations once
and shares them across baked models. It rotates geometry around the block center,
rotates culling directions and applies UV locking in the original face basis.
The [transformed baking comparison](../benchmarks/rust-blockstate-transforms-2026-10-06/scoped-parity.json)
checks every distinct model/rotation/UV-lock tuple in bundled blockstates and
all 128 rotation/lock combinations on 81 synthetic models, plus further arbitrary
element rotations. Positions, UVs and every orientation/inverse-face matrix
compare by exact float bits: 19,704 roots and 148,439 quads match, including
7,884 bundled model/transform tuples. Quaternion basis rounding and numeric identity
rechecking are retained. This remains geometry validation; atlas packing and pixel parity are separate work.
```sh
python3 tools/test_blockstate_transform_parity.py
```
Blockstate resources now decode into ordered variant selectors and multipart
conditions. The [native blockstate comparison](../benchmarks/rust-blockstate-model-decoding-2026-10-06/scoped-parity.json)
covers all 1,288 bundled definitions and 1,351 synthetic cases (2,639 total,
468 expected rejections). It uses the same `StrictJsonParser` and
`BlockStateModelDispatcher.CODEC` as the pinned loader. The Rust representation
preserves single versus weighted variants, all three quarter-turn axes, UV-lock
flags, positive weights and total-weight limits, ordered selectors, nested
AND/OR conditions and per-term negation.
```sh
python3 tools/test_blockstate_model_parity.py
```
Property binding now selects variant roots or multipart part indices for an
ordered native state list. The [binding comparison](../benchmarks/rust-blockstate-model-binding-2026-10-06/scoped-parity.json)
covers all 1,288 bundled definitions against 35,723 registered block states plus
the four special item-frame states, followed by synthetic overlap, malformed
selector and predicate cases: 3,802 definitions and 687,466 state results match,
with 220 expected binding errors. It checks the native replacement-before-abort
behavior for overlapping variants, eager multipart validation even when variants
cover every state, integer-property aliases, nested conditions and empty part
selections. The controlled chunk renderer now uses this binder for its model
catalog.
The Java integer-property parser is shared by `horizon-core`, client binding and
world-state decoding. Its [codec regression](../benchmarks/rust-blockstate-model-binding-2026-10-06/shared-integer-codec.json)
matches 221,261 output lines against each authenticated client/server JAR.
```sh
python3 tools/test_blockstate_binding_parity.py
```
`CompiledChoices` prepares cumulative weights once per definition and shares
those choices across bound states. Selecting an ordinary single variant consumes
no random draw; a weighted singleton still does. Multipart collection draws a
seed even for an empty subset and resets the same stream before each chosen
part. The [native choice comparison](../benchmarks/rust-blockstate-model-choice-2026-10-06/scoped-parity.json)
uses actual `WeightedVariants.collectParts` and `MultiPartModel.collectParts`
with inert observable leaves. All seven distinct bundled weight patterns,
synthetic large weights and random multipart graphs produce matching selections
and subsequent random values: 6,960 fixtures, 111,360 collections, both legacy
and xoroshiro streams. The development gallery now bakes and renders selected
models using the legacy stream and the default block position seed. Specialized
block seed callbacks remain separate work.
```sh
python3 tools/test_model_choice_parity.py
```
The sprite animation module reproduces the pinned metadata codec's integer
conversion, strict primitive types, optional null fields and frame dimensions.
It preserves repeated frames, filters out-of-image frame indices, and treats
sequences with fewer than two valid entries as static physical frame zero.
Frame lookup uses cumulative durations and binary search, so skipped rendering
frames do not require replaying all elapsed animation ticks. The native oracle
also checks dirty flags, draw requirements and interpolation timing, including
transitions near the maximum Java integer duration. Interpolation pixel output
and resource-loader failure recovery are outside this comparison.
```sh
python3 tools/test_sprite_animation_parity.py
```
The [animation comparison](../benchmarks/rust-sprite-animation-2026-10-06/scoped-parity.json)
records 907 fixtures and 258,996 native tick states from the actual client codec
and `SpriteContents.AnimationState.tick`, including all 63 bundled animated
texture metadata sections and synthetic boundary cases. The associated
[startup report](../benchmarks/rust-sprite-animation-2026-10-06/startup-smoke.json)
checks the renderer using this shared implementation. Images, input
fixtures and native traces stay private under `.local/`.
```sh
cargo +stable test -p horizon-client
cargo +stable clippy --workspace --all-targets -- -D warnings
python3 tools/test_native_client.py
# Headless Linux alternative:
python3 tools/test_native_client.py --vscreen
```
The mesh tests compare expanded rectangles against independently enumerated
exposed voxel faces, including solid neighbors, cavities, material boundaries,
negative chunk origins and random surroundings. The startup runner launches a
nine-chunk scene, requires 30 presented frames and a clean exit, verifies 3,169
greedy cube rectangles and 179 other model faces across 18 sprite layers, and writes GPU,
platform, binary and source information to ignored
`.local/horizon-client/startup-smoke.json`. It has an external timeout as well as
the application's frame deadline. These checks do not establish gameplay or
pixel parity with Java. Source and resource fingerprints must remain unchanged
during the run. An animation test covers duration boundaries, wrapping and
repeated physical frames independently of render frequency.
Initial local validation uses Linux x86_64, a headless Wayland compositor and
Vulkan llvmpipe software rendering. Textured geometry and resizing were visually
inspected; movement, arrow rotation and Ctrl+Q were exercised using virtual
physical key codes. Captured relative mouse movement and Escape release were
exercised through a virtual pointer. Native macOS/Metal and
Windows/Direct3D 12 runtime tests remain pending. Software-renderer frame rates
are not evidence of a hardware speedup.
The model gallery was also inspected at close range on Linux/Vulkan llvmpipe.
With the camera stationary, screenshot differences were confined to the lantern
as its frames changed. The [model-client startup report](../benchmarks/rust-native-model-client-2026-10-06/startup-smoke.json)
records the bounded presentation check and the scope of this visual inspection.
The [blockstate-client report](../benchmarks/rust-native-blockstate-client-2026-10-06/startup-smoke.json)
also checks all 16 gallery placements against actual Java registry binding, root
baking and model collection using `state.getSeed(position)`. A tagged empty
geometry adapter identifies the 20 selected model parts and their transforms;
the separate baking gate covers actual quad geometry. Preparation exports typed
property domains and canonical state order, which the Rust loader checks against
its imported registry before binding. Each distinct transformed model is baked
once during gallery preparation and reused across positions. The selected state
IDs, properties, positions and models are recorded in the startup report.
[Neighbor-face visibility](face-visibility.md) now has native client/server
coverage across all registered states and suppression-rule families. Integrating
that result with resident chunk model meshing is now covered by the scene check
below.
The [resident-model report](../benchmarks/rust-native-chunk-models-2026-10-06/startup-smoke.json)
checks all 10,919 occupied cells in the bounded startup scene against a separately
constructed native scene. Actual Java state-model selection, parent geometry,
rotated culling directions and `Block.shouldRenderFace` produce the same 5,991
visible and 59,636 culled model faces. The sixteen featured model selections also
retain their direct native comparison. Expanded greedy-rectangle tests cover
rotated and mirrored UV bases, texture and shade boundaries, negative origins,
neighbor seams and cavities; cube projection tests cover all XYZ quarter turns.
These checks cover CPU mesh behavior and GPU startup, not native shader pixels
or a hardware performance comparison.