🤖 Agent: launcher-only. The claim opens a checkout URL only on Horizon's own /shop/pay page (prefix allowlist, tested), and the 3 s poll stops after 20 min or once the set is claimed; without a checkout in the answer the one-click claim is unchanged.
🤖 Agent: TemptGoal.shouldFollow and TemptingSensor test both hands; player_view now fills off_hand, and lan_guest_views swaps each guest's off hand in through the same function, so single player on the server thread gets it too.
🤖 Agent: default ascii.png glyphs keep their 0..8 box, so sign text without Force Unicode draws exactly as before; only Unifont's half-scale glyphs change width.
🤖 Agent: the message is trimmed and the Chat option (Commands Only, Hidden) applied as the chat screen does; the test needs prepared resources and is #[ignore]d, so CI does not run it.
🤖 Agent: Only Show Secure Chat now drops a line before mods hear it (ChatListener.showMessageToPlayer's order, then onClientPlayerChat), and chat_received returns at once with no mods loaded, so the chat-type registry lookup costs nothing without them.
🤖 Agent: controlled particles are dropped on a resource reload so a stale sprite-set index cannot pick another set, and the only cost for vanilla particles is one more match arm in tick and in the translucent test.
🤖 Agent: one dispatch per frame, each non-finite value falls back to vanilla's on its own, and posed_rotation is only set when the pose differs, so billboards keep the vanilla rotation path when no mod moves the camera.
🤖 Agent: the 32 KiB cap is enforced on both encode and decode, a vanilla client ignores the unknown horizon:menu_extra channel, and the lan_bot test pins that the payload arrives right before its own window's OpenScreen.
🤖 Agent: an explicit passed:false or a result with zero executed checks now stays Partial, and a legacy count-only result with passes still reads harness green, as carried_harness_keeps_current_and_legacy_successes pins.
🤖 Agent: a drag saves the option and rebuilds the pack once, on release, not on every pointer move; the manifest already refuses min >= max and step <= 0, so slider_position and slider_value cannot divide by zero.
🤖 Agent: the pack stack is rebuilt only when a mod registered a listener, so a /reload without native mods pays nothing, and folder_ok refuses '..' and absolute paths, so a listener cannot read outside data/<namespace>/.
🤖 Agent: GUI glint now takes vanilla's ITEMS-atlas rectangles (Stitcher's order, split and filtering padding reproduced) while CIT glint keeps sprite-local UVs; the cost is one more decode of the item textures per pack load in prepare_glint_atlas, nothing per frame.
🤖 Agent: re-running tools/stamp_native_hooks.py on this PR's base (38df83828) reproduces the tree byte for byte, and main has not touched a fragment since; the native-hook-stamp workflow that should have opened this PR has failed its last five runs, which the orchestrator will look at.
- When the cosmetics service answers a claim with a checkout (the 0 CHF Paddle checkout
Ludovic chose for founder claims), the launcher opens it in the browser, but only
Horizon's own checkout page (https://playhorizon.dev/shop/pay?…). It then asks the
service every 3 s, for 20 minutes at most, until Paddle's webhook has granted the set.
The button says "Waiting for the checkout…" and a link opens the checkout again.
- The Wardrobe tab's claim button takes the same path.
- "Your purchases" under the founder panel: date, item, price (Free at 0), state (Paid,
Partly refunded, Refunded, Charged back) and the order number from Paddle's receipt,
for Mojang's purchase-history rule.
- The claim note no longer promises "no card asked" (unconfirmed at 0 CHF).
- 11 new strings in all 86 tables; the dropped one removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: matches ExperienceOrbRenderer: getBlockLightLevel is block+7 clamped with sky untouched (was a floor of 10 on both), the quad spans (-0.5,-0.25)..(0.5,0.75) at 0.3, and light is read at the eye (0.425).
🤖 Agent: without HORIZON_HDR_OUTPUT, offscreen_output() is None and every new branch is skipped; the crash marker is only written with a surface, so an offscreen HDR run cannot turn HDR off for the next windowed start.
🤖 Agent: inert until gpu-culling is switched on (only control.rs and scene_replay do), and the fade-end heap means a frame with no fade due does no extra work; martipops' read found every fade start sets the flag before prepare.
🤖 Agent: switch() now ANDs translucent_batching_offered() exactly as hdr_smooth_gradients already does, and the saved preference is untouched, so a device that can batch gets it back.
🤖 Agent: the loop now asserts the outbox really holds bytes (pending > 0 three times 50 ms apart) before sending Disconnect, which the old 64 MiB burst only assumed; the 256-MiB cap fails loudly instead of hanging.
🤖 Agent: only Public tree and Python tests move beside cargo, and every Python test that names cargo mocks it, so they never fight cargo for the target dir; Parity stays after cargo since it builds.
🤖 Agent: skipIf(os.name == "nt") sits above the bash/openssl skip, so Windows never reaches System32's WSL bash (#5383), and CI's Linux job still runs all signature cases.
🤖 Agent: The freeze came from map searches opened from chests, which built full jigsaw starts and scanned every chunk of each saved region for each candidate. Same answers on main and this branch (24 targets × 5 points × 4 runs); on a pregenerated world the bamboo-camp map search drops from 2.1–3.6 s to 0.04–0.07 s of server-thread CPU, and main hit the 60 s watchdog in 4 of 9 runs. fmt, both clippies and the touched crates' tests pass, plus 10 harnesses incl. server_world_gen (/locate against vanilla); dedicated_server's vanilla oracle crashed on the loaded desktop, so CI runs it.
🤖 Agent: Server API backup goldens: test-only, the download case reads the real streamed bytes. The union of this wave's 15 PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop. management_protocol (--fresh, on that union): every case of this PR matches vanilla; the only 3 differences were #8204's heartbeat cases, fixed there. Other harnesses PASS on the union: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container. dedicated_server could not run locally (the vanilla oracle's world generation hit its watchdog under the desktop's memory pressure), so it and the rest of the selection run in CI run-parity.
🤖 Agent: Server API lifecycle goldens: test-only, type-checked replacement of the volatile fields. The union of this wave's 15 PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop. management_protocol (--fresh, on that union): every case of this PR matches vanilla; the only 3 differences were #8204's heartbeat cases, fixed there. Other harnesses PASS on the union: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container. dedicated_server could not run locally (the vanilla oracle's world generation hit its watchdog under the desktop's memory pressure), so it and the rest of the selection run in CI run-parity.
🤖 Agent: Entity broadcast range: vanilla logs the requested percentage, then clamps 10..1000 and saves the clamped value. The union of this wave's 15 PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop. management_protocol (--fresh, on that union): every case of this PR matches vanilla; the only 3 differences were #8204's heartbeat cases, fixed there. Other harnesses PASS on the union: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container. dedicated_server could not run locally (the vanilla oracle's world generation hit its watchdog under the desktop's memory pressure), so it and the rest of the selection run in CI run-parity.
🤖 Agent: Ludovic approved the shop and the dark founder set (11-04). verify, launcher, parity-scope green on this exact head (rdbtCVS's options-test fix included); desktop fmt/clippy/tests passed on the shop changes. The claim window stays closed on the service until the Paddle 0 CHF checkout follow-up lands, so merging only shows the shop and worn cosmetics.
🤖 Agent: Prometheus entity/chunk counts read only when the supervisor samples server-status; main merged (rdbt's d938f6a1dd). The union of this wave's 15 PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop. Harnesses PASS on that union: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container. boat_submerged, boat_paddle_reset and dedicated_server did not run locally: the vanilla oracle's world generation hit its watchdog 3 times under the desktop's memory pressure, before any comparison. They and the rest of the selection run in CI run-parity.
🤖 Agent: Boat powder snow matches PowderSnowBlock.entityInside (stuck 0.9/1.5/0.9, nextBoolean then two randomBetween draws). The union of this wave's 15 PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop. Harnesses PASS on that union: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container. boat_submerged, boat_paddle_reset and dedicated_server did not run locally: the vanilla oracle's world generation hit its watchdog 3 times under the desktop's memory pressure, before any comparison. They and the rest of the selection run in CI run-parity.
🤖 Agent: Rider frost walker / block effects for passengers; old_position reset keeps the sweep to the vehicle's cell. The union of this wave's 15 PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop. Harnesses PASS on that union: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container. boat_submerged, boat_paddle_reset and dedicated_server did not run locally: the vanilla oracle's world generation hit its watchdog 3 times under the desktop's memory pressure, before any comparison. They and the rest of the selection run in CI run-parity.
main carries 40 fragments whose markers have not been consumed, so
`python tools/stamp_native_hooks.py --check` exits 1 there. This is one run of
that tool on current main: it numbers the fragments in first-parent merge
order, writes the markers into the hand-written API types, and inserts each
`doc_summary` as an `API N adds ...` paragraph in docs/native-mods.md.
No hand-written line: the stamp pass owns native-hooks.json, NATIVE_HOOKS_API
and docs/native-mods.md, and editing any of them by hand is what the fragments
replaced.
Co-Authored-By: Hermes Agent (Nous Research) <[email protected]>
🤖 Agent: execute on owner resolves the owner guest first. the union of all 15 of this wave's PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop; harness subset on that union PASS: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container (only a subset ran locally; the full selection runs in CI run-parity).
🤖 Agent: Per-worker idle release; cross-worker idle is mutexed, per-job cost a lock + Instant::now. the union of all 15 of this wave's PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop; harness subset on that union PASS: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container (only a subset ran locally; the full selection runs in CI run-parity).
🤖 Agent: World pack tags merged only for registries with tag files. the union of all 15 of this wave's PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop; harness subset on that union PASS: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container (only a subset ran locally; the full selection runs in CI run-parity).
🤖 Agent: Bucket ray uses ClipContext.Block.OUTLINE like BucketItem.use; main merged. the union of all 15 of this wave's PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop; harness subset on that union PASS: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container (only a subset ran locally; the full selection runs in CI run-parity).
🤖 Agent: Shader bool/bvec buffer rewrite; rdbt's main merge 0e010734d9 (one mod-line hunk) re-tested: horizon-shaders 249 tests OK. the union of all 15 of this wave's PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop; harness subset on that union PASS: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container (only a subset ran locally; the full selection runs in CI run-parity).
🤖 Agent: Routing of flat worlds through the pipeline; lands after #8163 (carries its commits). the union of all 15 of this wave's PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop; harness subset on that union PASS: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container (only a subset ran locally; the full selection runs in CI run-parity).
🤖 Agent: Superflat features match FlatLevelGeneratorSettings.adjustGenerationSettings. the union of all 15 of this wave's PRs on main acd184c1 passed fmt, clippy -D warnings, 3388 tests and the Python suite on the desktop; harness subset on that union PASS: flat_generation, flat_place, chunk_generation, datapack_worldgen, command_exec, pick_block_data, powder_snow_collision, snow_freeze, boat_authority, vehicle_container (only a subset ran locally; the full selection runs in CI run-parity).
Opening a generated chest with an exploration map nobody searched yet (a camp chest's camp
or secret map, a shipwreck's treasure map) runs the map's structure search on the server
thread. Two things made one such UseItemOn cost seconds on the official server (#8669):
- Every candidate chunk got its full starts built (all of its sets, jigsaw layouts
included). Vanilla's StructureCheck.canCreateStructure only places the wanted
structure's start piece and checks its biome. `may_start` now does that for jigsaws
(`place_start` / `lay_out_children` split out of `assemble_drawing`), and `locate`
builds a chunk's starts only where a wanted structure may start.
- Each candidate asked whether its chunk was old through the blender's region scan,
which decodes every chunk record of each region file it touches. Vanilla's
StructureCheck reads the one chunk. `OldWorld::is_old_chunk` now answers from a
scanned region's bits, else from that chunk's record alone.
The slow-event line also names the item, block, position and face of a use, and the use
is split into use_mods / use_block / use_item / open_loot / open_maps /
use_advancements spans (`spans::guard` ends a span where its scope does).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: clean-room check passed (black-box probes of their own, no source). Merged main and fixed#8999's test call to the removed Config::finish. Verified main + this head on the desktop: fmt, clippy -D warnings (shaders, render, all targets), 1270 tests, 0 failures; GPU fixtures not run.
#9087 removes Config::finish (its only work was the gdepth rule); the test reads the settings as they are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this merge head; the merge keeps #9091's clickable edition tabs with the new grass block and brand icon.
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this merge head; the merge keeps both new Launcher fields (settings_marked beside #9099's mods_sticky).
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this merge head; the main merge only re-applied the 14 px inset to Home's new head layout from #9091.
🤖 Agent: updated onto main after #8042, #8510 and #9002 landed (merge of main; #8999 fixture gets item_id_declared/material_maps; #9007 is stacked on #9005 so the trailing test modules resolve once). Verified on the desktop, main + #8999 + #9007 (+ #9005): fmt, clippy -D warnings (shaders, render, all targets), 1268 tests, 0 failures; GPU fixtures not run (no adapter over ssh).
🤖 Agent: updated onto main after #8042, #8510 and #9002 landed (merge of main; #8999 fixture gets item_id_declared/material_maps; #9007 is stacked on #9005 so the trailing test modules resolve once). Verified on the desktop, main + #8999 + #9007 (+ #9005): fmt, clippy -D warnings (shaders, render, all targets), 1268 tests, 0 failures; GPU fixtures not run (no adapter over ssh).
🤖 Agent: updated onto main after #8042, #8510 and #9002 landed (merge of main; #8999 fixture gets item_id_declared/material_maps; #9007 is stacked on #9005 so the trailing test modules resolve once). Verified on the desktop, main + #8999 + #9007 (+ #9005): fmt, clippy -D warnings (shaders, render, all targets), 1268 tests, 0 failures; GPU fixtures not run (no adapter over ssh).
The founder set is now navy and charcoal (Ludovic, 11-04). On the dark
appearance's near-black well they all but vanished (well 17,11,31 against a
tee front of 33,32,41), so the dark well is a violet grey, lighter at the
top like a showcase. The light well stays.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this head (473+85); menus open at once because an animation inside a deferred menu kept its invisible first frame under GPUI Fast (seen on the desktop run), pages and toasts keep the 200 ms pop.
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
🤖 Agent: verified in a union of the 21 open shader-pack PRs on main 3fb4c3138 (desktop): fmt, clippy -D warnings on all targets, 3766 tests, 0 failures. GPU fixtures not run (no adapter over ssh); harnesses not run (shader-pack-only code).
MinecraftServerSettingsServiceImpl.setEntityBroadcastRangePercentage logs the
requested percentage before DedicatedServerProperties clamps it to 10..1000.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this head; Modrinth's Java mods stay listed for Horizon instances with the existing Works/Partly/Doesn't run mark, and installs still check SHA-512 before writing.
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this head (474+83); one session-server lookup per account per 24 h, the empty answer (default skin) cached too, so an offline launcher doesn't retry each frame.
The pop animation inside a deferred menu kept its first frame under GPUI Fast: the
version menu never showed on the desktop run, the Language list lost its plate. Menus
open at once again (pages and toasts keep the pop); the Language list is anchored and
opens upwards when there is no room below.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: vanilla: PlayerPredicate lookingAt over every non-spectator entity; tick/location gated by may_look. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: indirectMagic(potion|cloud, owner) instead of main's Damage::player; overlaps #9053 (TheMrZZ), second to land resolves. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: 26.3 EntityPredicate is a dispatchedMap by sub-predicate id; TagPredicate.id is a HolderSet. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: hurtServer fires PLAYER_HURT_ENTITY at 0 damage outside i-frames; interact trigger after filled_bucket. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: WitherBoss.makeStuckInBlock is empty; berry damage kept. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: ContainerOpenersCounter edges for CONTAINER_OPEN/CLOSE; tautological test removed. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: ElderGuardian.customServerAiStep setHomeTo(blockPosition, 16); tests trimmed; carries #8966's commits (guardian.rs conflict). Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: Guardian.aiStep flop under !isSilent; tests folded into one. Lands before #8952 (stacked on it). Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: SUMMONABLE_ENTITIES = canSummon types; only player and fishing_bobber are noSummon in 26.3. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: AbstractBoat.tick startRiding branch, a refused rider is neither seated nor pushed (#8916 closed as its duplicate). Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: GlowSquid.hurtServer dims only if Squid.hurtServer is true, which needs getLastHurtByMob. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: fixed on the branch: the refusal is WitherBoss.addEffect's (Mob::add_effect); canBeAffected stays vanilla so AreaEffectCloud keeps the wither as a victim. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: MobEffectInstance.tickServer returns false at 0 (hasRemainingDuration), tickEffects removes it. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
🤖 Agent: vanilla: OldMinecartBehavior/NewMinecartBehavior seat through startRiding, WitherBoss.canRide false. Proof: union of main b1f5b238 + this batch's 14 heads on the desktop: fmt, clippy -D warnings, tests of horizon-client/-lib/-core/horizon-world green (GPU gfx/post_chain/menu_blur skipped). Harness subset only (not the full parity.py selection, which CI runs on this head): 22 passed (active_effect_tick active_effects effect_notifications status_effect consume_effect sculk block_entity_anim chest_pair command_parse chat_screen command_exec peaceful_summon living_hurt living_damage damage_source player_hurt damage_reduction server_player_damage boat_authority vehicle_container powder_snow_collision squid_client); dedicated_server and server_entities failed on the vanilla oracle's watchdog under desktop load, mob_ai not finished (oracle 1 h timeout under load): left to CI.
The two branches each added a test module at the end of guardian.rs, so
whichever lands second conflicts. Carry #8966 here; land #8966 first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
# Conflicts:
# crates/horizon-client/src/mobs/guardian.rs
Sign text drew every glyph as an 8x8 quad at the pen. Vanilla's
BakedSheetGlyph draws x+left..x+right and y+up..y+down, which the HUD
already does. With Force Unicode Font a Latin letter is an 8x16 Unifont
glyph at half scale (4 units wide, advance 5), so stretching it to 8
units overlapped its neighbours.
Fixes#8699
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this head (473+84 passed); below 1280 px the mods button folds into ⋯ so the invite field keeps a 150 px minimum to the right of Play (screenshots at 1000×700).
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this head; the GitHub mark is our own SVG in horizon-theme's glass icons, the Sculk card keeps its 2 px hover lift without the border.
🤖 Agent: desktop fmt/build/clippy -D warnings/tests 0 at this head; the card re-renders only when its offset changes (on_scroll + next frame), so a long list costs nothing extra while scrolling; a scrollbar-thumb drag catches up on the next event (noted in the body).
From three days up the span showed whole days rounded down, so a seven-day
window read "6 days left" a minute after it opened.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Game::player_view left PlayerView.off_hand as None, so TemptGoal, the
animal brains' TemptingSensor, piglins' loved items, goats, happy
ghasts and taming never saw what the player holds in the off hand.
Vanilla's TemptGoal.shouldFollow and LivingEntity.isHolding check both
hands. Fill it from the off-hand slot, as main_hand is filled.
Fixes#8858
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A section's bounds from the ScrollHandle are its layout position before the scroll, so
the list stayed on the first section until the pane reached its end (seen on the desktop
run: Account still marked over the Launcher card).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Menus and popovers take the cards' glass with a shadow instead of the darker float, and
arrive as the website's hzl-pop (fade and 6 px rise, 200 ms); Settings' Language list is
one of them now (it was the old square box and ran off the window). Hovers ease on the
design's quick curve, pages and toasts pop, the glass switch's knob slides; reduced
motion draws each at its end. In light, the Play group's icons take the accent's ink and
the dawn sky is washed with white.
Addresses dezer-x's launcher feedback (points 7, 8 and 11).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Shop and Wardrobe tabs on glass plates in both appearances (Geist type, the
glass ink, light wells a soft violet grey so cream and violet items keep their
edges); the founder panel stacks under a 1280 px window. The Wardrobe gets a Shirt
row; its launcher theme row is gone: launcher themes are free for everyone in
Settings > Appearance (Launcher theme: Horizon or First Light), never claimed.
The top bar's Wardrobe follows the cosmetics service only. Shop previews use the
7-day window. The Shop strings in the 61 languages main added since, and the new
ones everywhere.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A shirt's sleeves ride the arms: a top-level part may name its own bone (Item::limbs,
one root per bone under the item's attachment, Item::roots). The game draws each root
on its bone, the launcher's preview too. Slot::Torso, hidden under a chestplate.
Cosmetics run by default (Ludovic, 2026-11-04: on when the shop opens); other
players' and your own already default to shown. HORIZON_COSMETICS=0 turns them off;
horizon_cosmetics::forced() is HORIZON_COSMETICS=1 for the launcher's Wardrobe.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: reviewed by literal-review (agent-state/literal-review.md): a far chunk is reduced only to the cell width that reads it (405 → 103 µs per chunk in its bench), same output; verify + launcher green at this head.
Probe packs of ours, run in Iris 1.11.7, set colortexNFormat in many places. Horizon now
follows what Iris did with them:
- a format in a block comment counts only where `const` starts its line (our probes' Iris
values fit this; it isn't a rule Iris states): not on the `/*` line, not after ` * `, not
after another declaration on the line;
- a format in a vertex shader doesn't count;
- the colortex formats of the shadow programs' fragment shaders are read when the shadow pass
doesn't run (observed in our probes for shadow.fsh and shadowcomp.fsh); nothing else of
those files is read then (Horizon's choice: nothing else was probed);
- declaring `gdepth` no longer makes colortex1 RGBA32F.
Fixes#9063
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Vanilla mark was baked from Mojang's grass textures in the prepared files, so the
Java Edition tab and the New instance card showed a letter until the game was prepared.
It is now an original drawing (tools/launcher_grass_block.py), always there. Horizon's
mark was a redrawn tile with the bar in the accent colour; it is now the brand icon.
Addresses dezer-x's launcher feedback (points 5 and 6).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: management server/stop now ends horizon-server as vanilla's does. Desktop: management_protocol FAIL on main 017c84f1fe (supervisor TimeoutExpired), PASS on e132eeba6c (--fresh, 167 s); dedicated_server PASS both; horizon-server fmt/clippy -D warnings/134 tests pass; the new supervisor test fails with the fix reverted.
dezer-x's launcher feedback, point 14. The chip draws the account's head from its saved skin URL;
an account saved without one (older sign-ins, or a profile answered without its skin) fell back to
the default skin from a prepared version's files, and with none prepared to the user icon.
Such an account now asks Mojang's public session server (no token) for its skin, keeps the URL a
day beside the cached skins, and the face comes from the skin as before (textures.minecraft.net
only). Signed out or offline with nothing cached, the user icon stays.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dezer-x's launcher feedback, points 12 and 13:
- Sponsor on GitHub carries GitHub's mark (github.svg in the glass icons, drawn for Horizon:
filled, its cat cut out by a mask).
- Sculk's card stays at the sidebar's foot on every page, Servers too; its border is gone (it
still lifts under the pointer).
- On Servers, Sculk's banner sits right under the server, where it shows without scrolling past
the server's tabs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dezer-x's launcher feedback, points 1, 2 and 15:
- Overview (glass look) ends with the screenshots and the patch log: the Resources and
Minecraft folder rows (and their Sign in with Microsoft) are gone; Prepare stays in Play's
menu, the folder in the folder menu, signing in in the top bar.
- The invite code stays on the actions' row at every width; under 1280 px Play and the version
are narrower, the puzzle gives way (the Mods tab does the same) and the field takes the rest,
with the shorter "Paste an invite code".
- The Java Edition / Rust Edition tabs switch: to that edition's instance played last, or to the
New instance dialog on Vanilla or Horizon when there is none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Ludovic's point 16 on dezer-x's launcher feedback: mod translators are coming and the launcher
plays Java instances, so the Mods page searches Modrinth again (undoes what 0416fe600c hid, in
the 0.2.38 design).
- Tabs: Mods, Modpacks, Resource packs, Shader packs, Data packs.
- Mods, Modpacks and Resource packs pick their source under the search: Horizon's registry or
Modrinth, CurseForge beside them as coming soon, inert. Shader packs stay on the registry
(Horizon's .hshader), data packs on Modrinth, into the world picked there (its worlds read again
when the tab opens; source_for gave data packs Horizon's registry, which has none).
- A Horizon instance's Modrinth mods are listed, each marked Works, Partly or Doesn't run in
Horizon once its jar is checked; a Java instance's tabs still search Modrinth for its version and
loader. Installs keep the SHA-512 check before writing.
- docs/mod-browser.md, launcher/README.md and the Mods page's module comments say so.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dezer-x's launcher feedback, points 3 and 10:
- The project card beside the results stays in view as the page scrolls (the mockup's
lg:sticky lg:top-0): sticky.rs measures the row while it paints and moves the card down by what
the page scrolled past it, never past the row's end. A scrolled page is drawn from its layer,
so the page's scroll asks on the next frame whether the card must follow, and redraws only then.
- Sort by is on the search's right, as tall and as round as the search, its choices on a glass
popover with the chosen one ticked; Modrinth's filter bar no longer repeats it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dezer-x's launcher feedback, point 9. GPUI Fast draws a scrolled pane from its cached layer
without rendering the view, so the list only moved when something else redrew the page; at
the end the mockup's rule then jumped straight to Data collection.
- The pane's scroll asks, on the next frame, which section is read, and redraws only when it
changed.
- The reading line sits at 30 % of the pane, then slides to its bottom over the last 70 % of a
pane of scroll, so About and Data collection, too short to reach 30 %, each come in turn.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
dezer-x's launcher feedback, point 4:
- Each Versions and Patch notes card carries the 10 px bottom margin cull::column already
counted as their gap (the mockup's gap-2.5): the cards were fused.
- The glass look's scroll area starts 14 px under the top bar, so cards scrolled up stop short of
it; the pages' heads hold 14 px less above their titles, so nothing moves at rest.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: union B6 (fur li lmo oc sah se szl vec: 16 PRs) stacked on #8610, ALL = 87 languages: tables complete at main's 1093 strings, controller labels written as commands fixed; desktop launcher fmt/clippy/test + horizon-client-core green (619 passed); verify + launcher green.
🤖 Agent: union B5 (tt yi yo zu ba br cv fo: 15 PRs + #8600 closed) stacked on #8579: zu_za game table dropped (vanilla 26.3 ships no zu_za), Bashkir's two broken {done}/{total} placeholders fixed; desktop launcher fmt/clippy/test + horizon-client-core green (619 passed).
🤖 Agent: union B4 (gd lb ky kn mt eo haw ig la: 18 PRs) stacked on #8496: Esperanto now has a table, so the two launcher tests that used "eo" as a table-less language (i18n pick, settings parse) use "tlh"; desktop launcher fmt/clippy/test + horizon-client-core green (619 passed).
🤖 Agent: union B3 (ga eu gl fil ta mn lo so fy: 18 PRs) stacked on #8450: Lao's 323 U+200B removed, 56 Tamil entries with Horizon/Minecraft in Tamil script restored to Latin, missing strings filled; desktop launcher fmt/clippy/test + horizon-client-core green (619 passed); verify + launcher green.
It derived the open and close edges in the test from recheck_opener_count's
return, which the existing opener tests already pin; it never reached the
game events this PR gates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Keep the home and keep-existing-home cases and the restriction they give
in one test right after elder_server_ai_step (so it does not collide with
the flop tests #8966 appends to guardian.rs), drop the test that pinned
extra.home staying empty, and keep Mob::toward private.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: union B2 (az uz kk ka hy bs mk sq cy: 18 PRs) stacked on #8425: missing launcher strings filled, Instance written as "example" in hy/mk fixed, product names restored; desktop launcher fmt/clippy/test + horizon-client-core green (619 passed); verify + launcher green.
WitherBoss overrides addEffect to return false and canBeAffected to refuse
the wither effect only. AreaEffectCloud.serverTick picks its victims with
canBeAffected, so a cloud of strength or healing still takes the boss as a
victim: its instant effects land and the cloud shrinks. Keep
mob_can_be_affected and Mob::can_be_affected vanilla's canBeAffected and
put the blanket refusal in Mob::add_effect.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: union B1 (ca et lv sl sr is af ms be: 18 PRs, octopus of their heads + fixes): 66/58 missing launcher strings filled per table, 152 Serbian entries had Horizon/Minecraft transliterated (Latin again), game sr table now serves vanilla's Cyrillic sr_sp and a Latin transliteration for sr_cs; desktop: launcher fmt, clippy -D warnings, cargo test + horizon-client-core fmt/clippy/test green (619 passed); verify + launcher green.
🤖 Agent: Hub::set_simulation_distance broadcasts SetSimulationDistance and state.view.1 keeps the status reply in step; third of the management stack, after #8118.
🤖 Agent: max(2).min(view) replaces clamp(2, view), which panicked when a management value below 2 made the bounds cross; second of the management stack, after #8108.
🤖 Agent: hub.spawn_protection is the radius the block checks read, so the live set applies at once; base of the management stack (#8118, #8122, #8128 carry it in that order).
🤖 Agent: merged main; since #8046 can_target_player already refuses a player in Peaceful (spares_players), so I dropped the goal's own difficulty check and the Peaceful test still covers it.
🤖 Agent: only may_reach_out answers are cached (8 sections); the chunk queries keep vanilla's one-column cache, and the block_collision_scan harness checks that query trace against the oracle. Collision samples −21 % in a profile; A/B in the noise.
🤖 Agent: small win (tick_sculk_entities −0.2 to −0.3 ms, 3/3 pairs). Risk point: listener order. The cell lookup sorts back by insertion rank, and a randomized test checks it against the old full-list filter through inserts, removals and retains. sculk, server_player_sculk and mob_ai pass.
🤖 Agent: random_ticks −3.9 to −4.4 ms in 3/3 A/B pairs. Six #[inline] attributes, no body changed; random_tick, random_tick_sections, mob_ai, server_entities, natural_spawn and villager_farming match vanilla.
Carry the resolved management stack into the entity broadcast range PR while preserving all routes, port-ledger entries, and parity cases from current main.
Co-Authored-By: Codex (GPT-5.6) <[email protected]>
Since #8245 a stop that doesn't come from the supervisor's stdin or the
control API leaves the supervisor in its stopped phase. The management
`server/stop` only set `stop`, so horizon-server kept running after it,
where vanilla's process exits (the management_protocol harness timed out).
It now shuts down as `POST /api/v1/stop` does, through one helper.
Fixes#9027
Co-Authored-By: Claude Opus 5.5 <[email protected]>
In Vanilla Java Edition 26.3, WitherBoss.makeStuckInBlock overrides Entity.makeStuckInBlock as an empty NO-OP.
While Cobweb (WebBlock) already ignored Wither alongside Spiders, Wither was still getting stuck in and having its fall distance reset by Sweet Berry Bushes and Powder Snow.
This aligns effects_from_blocks with vanilla Java bytecode:
- Wither is immune to slowdown and fall distance resets from sweet berry bushes and powder snow.
- Wither still takes sweet berry bush damage when moving in mature bushes.
- Powder snow snowflake particles continue to spawn when moving.
Co-Authored-By: Antigravity <[email protected]>
The shadow pass drew a block entity's plain faces but left out its End portal or gateway faces
(the portal run of #8361). A pack's shadow pass sees block entities whole, so the portal faces
cast too, with end_portal.png bound as for the gbuffers draw; beams still do not cast.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every frame's span was `item_frame`, so a pack's entity.properties could not tell glow frames
(and the item or map in them) from plain ones. Each frame, and the item or map it holds, now
takes its own frame's type.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`Codec.INT` reads a JSON number through `Number.intValue`, so a pack.mcmeta with
`"min_format": [97.1]` declares format 97 and is compatible. Horizon rejected the decimal,
so such packs (Patrix 26.3) were listed as incompatible.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: vanilla Sniffer/ClientPacketListener event 63 and SnifferSoundInstance checked; with the head merged into main, ambient_sound, server_entities, sound_engine, sound_event and dedicated_server pass on playhorizon.
🤖 Agent: options failed on main (69ab59ef0) with 17524 differing cases from the extra threeDSkinLayers line. With this head it passes on playhorizon. The switch moves to horizon-extras.txt, and the old options.txt line is read once, so a player's setting is kept (unit test). fmt, clippy -D warnings and 3506 tests pass on the desktop.
🤖 Agent: command_exec failed on main (69ab59ef0) with one mismatch: a stand's summon-tick /data get printed attributes [movement_speed] where vanilla prints []. With this head it passes, 4374/4374 commands, on playhorizon. Vanilla only creates that instance in aiStep's removeFrost (AttributeMap.getInstance computeIfAbsent). fmt, clippy -D warnings and 2489 tests pass on the desktop.
#8091 wrote threeDSkinLayers into options.txt, a key vanilla doesn't have,
so every options.txt Horizon saved was one line off vanilla's. It now lives
in horizon-extras.txt with Horizon's other switches. A threeDSkinLayers line
in options.txt is read once, while horizon-extras.txt has none of its own,
so a player who turned it on keeps it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: both the portal-tick trigger and an entity entering now go through triggerCooldown's block event 1 to the window, so the beam starts there; gateway_available keeps reading the server copy's cooldown.
🤖 Agent: the packet is count 1, no spread, speed 1, so each client draws a Gaussian velocity, matching PotionItem.useOn's sendParticles(SPLASH, …, 1, 0, 0, 0, 1.0); the positions still come from the server's random.
🤖 Agent: hub.land already sent level event 1032 at BlockPos.ZERO; playing it as local ambience (pitch nextFloat()*0.4+0.8, volume 0.25), as LevelEventHandler does, is what makes it audible. Nothing else in Horizon plays block.portal.travel, so there is no double sound.
🤖 Agent: wobble is a Level.blockEvent (event 1, the WobbleStyle ordinal) and the dust is sendParticles(DUST_PLUME, 7), so both now reach the window; the server's own block_entity_event stays for its copy.
🤖 Agent: oSquish is copied before the step toward targetSquish, then decreaseSquish (0.9 for magma cubes, 0.6 otherwise), the same order as AbstractCubeMob.tick, so the client's squish animates between packets.
🤖 Agent: event 70 only sets the client's fuse to 80 (MinecartTNT.handleEntityEvent -> primeFuse on a client), and the window counts it down with the smoke; the explosion stays the server's.
🤖 Agent: the nextFloat() < 0.05 draw happens for every defending fox, silent or not, then Entity.playSound skips a silent one: the same random order as Fox.aiStep after super.aiStep.
🤖 Agent: the smoke is added after the fuse decrement and the discard, at the moved position, as PrimedTnt.tick does on a client; the server's addParticle never reached the window.
🤖 Agent: BowItem.releaseUsing and CrossbowItem.performShooting play at the shooter's getX/Y/Z, so the feet are vanilla; the eye-height source a few cm from the listener was what panned hard.
🤖 Agent: only the nautilus's breath (ambient) skips the creative/spectator gate. Vanilla's Dolphin's Grace also reaches creative players (DolphinSwimWithPlayerGoal uses forNonCombat targeting), but main already blocked that before this PR, so it is a separate gap.
🤖 Agent: the remove-all/re-add batches (effects, equipment, enchantments) use set_modifier_unclamped and clamp once afterwards, so an unrelated effect refresh keeps health_boost hearts; LivingEntity.onAttributeUpdated clamps only on a real max_health drop.
🤖 Agent: Tab on Create World sets focus=2, so has_caret_field() is false while a tabbed button is focused and Space still presses it; only a click into a field drops the button focus, as Screen.setFocused does.
LivingEntity.aiStep's removeFrost makes the movement_speed instance through
getAttribute on the stand's first server tick; AttributeMap.load makes it for
a save that lists it. A /data get in the summon tick showed it since #8420.
A stand now records when that instance exists and saves it from then on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: −4.8 to −5.0 ms MSPT in 3/3 A/B pairs (fast profile, which native mod builds use). Only #[inline] attributes, no body changed; 10 harnesses incl. mob_ai, entity_move and collision pass.
🤖 Agent: tick_lan −0.3 to −0.6 ms in 3/3 A/B pairs; the 9 attribute transcript harnesses (server_entities, mob_attributes, attribute_updates…) match vanilla. Risk point: the per-type cache holds only the type's default and synced id, never a mob's own modifiers, which still go through base_value_with per mob.
🤖 Agent: the NoAI branch now runs ravager::ai_step after ageable_step, as Ravager.aiStep does outside serverAiStep. The corpus group pins the stun-to-roar boundary at tick 2 for both AI modes.
🤖 Agent: release now rechecks nextDamageWillBreak after the wet/passenger test, as TridentItem.releaseUsing does, and the stack's max_damage component is sent over the network, so both peers admit the same throws.
🤖 Agent: I collapsed the growl's if-let into a let chain, which was the clippy failure. The dead path follows EnderDragon.aiStep after tickDeath: growl (pitch, then nextInt(200)) before the per-tick EXPLOSION, both at the risen position.
🤖 Agent: I added the missing RandomSource import and made water_entry pub, since only gameplay calls it and horizon-client-lib flagged it as dead code. The item splash draws from a random seeded per item (id, ticks), not the entity's own; that only affects particle spread.
🤖 Agent: I moved the block lookup, clamp and velocity into one sprint_particle helper shared with sprint_dust, so the hop's crumb differs only in drawing from the rabbit's own random (x then z), as Entity.spawnSprintParticle does.
🤖 Agent: Allay.updateDuplicationCooldown runs in aiStep, outside serverAiStep, so NoAI allays count down too; the normal path calls the same helper once, from allay::ai_step.
🤖 Agent: DyeColor.byId uses ByIdMap's ZERO strategy, so any color byte above 15 reads as white; masking with & 15 had turned 17 into 1. The new dedicated-server harness builds its binary the way test_dedicated_server_parity does.
A box across a chunk border goes back and forth between two columns on
every row, so the single remembered may_reach_out answer was recomputed
(a palette walk) at each crossing. The scan now keeps the last eight
(chunk, section) answers; the cells read and the chunk queries are
unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every frame now starts with texture A as each colortex buffer's "main". A
buffer that isn't cleared and ended the last frame on B has B copied to A
first; B keeps what was last written into it. So where a program leaves
pixels alone (discard) or blends, the texture it writes holds what Iris
1.11.7 shows there, measured with probe packs of our own: the previous
frame's last write in #9009's case 1, one accumulation per frame for a
buffer only blended onto, and 0 where a buffer flipped twice a frame was
never written.
Fixes#9009
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With a shader pack lighting the faces (`oldLighting` off), the mesher gave every block and fluid
face 1.0. Iris 1.11.7 gives our probe pack 0.9 (229 of 255) on every Nether face, lava sides
included, and 1.0 in the overworld. These values fit the dimension's up-face value of vanilla's
CardinalLighting (NETHER up 0.9, DEFAULT up 1.0); that is a fit of our probe's Iris values, not an
established Iris rule. Fluid sides under a pack drop vanilla's side factor.
Fixes#9012
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every game event (each mob's step is one) filtered the level's whole list
of sculk block entities by distance. The index now also keeps them by
32-block cell with their insertion rank, and an event reads the cells its
radius reaches (at most eight), sorted back by rank: the same listeners in
the same order as the filtered list. A test checks it against that filter
through inserts, removals and retains.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
In Iris 1.11.7 a `uniform.*`/`variable.*` expression that names
something its custom uniforms don't know fails as a whole and reads 0,
even when the name is in an `if` branch that isn't taken, and so does
every uniform or variable that uses it (probe packs of our own). Horizon
read only the name as 0 and kept the rest, and saw fogStart, fogColor
and eight more built-in names that Iris's custom uniforms don't.
Custom now works out, on its first evaluation, which items name an
unknown or hidden name, directly or through another item, and gives
them 0. Two names Iris sees that Horizon doesn't give yet
(currentRenderedItemId, lightningBoltPosition) still read 0 alone.
Fixes#8995
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Mob::species, alive and bounds (read by the game crate for every mob),
BlockFacts::get (an index into the state table), RandomTickMask::may_tick
(a bit test per drawn position) and World::block (a chunk lookup and a
palette read) ran as calls from the game crate in the fast profile that
native mod builds use. Marked #[inline]; bodies unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A pack without `gbuffers_clouds` left the clouds to the game, so they never reached the pack's
buffers. Iris 1.11.7 draws them with `gbuffers_textured`, else `gbuffers_basic` (not
`gbuffers_textured_lit`), blended, as its ShaderDoc's gbuffers table lists and as our probe packs
measure.
Fixes#9011
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`flip.<program>.<buffer> = true` on a buffer the program also writes was
ORed with the write's own flip, so the buffer flipped once. Iris 1.11.7
leaves it unflipped (probe packs of our own): a later program samples the
side the program didn't write (0 for a cleared buffer), and a blended
write lands on what the program wrote (0.25 + 0.5 reads 0.749). The
forced flips are now XORed with the written ones.
Fixes#9010
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without LTO across crates (the fast profile, which native mod builds use
as performance), a non-generic function from horizon-core is only inlined
into another crate when it is marked #[inline] or is a tiny leaf. Aabb::new,
Aabb::max, max_position, deflate, Vec3's arithmetic, BlockPos::offset and
chunk and PalettedContainer::is_global ran as calls on the server's hottest
paths (the collision scan, the snapshot, mob movement), each returning its
box or position through memory. They are marked #[inline]; their bodies are
unchanged, so every value is the same.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Iris 1.11.7 accepts the names of `iris.features.optional` and
`iris.features.required` in any case, and defines `IRIS_FEATURE_<name>`
with the name as the pack wrote it (probe packs of our own). Horizon
compared them with its uppercase list exactly, so `ssbo` defined nothing
and `compute_shaders` as required was noted as unsupported. The
HIGHER_SHADOWCOLOR check now matches in any case too.
Fixes#8996
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The world blit wrote its float colour into colortex0 whatever its
format, so a pack giving colortex0 an integer format (RGBA32UI, Arc
v0.15.2) failed wgpu's pipeline validation and was turned off. An
integer colortex0 now gets only what the pack writes: the blit leaves it
out (still copying depth and clearing the other buffers), it is cleared
in a pass of its own, and a pack without `final` gets a cleared screen
instead of a float copy of it.
Fixes#8459
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A `const` local whose initializer is a constant expression is a
constant expression itself (GLSL 4.60, 4.3.3, as #8462 quotes it).
naga's GLSL frontend kept it as a local variable, so a texel offset
given by one failed with "Sample offset must be a const-expression". It
now becomes a module constant, as a global `const` does, also inside a
loop.
Fixes#8462
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`.length()` of an explicitly sized array is a constant expression
whether or not the array is (GLSL 4.60, 4.3.3, as #8463 quotes it). In
a constant context (an array size) naga's GLSL frontend lowered the
array itself there and failed with "Unexpected runtime-expression"; the
array is now lowered outside it, for its type only.
Fixes#8463
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Iris 1.11.7 filters shadowtex0 and shadowtex1 linearly when a program reads
them through a plain sampler2D (probe packs of our own: a quarter texel past a
texel's centre moves a quarter of the way to its neighbour, in gbuffers and in
composite), unless the pack sets shadowtex0Nearest, shadowtexNearest or
shadow0MinMagNearest (shadowtex0), or shadowtex1Nearest or
shadow1MinMagNearest (shadowtex1). Horizon read the nearest texel, so a lookup
next to a block's edge took the higher block's depth and shadowed faces Iris
lights, with stripes on far terrain.
wgpu filters a depth format only in a comparison, so a shadowtex read that way
now reads an R32Float copy of the final depth (copied through a buffer after
the shadow pass's terrain), with the linear sampler, on devices with
FLOAT32_FILTERABLE. Others keep the nearest texel.
Part of #8464
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every tick's entity snapshot looked movement speed up by name for every
mob: the catalog's id, the type's synced default (two string-keyed maps)
and the type's own default base (a SipHash of the attribute's name).
They depend only on the type, so a snapshot keeps them per type; each
mob's base still comes from its own finalized bases first. The synced
instances are the same.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GLSL 4.60 8.11 lets an atomic memory function take a single component
of a vector in a buffer, as RenderPearl's hand light does with
atomicAdd(hand_light.right.x, rg); naga has no atomic vectors, so the
stage failed with "Pointer to atomic is invalid" and was skipped.
The rewrite that turns such shared vectors into arrays of components
now also covers members of std430 buffer blocks (named or not), in
graphics stages as in compute ones. The block keeps its std430 layout:
a padding member where the vector's alignment moved it, and four
elements per 3-component vector of an array. Other layouts, runtime-
sized arrays and blocks whose layout isn't known up to the vector stay
as written.
Fixes#8977
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A stage that enables GL_AMD_shader_trinary_minmax gets the extension's
min3, max3 and mid3 (genType, genIType and genUType overloads: the
per-component minimum, maximum and median of x, y and z) in its prelude,
for each one it calls and doesn't define itself. naga has none of them,
so such programs were skipped with "Unknown function 'max3'".
Fixes#8460
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The window's item splashes ran while r.head_lerps was still borrowed; they
now run after it is released. Main's item_areas test builds an ItemEntity,
which needs was_touching_water.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
horizon-client-lib builds without gameplay, so a pub(crate) water_entry and
its ItemWater were dead code there (clippy -D warnings).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The hop's crumb reused a copy of sprint_dust's block lookup, clamp and
velocity; both now call one sprint_particle helper and differ only in the
random that places the crumb.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Iris 1.11.7 reads a custom uniform's or variable's line in
shaders.properties with the macros expanded: the pack's options, the
standard macros and the file's own #defines (function-like ones too),
in the name as well as the expression (probe pack of our own). Horizon
read them raw, so an option such as RenderPearl's FOG read 0.
#define in .properties files now goes through the preprocessor's own
parsing (Macros::define_line), so function-like macros work there too.
Fixes#8976
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every random tick handed out each ticking section's mask by cloning an
Arc twice (the cache, then the chunk's last answer) and dropping both
again. The mask is usually out of cache, and a locked increment on a
missed line stalls the loop: about 5 % of the server thread on a busy SMP
profile. The masks never leave the tick thread, so they are Rc now. The
draws, the masks and the ticks are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: measured on the desktop with pack_load_bench, 5 alternating A/B rounds on main eaa7da4fb, medians: block sprites one by one 100 → 80 ms, prefetched 44 → 35 ms, from the disk cache 41 → 30 ms, CTM prefetched 84 → 64 ms; stages that don't insert sprites are unchanged. The still-texture key no longer clones each frame's pixels. One risk: two different textures sharing a 64-bit SipHash would share a layer, about 1e-12 at a few thousand sprites.
🤖 Agent: I kept only the concurrency groups on mod-registry and telemetry-server: a newer push cancels a superseded PR run, and main's runs all finish. I dropped the pr-labels and triage-command groups, where a newer run cancels a pending one even when its own job is skipped (a title-only edit, a plain comment), so labels or a /triage would be lost. I also dropped the cron move, which had no stated reason.
🤖 Agent: the gap block is exactly what `tools/parity_gaps.py readme` prints today (all ten lists #3512-#3521 at 0 open), and "all 281 harnesses" was out of date (there are 324+), so dropping the count is right.
🤖 Agent: CONTRIBUTING still said shader packs were removed on 24 October. They are back as a clean-room rewrite (horizon-shaders) plus Horizon's own API. The known-issues bullet was already removed on main (279a04a8eb), so I took main's file to resolve the conflict.
🤖 Agent: main does have the shader API (`manifest.rs`: API = "0.1", with a generated reference), so "none yet / not started" was wrong. I fixed the intro, which said the API was built "from scratch on top of" the removed one, listed the real stages (vertex was missing), and noted that F3 already shows pass costs.
🤖 Agent: every flag added here matches horizon-server's usage text and parsers on main (`restore --dimension`, `update --archive/--version/--timeout`, `rollback --timeout`, `[updates] keep = 1`). I put back the command descriptions the edit dropped and pointed import-resources at step 2, where it is explained.
🤖 Agent: the audio callback allocated a fresh `voices` Vec on every buffer, on the real-time thread; it now reuses one in `Mixer`, as `cameras` already did. Mixing order and gains are unchanged. I rustfmt-ed it.
🤖 Agent: `swim_neighbors` built an `FxHashMap` (a heap allocation plus 6 inserts and 8 lookups) for every node a water mob's pathfinder expands. A `[Option<u32>; 6]` indexed in `Direction.values()` order does the same: I checked each diagonal pair against the old `(dx,dz)` keys, and the call order of `swim_accepted` is unchanged. I rustfmt-ed it and replaced the index loop with a zip (clippy).
A moved VoxelShape cloned its three coordinate vectors (three allocations
plus their Arc) and its boxes vector (two more). Every block an entity's
movement meets is moved to its position, several per mob per tick. The
three axes now share one slice (axis i is values[starts[i]..starts[i+1]])
and the boxes are an Arc<[Aabb]>, so a moved shape makes three allocations
instead of seven. Every boundary gets the same addition as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
In vanilla Java 26.3, `GameEvent.CONTAINER_OPEN` and `CONTAINER_CLOSE`
are fired exclusively when `ContainerOpenersCounter.openerCountChanged`
detects an opener count transition across zero (0 -> 1 on open, 1 -> 0
on close). Spectators never join `ContainerOpenersCounter`, and
subsequent openers on an already open container do not re-trigger edges.
Guarding `post_block_game_event` with `if first` and `if last` prevents
spectators from emitting vibrations that alert sculk sensors and wardens.
Co-Authored-By: Antigravity <[email protected]>
- Launcher tables get every string main has added since they were written, and lose strings the
code no longer has.
- Product names translated or transliterated as words (Horizon, Minecraft, Java, Mojang…) are back
in Latin script.
- Fixed the meanings machine translation got wrong: a crash written as a vehicle crash, Nightly
as "every night", instance as "example", tick as a checkmark, the controller labels written
as commands, and similar.
- Removed the U+200B pairs that came with the translation tool.
- Game tables drop entries identical to en_us.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merges the heads of #8755, #8757, #8761, #8764, #8767, #8768, #8770, #8771, #8777, #8781, #8792, #8793, #8794, #8797, #8800, #8802 as they are (each one's table, its Language entry in
launcher/src/i18n.rs and its Bundled arm in lang.rs), so each PR is marked merged when this lands.
Their edits to existing tables are left out: main's versions are newer and more accurate.
The next commit fixes the tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Launcher tables get every string main has added since they were written, and lose strings the
code no longer has.
- Product names translated or transliterated as words (Horizon, Minecraft, Java, Mojang…) are back
in Latin script.
- Fixed the meanings machine translation got wrong: a crash written as a vehicle crash, Nightly
as "every night", instance as "example", tick as a checkmark, the controller labels written
as commands, and similar.
- Removed the U+200B pairs that came with the translation tool.
- Game tables drop entries identical to en_us.
- Zulu: vanilla 26.3 ships no zu_za language, so the game table could never be picked:
removed (the launcher table stays).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merges the heads of #8580, #8581, #8585, #8587, #8596, #8597, #8599, #8600, #8601, #8603, #8605, #8606, #8607, #8608, #8609, #8610 as they are (each one's table, its Language entry in
launcher/src/i18n.rs and its Bundled arm in lang.rs), so each PR is marked merged when this lands.
Their edits to existing tables are left out: main's versions are newer and more accurate.
The next commit fixes the tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Launcher tables get every string main has added since they were written, and lose strings the
code no longer has.
- Product names translated or transliterated as words (Horizon, Minecraft, Java, Mojang…) are back
in Latin script.
- Fixed the meanings machine translation got wrong: a crash written as a vehicle crash, Nightly
as "every night", instance as "example", tick as a checkmark, the controller labels written
as commands, and similar.
- Removed the U+200B pairs that came with the translation tool.
- Game tables drop entries identical to en_us.
- The launcher test used "eo" as a language without a table: it now uses "tlh".
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merges the heads of #8528, #8529, #8530, #8532, #8533, #8535, #8537, #8538, #8540, #8544, #8550, #8551, #8556, #8560, #8564, #8568, #8574, #8579 as they are (each one's table, its Language entry in
launcher/src/i18n.rs and its Bundled arm in lang.rs), so each PR is marked merged when this lands.
Their edits to existing tables are left out: main's versions are newer and more accurate.
The next commit fixes the tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Launcher tables get every string main has added since they were written, and lose strings the
code no longer has.
- Product names translated or transliterated as words (Horizon, Minecraft, Java, Mojang…) are back
in Latin script.
- Fixed the meanings machine translation got wrong: a crash written as a vehicle crash, Nightly
as "every night", instance as "example", tick as a checkmark, the controller labels written
as commands, and similar.
- Removed the U+200B pairs that came with the translation tool.
- Game tables drop entries identical to en_us.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merges the heads of #8451, #8452, #8453, #8454, #8455, #8456, #8458, #8467, #8468, #8469, #8472, #8473, #8475, #8477, #8483, #8484, #8495, #8496 as they are (each one's table, its Language entry in
launcher/src/i18n.rs and its Bundled arm in lang.rs), so each PR is marked merged when this lands.
Their edits to existing tables are left out: main's versions are newer and more accurate.
The next commit fixes the tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Launcher tables get every string main has added since they were written, and lose strings the
code no longer has.
- Product names translated or transliterated as words (Horizon, Minecraft, Java, Mojang…) are back
in Latin script.
- Fixed the meanings machine translation got wrong: a crash written as a vehicle crash, Nightly
as "every night", instance as "example", tick as a checkmark, the controller labels written
as commands, and similar.
- Removed the U+200B pairs that came with the translation tool.
- Game tables drop entries identical to en_us.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merges the heads of #8427, #8428, #8431, #8432, #8434, #8435, #8436, #8437, #8439, #8440, #8441, #8442, #8444, #8445, #8446, #8447, #8449, #8450 as they are (each one's table, its Language entry in
launcher/src/i18n.rs and its Bundled arm in lang.rs), so each PR is marked merged when this lands.
Their edits to existing tables are left out: main's versions are newer and more accurate.
The next commit fixes the tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Launcher tables get every string main has added since they were written, and lose strings the
code no longer has.
- Product names translated or transliterated as words (Horizon, Minecraft, Java, Mojang…) are back
in Latin script.
- Fixed the meanings machine translation got wrong: a crash written as a vehicle crash, Nightly
as "every night", instance as "example", tick as a checkmark, the controller labels written
as commands, and similar.
- Removed the U+200B pairs that came with the translation tool.
- Game tables drop entries identical to en_us.
- Also removed stray U+200B from main's bg, fi, he launcher tables and the da_dk game table.
- Serbian: the Cyrillic game table is vanilla's sr_sp (Српски); sr_cs is vanilla's Latin
Serbian, so it now gets the same table transliterated.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Merges the heads of #8394, #8395, #8396, #8398, #8401, #8402, #8405, #8408, #8410, #8412, #8413, #8414, #8415, #8416, #8417, #8418, #8424, #8425 as they are (each one's table, its Language entry in
launcher/src/i18n.rs and its Bundled arm in lang.rs), so each PR is marked merged when this lands.
Their edits to existing tables are left out: main's versions are newer and more accurate.
The next commit fixes the tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every comment on an issue starts a run (most skip their job), and a newer run cancels a pending one in the
same group: a /triage waiting behind another run would be dropped by the next plain comment.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A title-only edit's run skips its job, but as a newer run in the group it would still cancel the opened run's
labelling. The claim cron move had no reason.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
RemoveBlockGoal.canUse walks every cell within 24 blocks and a few
heights of the zombie (about 17,000 of them) for a turtle egg, once
every 10 to 20 seconds per zombie: a server's thousands of zombies
walked over a hundred thousand cells a tick, nearly always to find
none. The walk is passed over when no section of the cells it reaches
has a turtle egg in its palette, which is when it would find nothing;
anywhere else it walks as before. The walk and its bounds are split
out of find_nearest, and a test checks every walked cell is in them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Mob.aiStep's looting block, the foxes' item search and the dolphins'
tested each mob's reach against every dropped item of the level, so a
server's thousands of mobs times its eggs and drops made a quadratic
scan every tick. The items are now kept in 8-block cells for each pass:
a reach that meets no occupied cell meets no item, and its scan (which
would find nothing) is skipped. A reach near an item scans as before,
in the same order with the same tests, and the cells are rebuilt as
soon as an item is spawned or taken during the pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: HDR off or Smooth Gradients off keeps main's frame format at every site (checked each one); format selection now in tested functions; fmt, clippy and the touched crates' tests pass on the desktop at b750f4d7e0.
tick_carts ticks a copy of each cart, and the copy carried the cart's
whole loot roll: a mineshaft chest cart holds it as an NBT compound,
cloned and dropped every tick (a tenth of cart ticking with a level's
mineshafts loaded). Only opening the cart, a command or a save reads
the roll, never a tick, so the copy takes it and the stored cart gets
it back with the rest of the tick's state. The stored cart keeps its
slots meanwhile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
draws_wide_frame and frame_format take the 16-bit frame's choice out of
Renderer::new, with tests for every input that keeps the 8-bit frame.
wide_frame had slid between paper_white_nits and its doc comment.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A client mod is told of each chat line from the server just before it
shows, and may replace or cancel it: system messages (chat and action
bar), player chat and disguised chat. Chat Delay's queue now keeps each
message's sender and chat type, so the hook fires when the message
leaves the queue, as vanilla's showMessageToPlayer does, and a
cancelled message lets the next one through.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: village_sections reads the snapshot's villager slots, same order and tests (a slot keeps its species all tick); creature span −0.9 to −6.4 ms in 3 paired runs, median MSPT flat; mob_ai, villager_farming, natural_spawn, server_entities pass on the 4-PR stack.
A client mod sends what the player could type, through send_chat
(ClientPacketListener.sendChat / sendCommand): a leading / is a command,
anything else signed chat. No client chat event and no chat history. An
error when not in a server's play state, for an empty message, and when the
Chat option blocks the message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued: the dedicated engine now saves players and levels on SIGTERM/SIGINT/SIGHUP like vanilla's shutdown hook; the new dedicated_crash_save check fails on main and passes here (desktop).
🤖 Agent: box test first (same entities, same order) + no entity boxes for a zero move (vanilla collide keeps zero at zero); paired A/B: tick_carts 34.1→3.9 and 51.4→6.3 ms; entity_collision/move, mob_ai, server_entities, dedicated_server pass on the 4-PR stack.
🤖 Agent: spawn_cost is None for types no biome costs, so the skipped lookup cannot change a result; paired A/B: spawn_natural −2.0 to −2.2 ms per tick at 6,200 mobs; natural_spawn, spawn_rules, finalize_spawn, mob_ai, server_entities pass on the 4-PR stack.
🤖 Agent: same box, one dims() per box; paired A/B on the desktop: tick_carts −1.4 ms at ~5,800 mobs; mob_ai, entity_collision/move/obstruction, server_entities, dedicated_server pass on the 4-PR stack.
The engine run as `horizon-client --dedicated` caught no stop signal: SIGTERM or Ctrl-C ended it at
once, losing every player inventory, ender chest and chunk changed since the last autosave (up to 5
minutes). Vanilla's shutdown hook halts the server and saves on these signals. Catch them for the
dedicated role too and end the tick loop on the first one, so the normal stop saves players and
levels; a second signal still ends a stuck save at once. A signal ignored from the start (nohup's
SIGHUP) stays ignored, as the JVM leaves it.
horizon-server already turned these signals into a console `stop`; this covers hosts that run the
engine directly (panels, systemd, docker without the supervisor).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`HORIZON_HDR_OUTPUT=pq` (or `scrgb`) now offers that output to an
`--offscreen` renderer, which has no display to ask. HDR is drawn as with
a window (the 16-bit frame included) and a screenshot writes its `-hdr.png`
beside the SDR one; nothing is presented, and Auto takes 203 and 1000 nits.
A 16-bit frame with HDR off reaches the offscreen target as it reaches a
swapchain. Nothing changes without the variable or with a window.
Fixes#8879
Co-Authored-By: Claude Opus 5.5 <[email protected]>
EndGatewayBlock.entityInside starts that beam through triggerCooldown's block event. The server copy was the only one cooling down, so the window kept drawing nothing.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
EnderDragon.aiStep still growls and adds one EXPLOSION each tick after tickDeath has started the rise. The window returned before both.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
The traveling player was sent that event and never heard block.portal.travel.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
PotionItem.useOn sends five SPLASH particles with ServerLevel.sendParticles. Adding them on the server Game never reaches the client window.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
Main split item ticking into tick_move and tick_settle. The splash now runs on that path, before the move and again after the second fluid push.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
Inserting a sherd broadcast the sound, but the block event and the dust plume stayed on the server. Vanilla wobbles through Level.blockEvent and sends the dust with ServerLevel.sendParticles.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
AbstractCubeMob.tick updates the squish scale on the client as well as the server. The window only ran the landing splash, so the model stayed rigid.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
Entity.doWaterSplashEffect draws bubble and splash particles on the client. Dropped items never called it, and mob particles were only added on the server, so the window never drew them.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
move_cart resolves a zero delta to zero whatever the boxes around it
(try_resolve_step_movement returns it before reading any shape, and the
step-up needs a blocked horizontal move), so an idle cart on its rails,
like every mineshaft's chest cart, no longer scans the level's mobs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The window's cart kept its fuse at -1, so it never showed the smoke, the white flash or the swell.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
Fox.aiStep plays FOX_AGGRO through the server's playSound, so a server-thread player hears it at the fox.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
Vanilla Rabbit.handleEntityEvent(1) calls spawnSprintParticle and then
starts the hop clocks. The client played the hop and skipped the crumb.
Fixes#8867.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
PrimedTnt.tick adds one SMOKE particle each lit fuse tick on the client. The window ticks that fuse but never spawned the particle, and the server's Level.addParticle does not reach it.
Co-Authored-By: Grok <grok-4.7> <[email protected]>
Co-authored-by: Cursor <[email protected]>
🤖 Agent: reviewed the splitscreen account handoff; fixed sign-in-required accounts being handed over, the refresh lock, and leftover session files; desktop launcher + game crates green
🤖 Agent: release blocker for launcher 0.2.38. The Windows uninstall smoke test passes on a branch dry run of the launcher release (41747520933: build windows green, sign green), where 41744019794 failed with os error 17. GitHub launcher and verify are green on this head.
Squid.hurtServer reports a hit only when a living entity caused it, and GlowSquid.hurtServer dims the squid only when that returns true. Horizon dimmed on every hit, so a glow squid on a campfire, suffocating or drying out lost its glow.
Fixes#8724
Co-Authored-By: OpenCode (Jay's Unintelligence) <[email protected]>
MoveThroughVillageGoal.canUse (and the villagers' own village checks)
gathered the village sections from every slot of the tick's snapshot,
so each zombie at night read every loaded mob. The mob ticks now keep
the slots holding a villager (a slot keeps its mob and species all
tick) and village_sections reads those, in the same order, with the
same alive and POI tests.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
NaturalSpawner.createState asks every loaded mob's biome for its type's
spawn cost each tick, and spawning asks again at each tested position.
Only a few nether types have one in any biome, so for the others the
answer is none wherever they stand: the zoomed biome lookup (the
fiddled BiomeManager zoom, then the chunk) is skipped for them. The
types are read from the level's biomes each pass, mods' included.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each cart, each tick, collected the box of every cart, boat and mob of
the level (with a mob's pushable state) and kept only those within 2
blocks of it, so a server's thousands of mobs cost every cart a full
scan of allocations and pushable tests. The box test now comes first,
in cart_entity_collisions and in pickup_from_cart's candidates: the
same entities pass, in the same order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Carry current main, spawn protection, and view distance into the stacked simulation-distance PR while retaining every management route and harness case.
Co-Authored-By: Codex (GPT-5.6) <[email protected]>
The running launcher moved itself into %TEMP% so its folder could go, but a
rename never crosses volumes: with TEMP on another drive (GitHub's Windows
runner, or any player with TEMP elsewhere) it failed with os error 17 and the
uninstall left the launcher behind.
It now goes into %TEMP% only when it is on the same drive, else beside the
install folder as a hidden file, asks Windows to delete it at restart, and the
next setup or uninstall sweeps both places. The folder choice is plain path
text (uninstall_aside.rs), tested on every OS.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The launcher's other-account handoff skipped the services' check for a token with time left,
so an account marked sign-in required (a refused refresh, a token for another profile) could
still be handed to a splitscreen player. Such accounts are now always skipped (Offline).
- Renewing the other accounts takes the account-operation lock, as every renewal does, so a
rotated refresh token is not lost to another launcher renewing the same account.
- Session files written for a launch are deleted once the game would refuse them (2 min), when
the launch fails before the game starts, and when the game exits; the sessions folder is
0700 on Unix. The game consumes every listed file, even for an entry it then ignores.
- The splitscreen server rule (#6399) is a pure function with a test: offline profiles join only
home-network offline-mode servers; a Microsoft session lifts that while it holds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
WitherBoss.addEffect returns false, so nothing lands on the boss. Horizon refused only the wither effect itself (the WitherSkeleton rule), so splash potions, area clouds, /effect and tipped arrows all worked on it.
Both paths that add an effect now refuse every effect for the wither; the wither skeleton keeps its own rule.
Fixes#8776
Co-Authored-By: OpenCode (Jay's Unintelligence) <[email protected]>
Carry current main and the spawn-protection management route into the stacked view-distance PR while preserving both parity harness ranges.
Co-Authored-By: Codex (GPT-5.6) <[email protected]>
Share only the positive cooldown decrement with the NoAI path and cover
both AI modes and signed loader controls on real dedicated servers.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Preserve the spawn-protection management path alongside the operator, status reply, and online-player visibility management changes now on main.
Co-Authored-By: Codex (GPT-5.6) <[email protected]>
Keep owned sprite-set indices from referring to a different declared particle after the resource sets are rebuilt.
Co-authored-by: Codex <[email protected]>
Keep hidden controller ticks and population accounting while emitting no geometry, matching visibility-gated source particles.
Co-Authored-By: Codex <[email protected]>
LivingEntity.tickEffects drops an effect whose duration is 0, because MobEffectInstance.hasRemainingDuration is false there. Horizon counted it down from 0 to -1, the infinite-duration sentinel, so an effect loaded with duration 0 stayed forever and even took its first tick.
Fixes#8721
Co-Authored-By: OpenCode (DeepSeek V4.1 Flash) <[email protected]>
WitherBoss.canRide returns false, so OldMinecartBehavior.pushAndPickupEntities cannot seat the boss in a moving cart. can_ride_cart excluded only the warden.
Fixes#8790
Co-Authored-By: OpenCode (DeepSeek V4.1 Flash) <[email protected]>
🤖 Agent: setblock/data merge now load CustomName into block_names, data get shows it and clone copies it; the shared load_block_name keeps banners and signs on their own names as before.
The overlap-note check timed out and a rerun needs admin
rights, so this push starts it again on current main.
Co-Authored-By: Grok <[email protected]>
CI compiled this branch against a main that still built the
composite shader test's Resources without its rgb field.
Co-Authored-By: Grok <[email protected]>
Mob::bounds_at called width() and height(), and each ran the whole
dims() (the sleeping check, the species' baby, pose and scale rules).
Every box asked for (collisions, pushes, cart and spawn tests) paid it
twice. Same f32 rounding as before, so the same boxes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Windows the launcher fallback test fails: the fake uname, xcrun, pkg-config and cargo scripts are written in text mode, so their shebang is #!/bin/sh\r, and Git Bash puts /usr/bin ahead of every Windows PATH entry, so the real uname answers MINGW64_NT-... and the macOS fallback never adds --features gpui_platform/runtime_shaders.
Write the scripts with LF, and on Windows prepend their folder to PATH inside the shell, where Git Bash cannot reorder it.
Fixes#8823
Co-Authored-By: OpenCode (DeepSeek V4.1 Flash) <[email protected]>
Add reusable JSON golden cases for the stable status, metrics, logs, start, stop, and restart response contracts. Normalize only release, process, and elapsed-time values while checking their types.
Fixes#8816
Co-Authored-By: Codex (GPT-5.6) <[email protected]>
🤖 Agent: a moved block's cell now supports with vanilla's offset shape (getExtendedProgress: a block back at progress 0, in place at 1), so a rail on a chain-pushed stone still pops as in vanilla while a slime-carried rail survives (#8111, fails on main); dynamic_support 4.87M lines, piston_shape 1.10M and redstone 1000/1000 match on playhorizon.
A client mod can move and turn the camera a frame is drawn from: after
vanilla placed it, native_camera_pose gets its position, yaw and pitch and
may set them (a cinematic, replay or film camera). The picture, culling, the
sound's listener and what faces the camera follow it; the player, their aim,
the hand and chunk loading stay the player's. Values that aren't finite keep
vanilla's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: item_from_nbt now reads ids as vanilla's Identifier (minecraft default namespace), so display/command NBT with "stone" no longer maps to wire air; one shared function, so containers and equipment follow too.
Bundles Horizon's own Vèneto strings so vec_it players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Vèneto as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: the launcher redesign is signed off: the page-by-page audit against playhorizon.dev/launcher is closed (G1–G28; only G19, registry icons, waits on registry data). Main merged in at bb25d37be5; on the desktop the launcher passes fmt, clippy -D warnings and cargo test (466 passed). The Windows test this fixes only runs in the release build. This releases launcher 0.2.38 without the shop.
Bundles Horizon's own Ślōnskŏ strings so szl players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Its pistons never move, so the cases compared nothing. dynamic_support pins
a moving_piston's support per progress against vanilla instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: all five targets build and the four runnable ones start on their runners (x86_64-apple-darwin through Rosetta on the arm64 macos-latest); releases are created with --latest=false so a cargo-horizon-mod tag never takes GitHub's latest from the game; the YAML-grep test is gone, the three left test the manifest-to-release contract.
Registers Ślōnskŏ as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Davvisámegiella strings so se_no players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Decode full packed color bytes with the DyeColor zero fallback and cover
valid, invalid and unknown-pattern controls on real dedicated servers.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Registers Davvisámegiella as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Саха тыла strings so sah_sah players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Саха тыла as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Dedupe still textures by a hashed key instead of storing their full
frame bytes, and avoid retaining intermediate paletted atlas frames.
Co-Authored-By: Claude Code <[email protected]>
Bundles Horizon's own Occitan strings so oc_fr players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: Desktop proof on 04f8ef7a5c (cargo fmt --check, clippy --all-targets -D warnings, cargo test: lib 466 + bin 83 passed, exit 0). The risk was losing a player's files: a switch changes only instances.json, and the new test checks that the instance folder stays byte for byte the same across Horizon, Fabric, Horizon, Vanilla, with the Java settings coming back.
Registers Occitan as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Lombard strings so lmo players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Lombard as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Limburgs strings so li_li players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Limburgs as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Furlan strings so fur_it players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
PistonMovingBlockEntity.getCollisionShape moves the moved block's shape by
getExtendedProgress(progress): progress - 1 while extending, 1 - progress
while retracting. MOVING_PISTON's shape is dynamic, so isFaceSturdy reads it
every time. At moveBlocks (progress 0) the shape is a block back, outside the
cell, so a rail on a pushed block pops even when another block arrives under
it; at progress 1 it fills the cell, so a rail carried by slime finishes on a
slime block that is still moving. sturdy, canSupportRigidBlock,
canSupportCenter and the wire's support now test that shape. The redstone
harness gains both scenarios as fixed cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Replace per-node FxHashMap allocation in swim pathfinding with a fixed
six-element array, preserving traversal behaviour.
Co-Authored-By: Claude Code <[email protected]>
BaseContainerBlockEntity.loadAdditional reads CustomName from the tag a
command gives (setblock, data merge), saveAdditional writes it back for
data get, and clone copies it. Horizon keeps the name beside the block
entity in block_names, which these paths never read or wrote.
Fixes#8752.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registers Furlan as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
ItemStack.CODEC reads `id` through Identifier, so `{id:"stone"}` is
minecraft:stone. item_from_nbt kept "stone" as it came, which the
server's registry lookup then sent as air: an item_display summoned
with `{item:{id:"stone"}}` showed nothing (#8570). An id that is no
valid identifier makes no stack, as the codec's error does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: a stop that reaches the game (in game, RCON, cmd) now leaves the supervisor and API up for list edits, but a stop on the supervisor's own stdin still ends it (I added that: Pelican and 'run' wait for the process to exit), and while stopped that stdin takes start/stop; ROUTES bumped to 30 after main's new route, horizon-server tests 133/133.
🤖 Agent: Persian launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
`lod_grid_bench` printed a mean of 5.3 to 5.9 spans a column. A mean
alone says nothing about the shape of the distribution, which is what
decides whether a column's spans fit a fixed inline capacity or take a
heap block; sizing `Spans` off the mean would be a guess.
Print the distribution beside it, with a running total so the share that
fits any given capacity can be read straight off.
Measured at seed 0, 40 tiles a level, Full far detail: four spans or
fewer covers 38% of columns at level 0, 28% at level 2, 26% at level 4
and 32% at level 6, with a tail reaching 14 to 16.
These are the spans worldgen returns, before the client's `shade_sky`
splits fluid and leaf runs a light level at a time.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
🤖 Agent: Desktop proof on ed007155f1 (cargo fmt --check, clippy --all-targets -D warnings, cargo test: lib 465 + bin 83 passed, exit 0). The risk was the launcher's selection: a Nightly installed for one instance becomes the launcher's version only when it has none, and a second install queues behind the running one instead of being refused.
A chunk's detail arrived as `ReducedColumns`, which reduced the 16x16
columns to cells 2, 4, 8 and 16 blocks wide up front: 85 columns beside
the chunk's 256, and four passes of `Column::reduced` over every span of
every footprint.
One chunk is covered by one far tile, so one of the four widths is what
gets read, and a chunk whose detail arrives while full-detail terrain
still draws it reads none. Building all four cost 405 us of a far
terrain thread for every chunk the world sent (measured over 200 chunks
of 5.7 spans a column, the mean `lod_grid_bench` reports); reducing on
the first read of a width costs 103 us, and nothing at all for a chunk
no far tile draws reduced.
`OnceLock` a level each, so a second mesh job reading the same width
takes the reduction already built rather than racing to repeat it.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
The checkbox read the saved value; it now reads Off there, as its label does. HORIZON_VISBUFFER's resolve
pass draws into the frame, so it takes the frame's format. The float-frame readback test goes until a float
frame does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Greek launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
🤖 Agent: Swedish launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
🤖 Agent: enable_status moves from the fixed NetConfig into an AtomicBool on lan::Shared that both the legacy 0xFE query and the STATUS handshake read, so turning replies off drops the next ping without a restart (lan_bot covers the closed handshake); ids 210–219 are unique, and main is merged with #8144 and #8136 kept.
🤖 Agent: Czech launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
🤖 Agent: merged main after #8221: its powder-snow sweep and this PR's baseTick fire/water step both follow the trident's move, both kept, sweep first (no shared state or RNG); the arrow environment code now lives in ArrowScene::apply_block_effects for arrows and tridents alike, and I aligned the trident flames with the front-view yaw the other entities use.
player_voice_pitch returns 1.0; asserting it twice pins nothing that can
regress (CLAUDE.md: skip tests of trivial constants).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Windows takes a write of any size whole while its buffer has room, so the 64 MiB the test sent all went
through and the disconnect with it. Fixes#8734.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
StrollToPoi sets nextOkStartTime 80 ticks after each try; without it a
brute 10-100 blocks from a home it cannot reach ran a path search on
every goal-selector pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Urdu launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
ServerGamePacketListenerImpl.handleSeenAdvancements calls setSelectedTab
only when ServerAdvancementManager.get finds the id; the hub cleared the
tab for any id that was not a selectable root. Also puts visibility's
doc comment back on visibility.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Thai launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
🤖 Agent: Vietnamese launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
🤖 Agent: the focused field gets the mockup's 2 px accent-soft ring as an overlay child, so layout doesn't shift; the invalid red and the command/players fields keep their own borders.
🤖 Agent: Dutch launcher table, stacked on the ones before it (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 passed lib tests incl. every_language_has_every_string).
🤖 Agent: Italian launcher table, first of a 9-step stack (any merge order is safe); desktop on this exact head: cargo fmt --check, clippy -D warnings, cargo test all green (465 lib tests incl. every_language_has_every_string). The fallback test now uses eo, since it-IT is supported.
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's shader API reference is up to date again (367b20268d), which the
previous head's verify run failed on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: offline faces come from the player's own prepared files (no Mojang asset bundled), read on a worker and keyed by vanilla's offline UUID, so a skin change is picked up by its file time. Proof (GitHub's queue is backed up): on the desktop at 4d20c0efc0, inside launcher/: cargo fmt --check, clippy --all-targets -D warnings, cargo test (465+82 lib+bin) all exit 0.
🤖 Agent: the mockup's times are glass-only helpers; the light look keeps instances::play_time and worlds_page::ago, so its wording is unchanged (only the SMP count gets translated). Proof (GitHub's queue is backed up): on the desktop at 34d9859ff1, inside launcher/: cargo fmt --check, clippy --all-targets -D warnings, cargo test (465+82 lib+bin) all exit 0.
🤖 Agent: Overview's blur makes each screenshot's blurred copy once, off the UI thread and only while blur is on, so with blur off the tiles draw the same images as before. Proof (GitHub's queue is backed up): on the desktop at d2cf230cb6, inside launcher/: cargo fmt --check, clippy --all-targets -D warnings, cargo test (465 lib + 80 bin) all exit 0.
🤖 Agent: the light look; the layout follows a glass flag separate from dark, and the sky and tint still follow dark, so dark renders unchanged. Desktop on this head: fmt, build, clippy, 465 launcher tests pass.
On the HDR page, after Calibrate. Off, the frame stays 8-bit with HDR on, as
before. Like Translucent Batching it applies at the next start or resource
reload, and is no part of a preset. Saved as hdrSmoothGradients; the two new
strings are in en_us only, for the translators.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: stopped servers show their game mode as in the mockup, read from the server's own server.toml; fmt, clippy and 465 launcher tests pass on this head on the desktop.
🤖 Agent: Multiplayer rows get the mockup's status dot and player count; also adds the 'My Horizon' string main's 9 new language tables lack, which fixes main's launcher i18n test. fmt, clippy and 464 launcher tests pass on this head on the desktop.
Main's bg, da, fi, he, hu, lt, no, ro and sk tables miss it, which fails every_language_has_every_string_the_code_asks_for. Same lines as #8563, so the two merge cleanly.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A mod block opening its content mod's menu type asks native_menu_extra_data for bytes
of the mod's own; a horizon:menu_extra payload carries them with the window and the
block's position to Horizon clients right before OpenScreen. The client takes the
block from it instead of the last clicked one, and native_screen_pos /
native_screen_extra_data read both while the mod's screen is open.
Fixes#8675
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: merged main and gave the temporary-ban test's Lists the new whitelist_bypass field (main added that initializer after this branch); cargo check of horizon-client lib + tests passes on the desktop. Off unless the owner adds whitelist-bypass.txt.
BanIpCommands.banIp sends commands.banip.info with the count and
EntitySelector.joinNames of the affected players; the dedicated console
passed an empty string for the names. Build the line from the same list
used for the disconnects, and still send nothing when no one is affected.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
🤖 Agent: tests only: each hud test gets its own temp folder, so a strip one test adds can't leak into another's atlas; 8 repeat runs of the hud tests pass on the desktop.
animated_block_sprites_register_and_pack_their_first_mcmeta_frame and
a_strip_frames_size_comes_from_its_mcmeta shared one per-process folder.
The second adds a two-frame strip of its own, so the first, running beside
it, sometimes found that strip (first frame 0) instead of its own (1).
That broke main's verify on #8531's run.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The new key sits after "Offline" in all 25 tables; "My Horizon" added to the 9 tables that lacked it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As in the mockup, Edit shows the three kinds (Horizon, Vanilla, Modded)
and, for a Java instance, Mojang's versions and the loaders. Saving a
change calls instances::switch, which changes only the instance's entry
in instances.json: its folder, with its worlds, mods and options, is never
touched, and a Java instance's settings (memory, Java, arguments, window)
are kept aside while it plays Horizon. Before Save, a notice lists what
can't keep working: mods for another game or version, and worlds that a
different Minecraft version upgrades for good. The game's home and
modpacks play what they were made with.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#8221's powder-snow sweep and this branch's baseTick fire/water step both follow the
trident's move in trident.rs: both kept, the sweep first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: under a shaders/ pack, portal faces and beams no longer fall back to Horizon's entity shader at all, so a pack with no gbuffers_block (or no beaconbeam/textured/basic) shows no portal or beam; the portal texture is copied once per atlas rect change, not per frame.
🤖 Agent: merged main, whose registry-v1.bin snapshot already takes facts.json off most starts, so the slice parse now only helps a snapshot miss; the model-library parse beside Assets::load is the part that still moves time off the critical path (skipped for a server).
🤖 Agent: cancel drops the message for everybody before the broadcast, after secure chat and the filter, so a muted player's signed chain is never relayed; I added the test-stub field and registered chat.rs's free hook point in catalog_matches_hook_points.
🤖 Agent: merged main after the STOP: main now ticks orbs and primed TNT in join order (ordered_tick.rs, tick_one_tnt), so their snowflake calls moved there, still right after each move as PowderSnowBlock.entityInside runs inside it; verify green on this head.
The owner already had previousPlayerGameType; a guest had no such field,
and the save removed the key, so the game mode switcher of a guest who
rejoined had nothing to go back to. A guest now keeps it as
ServerPlayerGameMode.changeGameModeForPlayer does (the mode left becomes
the previous one; the mode in use changes nothing), and its file writes it
when there is one and reads it back.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PlayerMotion.ticks_frozen never reached the file: a guest who left inside
powder snow came back thawed, and one who thawed kept whatever TicksFrozen
the file it joined from had. The saved body now carries it, written only
above 0 as Entity.saveWithoutId does (the key is removed otherwise), and read
back with Entity.load's default of 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
store set the food level and saturation but left foodExhaustionLevel and
foodTickTimer at 0, so a guest who left mid-sprint came back with no
exhaustion and the regeneration timer restarted. The saved body now carries
both from the live FoodData (FoodData.addAdditionalSaveData) and puts them
back on the next body.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
store never set Motion, so a guest's file always said the player stood
still. The saved body now carries the live delta movement, written as
Motion (Entity.saveWithoutId) and read back for the next body with
Entity.load's rule: a component over 10 blocks a tick reads as 0.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PlayerPredicate.matches looks for the entity the player looks at
(ProjectileUtil.getEntityHitResult over every non-spectator entity but
the player, then hasLineOfSight) whatever the trigger. Horizon searched
only the mobs, and only for using_item, so a looking_at boat, minecart,
armor stand, frame, painting or crystal never matched, and nothing
matched in tick, location or any other trigger.
looking_at_entity now walks every entity list plus frames, paintings
and crystals, and player_entity sets looking_at for every trigger. The
per-tick tick and location triggers cast the ray only when one of their
criteria may ask (names looking_at or a reference predicate), worked
out once per tree.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UE3PMKbnTcxfBSeT5uQWCK
🤖 Agent: unblocks main's verify: #8186 hand-edited the generated shaders-reference.md; the Anti-aliasing section now comes from the generator, word for word, so the doc is unchanged and the_reference_is_up_to_date passes.
With HDR output on at the start, the frame every pass draws into is
Rgba16Unorm instead of the swapchain's 8-bit format. It clips at white after
every pass as the 8-bit frame does, so the picture stays SDR's, but keeps
the steps 8 bits band in dark gradients, which HDR shows. Where the device
can't draw, blend and filter it, and with HDR switched on later, the 8-bit
frame as before. SDR is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#8186 wrote its Anti-aliasing section into docs/api/shaders-reference.md,
which horizon_shader_api::reference::markdown generates, so main's
the_reference_is_up_to_date failed. The section is now generated, and the
doc is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Horizon version list showed only installed versions. Like the mockup,
it now lists every Nightly published for this platform (the launcher's
release list, with its GitHub cache), then the installed ones the list
lacks. Create or Save makes the instance at once and installs a picked
version that isn't installed through the existing install path (signed
checksums, progress), after the install running now if there is one;
Play shows Installing meanwhile. A version installed for an instance
becomes the launcher's only when it has none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
VehicleEntity.hurtServer destroys a cart when its damage passes 40 or
MinecartTNT.shouldSourceDestroy is true (fire, explosions). Horizon only
checked the damage, so a TNT cart hit by a weak blast was pushed but never
primed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
🤖 Agent: the set path updates both state.op_level and the lists' op_level, so ops added after a change get the new default level as vanilla's settings.update does; harness ids moved to 220–229 (150s clashed with #8108), main merged with #8144's hide-online-players kept.
🤖 Agent: unblocks main's verify: the inter-stage audit now covers visbuffer.wgsl from #7054 (still within 15 variables), and the two spans tests share one lock instead of racing on the process-wide switch. Tests only; render crate's 1295 tests + clippy pass on the desktop.
- inter_stage_tests: visbuffer.wgsl (#7054) only compiles after terrain.wgsl,
so the audit failed on main. It now checks the module the renderer builds
(terrain.wgsl + visbuffer.wgsl), which stays within the 15 inter-stage
variables.
- spans: laps_time_consecutive_steps turns spans on for every thread, so
notes_keep_the_slowest_steps, running beside it, recorded the note it
expects left out. Both take one lock now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: far cells now keep any column's blocks (trunks stay under crowns) instead of the majority rule; the cost is that a thin feature in a wide cell draws cell-wide, which the before/after shows reads better than floating slabs. Same stored format, so caches aren't rebuilt.
The Python tests and the public-tree check only read files, yet cargo
waited for them: about two minutes of every run. They now run in a
thread beside rustfmt, clippy and the tests; the harnesses, which build
with cargo, still come after it, and --verbose keeps everything in
order so the output doesn't interleave.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: matches vanilla's REDUCED_DEBUG_INFO callback (entity event 22/23 to each player on change, Login.reducedDebugInfo at join). Main merged in twice: limited_crafting landed in the same hub/lan/remote spots, both kept. Verify passed on the previous head with the same diff; fmt and clippy passed on this one. One gap left: a guest whose Login was built but who isn't placed yet misses a flip.
🤖 Agent: queued, verify and launcher green on 3f941752. CI never builds Windows, so the Windows-only code was checked separately: clippy -D warnings for x86_64-pc-windows-gnu is clean on horizon-threads (all targets) and horizon-client (with and without default features), with the C build scripts (mimalloc, ring) stubbed.
🤖 Agent: re-merged main after the merger's conflict stop (main's new shift-click cycling test kept beside the particle culling one); Off, the default, still calls the plain render path, so players who don't opt in pay nothing per frame.
🤖 Agent: unbreaks main's horizon-client-render test build (#8131 added Resources::rgb, #8065's new test initializer lacked it); clippy --all-targets -D warnings passes on the desktop.
main's i18n test failed: the new instance's default name reached main
after those nine tables were filled.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The palette's card now uses main's ui::dialog_plate (#8514). The light
look's lighter scrim moves into the shared helpers (ui::scrim and
dialog_plate), so the Edit dialog, the server dialog, the owner notice
and the palette all dim a light launcher the same way.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#8131 added Resources::rgb and #8065 added a test initializer without it;
both merged, so main's horizon-client-render tests no longer compile.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#8509's default name crossed with main's nine new languages, so main's
every_language_has_every_string_the_code_asks_for lacks it there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A harming splash potion or lingering cloud hurt mobs through Mob::hurt, a
player_attack from the player whoever threw it, and never recorded the thrower
in last_hurt_by_player, so on the server thread the kill reached nobody's
player_killed_entity. They now deal indirectMagic(potion or cloud, owner)
(HealOrHarmMobEffect.applyInstantaneousEffect), the thrower's kill when a
player threw it, and the mob keeps the potion or cloud as the killing blow's
direct entity (Mob::last_direct).
A falling block hitting the player now fires entity_hurt_player with the
falling block as causing and direct entity (FallingBlockEntity.causeFallDamage,
LivingEntity.hurtServer).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UE3PMKbnTcxfBSeT5uQWCK
🤖 Agent: Maximum memory in the Edit dialog; it edits the instance being edited, not the selected one, which was the risk here. Head includes lr6-memory's #8509 (instance dialog defaults), whose fmt/clippy/tests passed.
🤖 Agent: reviewed the .github changes: bots on pull_request_target/issue_comment check out ref: main only and take comment/branch text through env; checks jobs on the desktop hold no secrets or write tokens (tests/test_ci_runners.py pins it). Launcher failure was main's, fixed by #8640; merged main in.
🤖 Agent: el_gr game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: sv_se game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: cs_cz game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: pt_br game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: ar_sa game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: ja_jp game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: th_th game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: vi_vn game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: nl_nl game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: it_it game table, stacked with the other nine (any merge order is safe); "Horizon" kept as the name where the table had the word horizon, English copies dropped and the six controller options named as options, as #8385 did.
🤖 Agent: queued, verify and launcher green on 0657af35. Two fixes pushed: the client's glass bottle PASSes on a breath cloud again (the cloud's owner isn't synced, so BottleItem.use finds none client-side; the packet still goes), and the bottle now treats the cloud as gone exactly when vanilla does (age - 20 >= duration, tested at the last tick), after main's #8244 added Breath.victims.
A data pack that writes an id, tag or registry key without a namespace
("panda", "#planks", "the_nether", "inventory_changed") never matched:
the predicates compared the JSON string literally. Vanilla reads them
through Identifier codecs, where a missing namespace is minecraft.
`full` qualifies the id wherever a predicate compares one: id_matches
(plain id and #tag), the location dimension, entity sub-predicate keys,
effect keys, the loot condition type, the trigger name, the inventory and
recipe indexes, and recipe_unlocked / changed_dimension. damage_source's
tags[].id is a holder set (TagPredicate) and goes through id_matches.
The entity predicate's short key aliases follow from the qualification;
the non-vanilla "type" alias is gone (vanilla 26.3 refuses that key).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UE3PMKbnTcxfBSeT5uQWCK
A chest slot whose rules forbid the pickup, and binding-cursed armour in
the guest's own chest slot (survival against creative), reached through
`menu::click` - the path a `ServerboundContainerClickPacket` takes. Both
fail without the fix: the number key moved the stack into the hotbar and
the drop click threw it out.
`doClick`'s SWAP arm reads the slot's stack itself and puts it in the
hotbar (or, through `Inventory.getItem(40)`, the off hand), and its THROW
arm takes the stack through `Slot.safeTake`. Both therefore go through
`Slot.mayPickup`: a slot that withholds its stack - `ArmorSlot.mayPickup`
with a binding curse in survival, or a menu whose rules refuse - neither
hands it to a number key nor lets a drop click throw it out.
`lan::menu::swap` only checked `Slot.mayPlace` on each side, and the THROW
arm read the slot directly. The host applies both of those, so this was
the host's own authority rather than a client prediction.
🤖 Agent: fixes main's launcher build (palette now passes the wardrobe flag like the top bar) and the 30 strings 9 languages lacked; fmt, clippy -D warnings and 464+ launcher tests pass on the desktop.
glass_palette.rs called Page::in_top_bar() without the wardrobe flag #8569
added, so the palette now passes self.wardrobe_shown() like the top bar.
The bg, da, fi, he, hu, lt, no, ro and sk tables lacked the 30 strings the
mods page, palette and toasts added while those languages were landing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Two of them were filed as skips ("culling box only") and the illusioner's and
sniffer's were absent, so the ledger claimed Horizon needed nothing here.
An illusioner's box reaches 3.3 blocks either side of its position and a
happy ghast's drops to 2 below its feet, where the plain box reached 0.3 and
its own floor.
EntityRenderer.shouldRender culls with getBoundingBoxForCulling and its own
half-block inflate. Four renderers override it and Horizon culled every mob
with one plain box: IllusionerRenderer inflate(3, 0, 3) for the mirror images
it draws outside its own box, SnifferRenderer inflate(0.6),
HappyGhastRenderer setMinY(minY - getBbHeight() / 2) for the riders hanging
below it, and LivingEntityRenderer inflate(0.5) while a dragon head is worn.
The box is also taken at the frame's partial tick now, as
getInterpolatedBoundingBox does.
The desktop's checks runners have Mesa's software Vulkan (for parity), so verify ran GPU tests there that return
early on GitHub's runners, and one of them flakes on lavapipe (#8627): two of three runs on ludodesktop-checks-2
failed. WGPU_BACKEND=dx12 (no such backend on Linux) gives the tests no adapter there too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued, verify and launcher green on 47c26bc0 (own change is the last commit; #8203/#8258 already merged). One gap: the new test builds the grid shadow shader only for a pack without a vertex stage, so the DISPLACE_PACK branch (the material module's `vertex` called from the shadow grid) is checked only by reading compose, not by a naga validation.
12 light runners' checkouts grew 2.8 GB of packs and 1.3 GB of diagnostic logs each in an hour: 46 GB of the disk.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Far cells hung in the air: crowns over air, slabs of snow, sheets that
the fog turned pale. Three sources, all now joined to the ground:
- Reduced columns drew a height only where most of the footprint held a
block, so trunks (one column of 16 or 256) vanished under the crowns
they hold up. A height is now drawn where any column holds a block,
in the material most of them hold: what touches the ground in the
chunk touches it at every width.
- Seed-estimated trees in wide cells drew a crown with no trunk, one
block or more over the ground. A cell's own tree now stands on a
trunk and a crown from a neighbouring trunk reaches the ground.
- Wide seed cells are one point of the terrain: an overhang's lip or a
cave roof stood for the whole cell, and the aquifer-less sample put
water in caves under sea level. Under the top solid span such a
column is now ground.
On "New World (1)", 47 tiles a level around the player: floating
components per level 1..6 went from 24, 326, 325, 43, 150, 213 to
0, 0, 1, 0, 0, 0 (the one left floats in the saved chunk itself).
An ignored census test samples the tiles each far level draws around a
camera, from saved chunks and the seed as the far sampler gives them,
and counts components of drawn cells that touch neither the world's
bottom nor the tile's halo.
🤖 Agent: a new instance now opens named "My Horizon" (the mockup's defaultName) and numbers a taken default name, so Create no longer fails with "An instance needs a name."; stacked on #8499, land after it.
main ticks orbs and primed TNT in join order (ordered_tick.rs, tick_one_tnt): the
powder-snow snowflake calls move there, after each one's move as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: with the cosmetics switch off by default, the Wardrobe entry used to hide even when the service opened the claim window; it now follows GET /v1/shop (and the account's kept preview answer), and with no window set it stays hidden as before. Also fills main's 9 missing strings in 9 languages, which made the launcher's i18n test fail on main.
The Wardrobe page gets Wardrobe | Shop tabs when the cosmetics service shows the
Shop. The Shop has the founder set: when it can be claimed until, the time left, one
Claim button, the four items on cards (Free, Owned, Equipped, Soon), and every state
the mockups list: signed out, loading, offline, service down, claiming, claimed,
owned, not open yet, a preview for one account, closed (the items are gone; owned
ones stay), the claim failing. Equip stays in the Wardrobe. No price anywhere.
HORIZON_SHOP_STATE=<state> previews each one in development builds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use upstream's neutral light, color, AO, render type and shade defaults in the generated glint fixture. Preserve its geometry, UVs and assertions.
Co-Authored-By: Codex (gpt-6.1-sol) <[email protected]>
`Game::target_with_uuid` answered `Target::Player` for the level's own player
UUID. During a guest's turn the player in place is that guest, so a wolf
whose owner is the world's player (and not among the players online, for
example a single-player world moved to a dedicated server) made
`execute as <wolf> on owner run give @s ...` act on whoever ran the command.
Vanilla's `on owner` is `OwnableEntity.getOwner`, which resolves the stored
UUID through the level (`EntityReference.getLivingEntity`, then the level's
UUID lookup): an owner who is not in the level gives no entity. The player in
place is now the acting guest's UUID during a guest's turn and the level's
player otherwise. An owner who is online as another guest was already found
first by `target_by_uuid`.
Fixes#8488
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Three runners give ~180 runner-minutes an hour against ~100 checks runs an hour (21 of them main's pushes, which also
save the Rust cache GitHub's pull request runs read): a fifth of the pull requests fills them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A player's snowball, egg or ender pearl that hurts a mob (0 damage
included, as LivingEntity.hurtServer reports it) now credits the hit to
the thrower and fires player_hurt_entity with the projectile as the
direct entity, as the content-mod throwable arm already did. Scooping a
fish, axolotl or tadpole into a water bucket now fires
player_interacted_with_entity with the water bucket and the mob, as
ServerGamePacketListenerImpl.handleInteract does after
Bucketable.bucketMobPickup succeeds.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01UE3PMKbnTcxfBSeT5uQWCK
🤖 Agent: queued whole, verify and launcher green on 94cc6490. One gap left for later: nothing checks that the WGSL packed_corner decodes a record to the same vertex as Rust's packed_vertex; the new mesher/packed.rs tests only cover the Rust side.
Bundles Horizon's own Føroyskt strings so fo_fo players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Føroyskt as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
These tables were generated before the latest strings landed, so the
launcher completeness test fails on main and on every PR. Add the
missing translations.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Чӑвашла strings so cv_cu players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Чӑвашла as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: the ⌘K palette; Play only runs when the instance's Play button would, otherwise it opens the instance's page, so it never starts an unprepared game.
🤖 Agent: entity_id now comes from native_acting_entity (falls back to the guest list when lan_acting_entity is unset, as the mount hooks do), and a helper fragment gives the field an API number so a mod can require it instead of reading 0 on an older host.
Bundles Horizon's own Brezhoneg strings so br_fr players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
overlap note fetches up to 40 other pull requests' heads and takes 1-8 minutes there; the bursts of pushes left 350
bot jobs waiting for 8 runners.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registers Brezhoneg as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The rule is what a new call site gets wrong, so it is pinned: the helper answers
1.0, and two calls answer the same, which is what says it cannot be wearing a
draw of the level random behind a constant name.
The two call sites are not covered: they sit inside Game's packet handlers and
this crate has no seam that builds a Game.
LivingEntity.getVoicePitch jitters every voice by +-0.2 and raises a baby by
1.5; Player overrides it with a flat one:
public float getVoicePitch();
0: fconst_1
1: freturn
So both sounds this client plays for its own player - the hurt sound from
handleDamageEvent and the death sound from handleEntityEvent 3 - belong at pitch
1.0. They were played at 1.0 + (random.nextFloat() - random.nextFloat()) * 0.2,
which is a mob's pitch: the sound came out up to a fifth off for the one player
who cannot hear the server's copy (the server's packet reaches everyone else at
1.0), and each event also drew two floats from the client's level random that
vanilla never draws, shifting every random thing the client does afterwards.
player_voice_pitch() now holds the rule, with the bytecode in its doc comment,
and the stale comment that claimed the +-0.2 walk is corrected.
🤖 Agent: 8 cores now give 5 samplers and 2 meshers (was 4/3); samplers stay behind_server so ticks and frames keep priority, but steady-state far remeshing on 8-12 thread machines has fewer threads, measured on one 12-thread machine only.
🤖 Agent: the fields read match what launcher/src/native_mods.rs writes to native-mods.json (base, horizon, hooks, target, mods[id, version, sha256]); no local path is returned. Merged main after #8248 (both edited the API reference).
🤖 Agent: I replaced the per-entity rescan of the orb/TNT/falling lists (quadratic per tick in an XP farm or a TNT cannon) with one sort per run plus a binary search by join number; same order. Parity's mob_ai bee_hive_rain miss is in the client-lib probe, which holds no game/ code, so not this PR: rerun once.
🤖 Agent: checked in the 26.3 jar: AbstractArrow.tick adds the bubbles after applyInertia (motion x0.6) at pos - motion*0.25, and the crit trail from the start-of-tick motion; Projectile::arrow_trail already did both, it just had no caller on a client.
🤖 Agent: abort_generation now also runs on the server-thread and remote close paths; it leaves Save jobs queued, and leave_server builds a new Streaming afterwards, so the aborted flag can't leak into the next world.
🤖 Agent: shadowcolor2–7 only exist when the pack names HIGHER_SHADOWCOLOR (the 0b11 mask stays otherwise), so packs without it get the same two buffers; clear_rest now opens one render pass per cleared buffer, which is one or two extra empty passes a frame for ordinary packs, a cost worth merging back into one pass when the formats fit the bytes-per-sample limit.
🤖 Agent: roadmap item server-panels. I fixed the online-mode setting's text: it said Horizon's client joins offline servers only, out of date since Microsoft sign-in. The egg is not tested on a live panel.
🤖 Agent: checked the key against release-keys.txt, the Ed25519 SPKI DER prefix, and the message (horizon release <tag>, nightly channel for nightly-*). I replaced the test's GNU sed -i, which fails on macOS with Homebrew OpenSSL.
🤖 Agent: I made the destination follow the server's lookup order ([engine] resources, then an existing resources/, then the installer's game-files), else an import could land where the next start never looks; version from VERSION_ID.
🤖 Agent: GAME now is horizon_core::version::VERSION_ID, which port_regen updates; I dropped the regex-over-browser.rs Python test, which tested the source's words.
Bundles Horizon's own Башҡортса strings so ba_ru players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: ServerExplosion puts every hurt non-spectator player in hitPlayers with the knockback regardless of hurtMarked; the explode packet is sent before the tracker's motion packet, so the client ends on the server velocity.
🤖 Agent: Mob.doHurtTarget reads getAttributeValue(ATTACK_DAMAGE), which includes the held item's modifiers; no other path added them before, so no double count (vindicator + iron axe = 13 on Normal, as the wiki says).
🤖 Agent: matches LivingEntity.createEquipmentSlotAccess (filter on every slot but head/mainhand/offhand) and ItemStack.toString ("count item"). Body and saddle slots on mobs are still ungated: the rest of #8277.
native_player_chat is a free function in horizon_client_lib::lan::hub::chat, like the
menu, breathing and motion hooks: catalog_matches_hook_points now reads that file too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: vanilla sends DATA_TICKS_FROZEN whenever it differs from 0 (negative via TicksFrozen NBT), and the axolotl's air default is its 6000 max. The unit test returns early without .local, so CI's verify does not exercise it.
🤖 Agent: checked in the 26.3 jar: DiscFragmentItem.appendHoverText adds descriptionId + ".desc" in GRAY, not gated on TooltipDisplay, before the component lines, which is where detail_lines puts it.
Fireball.setItem is projectile.rs::Projectile::set_item, and
Fireball.readAdditionalSaveData is entity_nbt.rs::fireball_from_nbt, which now
reads `Item`. ThrownItemRenderer's two rows say a fireball's stack is drawn as
the item model instead of a sprite. Fireball.getSlot stays missing: nothing in
Horizon reads an entity's SlotAccess yet, so `/loot insert` has no path to a
fireball's slot even though the item itself is there.
ThrownItemRenderer draws ItemSupplier.getItem as the item model in the GROUND
context at 3.0 for a large fireball and 0.75 for a small one, full bright; the
item is DATA_ITEM_STACK, which rides ClientboundSetEntityData at index 8 (the
add-entity packet carries only an int `data`, an owner id for a projectile).
Horizon drew every fireball from the hardcoded `item/fire_charge` sprite and
never told a guest the item at all.
The stack the fireball holds now goes through resolve_item and submit_item_world
at the renderer's scale, and the host sends a non-default item as
DATA_ITEM_STACK, which the guest applies through set_item.
Fireball.setItem stores the stack in DATA_ITEM_STACK: an empty one goes back
to the default fire charge, anything else is kept as a single item
(copyWithCount(1)). Fireball.readAdditionalSaveData reads `Item` through that
setter, and addAdditionalSaveData writes what getItem returns.
Horizon had no such field. fireball_from_nbt read no `Item` key at all, and
fireball_to_nbt wrote back whatever `Item` the compound it was handed carried,
so a fireball summoned or loaded only ever showed, saved and reported the item
in that raw compound, and `/data get` said what the compound said rather than
what the entity holds.
Projectile now carries `item`, set_item ports the setter, fireball_from_nbt
reads `Item` into it and fireball_to_nbt writes it, so the entity is the one
source of that item and a stale `Item` in the compound no longer shadows it.
Registers Башҡортса as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Docs: main's players row (with position) and its done single-dimension
restore, plus this branch's /api/v1/mods row.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own isiZulu strings so zu_za players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers isiZulu as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
PiglinBruteAi.initMemories was listed missing: the memory is set in
Ctx::finalize and the brute now acts on it. The createIdleMovementBehaviors
note names the walk back and keeps the two behaviours still absent.
register_brute's goals ticked as ai_step ticks them, on a stone floor, with
the brute twenty-four blocks from its home: BruteStrollHome runs and its path
ends at the home block. Five blocks away, past the memory's hundred, and with
no home at all, nothing starts and no path is made. A hit from a mob that is
not in the world leaves a brute's home in place and still takes a piglin's
celebration spot.
PiglinBruteAi.initMemories stores the brute's block position as the HOME
memory, and createIdleMovementBehaviors spends it on two behaviours:
StrollToPoi(HOME, 0.6F, 2, 100) and StrollAroundPoi(HOME, 0.6F, 5). The memory
was already set when a brute is finalized (extra.home and the brain mirror),
but nothing read it, so a brute that was led or knocked away from its post
stayed away.
register_brute now gives it BruteStrollHome ahead of the plain stroll: within
a hundred blocks of home and outside the ten blocks the stroll can reach, the
brute paths back to within two blocks of the memory. Inside that reach the
stroll keeps the brute wandering around it, which is what StrollAroundPoi does
around the same memory in vanilla.
A hit also no longer clears it. piglin_hurt erased extra.home unconditionally,
which is PiglinAi.wasHurtBy erasing CELEBRATE_LOCATION -- the field a piglin
uses for the spot it celebrates at. PiglinBruteAi.wasHurtBy erases nothing, so
the erase is now a piglin's alone.
Bundles Horizon's own Yorùbá strings so yo_ng players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Yorùbá as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
These ten tables were generated before the latest settings, native-mod
and instance strings landed, so the launcher completeness test fails on
main and on every PR. Add the missing translations.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Three rows said the tab a player opens goes nowhere: the packet had no codec, the host kept no tab,
and the handler was absent. They name the codec and the hub arm that do it now.
The protocol test reads a vanilla-shaped OPENED_TAB payload and a CLOSED_SCREEN one, checks that
neither decodes as `Unknown` now, that a packet too short for its tab is malformed, and that the
clientbound tab round-trips with its absence flag.
The hub test builds a two-advancement data folder (a root with a display, and a task hanging off
it), sends the packets through `Hub::drain_packets`, and reads what the guest is sent: the root a
client opens is the tab the server keeps and is sent back to it, opening it again sends nothing, a
tab that is not a root clears the selection and is answered with none, an unknown id changes
nothing, and a closed screen keeps the tab.
Without the handler the guest is sent no tab at all and the host keeps none.
ServerboundSeenAdvancementsPacket is a typed packet now (action, then the tab only for
OPENED_TAB; a closed screen is the action alone), and ClientboundSelectAdvancementsTabPacket
exists on the clientbound side.
ServerGamePacketListenerImpl.handleSeenAdvancements: an OPENED_TAB names an advancement the host
looks up in its own tree; PlayerAdvancements.setSelectedTab keeps it only when it is a root with a
display, and clears the tab for anything else. Whenever the tab changed, the player is sent the tab
the server keeps, null when it keeps none - which is what closes the client's screen on a tab a
player may not have.
Horizon host-side had nothing: the opcode arrived as `Unknown` with no handler, so a player's tab
was dropped and its screen never corrected. `Guest::selected_tab` keeps it and
`ServerAdvancements::selectable` answers the root-with-display question from the same tree the
advancements packets are built from.
Bundles Horizon's own ייִדיש strings so yi_de players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: the Mods page in the mockup's layout; the kinds stay as tabs (Modrinth and shader packs would vanish otherwise), the placeholder only counts a fully listed registry.
Registers ייִדיש as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
A stopped server's row read "Stopped"; the mockup reads "Creative · stopped". The mode
comes from the open server's loaded settings, else from each folder's server.toml
([world] game_mode), read once per list load.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Татарча strings so tt_ru players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
ChestBlockEntity.startOpen returns early for a spectator, and so does stopOpen (BarrelBlockEntity and ShulkerBoxBlockEntity the same), so a spectator never joins ContainerOpenersCounter. The guest open path counted one anyway: the lid opened, the open sound played for everyone around, and the close path then skipped the spectator, leaving the lid stuck open. Count an opener only for a player who is not a spectator.
tools/lan_e2e.py gains a spectator-container check: the menu opens, the lid stays shut and neither the open nor the close sound is heard, on both sides.
Fixes#8517
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Татарча as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The desktop's checks runners have their own rustup homes with no default toolchain, and some Python tests run a
bare rustc before the Rust step installs one (25 tests failed on ludodesktop-checks-3).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Latina strings so la_la players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The bots move to a shared horizon-light label (8 light runners on playhorizon, from 2), and triage command, mojang
republish and nightly build's answer join them. checks' verify and launcher, mod-registry, telemetry-server and the
pack bench run on the desktop's isolated horizon-checks runners, forks included: main's pushes and the pull requests
whose number ends in 0-2 by default (GitHub's pool keeps the others, so both work at once), every one with
SELF_HOSTED_CI=all, none with light (desktop paused) or off (everything back to GitHub).
Jobs with a secret or a token that can push, publish or start workflows stay on GitHub: tests/test_ci_runners.py
pins it. playhorizon's job hook now also drops a checkout's .git past 4 GB: each light runner's held 6822 packs, 50 GB.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A dark cell packs block light 7 where the old code packed 10 in both nibbles,
torchlight 9 clamps to 15, and daylight keeps the sky byte; the quad's four
corners resolve to a quarter of its height below the orb.
ExperienceOrbRenderer.getBlockLightLevel is the level's block light plus
seven, clamped, with the sky light left alone; the pass raised both nibbles
to a floor of 10 instead. submit's quad runs from (-0.5, -0.25) to
(0.5, 0.75) under a 0.3 scale after translate(0, 0.1, 0), so the sprite is a
whole block tall about the orb and hangs a quarter of it below; Horizon drew
a symmetric 0.3 square 0.075 too low. The probe is the orb's eye, half its
box above its feet.
Registers Latina as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Add PR-scoped cancellation to mod-registry and telemetry-server, per-event
concurrency to pr-labels and triage-command, and move claim-command off minute 23.
Co-Authored-By: Claude Code <[email protected]>
Passenger attachments were known for a handful of mobs; zombies, endermen,
ghasts, cows, silverfish and others fell back to the box top, and values were
doubles where vanilla builds them from floats. Port the passengerAttachments
and ridingOffset values of EntityTypes (ghast and phantom offsets, wither
skeleton 0.875) and widen them from float as vanilla does.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The mockup rings whatever has the keyboard (`.hzl :focus-visible`: 2 px of the accent's soft
shade), so a dialog opens with its name field ringed. The launcher only made a focused field a
little lighter. Every glass text field of its own (servers_page::field in the dark look, and the
wide Proxy and Game arguments fields) now draws the ring while focused. It sits inside the field's
edge with its corners, because GPUI has no outline and a field clips what lies outside it. Fields
inside a chip or the console line keep their container's look.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The i18n test failed on main: those nine languages had none of the settings
and native-mods strings the others have.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Igbo strings so ig_ng players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Igbo as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Preserve the local-server restriction for offline profiles while allowing authenticated Microsoft accounts to join external servers. Keep both translation additions and account regression tests.
Co-Authored-By: Codex GPT-6 <[email protected]>
The mockup's Multiplayer rows start with a dot (green when the server answers) and say
"address · 3 of 10", or "address · Offline" with Join off. The launcher's rows said only the
address. Each saved server is now asked once for its players by the server-list status ping
the game's own Multiplayer list sends (telemetry::server_players, which the official server's
"N playing now" already used, through SRV, 3-second timeout), on the background executor.
One new string, "{online} of {max}", in the 16 languages.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Vanilla only makes Mob and AbstractBoat Leashable; other kinds summoned with a leash
tag kept and saved it. Drop it on load. Part of #8024.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
🤖 Agent: pushed c7c48fe7: a ServerPlayer is client authoritative, so in the 26.3 jar aiStep does not travel on the server and checkAutoSpinAttack tests the current box only. The tick-long sweep would have hit mobs that vanilla's server passes through. Mobs only for now (no players, no armor stands).
🤖 Agent: T(x) of an x already a T is the identity, so correct shaders are unchanged; only array and struct constructors of scalar or vector types are touched, and lines are kept for error spans. Pushed bbc4e8f6 adding the missing bvec4 to ELEMENTS.
🤖 Agent: checked in the 26.3 jar: Level.canHaveWeather is hasSkyLight && !hasCeiling && dimension != END, which the helper now answers for every weather rule. Pushed c93829b1 to put camera_rotation's doc comment back on camera_rotation.
🤖 Agent: save/load matches AbstractMinecart in the 26.3 jar (DisplayState via BlockState.CODEC, DisplayOffset written only off getDefaultDisplayOffset). Pushed 60a1ee31 so the cart loop looks the display state up once per cart instead of twice (cull and draw). Remote carts still draw the kind's block until the entity-data half of #8050 lands.
The parrot is built from the test mob kind, so the test needs no extracted data
and runs everywhere: 64 draws of a baby parrot's voice pitch stay inside
0.8..=1.2 and average 1.0, where a baby chicken keeps LivingEntity's 1.5.
Parrot.getVoicePitch is its own getPitch(random), the plain 1.0 +- 0.2 range,
whatever the parrot's age:
public float getVoicePitch();
0: aload_0
1: getfield #390 // Field random:Lnet/minecraft/util/RandomSource;
4: invokestatic #349 // Method getPitch:(Lnet/minecraft/util/RandomSource;)F
7: freturn
public static float getPitch(RandomSource);
0: aload_0 1: nextFloat 6: aload_0 7: nextFloat 12: fsub
13: ldc 0.2f 16: fmul 17: fconst_1 18: fadd 19: freturn
LivingEntity.getVoicePitch, which Mob::voice_pitch ports, returns 1.5 +- 0.2 for
any baby, so a baby parrot's ambient, hurt and death sounds were played a fifth
higher than vanilla's. The two draws are the same, so nothing else moves.
Bundles Horizon's own ʻŌlelo Hawaiʻi strings so haw_us players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
A shulker bullet and a fireball override the block light, a plain arrow does
not, and an ignited snowball does; an arrow in the top eighth of a block is
lit from the block above it while a wind charge keeps its feet (its entity
type declares no eye height).
EntityRenderer.getBlockLightLevel returns 15 for the shulker bullet's
renderer and for anything whose isOnFire is true, and getPackedLightCoords
samples the block the entity's light probe - its eye - is in, not the cell
under its feet. The projectile pass read the level's light at the feet for
every kind but the three fireballs, which it forced to 15 in both nibbles.
Registers ʻŌlelo Hawaiʻi as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The founder set's Shop lives in the Wardrobe page, which the top bar only showed with the
cosmetics switch on (off by default). Opening the claim window would have shown players nothing.
The launcher now asks the cosmetics service GET /v1/shop at start and every 30 minutes (no
session, nothing about the player) and shows the Wardrobe entry when the set can be claimed now,
when the public window has dates, or when the selected account's own answer from its last visit
(kept on disk) shows it: a claim, or a preview the service grants that account. With no window
set, nothing changes. The switch still shows the page on its own.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: narrowed to Ludovic's 2026-11-03 rule: an offline splitscreen guest follows P1 only to a server whose resolved addresses (after the SRV redirect) are all loopback/private/link-local; public servers and friends' invites refuse it on the profile page, pinned by a unit test.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Utility QoS for the whole session put macOS far terrain on the efficiency cores for good (Kacper's own finding on #5916). I added his step-back from 6f45ae4278: far threads stay behind only while Lod::set_near_loading says the near terrain loads. Linux is unchanged, since an unprivileged thread can't raise itself again.
Bundles Horizon's own Esperanto strings so eo_uy players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Esperanto as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
As the mockup's chip and profile menu (Player46008, pixelmoth): an offline name shows the face
of its own skin (skins/<name>.png), else of vanilla's default skin for its offline UUID
(OfflinePlayer:<name>), read from the prepared game files as a signed-in account's default
already is. It was a person icon. The head is read again when the name's skin file changes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: docs only. The README no longer names the DISPO notary key, and it points out that keychain.sh's MACOS_NOTARY_ITEM default is still that key (script left to the signing owner).
On Windows a RAM limit was a hard working set limit (SetProcessWorkingSetSizeEx
with QUOTA_LIMITS_HARDWS_MAX_ENABLE): Windows kept the game under it by moving
what it used least to the page file, and the game only stepped down once it
was already slow and faulting hard. Players saw a low limit make the game slow
instead of making it hold less (#6651, #6652).
Windows now does what Linux and macOS do: once a second the game compares its
working set with the limit and, past nine tenths of it, turns far terrain off
and lowers the render distance in use until it fits. The system cap, the page
fault pressure, the frozen-game check and the frame clock that fed it are
gone. The limit stays opt-in (no launcher value, no limit), and
HORIZON_MEMORY_HARD_MB is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
getMovementEmission had no row, so a lane reading the ledger saw the whole
rule as unported; it is movement_sounds on Entity and on the two mobs that
carry the override.
Mob::movement_emission on real types standing on stone: one step's walk from a
squid, a glow squid and a bat sends the minecraft:step game event with no
sound, a silverfish sends the event and no sound for the entity.silverfish.step
its own playStepSound names, a sheep walking the same block is heard at
entity.sheep.step volume 0.15 and pitch 1.0, and a shulker, whose emission is
NONE, sends nothing at all.
The test walks through movement_emission, the path Entity.move calls, and
fails without the three types in movement_sounds: the squid plays
block.stone.step at 0.15.
Entity.getMovementEmission decides which movement sounds a type makes, and
both callers of it were short a few types. Silverfish, endermites, guardians,
elder guardians and breezes were already answered with EVENTS, so their
footfalls and swim strokes stayed quiet; a squid, a glow squid and a bat also
answer EVENTS in the jar and were heard here, and a squid's beached flop
walked the stone under it at 0.15 of the block's step volume.
The list now holds all eight, and the walking path asks it too: movement
emission played the footstep regardless, so only step_sound's own silent arm
kept the five quiet, and a squid, which has no playStepSound override and so
takes the block's, was never in it. The step game event still follows for an
EVENTS type, as Entity.vibrationAndSoundEffectsFromBlock sends it.
Silverfish and endermite now name the step sound their own playStepSound asks
for (entity.silverfish.step, entity.endermite.step at 0.15) and are silenced by
the emission, which is where the jar silences them; the silent arm keeps the
types whose override is an empty body: the fish, the nautilus pair, allays,
bees, happy ghasts.
Bundles Horizon's own Malti strings so mt_mt players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
packaging/macos/README.md named "App Store Connect API - GitHub CI
(DISPO)" as the key behind the horizon-notary profile. That key belongs
to another Apple team and must never be used for Horizon. The README now
says the profile comes from an App Store Connect API key of team
YYJU63HSD4 (the Developer ID's team), and that keychain.sh notary needs
MACOS_NOTARY_ITEM because its default item is still the other team's.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registers Malti as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own ಕನ್ನಡ strings so kn_in players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers ಕನ್ನಡ as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Кыргызча strings so ky_kg players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Кыргызча as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Lëtzebuergesch strings so lb_lu players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Lëtzebuergesch as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Gàidhlig as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Gàidhlig strings so gd_gb players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
#8244 made a breath cloud last until tickCount - waitTime >= duration
(dragon::breath_expired); BottleItem.use takes any living cloud, so the
bottle reads that rule too instead of age >= duration, which refused the
cloud's last 20 ticks. The tests' clouds get #8244's victims field.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GitHub's Windows runners give TEMP as an 8.3 short path
(C:\Users\RUNNER~1\...), and the shell hands a shortcut's target back
expanded (C:\Users\runneradmin\...), so
installer::tests::the_shell_reads_the_target_of_the_shortcut_it_gets
failed on every launcher build since #7091 and no launcher release
went out after [email protected]. The test now builds its folder from the
canonical long path, as a real install folder is.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The glass layout now serves both appearances: light keeps the mockup's
pages, palette and toasts, with ink text on light plates over the dawn
sky, light status colours, and a lighter scrim behind the palette.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Home's played line and the Worlds tab's rows say times as the mockup does: whole hours
("41 hours", "1 hour", "25 min"), and when within a day "just now", "25 min ago",
"2 hours ago", then "yesterday", then the day ("Oct 31") instead of the ISO date. The light
look keeps its own wording.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The accent, glass and reset settings and the native mods card came to main
after this table; the i18n test needs every language to have them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As #8385 did for its tables: entries equal to en_us are dropped (the game
falls back to en_us per key), and the six controller options, which the
table had as verbs (look at the deadzone, invert appearance), are option
names now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After a world loads, the client keeps queueing a few meshes a second for a
minute or more (vanilla's leaf distance pass reshapes the trees). Two things
in the shared pool then kept the server's generator buffers and caches,
which nothing reads until the player moves into new ground:
- Every wake restarted every sleeping worker's idle wait, even a worker
that got no job, so with jobs queued every few seconds no worker ever
reached IDLE_RELEASE. A worker now counts from its own last job.
- `all_idle` (#7519 empties the generator's shared caches there) needs
every worker idle at once, which never happens while one source stays
busy. A source with nothing queued or running for IDLE_RELEASE is now
told on its own: `idle` for each worker's state and `all_idle`, once
until its next job. The release frees nothing a later job needs: the
generator's caches are emptied whole, as at their limit (#7519).
Each freed release hands the pages back to the OS (`release_free_memory`).
Measured with the alloc-census build, seed 1234, RD 12, offscreen, 150 s
after opening a new world (two runs): stream.generate 171.8 -> 45.3 MB,
the biome cache 1366 chunks -> 0, the live heap 645 -> 527 MB, the resident
set 1063 -> 1001-1014 MB.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
"Sign in to play" (Play without an account that owns the game) still had the old look:
pixel-cut corners, a solid violet panel and pixel buttons. In the dark glass look it is now a
dialog like the mockup's modals: a plate round at 28 over the dimmed, blurred launcher, the
Account card's white "Sign in with Microsoft" button, the code in the card's inner panel, and
the accent "Open link" with a text "Not now". Same strings, same behaviour; the light look keeps
the old sheet until its own PR.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
As the mockup's Overview:
- the eye button beside See all (and in the Screenshots tab) blurs the screenshots, as
blur-2xl does: a small blurred copy of each, made in the background, drawn stretched;
- the patch log shows the changes of the Nightly the instance plays (the newest Nightly's when
that one lists none), "Nightly 2026-11-01 · c9d474d · Nov 1"; a Java instance keeps the
launcher's notes, dated the same way;
- while the instance prepares, starts or runs, a Game log card over the screenshots shows its
newest lines and how many there are;
- one screenshot takes half the width (grid-cols-2), not all of it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The instance and Create server dialogs drew their plate from the bare scene, so the sun showed
through them at full strength, and the page around them stayed sharp under the scrim. The
mockup's modals are hzl-glass over bg-[#07050f]/50 backdrop-blur-xl: the page blurred away and
the plate dimmed with it. Both now do what the palette already did: the scene's blurred copy
covers the page under the scrim, and the plate wears the scrim's dimming under its white.
The palette uses the same two helpers (glass_ui::scrim_backdrop, dialog_plate).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A new instance starts named as the mockup's defaultName says: "My Horizon"
for a Horizon one (Java ones already had "Vanilla 26.3" / "Fabric 26.3"),
following its kind until the player types a name. A default name already
taken gets a number ("My Horizon 2"), so Create no longer fails on it.
The glass dialog's Nightlies read "Nov 1" (formatDay), not 2026-11-01, and
Edit offers Import a modpack… as the mockup's Edit does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The zoo's new cases caught particle coordinates that differ from vanilla's in
the last bits, where Horizon did the arithmetic in the other precision from
vanilla's expression.
Vanilla's `random.nextFloat() * 0.04` is a float times a double literal, so the
product stays double: the sculk sensor's and the respawn anchor's y velocity
were formed in single precision here (0.00036899326369166374 against vanilla's
0.00036899328231811525). The brewing stand's three sums are double products the
same way (6.519509056210518 against 6.519509053230286), and the ender chest's
`pos.getY() + random.nextFloat()` is the other way round - an int plus a float
rounds the sum to single precision before it widens (58.59754526615143 against
58.5975456237793).
tools/test_marker_particles_parity.py ran the whole ClientLevel.animateTick
loop, but the block it ticked was always a campfire. Every other block the
loop animates - the torches, the lit furnaces, the portals, the end rod, the
candles, the leaves, the redstone wire, the sculk sensor - had no case in any
harness, so their particle positions, their sounds and their draws from the
level random were unpinned.
Run the same loop over a world that is all of one state, for 79 states: the
three torches and their wall forms in all four facings both lit and unlit, the
furnace, blast furnace and smoker lit and unlit in every facing, both nether
portal axes, the end portal, the end rod in all six facings, the ender chest,
the spore blossom, the mycelium, one to four candles and the candle cake, the
brewing stand, six leaf blocks, three redstone wire states, two repeaters, two
levers, fire at two ages, soul fire, a blinking sculk sensor and a charged
respawn anchor, each at two seeds and forty of them at a random one. The
oracle's stand-in level answers isRainingAt with false, as the probe's
stand-in world does, so a leaf block's dripping-water gate can run instead of
reaching a dimension type the stand-in level does not have.
lab.free_port() closes the probe socket before the server binds, so two
harnesses running at once (other worktrees, a job queue) could be handed
the same port and one server failed to start. Each port is now also locked
in the temp dir until the process exits; a port another harness holds is
skipped.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Nine tables were generated before the remaining settings and native-mod
strings landed, so the launcher completeness test fails on main. Add the
missing translations.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: Native mods in Settings > Game is a profile picker (Performance/Dev/Release build), not the mockup's switch, because native mods are always on; it's the same setting as the Mods page's picker.
🤖 Agent: Glass (0-16 %) and Blur (0-48 px) re-tint and re-blur the plates live and save on release; out-of-range or non-number values in settings.json fall back without losing other settings (test glass_and_blur_default_to_the_mockups_and_stay_in_range).
🤖 Agent: the SNORM format is used only where get_texture_format_features allows RENDER_ATTACHMENT; one remaining risk is a pack that sets blend.<program> on a SNORM buffer on an adapter that renders the format but cannot blend it, which a BLENDABLE check would cover.
Strengthen only the original synthetic oracle: fractional contrasting data, hostile unused mip contents and deliberately invalid filtering sampler negative control. Production/API code is byte-identical to the already completed full-check head ec6054c.
Co-Authored-By: OpenAI Codex <[email protected]>
`PowderSnowBlock.entityInside` makes any entity that is not a living one stuck
(`makeStuckInBlock` 0.9, 1.5, 0.9) and, on the server, sends a snowflake when
the entity moved horizontally. Boats did neither: no particle showed when a
boat entered powder snow, and it was not slowed (#8156).
A boat whose box, or the box it had before its move, reaches into powder snow
now keeps a stuck multiplier at the end of its tick, as `AbstractBoat.tick`'s
`applyEffectsFromBlocks` does, and its next move goes its velocity times the
multiplier and spends the velocity (`Entity.move`). The server also sends the
snowflakes, twice a tick as `AbstractBoat.tick` applies the block effects twice,
for boats a player drives as for the others. A boat that is not in powder snow
moves exactly as before. Only the boat's own box is scanned.
Refs #8156
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
🤖 Agent: the (0, 0, 0, 1) start clear runs once per buffer rebuild (rgb_unset), and the per-frame cost is only a COLOR write mask and a Clear(BLACK) load in place of the overwrite on colortex0, so nothing is added per draw.
🤖 Agent: load_geometry keeps the 0..8 default, which is right because geometry stages load only for the shadow pass, so the colortex0-only default reaches every non-shadow program and nothing else.
🤖 Agent: checked against the 26.3 jar: sign_text_front/back are data components 118/119, and SignText.STREAM_CODEC is four NBT messages (fixedSizeCollection, no count), an optional filtered four, DyeColor's varint and a bool, which is what sign_text_tag reads and sign_text_bytes writes.
🤖 Agent: off by default and skin_3d_model returns before any work while it is off; the measured cost when on is +0.19 ms CPU per skinned player in view (pose + submit of the extruded slabs), so a crowd is where a distance fallback to the flat layer would pay off next.
The game's status now lists each level's loaded chunks and entities
(`levels`), the level in place and the parked ones counted the same way
(players and block entities apart). prometheus() turns them into
horizon_loaded_chunks{dimension} and horizon_entities{dimension}; a level
with no count has no line rather than a 0. The docs drop the item from
"Not there yet".
Fixes#8191
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The glass look keeps an instance's Maximum memory in its Edit dialog: a
SettingRow under the icon, with the mockup's Slider (as Settings' Glass
and Blur), the box in MB and the saved line. It sets the instance being
edited, not the selected one. Home's Overview no longer shows it there.
Overview drops the Microsoft account and Playing offline rows in the
glass look: its top bar has Sign in with Microsoft and says Offline.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
goals.rs: main's peaceful_targets_tests (#8046's fix) and this branch's
avoid_entity_targeting_tests sit at the file's end; both kept. Since that
fix, can_target_player already refuses a player in Peaceful through
spares_players, so the goal's own difficulty check is dropped.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Frysk strings so fy_nl players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Frysk as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
canBeAffected was in no verdict file, so the ledger ranked it unported; it is
Mob::can_be_affected now, one row on LivingEntity and one on each class that
overrides it.
Mob::add_effect on real types from the extracted data: a zombie, a skeleton, a
phantom, a zombie horse and a wither never list poison or regeneration (the
ignores_poison_and_regen tag is #minecraft:undead) while strength lands on
each of them; a silverfish refuses infested and a slime oozing, a magma cube
takes oozing; a spider, a cave spider and a nautilus refuse poison where a cow
takes it; a wither and a wither skeleton refuse the wither effect a zombie
takes; a parched refuses weakness and takes slowness.
The test goes through the list add_effect builds, which is what the client
draws and what the effect tick reads, and fails without the canBeAffected
gate: the refused effect is listed instead of absent.
LivingEntity.addEffect asks canBeAffected before it stores the instance, and
that rule had no port: Horizon stored whatever it was handed, so a spider and
a nautilus took poison, a wither and a wither skeleton took the wither effect,
a parched took weakness, and every undead mob listed poison and regeneration
it can never be ticked by.
can_be_affected now answers it the way the jar does. Five classes narrow it
first: Spider.canBeAffected (CaveSpider with it) and AbstractNautilus refuse
poison, WitherBoss and WitherSkeleton refuse the wither effect, Parched refuses
weakness. The three entity type tags answer for the rest: immune_to_infested
keeps infested off a silverfish, immune_to_oozing keeps oozing off a slime,
and ignores_poison_and_regen, which is #minecraft:undead, keeps poison and
regeneration off the undead. add_effect checks it before the absorption
modifier, as vanilla does before the map lookup.
WitherRoseBlock.entityInside no longer spells the wither mobs' refusal out
itself; the effect it asks for is refused inside addEffect, which is where
vanilla refuses it.
🤖 Agent: Lands the whole stack (#8330 to #8385, 10 game + 9 launcher tables). Product names translated as words (Discord as "Nesantaika", Fabric mods as textile mods, Horizon as "Хоризонт") were dropped or set back to English, and so were game entries that only copied en_us. The six controller options, which every table had as verbs ("look at the deadzone"), were rewritten. The 8 launcher strings main gained since were filled, and nb/nn now picks Norwegian.
Iris 1.11.7 reads shaders.properties with IRIS_FEATURE_<name> defined for
the features it has, whether the pack lists them as optional, as required
or not at all (probe pack); its programs still see only the optional ones.
Horizon defined them only after reading the file, so a custom uniform or
a screen behind `#ifdef IRIS_FEATURE_*` was left out. The Shader Options
page reads the file with the same macros.
Fixes#8493
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Iris 1.11.7's values in our probe fit a half-life of a tenth of the fade
time's seconds (probe pack across the scene suite's weather changes):
smooth(rainStrength, 20, 20) reads 0.87 four seconds after the rain is
full, where Horizon's fade of 20 seconds gave 0.2. Horizon's smooth()
lagged ten times behind those values.
Fixes#8492
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the scene's stars, runs, fireflies and meteor take the accent the way CSS mix-blend-mode: color does (hue from the accent, luminance kept); test the_accent_tint_keeps_luminance_and_takes_the_hue_like_css.
🤖 Agent: docs only. release-flow.md now describes the desktop runner as it is since 11-03 (gha user, no Linux label, stops within a minute when the maintainer is at the screen). parity-workflow.md says the 10-26 parity-host entry is retired.
A name no player has was answered "No player was found named X", and a
player banned already "X is already banned". Vanilla's `/ban`, `/kick` and the
other player commands answer `argument.entity.notfound.player` and
`commands.ban.failed`, translated into each client's language.
The replies of these commands are now a `Reply`: Horizon's own line, or a
vanilla translation key. A guest is sent the key as a translatable component,
the console prints its text. The other lines (usage, durations, "Muted ...",
"Banned ... until ...") have no vanilla message and stay as they were.
Follow-up to #8157, asked for in its review.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Bundles Horizon's own Soomaali strings so so_so players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Soomaali as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own ລາວ strings so lo_la players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers ລາວ as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Shader packs are back as a clean-room rewrite, so the stale removal passage and
known-issue bullet no longer apply.
Co-Authored-By: Claude Code <[email protected]>
Bundles Horizon's own Монгол strings so mn_mn players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
On a Horizon pack's options page a float or int option is a slider, as FOV
is, where it was a button stepping through every value. A float takes the
nearest step from its minimum (the last slot its maximum), an int whole
numbers, rounded to the f32 the pack's override gets so a slider back in the
middle is the default exactly. The value is saved, and the pack rebuilt, once
when the slider is let go, and only if it changed. Shift-click puts it back
to its default; Tab, Enter and the arrows work it as they do vanilla's
sliders, a step at a time and stopping at the ends. Bool and choice options
stay buttons.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Whole cube faces take one record whichever corner they start at and
whichever way they go round; quads off the block grid, the UV steps or
the light steps keep their vertices; and a quad without a record points
at its own four vertices after the padded records.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: checked in the 26.3 jar: Dolphin.playAttackSound is playSound(DOLPHIN_ATTACK, 1.0, 1.0) and Entity.playSound returns for a silent entity, so the new !m.silent on the axolotl arm fixes a second mismatch too; the test skips in CI without the extracted mob types.
🤖 Agent: checked in the 26.3 jar: Sniffer.jumpFromGround is super, then moveRelative(0.1F, (0, 0, 1)) when getSpeedModifier() > 0 and horizontalDistanceSqr < 0.01, which the arm mirrors after the jump power is set; I merged main in (both sides added tests at the end of living.rs's test module, both kept).
Registers Монгол as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
pack_uniforms.rs: main's smoothed-uniform half-life test and this
branch's fog and per-program tests sit in the same place; both kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own தமிழ் strings so ta_in players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The End exit fell back to the server's own `level.spawn`, which does not
follow `setworldspawn` and is not the travelling player's, so a player
without a respawn point landed at a stale spot or (0, 64, 0) instead of on
a free column around the world spawn. It now takes the player's own
Overworld point, else the world spawn, as a missing bed already should.
Fixes#8333
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registers தமிழ் as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Filipino strings so fil_ph players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Filipino as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Galego strings so gl_es players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
A server that names its players -- a rank prefix, a nickname -- sets a
custom name on the player entity. Both passes of `name_tags` then claimed
it: the player-list pass, which names every player a server sends, and
the custom-name pass, which names every mob holding a custom name. Two
tags were built for one player and drawn at the same point, so they sat
on top of each other.
The set of players the list already names, which the custom-name pass
skips, was built only when native mods were loaded (a8cdcce9d). Without
them it was `None` and the pass skipped nobody. It is built unconditionally
again, now through `named_by_player_list`, whose doc comment records why it
cannot be conditional. The same commit's other fast path -- leaving an
unnamed, non-mannequin mob out of the pass entirely, which no longer needs
`hooks` to be correct -- is kept.
This matches vanilla, where one entity draws one tag and a player is named
from its profile: `Player.getName` overrides `Entity.getName` and ignores
`customName`. Servers that decorate names through a team prefix and suffix
were never affected; those go through the player-list pass and its
`getDisplayName` handling.
The duplicate also appeared without the custom name being flagged visible,
whenever the crosshair was on the player: the custom-name pass names the
mob under the crosshair whether or not its name is flagged.
Co-Authored-By: Claude Opus 5 <[email protected]>
Registers Galego as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Euskara strings so eu_es players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Euskara as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Gaeilge strings so ga_ie players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Gaeilge as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Cymraeg strings so cy_gb players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Preserve existing fixture behavior with absent companion maps and no block-surface scope. Consulted only the necessary current-main struct construction sites for this integration.
Co-Authored-By: OpenAI Codex <[email protected]>
The independent candidate reserves block companions for terrain; the newly added main line draw has no colour surface identity and explicitly uses defaults.
Co-Authored-By: OpenAI Codex <[email protected]>
Registers Cymraeg as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Shqip strings so sq_al players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Shqip as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Proposed independently developed replacement for closed#7800, addressing #7747. Preserve colour-layer identity across split arrays, nearest encoded data transport, neutral defaults, resource ownership and colour-selected companion frames. Add original CPU and actual-GPU controls with a pinned input/exposure ledger. Frozen candidate production hunks preserved; integration adjusts a test-module append and clarifies comments.
Co-Authored-By: OpenAI Codex <[email protected]>
Bundles Horizon's own Македонски strings so mk_mk players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: the accent now takes any #rrggbb as well as the named swatches, and a typed colour survives settings.json (test a_typed_colour_is_worn_as_is_and_survives_settings_json).
Registers Македонски as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: only runs when a pack replaces core/rendertype_lines (no cost otherwise); a pack shader the backend rejects or with bigger blocks falls back to the built-in outline inside one error scope, so a bad pack can't fail the draw. Not yet seen in game with the RGB Block Outline pack.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Slot reuse is safe (a closed outbox refuses pushes, the pipe's on_ready is cleared before its slot is freed), and stalls and read timeouts are on the 100 ms sweep. Risk: Conn::read loops until WouldBlock with no per-pass budget, so one client flooding packets holds its thread and delays the other players on it until the rate limit kicks it; it was tested on Linux only, so macOS kqueue and Windows AFD get their first run from players.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Matches RecipeCraftingHolder.setRecipeUsed (special or known recipes only) and the Login/GameEvent 12 sync. Simpler way: KnownRecipes has seven impls where production uses only the recipe book Settings; and recipes.recipe(id) scans every crafting recipe per grid change while the rule is on.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. abs_diff and filtering spawners first fix the overflow on the menu's (i32::MIN, 0, 0) placeholder; the 64-block box is only Horizon's pre-filter, the player range test stays in client_tick_spawners. finalpantasy's draft #7818 covers the same issue.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Matches CycleButton.onPress (shift steps back, positiveModulo wraps): the old code passed right, which is always false by then. The Act::Extra part of the test sits in an if let Some, so it passes silently if that button moves.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Requeued after its STOP: the new head only merges main (now that #8008 landed); its own change is the 1.21.10 step alone (v1_21_10 tables, the bee/wolf anger INT to LONG and avatar main arm BYTE to HUMANOID_ARM), still behind HORIZON_OLDER_SERVERS=1, and it merges clean.
Bundles Horizon's own Bosanski strings so bs_ba players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Only manifest.rs reads MAX_POST_PASSES; the renderer sizes its pass chain from the manifest, so 12 needs no other change, and budget.gpu_ms (max 16) still bounds what the extra passes cost.
🤖 Agent: unlocking before close matters because flock belongs to the open file a forked child shares until exec; #7448 touches lock_world too, a small rebase for whichever lands second.
🤖 Agent: matches WidgetTooltipHolder.refreshTooltipForNextRenderPass (hovered || focused && last input was the keyboard): a click's focus no longer pins the tooltip; the hover highlight still follows the focus as before.
🤖 Agent: nothing found: Cow borrows from the cached StructureTemplate only change ownership; shuffle draws, palette pick and priority sort are untouched, so CI's worldgen harnesses are the check.
🤖 Agent: raider_died now resolves the killer's connection with player_uuid_of, the same lookup villager_murdered uses on the next line, so a guest's kill makes that guest the hero (Raider.die → raid.addHeroOfTheVillage(player)).
Registers Bosanski as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Հայերեն strings so hy_am players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Հայերեն as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own ქართული strings so ka_ge players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers ქართული as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: nothing found: tooling only (pacing_bench); the liveness/flight checks run outside the recorded windows, so the measured transition costs are unchanged.
🤖 Agent: the horse tame now runs in the rider's as_guest turn, so the owner is set before tame_animal fires for that rider, as AbstractHorse.tameWithName orders it; the host no longer gets a guest's advancement.
🤖 Agent: one point checked: occludes:false drops a solid render's dampening to 1 (0 with skylight_down) as getLightDampening does for non-solid full blocks; only content-mod blocks that set the key change, vanilla rows are untouched.
🤖 Agent: nothing found: the ambient/show_particles defaults (false, true) are MobEffectInstance.CODEC's, and the single ApplyEffects consumer now carries them.
🤖 Agent: checked on the 26.3 jar: HumanoidArmorLayer.renderArmorPiece reads HumanoidRenderState.isBaby and picks LayerType.HUMANOID_BABY for every slot, and the humanoid_baby textures exist for all eight materials, so this is the vanilla choice.
🤖 Agent: checked on main: text_displays.rs display_light answers the packed brightness override or the world light at the display's block, and both geometry passes call it.
🤖 Agent: checked on main: armor_stands.rs layer_color reads the stack's dyed_color for a dyeable layer and returns white otherwise, so the gap note about undyed leather was stale.
🤖 Agent: checked on main: native_entities.rs NativeDisplay::write stores the transformation, both durations and the flipped start_interpolation, and nothing writes billboard, brightness or shadow, which the new note says.
🤖 Agent: on_player_jump matches the 26.3 bytecode (isSaddled and dashCooldown <= 0, then getPlayerJumpPendingScale); in vanilla the rider's client runs it, so the host running it from START_RIDING_JUMP's data is the right side for a guest, and the 40-tick clock keeps the host's re-derived bar from dashing twice.
🤖 Agent: checked on main: sculk.rs flaps is the bat arm (awake, every tenth tick) and the movement pass posts minecraft:flap over air, so a bat's beat does reach a sculk sensor.
🤖 Agent: checked on main: piglin.rs ai_step's brute branch calls update_activity then maybe_play_brute_sound, and minecarts.rs push_along is the 1.0E-4 and 0.001 projection, so the three rows name their own fns.
🤖 Agent: checked on main: remote_chat.rs send_chat_acknowledgement is the take_offset and ChatAck-above-zero body, and level_loading.rs holds start_client_load and is_level_ready with the close delay, so the three rows now name the functions that do the work.
🤖 Agent: checked on main: commands.rs target_name's Target::Item arm reads the saved CustomName, then item_name, then the display name, and crossbow.rs mob_vanilla_ammo searches the off hand then the main hand for the weapon's supported stack, else an arrow, so neither row is missing.
🤖 Agent: the lava loop's bound is now drawn on every pass as in vanilla's for (i < random.nextInt(1) + 1), which only the new all-campfire tick cases could see; the harness grows from 25,578 to 58,268 lines, all tick cases on one input.
🤖 Agent: remote vibration packets now reach the provider arm with the sprite draw vanilla's Provider makes; the destination is resolved once at arrival, so a signal flying to a moving warden or allay lands where it was (noted in the PR as the gap left).
Bundles Horizon's own Қазақша strings so kk_kz players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Қазақша as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Proposed independently developed replacement for closed#7842, addressing #7811. Preserve compiler-evaluated constants in actual UBO layout, retain runtime-write precedence, and add original typed and real-GPU controls. Public research input ledger records pinned permissive shader-pack inputs, frozen candidate lineage and exposure limits.
Co-Authored-By: OpenAI Codex <[email protected]>
Bundles Horizon's own Oʻzbekcha strings so uz_uz players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Oʻzbekcha as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Since 2026-11-03 ludodesktop-horizon takes pull requests' parity jobs again
(labels horizon-game, ludodesktop; no Linux): user gha walled off from the
maintainer's files, low-priority slice, only while he isn't at the screen.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Azərbaycanca strings so az_az players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: index 7 now goes out in vanilla's dirty order (1, 7, 17) and only on change, as ServerEntity.sendDirtyEntityData does; Horizon's own client still ignores it for its own player (it counts the ticks itself), so only vanilla clients and other viewers see a change.
Registers Azərbaycanca as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. The move also puts the bench's server runs on lab.game_env, so they now get HORIZON_NO_DISCORD and scratch XDG and launcher homes, which the old hand-built env left out. Nothing else found.
🤖 Agent: test-only; the bytes are a real 26.3 SetTime, and the (0, 66678)/(1, 13071) keys only decode as plain registry ids, which pins WorldClock.STREAM_CODEC = holderRegistry against a later off-by-one 'fix'.
🤖 Agent: matches FlowerPotBlock.getCloneItemStack (new ItemStack(potted) before the caller's isEmpty check); the potted lookup now runs before the air check that used to return None for every potted_* block.
🤖 Agent: checked on main: run_loading writes startedCleanly false after unclean_start_recovery reads the last start's value, and Loading::finish sets it true and saves, so the protocol is whole.
Bundles Horizon's own Беларуская strings so be_by players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Checked hooks.rs Sim::entity_inside: only pressure plates, detector rails, buttons and tripwire act, which is exactly senses_entities, and after a block change every later position still goes in sorted order, so the calls and their order match. Nothing else found.
🤖 Agent: env_remove on the game's Command only drops the four Mesa/WebKit workarounds, so the launcher keeps them for its own window while a radeon player's game gets the GPU; a player who wants the game on llvmpipe on purpose now has to start horizon-client directly.
🤖 Agent: position comes from the guest's own field, so it's that player's last known position even while another guest is swapped into Game; the API is token-gated on 127.0.0.1, so exposing coordinates stays admin-only. #8245 edits the same 'Not there yet' line in docs/api/server.md and will need main merged after this.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Per-frame cost only when a pack declares [displacement], its option is on and the resource packs have LabPBR heights (native_pack.rs gates it); the shadow map and the water mirror still draw flat faces, as the PR says.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. API side only: [displacement] needs [material], ranges and option kinds are checked, and MaterialInput.displaced is 0 until #8258 draws the grid. Nothing else found.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. The reuse rule (same revision, from in [f, y]) is sound: the lowest ticking section at or above any Y in that range is still y. The tick_parity test now drives next_masked directly, so the game's own cached answer has no test of its own.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Per-player segments with a per-level SpawnScratch in levels.rs; the 3,000-step random walk against the uncached union is the right proof for the order the shuffle sees. Nothing else found.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Vanilla pairs the arrow at addEntity and its first sendChanges runs before the arrow ticks, so IN_GROUND reaches the client a tick later, as this does. Risk: the joined arrow's metadata is built a second time in lan_entities.rs, so it can drift from the tracker's own arrow_meta path.
🤖 Agent: one point checked: gpu_culling::supported still needs MULTI_DRAW_INDIRECT_COUNT and 8 storage buffers, so Metal and GL keep the CPU walk and only Vulkan/DX12 frames change, the two backends the device tests cover; a camera past the i32 plane terms falls back to a CPU frame, so far teleports can't drop sections.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Each mesh dispatch now clones plan.nodes and plan.split into the snapshot (thousands of keys, beside the samples map it already clones); an Arc on the plan's sets, rebuilt only on replan, would make that free. Behaviour and tests look right.
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. Checked: vanillagen/lod.rs shade_sky has dimmed water a level per block since ee7d443c (10-30), so lighting visited water as open sky (96877dc, b4629e0) was what drew the bright patchwork. The heightmap change (bed found under shaded water spans) has no test of its own.
Registers Беларуская as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: Review by Ludovic's AI agent (Claude), not by Ludovic himself. The 5% hold applies only to tiles split last plan, so the 8-to-10 band is unchanged and the new test pins both sides of the margin (670 vs 700 blocks). Nothing else found.
🤖 Agent: checked against main: nothing there covers ModConfigEvent yet (only native_config_dir, API 26), and no hook changes landed since this head's base, so the fragment still stamps cleanly; the reload poll is one stat per loaded file per second and nothing at all with no configs registered.
🤖 Agent: matches ServerPlayer.doTick's lastRecordedAirLevel check: the air score moves only when getAirSupply changes, negative values included while drowning. The red note check was a GitHub 504 in overlap_note.py, rerun.
🤖 Agent: dimension_folder refuses '..', empty segments and anything outside resource-id characters before any path is joined, and the zip entries go through enclosed_name plus a component-wise prefix match, so a crafted backup cannot write outside the dimension folder. Overlaps #8245 in api.rs/cli.rs; whichever lands second carries a small conflict.
Main moved the graphics settings to pages (General, Quality, Performance,
Advanced): Particle Culling now sits beside Entity Culling in the Culling
group, its test drives the row with main's helpers, and the docs list it
with the other culling modes. Lang tables and particles.rs keep both sides.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The launcher and nightly releases pass --latest=false too; without it every
cargo-horizon-mod tag would take GitHub's "latest" from the game.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PowderSnowBlock.entityInside draws nothing without horizontal known movement, so the
per-tick sweep (and its HashSet) is skipped for entities that do not move sideways.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
From 6f45ae4278 on #5916 (kacpherek's own fix): kept at utility QoS for
the whole session, macOS ran the far terrain on the efficiency cores long
after the near chunks were in, and it took minutes to settle. Each far
thread now steps behind (far_terrain_behind) while Lod::set_near_loading
says the near terrain loads, and back to the default once it is in. On
Linux and Android nothing changes: an unprivileged thread can't raise its
priority again, so they stay behind_server as before.
Co-Authored-By: kacpherek <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
BottleItem.use on the client never finds a dragon's breath cloud (an
AreaEffectCloud's owner is not synced), so with no water in reach it
PASSes and startUseItem gives the off hand its turn. The UseItem packet
is sent before this check either way, so the server still fills the
bottle from the cloud.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Hosting panels and `horizon-server run` send `stop` on stdin and wait for
the process to exit; with the stopped phase they would wait forever. While
the game is stopped, the supervisor's stdin now takes `start` and `stop`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Horizon's client signs in with Microsoft and joins online servers, so the
note that it can join offline servers only was out of date.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the mip timer brackets only each chain's first level (the largest, most of the cost), so read 'mipmaps colortexN' as a lower bound; the label's format! runs every frame even with timing off, a few ns.
🤖 Agent: checked in the 26.3 jar: AreaEffectCloud.serverTick discards at tickCount - waitTime >= duration, returns while waiting before radiusPerTick, and runs the victims map every 5 ticks; neither DragonFireball nor DragonSittingFlamingPhase calls setWaitTime, so the default 20 holds for both clouds.
🤖 Agent: Fire Aspect on a PvP hit only when the hit hurt, as vanilla's doPostAttackEffects. Simpler way for later: Enchantments::ignite_seconds duplicates the ignite half of enchant_effects.rs post_attack's loop, which could call it too.
🤖 Agent: modifier add/remove/value get now go through finalize's store, so the cached fields (speed, attack_damage, follow_range...) follow, and an attribute the type lacks answers commands.attribute.failed.entity as vanilla's null getAttribute does. Modifier.id as a Cow stays a borrow for the built-in ids, so no new allocation on the refresh path.
main now restores the block registry from registry-v1.bin (load_registry_with_flags); keep that,
and parse facts.json from a slice on a snapshot miss.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: checked in the 26.3 jar: LlamaSpit.onHitEntity hurts only for a LivingEntity owner and never discards (only onHitBlock does), so the spit flying on through an entity is right. Moving vehicle_targets into projectile_targets also gives fireworks' and potions' callers boats and carts, which matches VehicleEntity.isPickable for every projectile.
🤖 Agent: ui::scrolling_text is the existing AbstractWidget.extractScrollingStringOverContents port, so the three Horizon widgets now behave like vanilla buttons. The cycler's clip box is w - 18, inside its 9 px arrow gutters, so the sliding text never runs under the arrows. Nothing else found.
BSD sed (macOS) reads the expression after -i as a backup suffix, so the
test failed on a Mac with OpenSSL 3 or later on PATH (Homebrew's), where
it does not skip. Edit through a copy instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: checked: the oracle's getEntityCollisions keeps CollisionGetter's getSize() < 1e-7 shortcut and the bounds.inflate(1e-7) query, and the wall fixtures sit on the player's leading face plus or minus the inflation, so the probe's entities slice is pinned at the boundary that decides it; it carries #8278's two commits, queued before it.
🤖 Agent: no mob path scaled by the block speed factor before, so no double scaling. LivingEntity.getBlockSpeedFactor also lerps by movement_efficiency, which is 0 for every mob type by default, so leaving it out changes nothing today; it matters once armor with Soul Speed is on a mob.
🤖 Agent: checked in the 26.3 jar: LivingEntity.canAttack returns false for a Player target in PEACEFUL before canBeSeenAsEnemy, and spares_players feeds can_target_player, can_keep_target_player, HurtByTarget and the golem's DefendVillage, so every targeting path gets it. The w.player.is_some() half is redundant (every caller already has a player) but harmless.
🤖 Agent: checked in the 26.3 jar: handlePlayerPositionChange reads verticalCollisionBelow of the previous move before player.move, as this does; one small difference: the check runs after the anticheat drops a false ground claim, so with [anticheat] on a corrected claim skips the resend vanilla would do, which is harmless.
🤖 Agent: clearing the other texture too is right whenever a buffer flips more than once a frame (both 2 and 3 writes leave the first target stale); it costs one more full-screen clear per cleared buffer each frame, so a later perf pass could skip buffers that flip exactly once, whose alt side still holds that frame's clear.
🤖 Agent: checked in the 26.3 jar: EnderDragonFight.tick adds TicketType.DRAGON at (0, 0) with radius 9 while the boss bar has a player and removes it otherwise, so ARENA_LEVEL = 33 - 9 = 24 is right, and the source follows fight.players, the same 20-tick validPlayer scan the bar uses.
🤖 Agent: hide-online-players now lives in an AtomicBool on lan::Shared that status_json reads per ping, so a management change applies to the next server-list ping without a restart; harness ids 190–199 are unique across the open management PRs.
A ServerPlayer is client authoritative, so LivingEntity.aiStep does not travel on the server and checkAutoSpinAttack gets the current box twice (26.3 bytecode). Sweeping from where the tick's first move started hit mobs vanilla's server passes through.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The cull now needs the display state (a custom air state stretches nothing), and the draw needed it again: compute it once past the distance cut instead of two registry lookups per cart each frame.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ELEMENTS listed bvec2 and bvec3 but not bvec4, so a bvec4[] constructor or a struct with a bvec4 member was left unconverted.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
level_can_have_weather sat between the comment and its function, so rustdoc gave the camera's comment to the weather helper.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The destination ignored [engine] resources and an existing resources/
folder, both of which the server reads before the installer's copy, so an
import could land where the next start never looks. Follow the lookup
order, and take the version from horizon_core's pinned VERSION_ID.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Over the 19 tables merged above (fa, he, hu, ro, da, fi, no, bg, sk, lt):
- Game tables: entries equal to en_us dropped (the game falls back to
en_us per key), as main did for hi_in, id_id, pt_pt and ur_pk.
- Entries that translated a product name as a word (Discord as
"Nesantaika", Fabric mods as textile mods, Horizon as "Хоризонт",
Tailscale as "tail scale") dropped in the game, English in the launcher.
- The six controller options (Look Sensitivity, Invert Look X/Y, Sneak
Button, Move/Look Deadzone) were verbs in every table ("look at the
deadzone", "invert appearance X"): rewritten as option names.
- Launcher tables: the 8 strings main added since (offline names,
Microsoft sign-in), so every_language_has_every_string passes.
- Language::parse: nb and nn (what macOS and Windows report for
Norwegian) pick the Norwegian table.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The replay ran on the system heap, which after --world freed its
remeshed meshes kept 0.8 to 4.9 GB of them dirty ("Malloc Small"),
depending on how the meshes were laid out, so --memory's footprint did
not say what the game holds. It now uses the game's own allocator, as
horizon-client does: the heap's dirty memory is 26 MB after the run.
Also takes clippy's then_some in SectionGpu::vertex_ranges.
Most solid quads of a section are whole faces of a block's cube on the
block grid: their corners, UVs, light and colour follow from the block,
the face and a few bytes. The mesher now lays such a quad out as one
24-byte record (block and face, UV rectangle, layer, tint, alpha, each
corner's grey level and light) instead of four 24-byte vertices, and the
rest stay as vertices that their record points to. The quads keep their
order, so the side buckets, draws, occlusion culling and GPU culling
count quads as before. A record is kept only when expanding it gives
back the quad's four vertices bit for bit.
The solid pipelines now read the arena page themselves through the flat
draws' page groups, flat and per-section draws alike: a vertex index
below 2^28 names a vertex, above it a corner of the record at that slot.
They expand records to the same vertex words, so the shading after is
unchanged; the colour is converted as the M1's vertex fetch converts
Unorm8x4 (times 1/255), which keeps the old layout's picture exact
through them. The visibility buffer reads records in its resolve; its
IDs now hold a record bit and 6 bits of page, and frames with more than
64 pages draw without it. Translucent quads and far tiles keep their
vertices. With a shader pack (or one of Horizon's own) the sections are
meshed again as vertices, since its programs read the arena as such.
HORIZON_PACKED_QUADS=0 turns the layout off.
On the RD 48 capture, meshed again from its save, 30.4M of 34.4M solid
quads get a record: near-section vertex data falls from 3,362 MB to
1,367 MB and the process's graphics memory from 5.4 GB to 3.4 GB, with
the same GPU frame time. Per-section draws give the same image as
before. Flat draws differ by 1/255 in 15 to 25 pixels of 3.7M: moving
unchanged meshes to other places in the arena does the same (22 pixels),
since the draws then group by page differently.
GAME is now horizon_core::version::VERSION_ID itself, so it cannot drift;
a regex over browser.rs tests the words in the source, not behaviour.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
run_ordered_ticks scanned the orb, TNT and falling-block lists once per
entity it ticked (and again to find it), quadratic in their count per
tick. Collect the run's entities once, sort by join number and look each
up with a binary search (linear fallback for a list out of join order).
Same order and same skipped entities as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main's Account page added 8 strings after this table was written; the
launcher's i18n test needs every language to have them. Strings that
translated the name Horizon as the word horizon now keep the name.
The fallback assertions used it-IT as the language the launcher lacks;
Italian is here now, so they use Esperanto (eo) instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its language to the same lines of i18n.rs; this
step holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
One union of the 19 translations, which all add lines at the same places
(lang.rs Bundled arms, the launcher Language enum and Language::ALL):
each merges clean with main alone but not with the others. Every file
equals its PR's; lang.rs and i18n.rs carry all of them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The table translated the name Horizon as the word horizon (or dropped stray
zero-width spaces into it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each PR of this series adds its table to the same lines of lang.rs; this step
holds the ones before it, so the series lands in any order.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: reading the hit record from cached_value keeps the armour instances dirty for a bypasses_armor source, as getDamageAfterArmorAbsorb returns before getArmorValue; the record's two fields only feed hurt_log, so no gameplay changes, and native_scene_motion should get past its first fall hit.
🤖 Agent: the 25 names match the 26.3 RandomizableContainer implementors (RandomizableContainerBlockEntity subclasses plus DecoratedPotBlockEntity); both callers place a chest today, so worldgen output does not change.
🤖 Agent: create_dispenser already returns early outside the chunk box or on an existing dispenser, so only a frame's keep list can decline now; the jungle temple and desert pyramid frames keep nothing, so worldgen output does not change and the guard is defensive.
native_acting_player now takes the id from native_acting_entity, which falls back to the
guest list when lan_acting_entity is unset, as the mount hooks do; a helper fragment gives
the field an API number so mods can require it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the drowned arm compares getY() with ServerLevelAccessor.getSeaLevel(), and PieceWorld::sea_level is the same answer blocks() and mineshaft already use; nothing else found.
🤖 Agent: sulfur_cube::has_body reads extra.body, so the two tests set a field nothing read and only passed where entity-types.json is absent; nothing else found.
🤖 Agent: checked on main: execute_command dispatches transfer to cmd_transfer and lan/commands.rs lists it in DEDICATED_ONLY; Hub::transfer sends the packet.
Stacks the management series so the queue lands them in order; every
handler, setter, port-map row and harness check of the four is kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: checked on main: execute_command dispatches setidletimeout to cmd_setidle_timeout and lan/commands.rs lists it in DEDICATED_ONLY, so both rows are ports.
🤖 Agent: Streaming::forget no longer exists and forget_columns is the column drop; the per-debris draw lives inside tick_explosions_with, so inline is the right verdict.
🤖 Agent: checked on main: explosion.rs exploded_positions spares rails under combat.rail_proof_blast, set from fuse >= 0 in minecarts.rs and saved across nested blasts in native_explosions.rs.
🤖 Agent: checked in the 26.3 jar: LocalPlayer is the only class besides Player that references hasEnoughFoodToDoExhaustiveManoeuvres, and sprint.rs Conditions::possible is exactly enough_food || may_fly.
Bundles Horizon's own Bahasa Melayu strings so ms_my players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: checked on main: options_ui.rs layout puts options.accessibility.link beside Done (Act::AccessibilityHelp) and greys Rotate with Minecarts on in_world && minecart_improvements, so the two rows are ports; init keeps missing for the High Contrast half.
🤖 Agent: matches EndPlatformFeature.createEndPlatform(…, true): each mismatching block goes through destroyBlock(pos, true) (drops + 2001 particles) before the obsidian/air is set; still no regression test, the platform rebuild has none.
Stacks the management series so the queue lands them in order; every
handler, setter, port-map row and harness check of the three is kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registers Bahasa Melayu as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Stacks the management series so the queue lands them in order: both add
handlers, a lan.rs setter, a port-map row and harness checks at the same
places; all of both are kept.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Afrikaans strings so af_za players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Everyone who plays on another server owns the game (the project's rule,
decided 2026-11-03): P1's offline-profile guests may follow it only to a
server whose resolved addresses (after the SRV redirect) are all loopback,
private or link-local. Public servers, even in offline mode, and friends'
invites refuse them on the profile page with a clear message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registers Afrikaans as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: checked against Mojang's live manifest: 26.3 now points at 80608ff6 with the same client jar (e877b6a0) and asset index 331e5c80, so only translations moved; without this, prepare rejects every fresh launcher install.
Bundles Horizon's own Íslenska strings so is_is players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Íslenska as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Српски strings so sr_cs players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The cow's ambient sound goes through the same path a spawned mob's does, so the
test asks one cow with Silent:1b and one without for their ambient sound: the
silent one raises nothing, the loud one entity.cow.ambient at Cow.getSoundVolume
(0.4) and a pitch in the voice range, and both leave the same random state.
Mob.playAmbientSound, and every other sound a mob plays through playSound, ends
in Entity.playSound, which returns early for a silent entity:
public void playSound(SoundEvent, float, float);
0: aload_0
1: invokevirtual #736 // Method isSilent:()Z
4: ifne 34
Mob::voice pushed its sound event unconditionally, so a mob summoned with
Silent:1b went on humming: an ambient sound, a creaking's death, an armadillo's
roll, a horse's whinny, all of them.
The pitch is still drawn before the check, because Java evaluates the arguments
of playSound(event, getSoundVolume(), getVoicePitch()) before the call; gating
first would leave the random state two draws behind vanilla.
Registers Српски as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
native_add_server_reload_listeners lists a mod's listeners (NeoForge's
AddServerReloadListenersEvent): an id and a folder under
data/<namespace>/. On each data load of a server (a world opening,
/reload), before native_server_data_loaded,
native_server_reload_listener_apply hands over the folder's .json files
as vanilla's SimpleJsonResourceReloadListener reads them: <ns>:<path>
ids, the top pack's file for each id over the resource root and the
world's enabled data packs, a file that isn't JSON logged and left out.
Fixes#8400.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Slovenščina strings so sl_si players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Entity.remainingFireTicks starts at 0, so the shared base no longer writes -1.
Orbs and projectiles kept only Tags and Item from a summon; Silent, Glowing,
Invulnerable, CustomName and CustomNameVisible are kept too. AttributeMap.pack
saves the movement_speed instance every living entity makes on its first tick
(LivingEntity.removeFrost), not only zombies and piglins.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Inventory.dropAll drops the main inventory, then EntityEquipment.dropAll,
which walks its EnumMap in EquipmentSlot order: the off hand, then feet,
legs, chest and head. drop_inventory threw the armour first. death_drops
now collects the stacks in that order, with the vanishing-curse filter as
before, so each stack takes the same random throw as in vanilla.
Fixes#8367
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Registers Slovenščina as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: /tempban is a banned-players.json entry with expires, so pardon, banlist and the login check already handle it; parity 140/140 across two runs on this head (proven.txt). Translation follow-up filed separately.
🤖 Agent: The remote boat's yaw eases over the Lerp's 3 steps like LinearInterpolationHandler; parity 140/140 across two runs on this head, the failures were flakes or main's (proven.txt).
🤖 Agent: Mob climbing matches LivingEntity.onClimbable/handleOnClimbable (26.3 bytecode); parity 140/140 across two runs on this head, the failures were flakes or main's (proven.txt).
riders::start_riding on a shulker attached to the block to its east (face 5)
and a pig: the shulker boards and its face is DOWN. A vehicle that already
carries someone refuses the board and the face has still gone back from 3 to
DOWN, with no mount link made. A cow rider keeps the face data it had.
Shulker.startRiding sets its attach face to Direction.DOWN and only then hands
over to Entity.startRiding, which decides whether the ride happens at all. A
shulker stuck to a wall, a ceiling or the side of a block therefore rides a
boat or a minecart lid up, and one whose board is refused is left lid up too.
mobs/riders.rs had no such reset: extra.variant3 came from Attachable and the
save, so a shulker that boarded kept the face it had and rode on its side. The
reset now runs first in start_riding (before every refusal, matching the
vanilla ordering) and again in link, which the game loop's own mount path
uses.
Bundles Horizon's own Latviešu strings so lv_lv players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Latviešu as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Eesti strings so et_ee players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Two jobs queued for (0, 0), one for its neighbour, one far off and a save:
releasing nothing or a column that is not queued leaves all five (the count
vanilla's release answers with is zero too), releasing (0, 0) reports two and
leaves the neighbour's job and the save in their order, and releasing every
column leaves only the save. Without the release the first count comes back 0
instead of 2.
ChunkTaskPriorityQueue.release and ChunkTaskDispatcher.release were both
missing with a note about queued jobs not being cancelled; both now name
release_columns, which Streaming::forget_columns calls for the columns a view
drop or a server unload removes.
Vanilla's ChunkTaskPriorityQueue.release(chunkPos, clearQueue) empties what a
column still has queued when it unloads, and ChunkTaskDispatcher.release calls
it that way from the unload. Horizon's heap had no per-column release: a
teleport, a smaller render distance or a server's forget of a thousand columns
left every generation, decoration, light and mesh job queued for the world the
view had left, and each ran only to have its result dropped on arrival by the
Done::Proto range check.
release_columns drops the heap's jobs whose column is gone, keeping the jobs
without a column (saves, meshes to free) and the order of the rest, and
Streaming::forget_columns - where a remote view's drop and the server's unload
both land - calls it before the columns' other state goes.
Registers Eesti as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
A pool element that fails to decode (a mod's processor list naming a block
state in the pre-26.3 {"Name": ...} form, for one) left the pool out with
no message unless HORIZON_JIGSAW_DEBUG was set: every structure using it
silently stopped generating. Warn once per pool instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Català strings so ca_es players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Català as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
🤖 Agent: Settings' frame in the glass look; index_row and section_title draw SettingRows only inside glass_ui::flat_rows, so other pages keep their rows.
Pretends nothing without the extracted entity data: the three mobs are built
from the mob types and their blow goes through do_hurt_target, then the sound
events they raised are compared. Without the dolphin arm the list is empty,
and an axolotl with Silent:1b still clicks; the panda is the control that
already honoured both.
Axolotl.playAttackSound calls playSound, and Entity.playSound returns early for
a silent entity. The panda arm next to it already checked, the axolotl's did
not, so an axolotl summoned with Silent:1b clicked on every hit.
Mob.doHurtTarget ends with playAttackSound, which the dolphin overrides with
playSound(DOLPHIN_ATTACK, 1.0, 1.0). Horizon had arms for the panda's bite, the
axolotl's click and the killer bunny's snarl, but none for the dolphin, so a
dolphin that bit a player or a squid was silent. Its goal list does have a
MeleeAttackGoal at 1.2, so the arm is reached in play.
Registers Lietuvių as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Slovenčina as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Български as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Norsk as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Suomi as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers اردو as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Dansk as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Română as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Magyar as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers עברית as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers فارسی as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Ελληνικά as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Svenska as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Čeština as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers ไทย as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Tiếng Việt as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Nederlands as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Registers Italiano as a launcher language with a full table translated
from the English strings.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The row said the blocks under a player standing on air are never sent again. They are now, from
Hub::position_change, so the verdict is `port` with the function that does it.
The hub test drives the move packets a vanilla client sends: the first 200 ticks of the connection
send nothing, the four corners of the box each get their block with the server's state, a tick
later a second claim sends nothing (one send per 200 ticks), a move the server resolved onto
something is not helped, a client that does not claim the ground is not helped, and a box that sits
across one block gets it once.
Without the send the guest receives no block update at all.
ServerGamePacketListenerImpl.handlePlayerPositionChange: a move packet that claims the ground
while the last move the server resolved came down onto nothing is followed by
forceSendPlayerSupportBlocks, which sends the four blocks under the player's box again
(ClientboundBlockUpdatePacket, plus the block entity's update tag where the block has one), at
most once every 200 ticks of the connection.
The hub only kept the ground claim in `on_ground`, which the accepted move overwrites, so a
guest whose block below went missing without its client noticing stood on air for good: nothing
ever told it what the server has there. It now keeps Entity.move's verticalCollisionBelow of the
last accepted move (`rests_on_ground`) and sends the blocks below on a claim that contradicts it.
Bundles Horizon's own Lietuvių strings so lt_lt players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
--memory on adds the renderer's largest GPU resources to the report: the
terrain arena pages and what near sections and far tiles use of them,
the translucent arena and sorted indices, the quad indices, the block
sprites with their mips, the LabPBR maps, flat draws' buffers and the
visibility buffer's. On macOS it also runs footprint on the process, so
the arena numbers can be checked against the graphics memory the system
actually charges.
Bundles Horizon's own Slovenčina strings so sk_sk players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
removeTicketWithRadius is no longer missing: the levels are rebuilt from the
sources, so the fight's arena ticket is the source Game::update_chunk_tickets
sets while EnderDragonFight.tick reports a watcher. addTicketWithRadius now
describes both radius adds (PORTAL 3, DRAGON 9), purgeStaleTickets points at
tick_portal (the PORTAL timeout it already ran), and the two getLowestTicket /
getTicketLevelAt gap notes no longer claim only players and forced chunks.
A watcher 12 chunks from (0, 0) whose simulation distance is 2 leaves nothing
around the island ticking without the ticket (checked first), then the fight's
ticket gives entity ticking to 7 chunks in every direction, block ticking to 8,
random ticks where both reach 31, and the watcher's own chunks are unchanged;
dropping the arena stops it again. The random-tick bounds case now holds an
arena with no player near it, and both grid-against-map fuzzes draw one, so the
grid and tile maps have to carry it too.
EnderDragonFight.tick holds a TicketType.DRAGON ticket on the arena while its
boss bar has a player and removes it when none has:
addTicketWithRadius(DRAGON, new ChunkPos(0, 0), 9) is level FULL - 9 (24) of
both kinds, so the island's chunks are entity ticking out to 7 chunks and block
ticking out to 8, whatever the watcher's simulation distance. ticket_levels
knew players, forced chunks and PORTAL tickets, so a fight watched from the
edge of the arena only ticked the chunks inside the viewer's own simulation
distance: the island's mobs, crystals and scheduled ticks stopped a few chunks
from (0, 0).
Sources gains the arena (Some((0, 0)) while watched, None once nobody is, which
is the removeTicketWithRadius side: the levels are rebuilt from the sources),
the level map lowers it like the PORTAL ticket's ring, and the random-tick
bounding box covers it so the arena still random ticks with no player near it.
Bundles Horizon's own Български strings so bg_bg players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
`MotionWorld.entities` was already real — `EntityCollisions` inflates the
entity query by 1e-7 and turns each box into a collision shape — but nothing
ever reached it: `VanillaSceneMotionOracle.Level.getEntityCollisions` returned
`List.of()` and the probe passed `entities: &[]`, so no fixture resolved the
player against another body's box.
`entity_box_corpus` adds 29 fixtures of boxes: a wall whose near face sits on
the player's leading face plus or minus the amounts the 1e-7 inflation decides
between (0, 5e-8, 1e-7, 2e-7, 1e-4, signed zero, half a block), a box low
enough to step onto and ones too tall, a box the player starts inside, a gap
narrower than the player, diagonal and sprinting approaches, and boxes met
while jumping and overhead. The oracle answers
`getEntityCollisions` from them the way `ColliderGetter` does: the
`getSize() < 1e-7` shortcut, `bounds.inflate(1e-7)`, then one
`Shapes.create(box)` per intersecting body.
Dropping the slice again fails at output line 4263: vanilla stops at the wall
with `horizontalCollision` true and z held at 4039800000000000, the probe
walks through with z 40398504816ec226. With the slice the suite matches at
2,853,483 states per side.
The corpus claims collisions only. `LivingEntity.pushEntities` gives the
bodies a velocity change and can cram the player, and the snapshot carries
player state alone, so `getPushableEntities` stays empty and nothing here
claims the pushing half.
Mob::jump_from_ground on a sniffer whose move control wants to move, from a
standstill: the hop ends with 0.1 on z (yaw 0 faces south) and keeps its
ascent. A sniffer already moving 0.2 keeps that, one the move control did not
ask to move goes straight up, and a cow never takes the push.
The test runs through the jump the AI calls, with a real mob world from the
extracted data, and fails without the sniffer arm: the z velocity is 0.
Entity.DATA_TICKS_FROZEN (index 7) never left the server for a player:
sync_player_state now sends it to the player when the body's ticks_frozen
changes, beside the air and absorption, and frozen_meta puts it in the
player's tracked state for the others, with 0 among its defaults. A
vanilla client on a Horizon server reads its frost overlay from it, and
every client shakes a fully frozen player.
Fixes#8280
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own Norsk strings so no_no players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Sniffer.jumpFromGround hands its super's hop one more push: when the move
control wanted to move this tick (MoveControl.getSpeedModifier above zero)
and the sniffer is moving slower than 0.1 a tick across the ground, it calls
moveRelative(0.1F, (0, 0, 1)), a tenth of a block along its yaw. Mob
jump_from_ground had no sniffer arm, so a sniffer that hopped off the spot it
was sniffing at went straight up and came down where it left.
The sniffer arm sits with the rabbit's and the cubes' own jumps, before the
native hook, so a content mod sees the velocity the hop leaves behind.
A file lock taken with flock belongs to the open file, which a process
started meanwhile shares until it execs: closing our copy alone left the
world locked for that long, so worlds::tests failed now and then while
another test started a process. The launcher's world lock now unlocks
when dropped, and the test unlocks as the game does when it closes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
One row said there was no unclean-start recovery and the other that
startedCleanly is never written after load. It is written false at start,
read by unclean_start_recovery, and written true with the options saved at
the handoff.
Bundles Horizon's own Suomi strings so fi_fi players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
The row pointed at Game::text_display_geometry, which only calls it.
display_light answers the brightness override packed as light coords, and the
world's light at the position when there is no override.
The row pointed at Game::armor_layers and said a dyed leather piece keeps
the undyed brown. layer_color takes the stack's dyed colour when it has one
and answers -1 for a layer that is not dyeable.
Display.addAdditionalSaveData was marked missing as "no local display entity
to save". NativeDisplay::write stores the transformation, both durations and
the start delay into the entity's saved compound; the rest of the synched
data is not written.
The row said a bat's every-tenth-tick flap sends no minecraft:flap. flaps
has the Bat arm and the sculk movement pass posts the event when the mob
flaps over air.
Both brute rows pointed at register_brute, which only sets goals: the step is
ai_step's brute branch and the activity switch is update_activity. The
furnace cart's push is push_along, not the slowdown it is called from.
End portal and gateway faces (position-only quads Horizon's entity
shader fills with its screen-projected starfield) and beacon and End
gateway beams (whose texture that shader wraps per pixel) were left to
Horizon's own entity shader under a pack, drawn into the pack's buffers
as if they were Horizon's frame: under a pack that renders at reduced
resolution they came out black and in the wrong place.
They now go to the pack, by the programs of the pack format: portal
faces to gbuffers_block (block entities) with end_portal.png over each
face, white and at full light, the pack telling them by blockEntityId;
beams to gbuffers_beaconbeam and its fallbacks gbuffers_textured and
gbuffers_basic, after deferred and blended, each face cut where its
texture repeats (at most 64 pieces) so every piece holds one copy of
the sprite in plain atlas coordinates. A block entity's run, and the
beacon beams' own run at stage NONE, is split by part (plain, portal
faces, beams), and under a pack neither portal faces nor beams fall
back to Horizon's entity shader.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Only assetIndex differs from the last accepted version JSON. The new index
changes 142 minecraft/lang files and adds or removes nothing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Three rows of client-002 pointed at Game::tick_remote_level or
mark_message_processed. The real homes are LevelLoadTracker's own
start_client_load and is_level_ready, and Game::send_chat_acknowledgement.
ItemEntity.getName is the Target::Item arm of Game::target_name, and
Monster.getProjectile is Game::mob_vanilla_ammo: off hand then main hand,
the weapon's supported stack, else an arrow.
The command table gains a reply field list per command. hello and status
carry theirs; the generated reference gets a Replies section and the MCP
tool list declares outputSchema (with structuredContent in the call result).
A test requires every command in the group to name its reply fields.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The register row claimed no /transfer node and the transfer row that no
command sends a ClientboundTransferPacket. The node comes from the commands
report, Game::cmd_transfer drives it, and Hub::transfer sends the packet.
The register row said there was no literal and the setIdleTimeout row said
the timeout could not be changed in game. Both are in the tree: the node
comes from the data generator's commands.json and the command branch is
Game::cmd_setidle_timeout, which stores the minutes and names them.
With [shadow], the pick also lists the quads with relief near the camera
on every side, and the shadow pass draws their grid where the flat quads
leave those texels, from the material's module (it holds displacement
and the vertex stage). The pass's frame group gains the height maps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NbhTr6rUbVKx6iB2X73DNG
Bundles Horizon's own Dansk strings so da_dk players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
`AbstractNautilus.onPlayerJump` is `nautilus::on_player_jump` now rather than
an inline note on `charge_mount_jump`, and the `handleStartJump` row says
where the packet's scale lands and that DASH and the dash sound belong to the
dash itself.
The test goes through `mounts::start_riding_jump`, the arm
`handlePlayerCommand` START_RIDING_JUMP calls: a three-quarter bar leaves
0.733 on the rider, a full bar 1.0, an unsaddled nautilus takes nothing, and a
dash still in its 40-tick cooldown keeps the scale it had. The last part
follows the scale into the mount's travel (`equine::ridden_step`): the rider
stays on with an empty pending jump, the dash clock starts, DASH is raised,
the dash sound is queued for the next tick and the velocity turns along the
rider's look.
The nautilus is built from the trip's own `MobType`
(`sensor_scan_tests::kind`), so the test runs without the prepared resources.
`AbstractNautilus.onPlayerJump` keeps the released bar's scale on the rider
(`playerJumpPendingScale`), and `tickRidden` dashes with it. A guest's ride is
simulated by the host, so the only thing that reaches the mount is its
START_RIDING_JUMP packet — and `mounts::start_riding_jump` answered every
nautilus with None. The host was left re-deriving the bar from the guest's
`PlayerInput` bits, which it samples once per tick: a press and release that
arrive together are collapsed to the last flags, and that release never
dashes.
The packet's own scale now goes to the nautilus through
`nautilus::on_player_jump`, behind vanilla's gate (saddled, dash cooldown at
zero), and the mount's next travel spends it. The scale out of 100 is
`PlayerRideableJumping.getPlayerJumpPendingScale`, now one function
(`equine::jump_pending_scale`) shared with the host's own bar charge.
Nothing sets DASH here: Horizon's `flag2` gates the dash, so raising it before
the momentum lands would cancel the dash instead of starting it.
Bundles Horizon's own Română strings so ro_ro players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Magyar strings so hu_hu players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own עברית strings so he_il players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
picked_stack returned nothing when the block's own item was air, before
the flower pot lookup ran, and a potted plant's block has no item of its
own. picked_item now looks the potted plant up first, as
FlowerPotBlock.getCloneItemStack does (new ItemStack(potted)), and only an
item that is still air picks nothing.
Fixes#8272
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Bundles Horizon's own فارسی strings so fa_ir players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Ελληνικά strings so el_gr players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Svenska strings so sv_se players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Čeština strings so cs_cz players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
A capture holds finished meshes, so a mesher change could not be
measured on it. --world SAVE meshes every captured section again from
that save's region files (blocks, biomes and saved light, with ao,
biomeBlendRadius and cutoutLeaves from ./options.txt) on every core,
with the mode --leaf-culling names. A section whose 3x3 columns are not
all saved keeps its captured mesh. The report now also counts solid and
leaf quads.
With fancy leaves every face between two leaf blocks is drawn, so tree
crowns are full of faces that are almost never seen, and they dominate
the cutout quads of a forest. The new mode drops a face between two
leaves when the leaf it faces has another leaf or a full opaque cube
behind it: such a face only shows through two cutout layers, while the
faces just behind a crown's outer layer stay. The faced leaf must be in
the section, so the block behind it is at most a border cell of the
next section, whose edits already remesh this one; a missing chunk or
a Y outside the column never counts as covered.
On the RD 48 forest capture it cuts leaf quads from 7.6M to 4.6M and the
GPU frame by about 1 ms. Off by default; Off meshes exactly as before.
Another process on the same GPU, or the GPU's clock state, moves the
median frame time of a whole run by several milliseconds, which hid
changes of a few tenths. The summaries now also give the minimum and
the 10th percentile, which stay put across such runs.
Far terrain tiles were culled only by the frustum: every tile in view
was drawn in phase 1, also those behind nearer hills and mountains. At
render distance 32 that was about 1,000 tiles and a third of the solid
pass's time.
Tiles now go through the same two-phase occlusion test as sections. A
test carries its box's corners around the origin (a section's stays its
cell grown by a block); a tile's box is its mesh's bounds grown by a
block, and it has a culling slot of its own, kept while meshes of the
same tile replace one another. Tiles that are fading or have a stand-in
mesh stay in phase 1 untested, as their depth or mesh is not the box's.
The image is the same: the test only leaves out a tile whose every
fragment would fail the depth test, and tiles are drawn a little
farther back than their box. scene_replay reports the culling result.
On the captured RD-32 scene at 1440p with the visibility buffer, the
frame's GPU p50 goes from 8.31 to 7.95 ms looking ahead and from 8.73,
8.76 and 7.99 to 7.64, 7.85 and 6.94 ms turned 90, 180 and 270 degrees;
the screenshots are byte-identical with the visibility buffer, without
it, and with indirect section draws.
The resolve ran terrain_vertex on its triangle's three corners and
then interpolated every output, the face's local position and light
levels included, and worked out the face's normal for every pixel.
Only HDR output reads those, and fs_opaque computes the normal only
with it on. The corners now go into the sums one at a time, and the
light levels, position and normal are worked out only with HDR output
on.
The sums are the same, taken in the same order: the screenshot is
bit-identical to the previous commit's. The resolve goes from 1.72 to
1.69 ms in the RD-32 capture at 1440p.
The resolve worked out the barycentrics of both of a quad's triangles
and kept the one whose smallest weight was larger, then built the
chosen triangle's rows again for the gradients. The triangles share
the diagonal 0 2, so the side of it the pixel lies on decides: corner
1's unnormalised weight against the sign of triangle 0 1 2's
determinant. That is one cross product instead of six and one set of
barycentrics instead of two.
Inside a triangle both rules pick the same one, and on the diagonal
both triangles give the same values, so the image is the same up to
float rounding (a few hundred texel-edge pixels of 3.7 million, as many
nearer the normal path as farther). The resolve goes from 1.75 to
1.715 ms in the RD-32 capture at 1440p.
The visibility resolve read a quad's six 16-byte words through six
separate switches over the 16 bound pages. Where a GPU thread group's
pixels show quads of different pages, each switch ran once per page
among them, six times over. One switch now loads all six words, so the
group walks its pages once.
The loaded words are the same, so the shading is too; the image differs
from before only by float rounding in a few hundred texel-edge pixels
of 3.7 million, as many nearer the normal path's as farther. The
resolve goes from 2.15 to 1.76 ms in the RD-32 capture at 1440p.
The resolve drew a full-screen triangle per arena page. Pages' sections
lie all over the screen, so every GPU thread group ran the shading once
for each page among its pixels with most lanes idle: 36 ms at 1440p
with nine pages. It now binds up to 16 pages at once and reads a quad
as six vec4 loads; the origin comes through one copy of the drawn
pages' block tables, so the IDs stay 32-bit. The resolve takes 2.1 ms.
Both passes sample only the side of RGSS's blend with the nearest
texel that the blend keeps, one tap where texels are large, which
gives the same value; the cutout alpha test in the ID pass gets cheaper.
scene_replay gets --screenshot PNG, which saves the first view's last
frame, to compare the visibility buffer's image with the normal one.
HORIZON_VISBUFFER=1 draws the solid flat draws (near sections and far
tiles, both occlusion culling phases) into a depth and 32-bit quad ID
pass first, then shades each pixel once in a full-screen resolve per
arena page. The resolve reads the quad back from the arena, runs the
terrain vertex code on the triangle the pixel falls in, interpolates
with perspective-correct barycentrics and works out the UV gradients
analytically, so texels, mip levels, light and fog match the normal
path. Cutout and fading quads keep their alpha test in the ID pass.
Only frames drawn flat use it; shader pack terrain, material stages,
shadow passes and surface targets keep the normal solid passes.
Bundles Horizon's own Brazilian Portuguese strings so pt_br players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Arabic strings so ar_sa players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Japanese strings so ja_jp players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Past a few hundred blocks, and on every distant tile, RGSS asks for mip
levels at or beyond the sprite's last one, where a 16 pixel sprite is a
single texel (with the default Mipmap Levels of 4) and the sampler clamps
any higher level to it. All eight taps, four at each of the two levels,
then read that same texel, whatever their position under the clamped
sampler, and their mean is that texel.
The globals now carry that level (filtering.z): the views' last mip level
when a sprite is one texel there, else 0, which turns the shortcut off (a
lower Mipmap Levels setting, or anything that leaves the field unset). At
or past it RGSS takes one tap; one level below it, the upper level's four
taps become one. The mirror and crosshair groups keep the full chain the
main group then also has, since the level is only set when the two agree.
The result differs from the eight-tap mean only in how float rounding
falls when summing four equal values, a last-bit difference far below
one step of the frame's colour.
sampleRGSS mixes sample_nearest's one tap with the rotated grid's eight by
how far the texels have shrunk below a screen pixel, and every terrain
pixel paid for both. That weight is 0 wherever a texel covers a screen
pixel or more (all the nearby terrain) and 1 wherever it covers half a
pixel or less (everything from a few dozen blocks out, the distant tiles
included), so most pixels sampled nine times to keep one or eight of them.
Now a weight of 0 returns sample_nearest alone and a weight of 1 the
rotated grid alone; only the band in between still mixes the two.
mix(a, b, 0) is a exactly, so nearby terrain is bit for bit the same. At
a weight of 1 the mix's a + (b - a) can differ from b in the last bit of
a float, far below one step of the frame's colour. The taps that remain
use explicit levels or gradients, so they are fine inside the branch.
horizon-ctl render-capture on keeps CPU copies of the section and far tile
meshes the renderer receives (HORIZON_RENDER_CAPTURE=1 from the start);
render-capture DIR writes them with the next frame's camera, sky, fog and
lightmap inputs and the settings that change GPU work. The scene_replay
example loads a capture into the real Renderer offscreen and prints GPU,
render-call and per-pass times as JSON, with overrides for A/B toggles.
Tag::get and get_mut turned the &str key into a new JavaString (a
Vec<u16>) on every call. Far terrain reads saved chunks' palettes and
sections through them, and in a flight over visited ground the key
allocations and their UTF-16 encoding were the biggest part of that
work. The key is now encoded into a buffer kept per thread; the
compound is searched the same way, with the same result.
Phase 1 already gives each solid pass only its own quads, but the sections
occlusion culling leaves to phase 2 still listed every quad once, and both
the opaque and the cutout pipeline drew that whole list, each clipping the
other's quads in the vertex shader. With culling on (render distance 32 on
Metal), every phase 2 vertex ran the vertex shader twice.
Phase 2 now builds its list as phase 1 does: every section's opaque runs,
then its cutout runs (a fading section's quads all in the cutout part),
grouped so the opaque ones come first. A section keeps one occlusion test,
which now names a range of draws in each pass and turns both on when it
sees the box. The indirect path and the flat path (a call break where
phase 2's cutout pass begins) draw only each pass's own range.
The vertex shader still leaves out the other pass's quads: meshes that were
not sorted by side give both passes all of their quads.
Every opaque terrain pixel worked out a face normal from derivatives, the
scene's HDR light and the fog a second time, only to write it to the
frame's alpha, which nothing but HDR output reads. hdr_light.z now says
whether HDR output is on, and without it the shader leaves the alpha at
1 and skips that work. The colour is unchanged.
Bundles Horizon's own ไทย strings so th_th players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Tiếng Việt strings so vi_vn players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
Bundles Horizon's own Dutch strings so nl_nl players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
`cargo install --git ...` compiles the tool and its workspace, which for a
mod developer on a slow machine is a long wait to write one block (#2120).
`cargo binstall` skips the build when the release holds a binary for the
platform.
- crates/cargo-horizon-mod/Cargo.toml gains [package.metadata.binstall]:
one `cargo-horizon-mod-v<version>` release, one tar.gz per Rust target,
the binary at the archive's root; a `.sha256` beside each.
- .github/workflows/cargo-horizon-mod.yml builds those targets (Linux
x86_64 and aarch64, Windows x86_64, macOS x86_64 and arm64), starts
each binary it can run, and publishes them on the tag. A pull request
builds them without publishing.
- tools/horizon_mod_binstall.py is the one list of targets: the workflow's
matrix comes from it, and it checks the built archives and the tag
against the manifest before anything is published.
- docs/mods.md shows the binstall command first, the source build after.
Co-Authored-By: DeepSeek V4.1 Flash <[email protected]>
Mob.doHurtTarget reads the attack_damage attribute, which in vanilla
includes the main-hand item's attack_damage modifier. do_hurt_target used
only the mob's own value, so a vindicator with an iron axe hit for 5
instead of 13 on Normal, and every mob that attacks with a held weapon
(zombie with a sword, wither skeleton, piglin brute, drowned with a
trident) was short by the item's modifier before the difficulty scaling.
Adds gear::attack_damage and a table-driven test over nine mob and
weapon pairs, plus a test of the Hard/Normal/Easy scaling of a mob hit.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Vanilla's `for (int i = 0; i < random.nextInt(1) + 1; i++)` draws the
bound on the check that fails too; Horizon drew it once before the loop,
so every lava event left the level random a draw ahead.
The animateTick loop the suite drives ran over barriers only, so
CampfireBlock.animateTick - the crackle sound, its volume and pitch, and
the lava particle a lit campfire spawns - had no case. Run the whole loop
over a world that is all campfire, with the case's seed driving both the
sampled positions and the block's draws, and compare every sound and
particle as exact bits, then the random's next draw.
`VibrationSignalParticle.Provider.createParticle` is `ParticleEngine::make`
now that the arm builds the signal from the option's destination, and
`ParticleEngine.makeParticle` no longer has to admit that vibration options
return nothing.
`a_vibration_option_makes_the_signal_its_provider_draws` goes through
`ParticleEngine::add_with`, the entry the client's packet handler reaches,
with the destination and arrival time a `VibrationParticleOption` carries.
On the previous commit `make` returns nothing for the type, so the engine
records no vibration particle at all (`left: None, right: Some(1)`); here
the particle has lifetime 20, size 0.3 and no physics, its behaviour holds
the listener, and its initial yaw and pitch are the ones the vanilla
constructor computes. A bare `ParticleOptions::simple(Vibration)` still
makes nothing: only the option names a listener.
`ParticleEngine::make` answered `T::Vibration => return None`, so the only
vibration Horizon ever drew were the ones its own vibration system spawned
in-process through `ParticleEngine::vibration`. A remote server's
`ClientboundLevelParticlesPacket` with a `VibrationParticleOption` was
dropped twice: `Game::received_particle` fell into `Some(_) => return None`,
and the provider had no arm to build it from.
`ParticleOptions` now carries the option's `vibration` destination and
arrival ticks, built by `ParticleOptions::vibration(dest, ticks)`. `make`
builds the particle from it through `vibration_particle`, the body
`vibration` used, and takes the sprite set's own draw from the engine's
random, which is the `sprite.get(random)` the vanilla provider makes.
`received_particle` resolves the option's `PositionSource` in
`vibration_destination`: a block's centre, or the entity's feet raised by
the option's offset. An entity this client has no position for is no
destination and makes no particle, as vanilla's first tick removes the one
it made.
The destination is resolved once, where vanilla's `VibrationSignalParticle`
asks its position source again every tick, so a signal does not yet chase a
moving warden or allay: the particle engine has no entity lookup to give it.
sulfur_cube::has_body reads extra.body, so a test that sets extra.head only
passes while the prepared resources are absent and the test skips. Both now
set the field the code reads.
Bundles Horizon's own Italian strings so it_it players see translated
menus and options instead of the English fallback.
Co-Authored-By: DeepSeek V4.1 Flash Free <[email protected]>
`horizon-ctl --api 0.1` asks the game `hello` before its first command and
refuses one whose protocol is on another line, or one with no hello at all
(exit status 5, kind "api"). `0.1` takes 0.1.x. Docs: the "version that is
checked" gap leaves "Not there yet", changelog and stability table updated.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
`health::tests::a_bypassing_hit_leaves_the_armor_instances_dirty`: with base 7
armour and toughness, a `minecraft:fall` hit must leave both instances dirty
with creation's zero cache and report 0 in the hurt record, while a
`minecraft:mob_attack` hit (not in `#minecraft:bypasses_armor`) must recompute
both to 7 and report that.
On upstream/main the fall hit leaves `(base 7, dirty false, cached 7)`, which
the test prints as `left: [(7.0, false, 7.0), (7.0, false, 7.0)]`.
`PlayerMotion::hurt_caused_by` read `minecraft:armor` and
`minecraft:armor_toughness` before the hit, ahead of the damage path's own
`#minecraft:bypasses_armor` gate. Native `AttributeInstance.getValue` is what
recomputes a dirty instance and clears its flag, so a bypassing source (fall,
stalagmite, magic, drown) cleared flags vanilla keeps, and the scene snapshot
compares both the cache and the dirty bit.
`LivingEntity.getDamageAfterArmorAbsorb` is the only reader of these two
instances on a damage path; it returns before `getArmorValue` and
`getAttributeValue(ARMOR_TOUGHNESS)` for a source in the tag, and a hit that
does meet the armour has already recomputed both in `after_armor`. The hurt
record now takes the caches the hit left, through `LiveAttributes::cached_value`
which never recomputes, so the record stays the value vanilla's damage path
read (after `hurtArmor`) and a bypassing hit leaves both instances as their
creation left them.
Found by `tools/test_native_scene_motion.py` on upstream/main: it failed at
output line 2245308, vanilla `minecraft:armor:...:true:...` against Horizon's
`...:false:...`.
🤖 Agent: MIXINS_REV ddd9700 → e8cd0aa (horizon-mixins#25, the only commit between them): native mods build again with horizon-client's #![recursion_limit]; launcher fmt/clippy/538 tests pass.
`Streaming::forget` is `Streaming::forget_columns` (the unload packet path takes
the whole set of columns at once), and `ClientExplosionTracker.addParticle` is
not a fn of its own: it is the body of `tick_explosions`'s per-debris loop, so
the row is `inline` at `ParticleEngine::tick_explosions_with`.
`loot_table_nbt` against a registry of the blocks in question: a chest, a
dispenser, a hopper, a shulker box, a coloured shulker box, a crafter and a
decorated pot each get the table and the first long of the random, and a
furnace, an ender chest and air get nothing and leave the random untouched.
`RandomizableContainer.setBlockEntityLootTable` asks for the block entity at the
position and hands the table to it when it is a `RandomizableContainer`: every
`RandomizableContainerBlockEntity` -- chest, trapped chest, barrel, dispenser,
dropper, hopper, shulker box and the crafter -- and the decorated pot, whose
block entity implements the interface on a plain `BlockEntity`.
`set_block_entity_loot_table` knew only the three blocks its callers place
today. Both of them (the dungeon chest in `cave.rs` and the bonus chest in
`bonus_chest.rs`) place a chest, so this changes nothing a player can reach: the
wider set is defensive, matching the vanilla interface rather than the two calls
that reach it. The NBT the container takes moves into `loot_table_nbt`, next to
the set, so the decision and the seed draw it guards can be read in one place.
🤖 Agent: dark-only restyle of the shared controls; the light screenshot is pixel-identical to main, so the risk is limited to the dark look of pages not yet rebuilt.
The pass timer (F3, HORIZON_SHADER_PACK_TIMING) left out a pack's
compute dispatches and the mip chains rebuilt before the programs that
ask for them, so their cost never showed. Compute passes now take
timestamps as render passes do, and each mip chain is timed by its first
level, labelled with its buffer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`PieceFrame::create_dispenser` gets two cases with a piece-world double that
records the blocks and block entities written: over air it must write the
dispenser and one block entity carrying the table and a seed, and over a block
the piece keeps it must write neither while still reporting the placement.
`StructurePiece.createDispenser` places the dispenser and then reads the block
entity back: `level.getBlockEntity(pos) instanceof DispenserBlockEntity` before
`setLootTable(table, random.nextLong())`. `create_dispenser` wrote the tag
unconditionally, so a placement the piece declines -- a block it keeps, or a
position outside its box -- still left a loot-table compound behind and still
drew the seed, which every later draw the piece makes then sees.
`PieceFrame::place` now has the `StructurePiece.placeBlock` half that answers
whether the block was written (`place_block`; `place` keeps its signature for
the 137 callers that do not care), and `create_dispenser` sets the table only
when the dispenser is there.
`MinecartTNT.getBlockExplosionResistance` and `MinecartTNT.shouldBlockExplode` both still
said `missing`. Both are in the tree: while a TNT cart is primed, its blast leaves a rail —
and a block with a rail above it — with no blast resistance and out of the destroyed set, in
`crates/horizon-client/src/game/explosion.rs::Game::exploded_positions`. The flag behind it
is `combat.rail_proof_blast`, set from the cart's fuse in `minecarts.rs` and carried through
a nested blast, and the case is covered by the command corpus group `tnt minecart rails`.
Nothing to fix, so no code: the rows now name the function that decides them.
horizon-mixins#25 accepts the compiler's crate-level attributes (`#![recursion_limit]`
and the like) and the known tool attributes on a module. Since horizon-client's lib.rs
gained `#![recursion_limit = "256"]`, every native mod failed at ddd9700 with
E_UNSUPPORTED_MACRO (#8169). e8cd0aa is that merge and the only commit since ddd9700.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`Player.hasEnoughFoodToDoExhaustiveManoeuvres` is
`foodData.hasEnoughFood() || abilities.mayfly`, and the one place vanilla asks
it is `LocalPlayer.canStartSprinting`. Horizon has that exact test as the
sprint gate's `enough_food || may_fly`, with `enough_food` filled from
`FoodData::has_enough_food` (a food level above 6) in `motion.rs`, and
`player_food::enough_food_for_manoeuvres` is the same predicate as a free
function the food probe already compares against the jar.
The row said `missing`; it now points at the gate that decides it.
Each frame the solid quads of the sections within [displacement]
distance are copied out of the arena, a compute pass picks the ones with
LabPBR relief that face the eye and are in view into an indirect draw,
and the material's displaced pipeline draws each as a grid of cells
moved along the face's normal by the pack's displacement, from the
texture's mean height so that face edges meet. The flat quads leave
those pixels to the grid. The height maps are visible to vertex shaders
now. The shadow map still takes the flat faces.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NbhTr6rUbVKx6iB2X73DNG
A player who starts the launcher with LIBGL_ALWAYS_SOFTWARE=1 to get a
visible window (transparent window on the radeon driver) had the game
inherit the same variables: horizon-client then rendered on the CPU at
2-3 FPS. The game picks its own backend, so strip those four variables
from the child environment and leave the rest alone.
Fixes#7832
Co-Authored-By: DeepSeek V4.1 Flash <[email protected]>
`ocean_ruin::post_process` now has a test with a piece-world double: a solid
seabed at 77, a sea level the test chooses, and a log of every `set`. The same
piece is post-processed with the sea level at 90, at the marker's own height
and one below it, so the water/air decision has to follow the world and not a
constant.
`OceanRuinPieces.OceanRuinPiece.postProcess` picks the block a `drowned`
marker leaves by comparing its height with `level.getSeaLevel()`, and
`post_process` compared it with a hardcoded 63. In a world whose sea level is
not 63 -- a datapack dimension, a superflat with its own sea level -- every
marker above 63 is left dry and every marker below it is flooded, whichever
side of the real sea level it is on.
`PieceContext` already carries the world's answer (`PieceWorld::sea_level`,
which `blocks()` and `mineshaft::generate` use), so the piece asks it.
Adds packaging/server/pterodactyl-egg.json and panel-start.sh, a docs/hosting.md section,
and tests/test_server_panels.py. Part of #8238.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
browser::GAME was a literal "26.3" that no generator touched, so a version port
left the mod browser filtering for the old release. It now reads
horizon_core::version::VERSION_ID, which tools/gen_version_consts.py regenerates.
A test checks the two agree.
Part of #4758
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Rotate with Minecarts is greyed out now, so the row names the one half
that is still missing: High Contrast stays clickable when the
high_contrast pack is unavailable and gets no error tooltip.
`AccessibilityOptionsScreen.addFooter` builds the Accessibility Help link
button beside Done, and `Act::AccessibilityHelp` confirms
`CommonLinks.ACCESSIBILITY_HELP` over the options screen, the
`ConfirmLinkScreen.confirmLink` button vanilla adds there.
`AccessibilityOptionsScreen.isMinecartOptionEnabled` greys Rotate with
Minecarts out unless a world is open whose feature flags carry
minecart_improvements, read from the level or the remote server.
Since the pass culls the near solid sections itself, a section it draws
appears whole where a CPU frame fades it in or out (vanilla's chunk
fade-in, far terrain's fades). A recorded section now gets FADING in its
record, which cull skips, and its walk mark, so the CPU draws it with its
fade, exactly while the instance loop's own expression for it is not a
whole section's (section_visibility and the far-terrain fade, bit for bit).
The flag is decided at upload, when a section leaves or is held for far
terrain, and anew for every record when the fade-in time changes.
A heap of fade ends in GpuCulling says when to look again, so a frame with
no end due does no fade work; Records counts the flagged records, and
switching GPU culling off forgets the flags with the records. The debug
check also compares each flag and mark with what the fades say.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`restore NAME --dimension minecraft:the_nether` (API: `dimension` in the POST /api/v1/restore body; MCP: `dimension`) brings back only that dimension's folder and leaves the other dimensions, level.dat and the players alone. The folder it replaces goes to backups/replaced-<time>/.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Checks the folder (version stamp, derived files and checksums, asset folders), copies it through a temp folder and an atomic rename, keeps the old copy on any failure, and prints the next step.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Checks SHA256SUMS.sig against the release key, then the archive against
SHA256SUMS; older OpenSSL, LibreSSL or none says so and keeps the GitHub
SHA-256 check. Closes#8241.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Keep the supervisor and control API alive after a game-issued stop, add an API start endpoint, and route file-backed operator and whitelist edits through the existing vanilla list handling. Cover the stopped lifecycle in unit and end-to-end tests.
Fixes#8232
Co-Authored-By: Codex (GPT-6.1) <[email protected]>
/attribute on a mob now handles base, modifier add, remove and value get for
every attribute the mob's gameplay reads from finalize, not only max health,
movement speed and attack damage. Modifier ids typed at run time are kept.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Whole-frame equality kept meeting offsets that were no tooltip: the
click cycles the extra (one glyph of a value label), the key-bound rows
swap in their capture markers, and the focus sprite is drawn for hover
anyway. Instead the same drawn UI counts the frame with the pointer on
the clicked widget — description at the cursor — against the one after
it moved off: with the fix fewer vertices; while the old draw kept the
description, the anchored tooltip made the two counts equal.
Two changes to the head of the core bundle, which every other table waits
on.
The model library reads every file under `assets/` and needs no block
registry, but it sat inside `Assets::reload`, behind both the registry read
and the block facts. It now runs beside `Assets::load` instead, so its
reading overlaps the registry's rather than following it. A server draws
nothing and parses no models, so it skips this and is unaffected.
`facts.json` is about 10 MB and is parsed before anything else in the
bundle can begin. `serde_json` cannot borrow out of a reader, so
`from_reader` copies every string it parses; over one slice it borrows.
`BlockRegistry::from_facts_json_bytes` reads the file whole and parses from
that, and `from_facts_json_with_flags` keeps its reader for callers that
have one.
Steps are added inside the model and sprite work (`core.models_parse`,
`core.models_bake`, `core.bake_states`, `core.bake_standalone`,
`core.item_library`, `core.fusion`), which is what the above was found
with: before them the whole 2.5 s of a cold `core.block_models_sprites` was
one opaque span.
Measured on 12 threads, five warm runs each, the cold first run excluded:
core, median 448 ms before 404 ms after -10%
Of that, `core.assets` falls from 106 ms to 73 ms and the model parse
(65 ms) moves off the critical path entirely.
Co-Authored-By: Claude Opus 5 <[email protected]>
Sampling is the cold-load bottleneck and already had half the cores rather
than a quarter. On a 12-thread machine that is six samplers beside five
meshers, and the mesher has little to do until tiles are sampled, so its
threads sit idle through the part of a cold build that waits on sampling.
Measured on 12 threads, building 4336 tiles at LOD distance 256, from the
game answering the control socket until every target tile is sampled, four
runs a configuration:
samplers/meshers sampling whole build
6/5 (before) 15.0 s 23.9 s
8/3 11.8 s 18.6 s
9/2 10.6 s 19.1 s
Two thirds, so eight here. Nine finishes sampling sooner still but leaves
the mesher two threads and the whole build ends later. The frame and the
ticks keep their core at every size, which the test now checks directly
rather than only through the expected numbers.
Co-Authored-By: Claude Opus 5 <[email protected]>
Same missing conversion as array constructors: S(1u, 2) with an int and a
float member composed unconverted and failed naga validation.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Include each dedicated player's current coordinates in server-status so the server-admin HTTP API and MCP players tool can report them. Update the generated API reference and server documentation, with focused serialization coverage.\n\nFixes #8224\n\nCo-Authored-By: Codex (GPT-6.1) <[email protected]>
Drivers accept int[2](1u, 2); naga composed the arguments unconverted and its
validator refused the program (Composing 0's component type is not expected).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
#7550 skipped a provider glyph whose advance leaves 0..32 in Font::resolve
(drawing) and provider_advance (the by-width table), so every glyph of a
server pack's `space` provider (kerning, -512 to 512) or of a tall bitmap
(Nexo's, wider than 31 px) stopped drawing.
Vanilla's FontSet.computeGlyphInfo draws the first provider's glyph
(SelectedGlyphs.any) whatever its advance, and only the glyph obfuscated
text draws (SelectedGlyphs.nonFishy, from getRandomGlyph) moves on to the
first provider with a good advance, else the missing glyph. FontSet.reload
files each character in glyphsByWidth by its first glyph's advance, fishy
or not.
Font::resolve now skips a fishy glyph only for nonFishy
(Font::glyph_non_fishy, cached apart), provider_advance takes the first
glyph's advance, and the table is keyed by i32 so a negative advance does
not land in the 0 bucket. #7550's test claimed font.glyph('a').advance is 3
for a first glyph of 65, contrary to vanilla; it now covers what vanilla
does, and the fall-through #7550 wanted stays for obfuscated text.
Fixes#8222
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The fresh comparison UI drew the same already-cycled settings; cycling
again gave it the next value and the label widths drifted for text, not
tooltips (verify: 34896 vs 34926).
A packet captured from a vanilla 26.3 server at join carries two clocks:
`Long 13071`, `VarInt 2`, then `(0, 66678, 0.0, 1.0)` and
`(1, 13071, 0.0, 1.0)`. The key is therefore a plain registry id —
`WorldClock.STREAM_CODEC` is `ByteBufCodecs.holderRegistry`, not `holder`,
so there is no inline form and no +1. Reading it as a holder id shifts the
first key to "inline" and the whole packet stops decoding.
Co-Authored-By: Hermes <[email protected]>
Abort the client-side streaming workers when a world close or native application termination starts. The integrated server already stops its own workers, but the window-side registration otherwise waits during drop after extended sessions.\n\nFixes #8178\n\nCo-Authored-By: Codex (GPT-6.1) <[email protected]>
Match vanilla's movement gate, random order, and snowflake velocity for minecarts, living mobs, and controlled mounts. Relay the same particle parameters to LAN clients.
Co-Authored-By: Codex (GPT-6 Luna) <[email protected]>
TagNetworkSerialization.serializeToNetwork sends each synchronized registry's
tags as TagLoader bound them from the whole pack stack. Horizon's server read
the resource root's tag files only and merged the world's data packs into
the dialog tags alone, so no client (vanilla or Horizon) was told that a
pack put dirt in #minecraft:mineable/pickaxe or a block in #climbable. After
/reload, keep_registries read the root's tags again without any pack, the
packs' dialog tags included.
merge_pack_tags merges, for every synchronized registry the world's packs
have tag files for (tags/<path>, a mod's registry under tags/<ns>/<path> as
Registries.tagsDirPath has it), the root's files and then each pack's in
order ("replace": true, nested tags expanded), at load and on /reload.
Registries no pack touches keep load_tags' tags.
Refs #7843
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Clicking an extras button cycles the extra, so the clicked frame's labels
differed from the fresh one and the vertex counts drifted for text, not
tooltips (verify: 34884 vs 34878). The comparison UI now cycles the same
extra directly, leaving the click's focus the only difference; a tooltip
would still add its vertices.
The clicked widget is also picked among the non-key extras, whose click
selects a key instead of cycling.
Every play connection had a reader and a writer thread (plus a post thread
waking the writers): 100 bots meant 200 threads fighting over the cores.
They now live on a fixed pool, one thread per two cores, each polling its
players' sockets through mio (epoll, kqueue, AFD). The game thread only
queues bodies in the player's outbox, bounded by MAX_PENDING bytes; framing,
compression, encryption, socket writes, reading, keep-alives and timeouts
run on the pool. The integrated server's own player goes through the same
threads: its in-memory pipe tells the thread when bytes arrive.
horizon-protocol: Connection::detach gives the stream up, ConnReader reads
from any source without blocking (read_from, buffered), ConnWriter frames
into a buffer (frame_into), and Pipe gains non-blocking reads and on_ready.
SharedFrames hashes a body once (XXH3-128 keyed by a random secret) where it
hashed it twice with SipHash, and a body being compressed is marked: a
second sender waits for that frame instead of compressing it again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On a running server, `horizon-server config set` and PATCH /api/v1/config sent
only console commands, so the message of the day and the player limit were
always reported as "restart required", although the game takes both live
through the management settings (`serversettings/motd/set` and
`serversettings/max_players/set`).
`live_line` now returns the control line to send: the same management
request for these two settings, `console ...` for the ones vanilla changes
through its console, and nothing for the supervisor's own settings. The
CLI talks to the game's control endpoint the way the API already did, and
a reply with an error leaves the setting for the next start.
Fixes#8206
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A click leaves the clicked widget focused (focus_from_keyboard false),
and the draw loop drew the tooltip for hover OR focused. So after
clicking a button, walking the pointer away (or off the screen) left its
description up indefinitely: blocking the button — the report's
"description doesn't disappear", over the label that does scroll.
Vanilla draws the tooltip while the widget is hovered, or against the
widget while the focus came from the keyboard or a controller
(MenuTooltipPositioner's focus tooltip); a mouse-only focus does not.
Draw it for the pointer, or for keyboard focus only: the description
follows the pointer back out.
The cache cleared all 512 entries when it filled, so a server with more
distinct skins than the cap would rebuild every model on screen in a single
frame. `game/skin_models.rs` keeps a use counter per entry and an insert
evicts exactly one least recently used model; the cap and the
(512 entries) behaviour are otherwise unchanged.
Co-Authored-By: Hermes <[email protected]>
The GPU path for entities (skin_frame, since #7801) starts each frame's draws
in begin_skinning, which only entity_geometry runs. render skips
entity_geometry when the frame shows the panorama (the title screen, a
server's loading screen), so the draws stayed those of the last world frame,
and the renderer drew them again every frame: after Save and Quit to Title,
the mobs that were in view stood over the panorama. The panorama's frames now
clear those draws; the models the renderer stores stay, so the next world
draws its entities as before.
Fixes#8192
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The entity shader's vertex output had 15 user-defined locations, and the
fragment stage's front_facing counts as one more: 16 against the 15 an
OpenGL 3.3 GPU (a GeForce 9100M G) allows, so 'Breaking cracks' (and every
entity-shader pipeline) failed to build at start-up.
The spherical and cylindrical fog distances now share one vec2 location
(fog_distance); each component is the same expression, interpolated on its
own, so the output is unchanged. A naga test audits the vertex outputs and
fragment inputs of every assembled shader and every standalone .wgsl file
against that limit, front_facing / sample_index / sample_mask /
primitive_index included, and fails for a .wgsl file it cannot place.
Fixes#8195
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
source_hashes named each tested file by resolving it, then taking it relative
to the repository. In a worktree whose `.local` is a link to another
checkout's (a junction on Windows, as the contributor docs suggest for the
prepared resources), resolving leaves the repository, and every harness that
hashes its inputs under `.local` stopped with "is not in the subpath of".
repo_path tries the path's absolute spelling first, which stays under the
link, and the resolved one after, so a repository reached through a link
(macOS's /var) is still found. The test makes a `.local` link (a symlink, or a
junction where symlinks need a privilege) and fails without the change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The manifest's [displacement] (distance 4..32 blocks, cells 4..64 a
side, and the options that turn it on, pick the cell count and make
cells steps), the material stage's
fn displacement(input: DisplacementInput) -> f32, checked with its
signature and composed with an entry point that calls it, and
MaterialInput.displaced. The renderer passes 0 for now; the grid that
draws the relief comes next.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NbhTr6rUbVKx6iB2X73DNG
A pack's 1D image is now a D1 texture with a D1 view, sized against the
device's 1D limit; `image1D`/`uimage1D` bindings and their stand-ins are D1,
and its `sampler1D`/`usampler1D` reads it through the image's own dimension.
Fixes#8177
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An Iris-format `image.<name>` gives its size in pixels after `relative`: two
for a 2D image, three for a 3D one, and one for a 1D image (a table read by
index, such as a per-block lookup a setup program fills). CustomImage::parse
refused the one-size form, so the image was "not an image" and its sampler and
`image1D` reads got nothing.
ImageSize::Line holds that width; `extent` is [width, 1, 1] and `is_1d` tells
the renderer. An `image1D` declaration without a format gets the image's, as
2D and 3D ones do.
Refs #8177
Co-Authored-By: Claude Opus 5.5 <[email protected]>
pick_fluid stopped at any non-air cell without a fluid instead of clipping
the block's outline, so a water bucket could not fill through the empty part
of a torch's cell. ClipContext.Block.OUTLINE lets the ray through; the ray
now stops only where it meets an outline box. This covers the buckets
(empty, fish), glass bottle, lily pad / frogspawn and spawn eggs, which
all use this ray.
Fixes#8180
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
While a pack's post chain draws the frame, `taa`, `fxaa` and `upscaling` have no
effect and the projection is not jittered (gfx.rs turns TAA off under a pack
post or a native post, and the resolve that does TAA, FXAA and the upscale is
the step the pack's last pass replaces). `resolutionScale` still applies. The
reference said nothing, so pack authors assumed the settings still worked, and
a temporal pass of their own, which has no jitter to work with, did nothing.
Say it, and what a pack can do instead.
Refs #8173
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
GLSL gives a `bool` in a `layout(std430)` buffer a size and an alignment of
4. naga does not let a bool into a storage buffer at all, so the program
failed with "Global variable [6] '' is invalid: ... The type is not
host-shareable": no name, no member, and the line of the include site (#8175).
Such a member now becomes a `uint` of the same size and every use of it is
rewritten before the stage is scanned: a read is `(flag != 0u)` (also through
an instance, `state.flag`, and an array, `flags[i]`), a plain assignment is
`flag = uint(...)`. Nothing is added or removed from a line, so the pack's
line numbers stay right. It applies to render and compute programs.
What the rewrite cannot make sure of is refused with the member's name and its
line, instead of naga's anonymous error: a compound assignment (`flag |= x`),
an assignment used as a value, a local or parameter of the same name, a `bool`
vector member and an instance array of such a buffer.
Tests: the five shapes of the issue translate; the refusals name the member and
the line; the rewritten text is checked exactly; a compute program reads and
writes a bool member. Before the change the first test fails with the exact
message of the issue for every shape.
Fixes#8175
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
LivingEntity.onClimbable answers false for an entity that isFallFlying in a
#minecraft:can_glide_through block (vines, twisting and weeping vines, cave
vines) before it looks at #minecraft:climbable or a trapdoor over a ladder.
Mob::on_climbable skipped that test. A ladder is not in the tag, so a glider
still holds on to it. Spider.onClimbable overrides the whole method and is
unchanged.
Refs #8140
Co-Authored-By: Claude Opus 5.5 <[email protected]>
zombie-shield-partial gave vanilla itself two results from one script
(#8164): blocked in the 2026-11-03 audit (Bob -0.08191 0.31362), not blocked
in a local run (-0.21840 0.27516, as Horizon). The zombie stood one block east
of Bob, behind him while his position packets face him west, and at exactly 90
degrees once KnockbackClient's use packet turns him south
(ServerGamePacketListenerImpl.handleUseItem snaps the player to the packet's
rotation, 0 0). Which rotation counts at the tick of the hit is timing.
The zombie now stands south-west of Bob, facing him: 45 degrees from his view
either way, so the half-blocking shield blocks every run. No other case
changes.
Fixes#8164
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GLSL (4.60, 4.5) lets `centroid` and `sample` stand beside `flat` and means
nothing by them, since a flat varying is not interpolated. naga validates the
pair and refuses it ("Cannot combine Flat interpolation with the Centroid
sample type"), so a pack that writes `flat centroid out vec2 uv;` or a block
member `flat sample vec2 uv;` lost the whole program. The declaration scanner
now keeps `flat` alone when it is there, in either order and on either side of
the storage qualifier.
Tests translate the varyings and the interface block members of #8174 through
the whole pipeline: 3 profiles, 7 qualifier combinations, both qualifier
orders, and 11 block member shapes. The block cases are the ones whose
"Expected Void, Struct or a type, found Interpolation(Flat)" #6875 fixed; they
had no test above the function that flattens a block. Before #6875 they fail
with exactly the message of the issue.
Refs #8174
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Eight leave no room for ambient occlusion (a pass and its apply) and
TAA: reimagined_horizon chains 11, bloom (4), ambient occlusion (2),
screen-space reflections, light shafts (march and apply), TAA and the
final pass.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NbhTr6rUbVKx6iB2X73DNG
A chunk's random ticks asked the section cache for every ticking section
and cloned each mask into a list, again after every block change, building
masks for sections the pass had already left. The cache now answers with
the next ticking section from a Y up, building and sharing only that mask;
an answer stands while no block changes and the asked Y stays below it.
Draws, positions and the ticks they reach are unchanged.
Fixes#7886
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Hooks that only know entity ids (living_incoming_damage, living_knockback) can now be
matched to players.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A superflat world that decorates (`features` or `lakes`) but generates no
structure set, because `generate_structures` is off or its
`structure_overrides` is `[]`, still went through FlatTerrain, which lays the
layers and the Void platform only. FlatLevelSource decorates with
adjustGenerationSettings whatever its structures do.
FlatTerrain::structure_settings becomes pipeline_settings: the settings of a
superflat world that lets a structure set generate or that decorates. World
loading streams such a world on the flat pipeline when its structures generate
or it decorates; the pipeline then has no structure starts when
generate_structures is off, and no set when the overrides are empty.
FlatTerrain keeps the worlds with neither, and the Void.
Refs #2912
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`PistonBaseBlock.moveBlocks` writes `moving_piston` into the destination cells
and then updates the neighbours of every source cell. A cell that is both a
source and a destination of a chain push still holds `moving_piston` when that
update runs, so a rail on top of it was asked whether it could survive against
the bare `moving_piston`, found no rigid support and dropped as an item
(#8111).
`support_state` already stood in for a retracting piston base's block-entity
collision shape (#4728); any other moving cell now reads as `moved_state`,
which is what `PistonMovingBlockEntity.getCollisionShape` returns.
Co-Authored-By: Hermes <[email protected]>
`ClientboundSetTimePacket` keys its clocks with `WorldClock.STREAM_CODEC`,
which is `ByteBufCodecs.holderRegistry`: the wire form is `varint(registry id
+ 1)`, with 0 reserved for an inline definition. Read as a bare VarInt, a
vanilla server's overworld clock (1 on the wire) landed on the End's entry, so
the name lookup in the `C::SetTime` handler never matched
`minecraft:overworld` and `day_time` stayed where it was — `/time set day`
updated the game time but the client's sky and time never moved (#7542).
Horizon's own writers already pass 0-based registry ids (the LAN hub and the
1.21 adapter), so they now emit the holder form vanilla expects instead of a
bare 0, which vanilla would decode as an inline holder.
Co-Authored-By: Hermes <[email protected]>
A cow let go at rest drops 0, 0.0784, then 0.155232 in open air; in a column
of vines every drop from the third tick on is 0.15F and its fall distance
never grows past one move.
Refs #8140
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A player carried by a boat only ran `passenger_pose_tick`, never the part of
`LivingEntity.aiStep` that applies the effects of the blocks it is in. Powder
snow's FREEZE never added a tick of frost to the window's own player, and
nothing thawed it, so `ticks_frozen`, which the frostbite overlay reads, stayed
where it was: the server froze and hurt the rider, who saw no frost (#8156).
`passenger_block_effects_tick` samples the blocks the passenger is in, as the
client does on foot, and thaws two ticks a tick out of powder snow as the
server does; the riding branch of the client tick calls it after the pose.
The server's copy of a riding guest is untouched: it already freezes in
`server_block_damage`.
Refs #8156
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
LivingEntity.handleRelativeFrictionAndCalculateMovement calls
handleOnClimbable for every living entity: when onClimbable() (the block it
stands in is #minecraft:climbable, or an open trapdoor over a ladder facing
the same way), the fall distance is reset and the motion is clamped to 0.15F
across and to -0.15F down before the move; after the move, a mob pressed
against a wall or jumping while still on a climbable goes up at 0.2. Only
Spider overrides onClimbable (isClimbing).
Mob::travel_in_air did this for spiders only, so a cow or a villager let go
in vines fell freely and took fall damage (a cow from 10 to 3 health over 10
blocks). The spider keeps its own test, and now clamps to the float 0.15F as
vanilla does instead of the double 0.15. The pusher's onClimbable in ai.rs
uses the same test.
Refs #8140
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A 180-degree gap is ambiguous (Mth.wrapDegrees maps it to -180), so the test
now uses -90 to 80, whose short arc is unambiguous.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Vanilla only has /ban, which is permanent, so an operator who wants to
send a player away for an hour or a week has to remember to pardon them.
`/tempban <player> <duration> [reason]` (durations as /tempmute: 30m, 12h,
7d, 2w) writes a banned-players.json entry with `expires` set, as the chat
filter's automatic temporary ban already does, so the login check,
/banlist and /pardon treat it like any ban and it survives a restart.
Operators of level 3 (/ban's level) run it in game on dedicated servers;
the console takes the same words. A player who is online is kicked with
the reason and the time the ban ends, the message they read again when they
try to join (now one function, `ban_message`, shared with the login check).
A player whose ban is still in force is left as they are, as /ban does;
a ban that has ended no longer counts. It is logged as `[tempban]` and goes
to the event stream as a `ban` with `reason`, `by` and `until`. Like the
mute commands it is handled as raw words rather than in the vanilla tree,
so it has no completions.
Refs horizon-rust/horizon-mods#170
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
tools/test_flat_generation_parity.py runs VanillaChunkGenerationOracle with
HORIZON_FLAT=<flat_level_generator_preset>, which builds a real FlatLevelSource
from the preset registry (with ChunkMap's random state for a generator without
noise, and createState's structure state), and chunk_dump with the same
variable on OverworldGenerator::flat. Every block, quart biome and block entity
of the target chunks is compared after decoration, for the three presets that
decorate (Overworld, Desert, Tunnelers' Dream) and three that only place layers
and structures. A decorating preset must hold blocks beyond its layers in
vanilla, so the decoration is known to be covered.
Refs #2912
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A superflat world on the structure pipeline (Generator::flat, #5509) built its
decoration with no placed feature at all. FlatLevelSource decorates with
FlatLevelGeneratorSettings.adjustGenerationSettings of its biome (26.3
bytecode): the lava lakes of createLakesList in the lakes step when `lakes` is
on; the biome's own features when `features` is on and the layers are not all
air (or the biome is the_void), except those of the underground and surface
structure steps, and of the lakes step when `lakes` is on.
FlatSettings now carries `features` and `lakes`, and Decoration::new_with takes
that adjustment. The FillLayerFeatures stay last in TOP_LAYER_MODIFICATION, as
the builder adds them after the biome's features.
Refs #2912
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A client that plays on a server (the window of a single-player world is
one) put a boat's yaw at once when a move packet came, every third tick,
while its position was eased over the three ticks. A boat pulled on a lead
turns every tick, so it turned in steps. AbstractBoat creates
LinearInterpolationHandler.create(this, 3): position and rotation both move
a 1/steps share of the way each tick (Mth.rotLerp, the short arc).
The yaw of the boat now follows the lerp's rotation target with the same
steps as its position. A driven boat and minecarts are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A cleared colortex buffer had only its "main" texture cleared at the start
of the frame. The main/alt mapping carries over frames, so with an odd
number of flips a frame the texture a program writes first was "main" the
frame before, and still held what a later program wrote after that frame's
clear: pixels the first write discards, or a blended first write (with
#8044), read the previous frame's data. Iris 1.11.7 reads the clear value
there (probed with our own packs: a discarding first write reads 0 where
Horizon read the previous frame's 0.25; a compute imageStore then a blended
write (with #8044) blends onto 0 where Horizon blended onto 0.25).
The frame-start clear now also clears the other texture of every cleared
buffer, grouped like the existing clears. Buffers with Clear=false keep
both textures.
Part of #6812
Co-Authored-By: Claude Opus 5.5 <[email protected]>
In vanilla Java Edition 26.3 (SpawnerBlockEntity.clientTick and BaseSpawner.clientTick),
client ticking is only performed for authentic spawners, trial spawners and vaults.
Non-spawner block entities (including dummy container menus at i32::MIN) never reach
spawner distance calculation or client ticking.
In Horizon, Game::tick_remote_spawners previously iterated through all block entities
and performed signed coordinate subtraction (pos.0 - center[0]).abs(). When a dummy
container menu at (i32::MIN, 0, 0) was present, this panicked on signed integer
overflow in debug mode (i32::MIN - 1024 underflow or i32::MIN.abs() overflow).
Filter upfront for Spawner, TrialSpawner and Vault block entities in nearby_remote_spawner_keys
and compute Chebyshev distance using unsigned integer absolute difference (abs_diff)
to avoid any possibility of signed integer overflow at coordinate boundaries.
Co-Authored-By: Antigravity <[email protected]>
Destroy replaced platform blocks with drops before setting obsidian or air, matching EndPlatformFeature. This preserves sand and container contents on portal arrival. Fixes#8127.
Co-Authored-By: Codex <[email protected]>
In vanilla Java Edition 26.3, CycleButton.onPress checks input.hasShiftDown()
and calls cycleValue(-1) when Shift is held down, cycling options backwards
with positive modulo wrapping.
In Horizon, OptionsUi.click previously ignored self.shift and passed right
(which was always false due to earlier early return), preventing cycling backwards
on Act::Cycle, Act::Extra, and in GraphicsUi::click.
Pass self.shift into cycle_option, item.cycle, and graphics.click, allowing
Shift-clicking to cycle options backwards with wrap-around across all options pages.
Co-Authored-By: Antigravity <[email protected]>
The candidate union is kept as one segment per player (the chunks of its
square that no earlier player's square holds). A border crossing rebuilds
the mover's segment and the later overlapping ones, with no hash set, so
clustered and mixed groups no longer rebuild every player's square. Each
level keeps its own cache, so players in several dimensions stop
invalidating it every tick. Order, count and live proximity are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Share arrow environment fire effects with player tridents, retain grounded and return timing, persist Fire, and synchronize and render the burning flag. Fixes#7500.
Co-Authored-By: Codex <[email protected]>
Adds the hook point player_chat(player, name, message, replies, cancel): heard once for each chat message a player sends on a server, after the secure-chat chain and the chat filter and before anyone sees it. cancel drops it for everybody; lines in replies go to the sender. The text is read-only (a rewritten signed message would not verify).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A colortexNFormat of RGBA8_SNORM, RG16_SNORM and the other SNORM formats was
stored in a half float, so a buffer read back -1.5 or 1000 unclamped and
0.0999756 for 0.1. Iris 1.11.7 reads them clamped to [-1, 1] and quantised
to 8 or 16 bits, as probe packs of our own show. Where the adapter can
render to the SNORM wgpu format, the buffer now uses it; elsewhere the half
float stays.
Part of #8106
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A colortex buffer of a three-channel format (RGB8, RGB16, RGB16F, RGB32F,
RGB32UI, RGB565, R3_G3_B2, R11F_G11F_B10F, RGB8_SNORM, ...) is stored in a
four-channel wgpu format, so it read back whatever alpha was written or
cleared. In Iris 1.11.7 it reads alpha 1 in every case, as probe packs of
our own show. Passes of colortex programs now leave such a buffer's alpha
alone, its clears and first contents set it to 1, and the world blit loads
a three-channel colortex0 with alpha 1.
Part of #8106
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A pack that names the HIGHER_SHADOWCOLOR feature (iris.features.required or
.optional) gets six more shadow buffers, shadowcolor2 to shadowcolor7, as
Iris's documentation describes them: identical to the first two. Shadow and
shadowcomp programs write them through RENDERTARGETS or DRAWBUFFERS, every
program samples them, and each has its own format, clear switch and clear
colour (white by default). Without a directive a shadow program still writes
shadowcolor0 and 1 only, as Iris 1.11.7 does with the feature in probe packs
of our own. The feature is defined as IRIS_FEATURE_HIGHER_SHADOWCOLOR for an
optional request and no longer noted as missing when required.
Not included: the shadowcolorimg2 to 7 images, which need image access to the
shadow buffers in the first place (#7945).
Part of #7951
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A sign picked with Ctrl + Pick Block carries its text as the item's
sign_text_front/sign_text_back components (SignBlockEntity
.collectImplicitComponents). Vanilla's client places the copy itself
(BlockItem.place inside MultiPlayerGameMode.useItemOn), and
updateBlockEntityComponents gives the predicted sign that text, so the
editor the server opens (SignBlock.setPlacedBy) shows the copied lines and
Done sends them back unchanged.
Horizon lost the text on the way: the server never wrote the two
components on the wire, the client read past them, and the client's
predicted sign never took the stack's components. The editor opened on an
empty sign, and Done wrote the empty lines back.
- item stacks write and read SignText.STREAM_CODEC for both components;
- the client applies the stack's components (not block_entity_data, which
updateCustomBlockEntityTag leaves to the server) to the block it predicts;
- lan_e2e gains a sign_copy check, for the host and a guest.
Fixes#8102
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Rain, thunder and lightning rules ask one canHaveWeather helper (sky light,
no ceiling, not the End) instead of comparing the dimension id. Adds a unit
test for the rule.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A pack that replaces core/rendertype_lines (an RGB outline animated from
GameTime, say) left the selection outline black: only core/text was run.
Its shaders now go through the same naga translation (pack_text's loader,
generalised to load_core) and draw both strokes of the outline, as
vanilla's LINES and secondaryBlockOutline both use rendertype_lines.
The glue feeds vanilla's lines vertex format from the outline's edge
instances: Position (the start for vertices 0 and 1, the end for 2 and
3), Color, Normal (the direction) and LineWidth, the side from
gl_VertexIndex % 2, as vs_line draws them. The blocks are vanilla's:
Globals (GameTime, ScreenSize), Projection (the lens with the bob and
TAA jitter, 0 to 1 depth like 26.3's), DynamicTransforms (the rotation
with the PERSPECTIVE layering scale) and Fog, so ProjMat * ModelViewMat
is vs_line's view_proj. A pack shader the backend rejects, or whose
blocks outgrow vanilla's, falls back to the built-in outline. Shader
packs that draw the outline still take it.
Fixes#8020.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without a RENDERTARGETS or DRAWBUFFERS directive Horizon bound buffers 0 to 7
to a program's outputs, so gl_FragData[n] or layout(location = n) wrote
colortex n. Iris 1.11.7 writes only colortex0 for gbuffers, begin, prepare,
deferred and composite programs, and shadowcolor0 and 1 for shadow and
shadowcomp programs, as probe packs of our own show. Non-shadow programs
now default to buffer 0; shadow programs keep their two buffers.
Fixes#8107
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`AvoidEntity::can_use` picked what to flee with a distance and height test,
where `AvoidEntityGoal` targets through `TargetingConditions.forCombat()`:
`canAttack` (no player is a target at all in Peaceful), the range scaled by
`getVisibilityPercent` with a floor of 2, and line of sight. Fish, ocelots,
foxes, rabbits, pandas and illusioners fled a player standing behind a wall or
on the other side of a hill, a sneaking or invisible one from the full 8 or 16
blocks, and still fled in Peaceful; creepers fled cats and skeletons fled
wolves through walls.
The player arm now runs `Mob::can_target_player` (the existing port of
`TargetingConditions.test`) after the goal's own predicate, and the species arm
needs `Mob::sees`. The target lookup moved into `AvoidEntity::avoid_target`, so
the conditions are testable without the random retreat position and the path.
Co-Authored-By: Hermes <[email protected]>
Template jigsaws cloned each connector's name, pool and target, plus
the block state's orientation property, every time a piece's
connectors were shuffled. Borrow them from the cached template and
registry instead. Palette selection, rotation, shuffle draws and the
priority sort are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Part of #8050. AbstractMinecart.read/addAdditionalSaveData: a saved custom
display block and offset now draw and survive a save; the offset is omitted
when it is the kind's default.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Raider.die made the world owner the hero of the village for any player's
kill, so on LAN the host got Hero of the Village for a guest's kills and
the guest never did, and on a dedicated server nobody did. The killer's
connection now resolves to its own UUID, as villager_murdered already does.
Same mix-up in two commands: /fetchprofile entity @s in a guest's command
reported the host's profile, and /execute on owner missed guest owners.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Synchronize creative flight through normal movement before End and Nether measurements, validate each dimension hop, and reject dead portal fixtures instead of reporting a false portal failure. Refs #7702.
Co-Authored-By: Codex <[email protected]>
A nautilus a guest tamed, a horse a guest tamed by riding it, and the
allies a guest's mace smash and sword sweep spare all went by the world
owner's UUID (level.player_uuid, or a placeholder on a dedicated server)
instead of the acting player's.
The nautilus, the smash and the sweep run in the guest's turn, so they
take the acting player's UUID, as wolves and cats already do. A horse is
tamed in the mob tick, so its tame now runs in its rider's turn, found
the way Buck finds it: the rider owns it, then gets the tame_animal
trigger (AbstractHorse.tameWithName(player)), not the host.
Fixes#8047.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The outer skin layer is drawn as one box inflated by 0.25 px, so a hat,
jacket or sleeve reads as a flat skin over the body. With
threeDSkinLayers (Options -> Skin Customization, off by default) every
texel the skin paints becomes a slab one texel deep, with its sides filled
in where the next texel is transparent: the layer gets a silhouette of its
own and the gaps between painted texels stay open.
The geometry is built once per skin (and slim/wide) from the 64x64 image
the game already decodes and cached with it; the model tree and UVs are
the ones the renderer already poses, so the CPU and GPU skin paths draw it
unchanged.
Co-Authored-By: Hermes <[email protected]>
According to vanilla Java Edition 26.3 (RecipeCraftingHolder.setRecipeUsed,
CraftingMenu.slotChangedCraftingGrid, and MinecraftServer.onGameRuleChanged):
- When the limited_crafting game rule is active, crafting recipes that are
not special and not contained in the player's recipe book are refused,
leaving the crafting result empty (None). Special recipes (Recipe::Special)
bypass the recipe book check.
- When limited_crafting changes, MinecraftServer sends ClientboundGameEventPacket
type 12 (LIMITED_CRAFTING) to all players, and ClientboundLoginPacket carries
the rule on join.
Co-Authored-By: Antigravity <[email protected]>
LivingEntity.canAttack and TargetingConditions.test refuse a player target
when the difficulty is PEACEFUL; spares_players now does too.
Fixes#8046
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A content block occluded as its template does, so a thin panel or a block
drawn by its block entity on a full-cube template hid the faces of the
blocks beside it, leaving a hole in the floor under it.
`properties.occludes: false` is vanilla's `noOcclusion()`: the block's
gameplay state can't occlude and isn't a solid render, and its render
definition can't occlude (the faces beside it are drawn). A solid render's
full light dampening becomes a non-solid block's, as vanilla's data has it
for leaves and spawners: 1, or 0 when skylight passes down.
Item 2 of #5550.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ChJKHFsqiw1XFe27mMdroL
A consumable's `apply_effects` read only each effect's id, duration and
amplifier, so a data pack's or content mod's food that asked for a quiet
effect (`"ambient": true`, `"show_particles": false`) gave a normal one.
Each `MobEffectInstance` now keeps both, with vanilla's codec defaults
(false and true), and the effect the player gets carries them.
Fixes#8080
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01ChJKHFsqiw1XFe27mMdroL
In vanilla Java Edition 26.3 (BottleItem.use), using a glass bottle
within reach of an active Ender Dragon breath cloud shrinks the cloud
radius by 0.5, plays item.bottle.fill_dragonbreath, emits FLUID_PICKUP,
and turns the bottle into dragon_breath. In MultiPlayerGameMode.useItem,
the client forwards the use item packet without requiring a local target.
Co-Authored-By: Antigravity <[email protected]>
generated.rs is no longer tracked, and xtask's generator moved to
horizon-mod-api-gen, so the dispatch tests' stand-in types get NativeConfig
and NativeConfigFile there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Entity.move ends by multiplying the horizontal velocity by
getBlockSpeedFactor. Only the player did it; zombies, husks, boats and
derailed minecarts skipped soul sand and honey. The step is now one
function, entity_move::scale_by_block_speed, called by the player's move
and by the mob, boat and derailed-cart moves; the block lookup for kinds
without a motion world is motion::block_speed_factor_at.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Read back exactly with a probe pack in Iris 1.11.7 and Horizon, same scenes:
- fogStart/fogEnd are vanilla's environmental fog (0 to 1024 on a clear day,
-160 to 768 in rain, -8 to 96 under water, 10 to 96 in the Nether), not the
render distance's (172.8 to 192); fogDensity is 0.
- Composite and final programs read fogMode 0, fogShape -1, alphaTestRef 0.1,
blockEntityId -1; gbuffers programs read fogMode 9729 (GL_LINEAR), fogShape
1 (cylinder) and centerDepthSmooth 0.
Fixes#8060
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A level's weather, natural spawning, map colouring, shader hasCeiling and
spawn search now read the type's has_ceiling, ambient_light and sky light
(Level.canHaveWeather, NaturalSpawner, MapItem.update, PlayerSpawnFinder),
carried in LevelShape from data packs, remote servers and LAN guests.
overworld_caves gets its ceiling. Fixes#7864.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
`blend.<program>` was read for gbuffers programs only, so a begin,
prepare, deferred or composite program with one wrote its source over the
buffer. Iris 1.11.7 blends them (probed with our own pack: 0.25 over 0.75
with SRC_ALPHA ONE_MINUS_SRC_ALPHA ONE ZERO reads 0.625) and ignores
`blend.<program>.<buffer>` for deferred and composite programs (probed;
begin, prepare, setup and final are assumed to match). These passes
already load the buffers they write, so what the buffer holds is the
destination.
Part of #6812
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`currentRenderedItemId` was a frame uniform reading 0 in every program, and
a hand program that read it without declaring it failed to translate, so
the hand fell back to gbuffers_textured_lit.
- `HzDraw`'s fourth int, a pad until now, is `currentRenderedItemId`: -1 for
terrain, water and the sky, 0 for entity runs (items drawn in the level
are not done).
- The hand draws one run per arm and held item, each with its own `HzDraw`
slot. A block item reads its block's block.properties id, or 0 when that
file lacks it, whatever item.properties says; any other item reads its
item.properties id, 65535 when the file lacks it, and 0 without the file.
- The hand's programs may read `currentRenderedItemId` undeclared; terrain's
and entities' may not.
This is what Iris 1.11.7 does, observed with probe packs of our own.
Part of #6812
Co-Authored-By: Claude Opus 5.5 <[email protected]>
GPU frames now leave out the near solid sections that cave culling's graph
does not reach, as CPU frames do: cull also asks reached, which answers as
Occlusion::contains from a copy of the graph the frame's walk reads, the
clamped box and its sky band included. Since the previous commit they drew
every near solid section in view: the same pixels, but the GPU also shaded
every section the graph hides.
The graph keeps a bit per reached cell beside its cells (set_reached writes
both), and the occlusion store a process-wide generation that moves
whenever an answer of contains may change: a search lands, a partial update
reaches more, or the graph goes (clear, a teleport's remove_columns, a lost
camera). The pass keeps a copy of the bits and sends it again through the
staging belt only when the generation moves.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The slow-tick log said a 39.5 s freeze of the official server (2026-11-03)
was all tick_lan_packets, but not whose packets. Time each guest's packets
in the hub and each hub event in the game while spans are on (always on a
dedicated server), keep the three slowest of at least 10 ms, and end the
slow-tick line with them ("; slowest: Alex's 40 packets 39000.0").
tick_lan_packets also gets spans for its poll, drain, events and the
mods' commands, grants and menus.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
1.21.9 and 1.21.10 (protocol 773) get their packet and registry tables;
generate_packet_id_adapter.py learns --rename for horse_screen_open,
which 1.21.11 renamed mount_screen_open. Its packets are otherwise
1.21.11's; entity data takes 1.21.11's types for the bee's and the
wolf's anger end (a LONG, an INT before) and an avatar's main arm (a
HUMANOID_ARM, a BYTE before), and the serializers lack 1.21.11's zombie
nautilus variant.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NbhTr6rUbVKx6iB2X73DNG
The pass now decides which near solid sections it draws instead of drawing
the ones the walk chose: cull keeps a record whose column is within the
render distance (streaming's in_view_distance) and whose box passes an
integer frustum test. The box is the section's bounds rounded out to whole
blocks; the test adds integer terms the CPU makes each frame from the
camera's planes, each rounded up, with a margin for the walk's own f32
rounding, so it keeps every box the walk keeps. A camera so far above or
below the level that the terms could overflow makes the frame a CPU frame.
The walk lists only what it still draws within the render distance: the
sections with translucent quads or without a record (Resident's marks, kept
through every insert, removal and remove_columns), and past it everything as
before. Freed records are written to the GPU again, as the pass now reads
every record.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
native_register_configs lists a mod's configs: a kind (Client, Common,
Server), a file in config/ (<mod id>-<kind>.toml unless named) and the
text written there when it is missing. Client configs load on the
window when the game starts, Common once per process, Server before
native_server_about_to_start for each world, from the world's
serverconfig/ copy when there is one. native_config_loading hands over
the text, which the mod parses. The files' modification times are read
once a second; changed text fires native_config_reloading. A server's
configs fire native_config_unloading when it stops. Fixes#7950.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Hand off private per-account sessions when Splitscreen is enabled, retain unavailable accounts in the picker, and connect guests with their own authentication, skins and secure-chat keys. Preserve per-launch account selection and validate the metadata UUID before accepting a session.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Codex GPT-6 <[email protected]>
Every tick tick_blocks gathered the blocks inside every mob's, item's
and other entity's box, sorted the whole list by position and called
entityInside at each one. With hundreds of mobs that is thousands of
positions sorted per tick, and the list cannot be kept ordered between
ticks: it is each entity's own sorted run, in entity order, and the
entities move.
entityInside only acts on pressure plates, detector rails, buttons and
tripwires; at any other block it just reads the state. The positions
are now split by what is there first, and only the sensing ones are
sorted and visited. A non-sensing position is skipped only while the
world's block revision (and chunk membership) is unchanged since it was
looked at, so it still holds the same block and the call would have
done nothing. If a call changes any block, every later position goes
in sorted order as before, so the calls and their order are the same
as sorting the whole list.
Entity.applyEffectsFromBlocks runs for every player every tick, and on
a server it was the biggest part of a guest's tick. Most of its cost
was bookkeeping around the blocks, not the blocks: both visited sets
(the sweep's and checkInsideBlocks') hashed positions with SipHash and
grew from empty on every sweep, and every block the path crossed, air
included, was kept in a list and read from the world a second time.
The sets now use the fast hash (they are only asked whether they hold
a position, never iterated) and the sweep's starts sized for a short
move. Each block is read once as the sweep visits it and air is passed
over there; nothing in the loop changes a block, so the states read
are the same, and the blocks reach the effects in the same order with
the same steps and iteration count as before.
Visited chunks lit their sea floor and water as open sky
(light_open_water), to match the seed's sea when it was fully lit. The
seed now shades water a level per block (shade_sky), so every place the
player had passed showed a bright, shallow-looking floor through the far
water, in a patchwork against the dark seed sea. The chunk's own light is
kept now, for saved chunks (decode_lod_surfaces) as for loaded ones.
Heightmap tiles (level 6 and up) took the span under the top one as the
ground; with shaded water that is another water span, so the far sea had
no bed and showed fog through it. The ground is the top solid span.
Columns were already encoded once per tick however many players take
them, and their new sections went to the warm-up threads, but each
column's body (scanning every section's light layers and copying them
and the section bytes into the packet) was still written one by one on
the tick thread, about a third of the hub's time with walking players.
A body is now split in two: what reads the level and the caches (the
section bytes, heightmaps, block entities and which light sections
exist) stays on the tick thread, column by column as before; the light
layers and the write, which read only the column's own immutable
sections, are shared out to the warm-up threads, the tick thread taking
bodies too. The bodies come back in the columns' order and the sending
loop is unchanged, so every connection gets the same bytes in the same
order. A batch of fewer than four columns is written inline.
A tile split by the last plan stays split until its distance is 5% past
the ring of full detail around the camera, or past the world-wide
threshold (3 tile widths). Those two had no hysteresis, unlike the 8 to
10 width rule, so a tile on the line changed level each time the camera
crossed it by a block.
A tile meshed next to a planned tile that was not drawn yet treated it
as empty air and drew its full side, so tall walls stood at the edge of
the filled area and vanished once the neighbour arrived.
mesh_input now marks an edge unknown when nothing is drawn across it but
the finest planned tile there is a leaf (Snapshot::planned_leaf), and the
mesher draws no side against an unknown edge. Past the planned range, or
past a split tile's out-of-range child, the edge is still empty. The
neighbour's arrival already remeshes the tile through the selection
change; a planned tile that leaves the range before it was drawn now
remeshes its neighbours in replan.
behind_server lowers helper threads only on Linux and Android; on macOS and Windows the far terrain threads (sampling, the far terrain thread and meshing) ran at normal priority and competed with the threads that mesh and light the chunks around the player, most visibly while a world opens. far_terrain_current keeps behind_server on Linux and lowers the thread one step elsewhere: utility quality of service on macOS (background would throttle it on battery) and below-normal priority on Windows. The far decoration threads already go to idle.
Preserve main's face-normal and sprite-rectangle caches while keeping GUI
vanilla atlas coordinates separate from CIT sprite-local glint UVs.
Reject malformed texture sections before stitching and read metadata from
the same prepared snapshot as rendered textures. Add rejection, control,
prepared-resource and real-oracle emitted-UV regression coverage.
Co-Authored-By: Codex (gpt-6.1-sol) <[email protected]>
Reconstruct the pinned item atlas layout from the active sprite sources so
standard GUI glint receives the same atlas UVs as vanilla. Use the device
limit and selected filtering when rebuilding the renderer; keep the HUD
texture coordinates and CIT glint coordinates separate.
Add independent vanilla UV and packing regressions, including animation,
generated sources, padding, invalid metadata and atlas-limit boundaries.
Co-Authored-By: Codex (gpt-6.1-sol) <[email protected]>
From the full feature (#3541): on a server P1 joined over the network (dedicated, LAN,
invite) the other players connect to the same address as their own profiles. Names in the
server's player list can't be picked; an online-mode server refuses offline profiles before
they try; a player turned away while coming in picks again, told why (an online-mode refusal
says each player needs its own account); P1 leaving the server takes everyone with it; the
server's resource packs are asked once, of P1, and the others follow its answer.
Live scenarios: server, packs, dedicated_profiles, integrated_profiles.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-01 14:28:21 -05:00
819 changed files with 169788 additions and 6398 deletions
@@ -5,7 +5,8 @@ everything is that **it plays exactly like vanilla**. This page is the short ver
welcome on [Discord](https://discord.gg/playhorizon).
This repository holds the game and the launcher, plus the tools, tests and docs that build, check and release them.
The website (playhorizon.dev) is built in a separate repository from this one's README, `docs/api/` and roadmap. Anything else (videos, Discord and other community material) lives elsewhere.
The website (playhorizon.dev) is built in a separate repository from this one's README, `docs/api/` and roadmap. Anything
else (videos, Discord and other community material) lives elsewhere.
**Where to start: [playhorizon.dev/hub](https://playhorizon.dev/hub).** It shows what is being done now and next
([ROADMAP.md](ROADMAP.md) in the repository), the work board, good first tasks, and the forms to report a bug or
@@ -14,23 +15,25 @@ queue (the hub, or `/suggest` on Discord) rather than to issues, which are for b
## Build it
You need Rust (stable), Python 3.10+, JDK 25, and on Linux the ALSA and udev headers (`libasound2-dev libudev-dev`).
Horizon never downloads the game: it prepares its resources from your own copy of Minecraft 26.3, as described in
[Build from source](README.md#build-from-source).
- **Prerequisites:** Rust (stable), Python 3.10+, JDK 25, and on Linux the ALSA and udev headers
(`libasound2-dev libudev-dev`).
- **Resources:** Horizon never downloads the game; it prepares its resources from your own copy of Minecraft 26.3, as
described in [Build from source](README.md#build-from-source).
- **Commands:**
```sh
cargo run --release -p horizon-prepare -- --prepare --server-jar ~/Downloads/server.jar
cargo run --release -p horizon-client
```
```sh
cargo run --release -p horizon-prepare -- --prepare --server-jar ~/Downloads/server.jar
cargo run --release -p horizon-client
```
The launcher is its own Cargo workspace in `launcher/` (`cd launcher && cargo run`; on NixOS, `nix-shell` first).
- **Launcher:** its own Cargo workspace in `launcher/` (`cd launcher && cargo run`; on NixOS, `nix-shell` first).
## Vanilla first
Wherever vanilla has a feature, vanilla is the specification, down to the order of random calls. "Parity" here
means Horizon gives the same results as the pinned 26.3 client and server for the same inputs, checked by a
**harness**: a `tools/test_*_parity.py` script that runs a small Java oracle against the real game and a Rust
probe against Horizon, and compares their outputs. There are about 290 of them.
Wherever vanilla has a feature, vanilla is the specification, down to the order of random calls. "Parity" here means
Horizon gives the same results as the pinned 26.3 client and server for the same inputs, checked by a **harness**: a
`tools/test_*_parity.py` script that runs a small Java oracle against the real game and a Rust probe against Horizon,
and compares their outputs. There are about 290 of them.
- **Read the vanilla code before writing Horizon's.** `tools/rea_vanilla.py` queries the pinned game bytecode:
@@ -122,13 +122,13 @@ It runs rustfmt, clippy, the touched crates' tests, the Python tests, the public
## Project status
**Player-visible gaps: 833 open** of the 833 the [port ledger](tools/port_ledger.py) found, reading every vanilla 26.3 method against Horizon's code: vanilla behaviour a player would notice that Horizon doesn't have yet. By area: Screens, HUD and client packets [197](https://github.com/horizon-rust/horizon/issues/3518); Entity and block rendering, models [129](https://github.com/horizon-rust/horizon/issues/3519); Animals [110](https://github.com/horizon-rust/horizon/issues/3512); Server and players on the server [106](https://github.com/horizon-rust/horizon/issues/3520); Blocks, redstone and fluids [86](https://github.com/horizon-rust/horizon/issues/3516); Entities, projectiles, vehicles and decorations [64](https://github.com/horizon-rust/horizon/issues/3514); Monsters and bosses [53](https://github.com/horizon-rust/horizon/issues/3513); Mob AI, villagers and raids [35](https://github.com/horizon-rust/horizon/issues/3521); World, world generation and saves [32](https://github.com/horizon-rust/horizon/issues/3517); Items, enchantments and inventories [21](https://github.com/horizon-rust/horizon/issues/3515). This number goes down as gaps close (`tools/parity_gaps.py`); the harness results below say that what is tested matches.
**Player-visible gaps: 0 open** of the 833 the [port ledger](tools/port_ledger.py) found, reading every vanilla 26.3 method against Horizon's code: vanilla behaviour a player would notice that Horizon doesn't have yet. By area: Animals [0](https://github.com/horizon-rust/horizon/issues/3512); Monsters and bosses [0](https://github.com/horizon-rust/horizon/issues/3513); Entities, projectiles, vehicles and decorations [0](https://github.com/horizon-rust/horizon/issues/3514); Items, enchantments and inventories [0](https://github.com/horizon-rust/horizon/issues/3515); Blocks, redstone and fluids [0](https://github.com/horizon-rust/horizon/issues/3516); World, world generation and saves [0](https://github.com/horizon-rust/horizon/issues/3517); Screens, HUD and client packets [0](https://github.com/horizon-rust/horizon/issues/3518); Entity and block rendering, models [0](https://github.com/horizon-rust/horizon/issues/3519); Server and players on the server [0](https://github.com/horizon-rust/horizon/issues/3520); Mob AI, villagers and raids [0](https://github.com/horizon-rust/horizon/issues/3521). This number goes down as gaps close (`tools/parity_gaps.py`); the harness results below say that what is tested matches.
The table is the project's own audit: every subsystem matches vanilla in every harness that compares it. 100% means 100% of what the harnesses compare, not that nothing else differs.
### Java parity: 100%
Scores: 0 (absent), 0.25 (started), 0.5 (playable, major features missing), 0.75 (mostly complete) or 1 (complete and verified by differential tests); the percentage is their average. Last audited 2026-10-13: all 281 harnesses pass on Linux and macOS. The [port ledger](tools/port_ledger.py) adds a method-by-method view of what is ported.
Scores: 0 (absent), 0.25 (started), 0.5 (playable, major features missing), 0.75 (mostly complete) or 1 (complete and verified by differential tests); the percentage is their average. Last audited 2026-10-13: the harnesses pass on Linux and macOS. The [port ledger](tools/port_ledger.py) adds a method-by-method view of what is ported.
Ordinary-scene captures and measurements for PR #7054. The scene contains 24 mobs, about 9,400–10,100 rain particles, and about 50–100 campfire smoke particles. Two opposite-order passes were run with 2 s warmup and 8 s measurement per condition. Host: Intel Core i7-3770S, Intel HD Graphics 4000, Vulkan/Mesa, 675×727, render distance 4, VSync off, 260 FPS cap.
These are means of the two samples. CPU is the harness's average CPU time per frame; p99 is frame time. The normal scene is not expected to show large gains because most particles are visible.
*The asynchronous samples predate the final switch to a dedicated particle worker. They are retained only as historical context and are not a measurement of the final worker implementation. No FPS improvement is claimed from these ordinary-scene runs. The Off path delegates directly to `render`; `disabled_culled_render_matches_the_regular_particle_path` checks that it produces the same vertices.
The FPS and CPU differences are within run-to-run variability for this GPU-limited scene. The first four conditions are the reviewer's requested Main-versus-branch default and existing synchronous-mode comparisons. Particle Sync/Async rows are branch-internal mode comparisons.
## Captures
The screenshots show the same ordinary test world and FPS overlay:
Loaded 100 of 819 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.