#10553 wrote stride % self.per_word == 0, which clippy 1.99 rejects
(manual_is_multiple_of) under -D warnings: main's checks and every PR's
verify fail to compile horizon-core. per_word is never 0, so the result
is the same.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Requeued at the author's main merge 552f2c1b4: same net patch as d8676fff0 (identical patch-id), whose parity failed only command_exec's 26 /say lines that #11687 has since fixed on main. The change: shader packs in the End draw the sky fan under the dragon fight's fog like the rest of the pack's sky.
🤖 Agent: a refused START_FALL_FLYING now flips the glide bit of sent_shared so a flags update goes out, as LivingEntity.stopFallFlying's set(true)+set(false) dirties the flag in vanilla (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: the Patch notes tab's three orphan strings are gone from the 86 lang files, so the launcher's i18n tests pass (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: /damage ids now come from the world's damage catalog and are replaced on each world open, so a pack's type doesn't leak into the next world (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: the single-player memory connection sends every ready view column in one batch before PlayerLoaded (about 0.5 s saved at render distance 12), then the 64-per-tick cap comes back; socket guests keep vanilla pacing. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: new client-only, read-only native hook chat_suggestion_applied fires after Tab or a click applies a suggestion, with the replaced range in UTF-16 units of the list's original input; fragment stamps cleanly on main. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: port-pack events answers VillagerTradesEvent/WandererTradesEvent with the trade_set and villager_trade registries 26.x moved trades to. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: Studio playback of .mcpr recordings through the normal client handlers (B1 slice of #9712, +2120), only active when a recording is opened; F1 now hides chat and tab list as vanilla Hud.extractRenderState does. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: new horizon-polytone crate reads Polytone targets and biome/dimension attribute layers from the public wiki only (Polytone is GPL, its source not read); no caller yet, first step of #11486. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: the hook runs after the vanilla shuffle so RNG draws are unchanged and a no-mod dispatch is an empty loop; fragment wind_burst passes tests.test_stamp_native_hooks and --check --only on main merged in (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: Studio rows and buttons now register as focus targets and the wheel moves one whole 40 px row per notch, which also stops part-scrolled rows drawing over the title and makes the last recording reachable. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: only block/ and item/ paths equal to the old id are renamed, so a mod's own pulley:block/chain and chain_command_block_* stay, and files are rewritten only when something changed (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: recipe suggestions reuse json_ids_of_every_namespace like /loot, and the dead ids_under goes (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: The unsupported placed features are now named in the log (capped at 20), which is what a pack author needs to fix them. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: under TAA the flame gate now uses a frustum widened by one rendered pixel per side (jitter is at most half a pixel: 2× margin), one extra frustum per frame, shader packs still bypass. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: with entity culling off the decision is Bypass before any bounds are read, so the posed-envelope walk over every model point of every copy is now skipped (TAA condition dropped), RNG order of cluster_poses pinned by a test. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: TCP keepalive (10 s + 3×3 s) now ends dead friends tunnels and relay bridges in ~19 s instead of holding them until the 120 s idle; the stack buffers replace one Vec per connection, not per read. (union of 74 heads on main: fmt, clippy -D warnings, tests green).
🤖 Agent: Chunk-section block writes now do one position lookup instead of two on the worldgen hot path; output unchanged, and CI parity on main+head (worldgen, structures, light and the save harnesses it selects) passes in full.
🤖 Agent: fixes main's red clippy since #11561 (1.95 lints on interpolation.rs: is_multiple_of on non-zero unsigned divisors, as_chunks_mut::<4> quads); workspace clippy clean on main + this.
is_multiple_of on a usize cell size, and as_chunks_mut::<4> for the four
columns' quads (each quad is now a [f32; 4]).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Native mods can draw terrain models with an explicit material (shade, UV shift); the shading follows Flywheel's (MIT, credited). Main merged in after #11117 landed; its test now builds faces with #11356's BakedFace::new (clippy -D warnings passes).
🤖 Agent: The brewing stand's fuel slot and quick-move both read the brewing_fuel component of the stack, as BrewingStandMenu.FuelSlot and quickMoveStack do.
🤖 Agent: older clients 26.1→1.20.5 behind accept_older_clients (off by default); a 26.3 client never reaches ClientSession or the components_before_1_21_6 rewrites; per-step unit tests + lan_bot joins one bot per release; doc of older_client fixed.
🤖 Agent: attributes only, no code removed; the 30 kept allow(dead_code) now carry a reason (19 native mod API). Windows/macOS-only code is the risk, CI's other platforms cover it.
🤖 Agent: socket2 0.6 keeps every call direct.rs makes (set_reuse_address, the unix-only set_reuse_port, set_only_v6, connect_timeout), so the friends socket code needs no change; lock loses 11 packages.
🤖 Agent: one workspace rust-version (1.95, which horizon-mods already needed); the second commit is clippy's let-chain/as_chunks/is_multiple_of rewrites, all on unsigned values with non-zero divisors, so no behaviour moves.
🤖 Agent: near sections no longer dither over far terrain (fade() is 0 or lod::HELD), and the replaced tile still stays behind for lod::FADE through previous, so no hole opens; the test bound lowered to 1,000 matches the half that now come in whole.
🤖 Agent: respawn_world_point and disconnect_crafting reuse generate/world only after a generate/finished marker, written after server.stop(); an old half-made world regenerates once.
🤖 Agent: LocatorBarClient.arc now flags a catch-up tick only before the jab (tick < stabTick); a catch-up right before it still changes the jab's motion, which the harness compares with vanilla, so lunge_motion keeps its signal.
🤖 Agent: drops the harness/probe/field counts from AGENTS.md and CONTRIBUTING.md; the commands and paths around them are unchanged, so nothing a reader runs moves.
🤖 Agent: horizon-ctl screenshot-sequence takes repeatable frames: each waits for the GPU (30 s at most) and for the pack's centre-depth copy, and F2 is refused while a sequence runs. Lands after #11238.
by_registry outlives a world, so a pack's type stayed accepted by /damage in
the next world opened. Same as sync_enchantment_registries.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The flame culling doc now says TAA uses the widened frustum; the run
notes and samples stay in the PR body (run logs are not docs).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
entity_cull_decision_in_bounds returns Bypass for EntityCullingMode::Off
before reading the bounds, so the TAA condition only kept an unused walk.
Drop the docs/performance run notes (run logs are not docs).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: mobs path-stop at shulkers and a held-still happy ghast; earlier parity failures were env + main's /say regression (#11687 fixed it); CI parity reruns with run-parity before landing.
🤖 Agent: raid wave positions (new and cached) now need an entity-ticking chunk in entity ticking range (ServerLevel.isPositionEntityTicking), not just a loaded one; raid, raid_village and raider_celebration harnesses passed.
🤖 Agent: the player save now writes and reads the recursive hidden_effect chain (Poison I under Poison II resumes after a reload); SavedEffect became a struct, guest data uses the same path; active_effects, effect_sync, player_effect_amplifier and status_effect harnesses passed.
🤖 Agent: HORIZON_STILL now settles a frozen world's rain, thunder and rain fog at the level instead of easing them; it only runs with that dev variable set.
🤖 Agent: The shader pack reset (both kinds) lands the in-flight frame before replacing targets, so the next frame draws as a fresh pack's first; dev tooling, no change without the reset command. #11239 follows it.
🤖 Agent: Shader pack shadow settings are now read from the stage that declares them (fragment const settings), with main's noiseTextureResolution kept on the formats stage; only shaders/ packs see a change.
🤖 Agent: Native mods can draw a still-fluid volume in a block; the fluid shading follows Ponder's FluidRenderHelper (MIT, credited in THIRD_PARTY_NOTICES) and nothing changes without a mod calling it.
🤖 Agent: /raid check now finds a raid still counting down (getRaidAt has no started filter), and /raid sound plays the horn only for "local" at volume 2 with the level random's seed; raid and raid_village harnesses passed.
The commands' minecraft:damage_type ids came from the vanilla export
alone, so /damage refused a type from a content mod or a world's data
pack with argument.resource.not_found. Vanilla's ResourceArgument looks
the id up in the server's registries, packs included.
build_registries now adds the damage catalog's types to the registry's
ids after the export is applied, keeping the export's tags.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: The viewer's transfer is a separate server-side path: it only moves ingredients the player already has, leaves grid and inventory untouched when one is missing or the inventory is full, and never teaches the recipe; the vanilla recipe book keeps its own path. Second of the Recipes series, after #10523.
🤖 Agent: The focused text field is exposed to native mods before the character is inserted, so a hook can veto it; the hook fragments stamp cleanly on main. Main merged in (its ChatScreen is_draft argument added to the test).
A memory connection that has not reported the view loaded now sends
every ready view column in one batch, and the recorded quota is raised
to cover that send so the following tick is capped again. A socket
guest, a player who has already loaded, and a respawn stay on the
64-column quota. Unlit columns stay withheld, the far ring still spends
only the leftover vanilla quota, and the gates that send nothing are
unchanged.
Fixes#6763
🤖 Agent: Transient block entity fields from the update tag reach the native API without being saved; only mods that ask for them see anything new. Main merged in (its new Joined field added to the test).
🤖 Agent: only the automatic count changes, and only where hw.perflevel0.physicalcpu answers (Apple silicon): M4 Pro 13 → 9 workers with no loading or pacing regression over 3 alternating rounds; off Apple the count is unchanged (tested on Linux), and a manual Worker threads setting still wins.
🤖 Agent: #10015's early return dropped /say from chat_capture, so command_exec lost 26 announcement lines; the broadcast now skips only the acting guest (lan_acting), whose copy stays in the capture in order. Console and command-block paths are unchanged. command_exec PASS fresh on the desktop; the control on main 004a39be0 FAILs 16320/16346.
🤖 Agent: a head-on stop on a straight rail no longer blows a TNT cart up (Entity.move zeroes the blocked axis and OldMinecartBehavior never re-applies it), and a glancing hit off its rails now does; tnt_saved_power, tnt_data_adoption and both vehicle harnesses passed.
🤖 Agent: The player-expect move now refuses a full server like a normal join would, and operators still bypass it as vanilla's PlayerList.canBypassPlayerLimit does.
🤖 Agent: 26.2 packet layouts are only used inside a session that negotiated V26_2, and accept_older_clients stays off by default, so a 26.3 client's path is unchanged; the lan_bot tests cover both.
🤖 Agent: The Recipes settings button opens from the inventory browser; the JEI-derived icon is MIT and credited in THIRD_PARTY_NOTICES. First of the Recipes series (#11442 → #11453 follow).
🤖 Agent: only reinforcement_room uses the oracle's light op, so the wait (ThreadedLevelLightEngine.waitForPendingTasks, ticking like settle in a frozen world) changes no other harness's records; command_reinforcements --fresh --repeat 3 PASS 3/3 at desktop load 40-48, where main failed 7 of 10.
🤖 Agent: code-owner approval given; pruning only touches marker-carrying Nightlies, manual/legacy ones are outside the quota (tests cover all four cases).
🤖 Agent: vanilla's CheckerboardColumnBiomeSource sets bitShift = size + 2 with no clamp, and Java's >> masks the distance; wrapping_add(2) as u32 with wrapping_shr does the same. biome_source PASS with fresh Java on the desktop (main + this head).
🤖 Agent: a map crafted from the grid gets Item.onCraftedBy on the taken stack and the scaling recipe reads the level's maps; merged with main's merchant on_take; craft_map_take and merchant_crafted pass.
🤖 Agent: dropping the --reference-only assertion makes a plain run go through runner.main()'s full Java/native comparison, and then the strict tags/pot report, so CI now compares instead of exiting in 0 s. mob_discard's default --resources is lab.RESOURCES (the prepared 26.3 folder) and it gets the missing import os. The two sound harnesses take main's #11555 version.
🤖 Agent: data get on a leashed boat now reads its leash from the same save path the world writes (write_boat_leash); merged main twice, boat_leash_view harness passes and the u/3 union builds with clippy -D warnings.
🤖 Agent: breeding now adds one fresh orb of the whole 1-7 roll (no award split, no merge), and XP bottle orbs start 0.25 along the hit face's normal with their motion turned away from it; orb_lava/orb_water/orb_saved_uuid/bed_explosion harnesses passed.
🤖 Agent: OptiFine custom skies draw their layers over the vanilla sky, only when a pack ships optifine/sky and the Horizon sky option is on (a frame without one only clears an empty list); merged main, clippy and the sky tests pass.
The automatic pool size was every core but one. On Apple silicon the
efficiency cores count too, so a base M4 (4 performance + 6 efficiency
cores) ran 9 workers that kept the render and server threads waiting for a
performance core: fast flight's p99 frame went from 20 ms to 7 ms with 3
workers (#11294). The automatic count now leaves one of the performance
cores (hw.perflevel0.physicalcpu) instead; elsewhere it is unchanged, and
Graphics > Worker threads still overrides it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: a negative Age makes a baby only where AgeableMob.canBeABaby allows (frog, parrot, zombie nautilus, camel husk, slime, magma cube stay adults: box, voice, loot); ageable_integer_load, entity_death_loot and baby_pig_attachment pass.
🤖 Agent: Realms reset-world, templates and upload, stacked on #9982 (land after it); upload goes only to the endpoint the service returns; realms tests pass.
🤖 Agent: Realms backup list and backup info screens on main's Realms code (only the backups commit kept; the carried settings/invite copies already landed); restore asks the service and re-reads the list; realms tests pass.
🤖 Agent: advancements= in a selector reads the selected player's own progress, a guest's included (EntitySelectorOptions on the ServerPlayer); guest_advancement_selector passes on the merge, now runnable by parity.py.
The two reset-world commits rebuilt on the backups branch (both PRs extend the
same configure screen): the scroll wheel covers the product page, template and
upload lists, and the worlds tab has Options and Backup side by side with
Reset below.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This branch also carried early copies of the settings-tab and invite-code
commits, which landed on main as #9926 and #9968 and changed since. The tree
is main with only the backups commit on it, plus the configure screen's
tier fields that main added.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#10015 broadcast /say from cmd_say to every guest and dropped the line
from chat_capture. The acting guest still saw it, but the command
harness reads the capture, so command_exec lost every /say line
("[Player] relative dimension", the h154 function's "[Pig] H154
fallthrough") and failed parity-main at ebcfa2263a.
When a guest's command is captured, the broadcast now skips that guest
and the line stays in the capture, in order with the other output, which
is delivered to them as before. The console path is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: roadmap.toml is the source; ROADMAP.md and roadmap.json regenerated (build --check passes). older-servers kept on purpose (1.8 PvP servers), only the sign-in item goes.
Mojang's approval covers Microsoft sign-in only; other account providers
(Ely.by, Yggdrasil servers) are against the rules Horizon plays by, so the
item can't ship. #1270 stays closed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ThreadedLevelLightEngine runs the checkBlock tasks a /fill queues at most
1000 per update. command_reinforcements fills ~100k blocks around its room
and then requires block and sky light 0 there; on a loaded runner the
vanilla side still read the old light (7, 9, 10 at different positions),
which failed parity-main and 7 of its bisect steps back to 11-05 merges.
The light op now waits for waitForPendingTasks on that chunk, ticking as
settle does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: follow-up to #10129: with glass off, macOS still gets its 32 px title bar (plain side colour), so content no longer sits under the traffic lights.
🤖 Agent: follow-up to #10177: the unmaximize-before-fullscreen step runs on Windows only, and apply() no longer re-requests fullscreen each call while the window reports maximized.
parity.py runs a harness with no arguments; --side was required, so every
automatic selection failed it at once. The default now runs Java, then native
against Java's facts.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Raid.findRandomSpawnPos and the cached wave position both ask
ServerLevel.isPositionEntityTicking: an entity-ticking chunk in entity
ticking range. Horizon accepted any loaded chunk, so a wave could spawn
past the simulation distance, where its raiders would never tick.
Part of #11374 (the spawn positions; the raid/spawner order stays open).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: a copper chest someone has open now skips its weathering step (WeatheringCopperChestBlock.randomTick checks getEntitiesWithContainerOpen); random_tick and random_tick_sections harnesses passed.
🤖 Agent: using items (fishing rod, ender eye…) now posts vanilla's item-use game events through causeUseVibration, so sculk sensors react as in Java; sculk, server_player_sculk, server_wool_vibration and use_item_on pass against the 26.3 oracle in the gate's harness run (242 selected; failures were host setup only plus the lunge_motion timing flake).
🤖 Agent: crafting with a merchant result slot now fires vanilla's merchant crafted trigger and updates the trade, checked by the new --merchant-crafted-fixture in command_exec; merchant_crafted, command_exec and trading pass against the 26.3 oracle in the gate's harness run (242 selected; failures were host setup only plus the lunge_motion timing flake).
🤖 Agent: item entities, armor stands and boats now save the shared Entity flags (Invulnerable, CustomNameVisible, Silent, NoGravity, Glowing, HasVisualFire, Tags) as vanilla's saveWithoutId does. Its 242 selected harnesses ran on main + this head + #11017 + #11022: save_roundtrip, world_reopen, entity_nbt_boolean, the armor stand and boat ones pass; the only failures are setup (no sound/lang assets on the host, harness args) and the known lunge_motion tick-timing flake under software rendering.
MinecartTNT.tick reads the delta after AbstractMinecart.tick. The move
zeroes the blocked axes and no track step puts them back, so a cart
stopped head-on by a wall on a straight rail does not blow up, while one
glancing off a wall keeps its free axis and does, on or off its rails.
Horizon kept the speed the cart asked for before the move on the rail
paths and none off them.
Part of #11380 (the impact detonation; the team push points stay open).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: overflow honey now leaves through Player.drop (eye height − 0.3, thrown along the view, pickup delay 40) instead of popping at the feet; held_drop harness passed, plus Game tests for Peaceful anger and event 2011.
MobEffectInstance.Details nests the instance an effect hides under
hidden_effect, and the player save writes the whole chain. Horizon wrote
only the top instance, so a weaker, longer effect (Poison I under
Poison II) was gone after a reload instead of resuming. SavedEffect is
now a struct carrying that chain; guest player data goes through the
same code.
Part of #11384 (the player side; mobs still drop weaker instances).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Let native mods keep shared-item text aligned when a player accepts or
cycles chat suggestions. Report the original input, UTF-16 replacement
range, selected suggestion and resulting text. Cover keyboard and mouse
handling, server suggestions, emoji and length limits.
Part of #11212.
Co-Authored-By: Codex (GPT-6) <[email protected]>
🤖 Agent: the device list is now read on a worker thread, so a slow Windows audio driver no longer stalls a frame; the render thread only swaps in the result. No vanilla behaviour changes, no harness covers it.
RaidCommand.getRaid is getRaidAt, which returns a raid that has not
started its first wave yet. playSound plays the horn only for the text
"local", at volume 2 with the level random's seed. The status line
concatenates its floats the Java way (20.0, not 20).
Fixes#11372.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: effects now carry vanilla's showIcon apart from the particles (5-arg constructor, update, save codec default, packet bit 4), so warden Darkness and /effect hideParticles no longer show a HUD icon; 8 effect/HUD/sculk harnesses pass.
- Animal.finalizeSpawnChildFromBreeding adds one new ExperienceOrb of the
parent's 1-7 roll: breeding no longer splits it (4 into 3 + 1) or merges.
- ThrownExperienceBottle.onHit awards with direction: away from the block
face it hit, or back along its motion after an entity hit; the new orb
starts 0.25 that way and a random motion against it is turned round
(ExperienceOrb(level, pos, roughly, value)) (#11388).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: lightning now places fire (and draws its extra-source offsets) only when canSpreadFireAround passes at the bolt, so fire_spread_radius_around_player holds for strikes far from every player.
WeatheringCopperChestBlock.randomTick runs changeOverTime only when the
chest's getEntitiesWithContainerOpen is empty, so an open chest neither
oxidizes nor draws its odds (part of #11378).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`horizon-port-pack events` gives a fifth answer, data: NeoForge's
`VillagerTradesEvent` and `WandererTradesEvent` are gone since vanilla 26.1
made trades the `trade_set` and `villager_trade` registries (NeoForge's 26.1
primer), so a port writes trades as data instead of waiting for a hook.
On ATM10 that answers 33 uses (20 and 13 mods).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the Mods page's install and port messages go through tr/trf with named arguments, so they translate; lang files merged key by key onto main, i18n tests pass.
🤖 Agent: a mob's Absorption now refills on every accepted add (AbsorptionMobEffect.onEffectStarted), and heal/harm and effect intervals shift like Java's int << / >> (distance mod 32, wrapping) instead of clamping or panicking at amplifier 32+.
Guest break refusal is the sequence ack every PlayerAction arm sends, the packet rate limit is PacketRate::received,
and MinecraftServer.shouldRun has no haveTime budget to defer against in Horizon's loop.
Send pause-menu and death-screen exits through close_studio_replay when playback is active. Clear the replay timer and restore the prior camera so the title screen and the next world use normal player and hand rendering.
Co-Authored-By: Codex <[email protected]>
Open and validate a replay archive before replacing the current screen. Track standalone replay sessions so closing one disconnects its synthetic remote and restores the title screen. Start playback in first person after applying timestamp-zero world packets.
Co-Authored-By: Codex <[email protected]>
Inflate recording streams to a bounded temporary file, verify the stream CRC during the copy, and index a sparse set of seek anchors instead of retaining every packet in memory. Preserve whole-frame recovery and timestamp-group seeking.
Co-Authored-By: Codex <[email protected]>
- Dump resident chunk columns, player pose, center chunk, and dimension login into .mcpr stream at timestamp 0ms on recording start
- Allow all chunks in remote_chunk_in_range during studio replay and freecam so distant chunks are never discarded
- Reset title mode to Mode::Main when closing studio replay so the user returns to the main menu instead of multiplayer
- Add interactive glass button for '▶ Edit' on recording cards in StudioUi
- Allow single-clicking '▶ Edit' on any card to immediately load and open the editor
- Allow clicking the bottom 'Edit Replay' button to open the selected or first replay
- Handle Event::OpenStudioReplay in Loading::menu_option_events by queueing PendingWorldEntry::StudioReplay
- Handle PendingWorldEntry::StudioReplay in Game::poll_pending_world_entry to enter replay
- Add Session::replay and init_remote_for_replay to initialize client state when entering replay from title screen
- Dismiss title screen and uncapture cursor so timeline and studio HUD controls are interactive
- Release mouse cursor on Escape key in studio replay mode before opening pause menu
- Implement standard .mcpr container reader/writer (metaData.json + VarInt timestamped recording.tmcpr stream)
- Add asynchronous StudioRecorder worker to record clientbound packets with zero impact on gameplay FPS
- Add ReplayDriver with timeline seeking, variable speed control, pausing, and looping
- Wire zero-alloc packet recording tap into Session for singleplayer, LAN, and remote multiplayer
- Add in-game /studio chat commands and control socket commands for recording and replay management
- NeutralMob.isValidPlayerTarget also fails on Peaceful: a bee's anger at
the player ends there.
- BeeGrowCropGoal sends level event 2011 (data 15) before the crop grows;
the client draws it like 2012 (spawnParticleInBlock, happy villager).
- A honey bottle that no longer fits is player.drop(stack, false): from
below the eyes, thrown along the view, no thrower (#11377).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: an empty frame now refuses a map tracking more than 256 counted markers (ItemFrame.interact's isTrackedCountOverLimit FAIL): the map stays held, no swing; exactly 256 still goes in.
🤖 Agent: /say from a command block reaches every player under the block's name, and a captured guest command no longer echoes it back as feedback (kacpherek's point); lan_e2e command-block-chat not run here.
LightningBolt.spawnFire places fire, and draws its extra sources, only when
ServerLevel.canSpreadFireAround passes at the bolt: the
fire_spread_radius_around_player rule now holds for lightning (#11376).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the omen sound now plays from the player's addEffect for any newly added omen (command, potion, mod) at the feet, as MobEffect.onEffectAdded does; the three special paths moved from the eye to the feet too.
🤖 Agent: TNT, falling blocks and XP orbs now collide with boats, shulkers and a held happy ghast (Entity.collide's canBeCollidedWith); sand landing on a boat over air breaks into an item like vanilla's wouldContinueFalling.
🤖 Agent: 0.2 shader packs get the float HDR frame only under HORIZON_HDR_FRAME=float; the default and 0.1 paths keep their formats (frame_format tests cover both), so the vanilla look is unchanged.
MobEffectInstance.showIcon was not modelled: every effect showed its HUD
icon and every UpdateMobEffect carried the icon flag. Warden Darkness
(no particles, no icon) showed an icon. The flag now follows vanilla's
constructors, update, saves (show_icon defaults to show_particles),
consume effects and the packet flags (#11385).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: a loaded furnace keeps its saved cooking total until the item finishes, and a hopper or dropper filling the empty input slot restarts the cooking as setItem does (kacpherek's point); furnace_loaded_total and furnace_output_components pass.
🤖 Agent: a restored chat draft draws grey italic until edited, as ChatScreen's isDraft does; text a mod opens chat with is not a draft. Merge with main builds and the chat tests pass.
ItemFrame.interact returns FAIL when MapItemSavedData.isTrackedCountOverLimit(256):
the map stays in the hand and the frame stays empty (part of #11383).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Frost Walker's speed modifier now follows a boots change made by a guest too (the guest's armor is swapped in before the comparison); armor_changes copies only changed slots. armor_stand_frost passes on a union with main.
respawn_world_point and disconnect_crafting reused generate/world whenever the
folder existed, so one crash left them failing until someone deleted it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Since #11223 the server takes what follows a jab on the next tick, so the
harness's time query comes back a tick later and every jump failed as a
catch-up. The motions are identical to vanilla's and stay compared.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: a spectator using a portal block travels to its destination, never builds a Nether exit, and gets spectator.cannot_teleport without one (ServerPlayerGameMode.useItemOn); nether_portal harness passes on a union with main.
🤖 Agent: damage a mob's absorption takes is credited to the thrower (guest throws settle inside as_guest, kacpherek's point); mace and trident harnesses pass on a union with main.
tools/parity.py runs a harness with no arguments, and these three required
--binary, --work-dir or --resources (mob_discard also used os without importing it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: region saves reuse one deflater per writer instead of building one per chunk; the test checks the compressed bytes are identical to a fresh deflater's. Union check (main + the gate's 9 heads): fmt, clippy -D warnings, 5666 tests green; verify green after the runner's full-disk rerun.
🤖 Agent: the second rider's PlayerInput/MoveVehicle is now ignored like vanilla's getControllingPassenger check; riders are listed in boarding order so the first one in keeps the tiller.
🤖 Agent: only SUCCESS_SERVER block uses (beds, light, a charged anchor, signs) swing back to their own player; item and entity uses still echo, as the body says. use_item_on, flat_place and dedicated_server pass locally.
🤖 Agent: each moving block's collision view now advances right after its own push, so later pushes in the same tick see it at its new progress; the cost is one lookup per moving piston. piston_push and piston_movement pass locally.
LivingEntity.addEffect runs onEffectStarted for every effect it accepts,
so Absorption given again refills a mob's shield to 4 per level of the
effect given. Horizon refilled it only for a new or stronger effect.
HealOrHarmMobEffect and the periodic effects use Java's shifts, which take
the distance modulo 32 and wrap: amplifier 32 is amplifier 0 again, and a
ticked heal is at least 0. Horizon clamped the distance to 30, and an
amplifier of 32 or more panicked on Regeneration's interval in a debug
build (#11386).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: horn and brush uses end with stopUsingItem (the interaction vibration), and a mob in front of the brushed block stops the brush out to the block reach; item_use_catalog harness passes on a union with main.
MobEffect.onEffectAdded plays Bad Omen's, Trial Omen's and Raid Omen's
sound (withSoundOnAdded) whenever LivingEntity.addEffect adds one that
is new, at the entity's feet. Horizon played them only on the raid, bottle
and trial spawner paths, at the eye, so /effect give, a potion's custom
contents or a mod adding one was silent (#11373). The generic add now
plays it for players and, from /effect, for mobs; the three special paths
play at the feet.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the item name and the action bar take fixed heights above the hotbar (Hud.extractSelectedItemName 59, extractOverlayMessage 68) instead of following the bars; hud harness passes on a union with main.
🤖 Agent: a breeze turns a thrown potion back at half speed (Breeze.deflection) once in a row, stopping at the hit point that tick; the REVERSE draw uses the potion's own random.
🤖 Agent: advancements with no criteria get their rewards on load (checkForAutomaticTriggers) but stay not done, as AdvancementRequirements.test answers false; checked on a union with main.
🤖 Agent: the fade out ending at startFadeIn now runs to the end of the day (since()==0 read as DAY), which was kacpherek's review point; checked on a union with main (fmt, clippy -D warnings, tests).
🤖 Agent: the solid terrain pipelines are built with HDR_LIGHTING off and the lit copy only exists when hdr_output is Some, so HDR players switch per frame by self.hdr; frame-identity label makes CI check HDR off, offscreen HDR and HDR toggled off in a running game. −16 to −22% GPU on M4 Pro, image identical in-process.
🤖 Agent: merging. Empty region files (13 303 of 13 620 in the test world) are no longer rewritten and synced on world open, and region files convert on all cores but one with byte-identical output; datafix harness 6/6.
terrain.wgsl's solid shaders carry HDR output's lighting behind a runtime
`hdr_light.z` test. The branch is never taken without HDR, yet on Apple GPUs its
code alone costs ~15% of the solid pass (register pressure). A pipeline
constant `HDR_LIGHTING` now guards it: the solid pipelines (per-section, pulled
and flat) are built without it, and a copy with it only where the display
offers HDR output, picked per frame by whether HDR is on.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the session state a client HUD reads (world id, screen kind) comes from fields the window already keeps; merged with #10195 and checked with clippy, the native_client tests and the hook stamp test.
🤖 Agent: client_hurt reports the packet's causing entity (the shooter, not the arrow) and treats an untracked entity as not a player, so a mod never gets a false PvP signal. Main merged in (test conflict with #11512's open-chat test, both kept); verify green on this head.
Audio::should_change_device listed the output devices on the render
thread once a second. Windows has no cheap fingerprint to skip the list
(Linux reads /proc/asound/cards, macOS names the devices), so every
second the render thread waited ~90-120 ms on WASAPI's endpoints, even in
the main menu (#11490). The look now runs on a short-lived thread every 20
ticks; a later tick applies what it found with the same rules.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PrimedTnt, FallingBlockEntity and ExperienceOrb move with Entity.move,
whose Entity.collide includes EntityGetter.getEntityCollisions: a boat,
a living shulker and a happy ghast held still stop them, as they stop a
dropped item. Horizon resolved their moves against blocks and the world
border only (#11518). The ordered tick gathers those boxes once per run
(again after an explosion) and passes the nearby ones to each move.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A boat's passengers went to the clients in the order the players joined
the server, so the player who got in second could hear it sits first and
take the boat over, and the server took MoveVehicle from either seat: with
two players in one boat it kept snapping back (#11541). The passengers now
go in the order they boarded, and handleMoveVehicle ignores a player who
isn't the vehicle's controlling passenger, as vanilla does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: native_set_chat_input filters text to what typing allows (no control chars, DEL or §) and clamps the cursor, so a mod can't inject formatting codes into the chat box.
🤖 Agent: Launcher-only follow-up to #10529: the Home tab strip is flex_none on wide windows again (glass::tabbed keeps its notch at 1440 px) and only scrolls when compact; settings rows keep 40 px between name and control and wrap the control only when narrow. No new strings; fmt/clippy/tests green on the union of this wave's heads.
🤖 Agent: follow-ups to #11033/#11044/#11043/#11016 that missed the merges: death protection is one prototype lookup per hand per tick (was a full component map for every armed mob), checked against the component map for patched and stripped stacks. Union check: fmt, clippy -D warnings, 5666 tests green.
🤖 Agent: the interpolation change is checked bit for bit against the old formula over the test's whole range, so no position moves; one multiply fewer per entity per frame. Union check (main + the gate's 9 heads): fmt, clippy -D warnings, 5666 tests green.
🤖 Agent: inflates each region chunk in place instead of through a second buffer; the differential test decodes the same chunks with the old block decoder and compares every section. Union check (main + the gate's 9 heads): fmt, clippy -D warnings, 5666 tests green.
🤖 Agent: Optimize World now upgrades region files in parallel with one sync per file instead of one per chunk (F_FULLFSYNC on macOS was the cost); the 1-vs-4-thread test checks the counts against the fixture's own layout, and a canceled recreate leaves every file untouched. Union check: fmt, clippy -D warnings, 5666 tests green.
🤖 Agent: the launcher keeps each native shader's identity and options across the settings screen rewrite; its parent #10168 is merged, and the union with main passes clippy and tests.
🤖 Agent: client mods read entities and textures through the Host only, no new write path; #10196 conflicts with it in native_client_services.rs and gets main merged after this lands.
🤖 Agent: the invite-code calls go through one frontend() helper with x-mojangauth, only to the Realms frontend URL; builds and tests clean on a union with current main.
PistonMovingBlockEntity.tick pushes entities from each block entity's own
tick, and getCollisionShape reads its progress: the moving blocks that
ticked before it have advanced, the ones after it have not. Horizon
pushed every entity against last tick's progress of all of them, so a
TNT duper's TNT was pushed through a block that was about to move
(#11399). Each piston's collision view now advances right after its own
push.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: native_take_screenshot refuses while another screenshot waits for the frame, so a mod's call can't overwrite F2's or the world icon's pending grab.
🤖 Agent: parity.py starts harnesses with no arguments; these two required --binary/--work-dir and failed parity-main in 0 s. On the desktop both now reach their servers (the Java sides ran); the full runs there timed out only because the desktop's swap was full.
Removing each one and asking the compiler (the workspace's targets and the
server build): most guarded nothing, since `pub` methods of `pub` types already
count as used. The ones native mods need stay, now saying so, as do the fixtures
that share another fixture's source and code whose caller has not landed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: removes only the minecart boxes from the item's collision list; boats, shulkers and a held happy ghast still hold items, as AbstractMinecart doesn't override canBeCollidedWith. entity_collision, held_drop, shulker_space, boat_item_hit pass locally.
🤖 Agent: merged after #11019 and main (hook and doc blocks kept whole, quinn kept beside the new optional crates). Without a mod, PlayerSender::send pays one relaxed WATCHED load and sends the bytes as they were, and worldgen, LOD columns and redstone keep vanilla's values.
arboard 3.7 moves to windows-sys 0.61 (removing 0.60, windows-targets 0.53 and
its eight platform crates). socket2 0.6 and if-addrs 0.15 in horizon-friends
remove socket2 0.5; the launcher's lock also loses windows-sys 0.59.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the check, not the 1.8 rules, was broken: the switch row sat half under the list's edge so the click missed it; verified 7/7 legacy, 9/3.6 modern on the Mac.
🤖 Agent: the fight bots' swing carried a hand byte 26.3's swing packet doesn't have, so every server kicked them on the first hit; it now sends the same Punch as --attack.
🤖 Agent: twelve hook groups land together. Without a mod, the move, teleport and trample hooks are fn pointers over an empty mods() slice, so a player's move costs one indirect call and no allocation; a mod command's permission node falls back to its own level when no provider answers.
Id tables from the vanilla data reports (1.20.6's packet list rebuilt from
the archived wiki page, which names no resources, via 1.21.1's report), with
the renames game_profile, set_carried_item and horse_screen_open. Packet
layouts from minecraft.wiki's archived pages for protocols 766 to 769 and the
release notes: 1.21.5's chunk format and per-entity data, hashed container
clicks, 1.21.2's window ids, teleports, movement flags, input and sea level,
1.21's particles, explosion sound and trades, 1.20.5's strict error handling.
1.21.5 item components (attribute modifier display, equippable shearing)
behind a quirk. A 1.21.1 client gets no recipe book.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: only the slider's step table changes; saved values between steps snap to the nearest step (test covers 3000 → 13/23), so existing options.txt files still load.
🤖 Agent: quinn is optional behind the quic feature with default features off (runtime-tokio + rustls-ring only), so no extra TLS backend lands in Cargo.lock beyond the 7 packages.
Clippy follows rust-version, so crates that move from 1.85 now get its
collapsible let chains, as_chunks and is_multiple_of suggestions. Almost all
from `cargo clippy --fix`, none changes what the code does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The check failed on main with 1.8 rules that work: the switch row now sits
under the PvP page's HUD rows, the scroll stopped as soon as the row showed
at the list's lower edge, and the click at its centre landed outside the
list, so the rules never turned on (legacy hit 9, not 7). It now scrolls
until the row stops moving and asserts the click flipped it.
The swap and back went out in one tick: since the client sends a slot
change from its next tick, as vanilla does (#10084), that sends nothing,
and the modern hit kept its full charge. The stick is now held 3 ticks.
Mac mini: legacy (7, 7), modern (9, 3.6).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#11226 clamped scale to 0 before the + 2. Vanilla has no clamp and Java masks the shift
distance, so tools/test_biome_source_parity.py failed at scale -1 and below.
Fixes#11519
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: .github only; the by-hand ARM64 job's launcher --version step now runs from bash as launcher.yml does, and passes on the ARM64 runner (run 42320852405), where the pwsh version failed on any code.
Opening a world saved by an older version runs the converter in
datafix/files.rs (upgrade_world), which rewrote every region file on one
thread. Now:
- upgrade_regions spreads a folder's region files over worker_threads()
workers (all cores but one). Each file is still read, fixed and written
whole by one worker through the same staging folder, so the bytes do not
depend on the thread count. A cancel or an error stops every worker
before its next file; the error returned is the first file's in order.
- A region file without chunks is left as it is instead of being rewritten
as an empty 8 KiB file and synced. The game leaves many of them: 13 303
of the 13 620 overworld files of a 1.21.1 ATM10 test world.
Tests: 4 workers give byte-identical files to 1 and every chunk at the
current version, an empty file stays empty, and a cancel from the first
chunk converts at most one file per worker and leaves the rest old.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both asserted --reference-only when no --reference-dir was given, so tools/parity.py,
which passes no arguments, failed them before they ran anything.
Fixes#11520
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Members declared 1.85 to 1.95, or nothing. horizon-mods needs 1.95 and sits
under horizon-client-lib, cargo-horizon-mod and the launcher, so 1.95 is the
version the game really needs; the lower numbers promised builds that could
not happen. Set it once in [workspace.package] and inherit it everywhere.
The separately built workspaces (launcher, telemetry server, mod registry,
third_party) cannot inherit it and keep their own.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: one line in docs/hosting.md (Intel Macs have had server builds since the 10-31 Nightly; install.sh already maps Darwin-x86_64); the README half is already on main via bf4657da26.
🤖 Agent: Auto's backend order now reads the machine's arch (IsWow64Process2), so the x64 build on a Snapdragon PC starts on DX12 like the ARM64 one; the ARM64 runner built and started the game on this head. Unverified on real Adreno hardware (asked #7513's reporter).
🤖 Agent: merged main in again (only the test module list in pack_post.rs collided, with #11196's sky_disc_blend_tests). The last parity failure on 4fa3bf699 was command_exec's '/advancement grant @s everything' count (116 vs 115), which fails identically without this PR; this head reruns parity. sizes_differ is a few array reads per pass per frame.
The fight behaviour's swing went out as a raw Punch with a hand byte
26.3's ServerboundSwingPacket doesn't have: every server kicked the bots
on their first hit ("packet 46 was larger than expected: 1 extra bytes").
It now sends the same Punch as --attack.
--crits makes fight bots swing at a sword's full strength (13 ticks) and
jump into every other swing, landing it on the way down: a critical hit,
then a sweep from the ground. tools/perf PvP scenes get crit and sweep
particles from real hits.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
tools/parity.py starts every harness with no arguments, and both scripts
required --binary and --work-dir, so parity-main failed them in 0 s
(argparse usage error) since #10524 and #10266. Like
hostile_sound_categories, they now build the game through net.sides and
pick a work folder under .local/ when none is given.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The harness, probe, Game field, impl file and module counts had drifted
(324 harnesses against 502 on disk). Nothing regenerates prose, so name the
things and let the tree give the numbers. CONTRIBUTING.md's "about 290"
harnesses goes with them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Follow-up to #10529 (atsauban's responsive wrapping). The Home tab strip
took the full width at every size, so glass::tabbed drew one plate and
lost its notch even at 1440 px: it is full width and scrolls only when
compact. setting_row went from 40 px to 16 px with justify_between for
every row; it keeps 40 px between name and control and wraps the
control under the name (12 px) when the window is too narrow. The
breakpoints are named constants beside WIDE.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#10129 made the native title bar transparent on macOS but drew the bar only with
glass on, so with glass off the page started under the traffic lights. The bar
is always there: glass, or the side colour as on Windows and Linux.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#10177 unmaximized before every fullscreen request on every system, and apply()
asked for fullscreen again on each call while the window still reported
maximized. enter_fullscreen keeps the #9984 fix where it applies (winit keeps a
maximized window inside the work area on Windows); the resumed() checks share
one helper.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#10166 landed with its messages as raw strings. Every message a player reads
goes through tr/trf with named placeholders, and every language file gets the
English stand-ins.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Death protection: one prototype lookup per hand instead of a component
map rebuilt from the stack's patch. update_totem_ready runs for both of
the player's hands and sync_mob_death_protection for every armed mob,
every tick; only a stack that patches the component takes the old path.
- Remote projectiles: the swept bubble-column cells are deduplicated
without a HashSet per projectile per tick.
- Ident::read hands a namespaced identifier back without a second String.
- parked_worlds keeps its doc comment (#11016 put parked_border under it).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
handleUseItemOn sends the use's swing to the player's own client only when
the result is not SwingSource.PREDICTED. Horizon sent every swing back, so
on a slow connection the echo of a placement's swing arrived after the first
half of the client's own swing and swung the arm again (#9887). A placement
and a block's own SUCCESS now count as predicted; the beds, light block,
respawn anchor and sign stay SUCCESS_SERVER and are still sent back.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The check runs every tick for every player; cloning all four stacks each
time allocated their components for nothing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
BrushItem.calculateHitResult picks entities with blockInteractionRange, not
the entity range: a mob 3 to 4.5 blocks away in front of the block stops it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the AMP update stage runs the egg's own install script byte for byte (test pins it), so both panels install and SHA-256-check the same way; not imported into a live AMP.
A client mod that converts text as it's typed (Emoji Type's :code: to emoji)
reads the chat box's text and cursor and writes the result back as an edit,
so completions refresh as for typing. Only what typing lets in reaches the
box. Stacked on native_open_chat.
Part of #10107
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Its first runs failed at "horizon-launcher --version failed": pwsh read no
output from the GUI-subsystem launcher, while launcher.yml's same check, from
bash on the same runner, passes on every main push.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A new crate, horizon-polytone, reads the Polytone resource pack format
clean-room from the public wiki: modifier targets (implicit path id, ids,
tags, regex, required) and the attributes_modifiers layers of biome and
dimension modifiers (groups, REMOVE, values, modifier objects, colors,
colormap references, expressions kept as source). Nothing uses it yet.
Part of #11486
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Packet and registry id tables generated from the vanilla data reports of the
three releases; packet layouts from the public protocol documentation
(minecraft.wiki's archived packet pages for protocols 770, 771 and 772).
Each step has a reader (older server) and a writer (older client) with round
trips; entity data serializers without 1.21.9's additions. The end-to-end
bot test now joins as every release down to 1.21.5.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A 1.21.1 model's minecraft:block/chain or item/chain (parent or texture) became
iron_chain in 26.3, so a mod block drawing vanilla's chain baked with the missing
texture. Carried models and block states get the RENAMED_IDS rename data files
already had.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The recipe index reads every content namespace since 31756ebfe, but /recipe's
suggestions still listed only minecraft: ids. List data/<ns>/recipe of every
namespace, as /loot's loot tables already are.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Its rows and buttons only painted, so a pad reached them through the crosshair alone. The D-pad now
walks the recordings to the buttons, the right stick scrolls them, and Done declares its button.
A notch is now one whole row, since nothing clips a row that's partly scrolled off, and the scroll
limit counts the rows that fit, so the last recording can be reached with the mouse too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Mirror translate's per-release readers as writers (client_session/step_*),
keep sent entities for entity data and 26.1's spectate by UUID, drop the
world_clock registry and split 1.21.11's interact into interact and attack.
End-to-end test joins a bot of each release through the adapter's Session.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The warning only gave their count, so a data pack or content mod author
could not tell which features were skipped or why. Log each one with its
reason, up to 20.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use a conservative input-pixel guard for the billboard camera frustum
without changing the ordinary model frustum, flame envelope or shader
pack fallback. Keep this PR independent of experience-orb culling.
Retain the separate flame measurements and image controls, with their
original binary provenance and an explicit note about the PR split.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Reuse resolved local model depth when placing dropped-item stack copies.
Skip the unused posed occlusion envelope with entity culling and TAA off,
and retain the original preparation under TAA after measured tail regressions.
Keep the existing cluster API and random sequence, with a regression test,
paired production measurements and explicit rendering-validation limits.
Co-Authored-By: Codex (GPT-6) <[email protected]>
player-expect is refused as "the server is full" once the places the
player limit counts and those of the other expected players reach
max-players: each expectation holds a place, so two proxies sending
players at once cannot fill a server past its limit. As at login, an op
who bypasses the limit is never refused, and a renewal holds no second
place.
Once a stop has begun (/stop from the console, a player or the control
endpoint, or a stop signal), player-expect, player-import and
player-confirm are refused as "the server is stopping": a player must not
arrive on a server that is about to close. A retry after the commit is
still answered, and export, release and drop stay open, since they are how
players leave. transfer-status says whether the server is stopping.
Refs #7484
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A server seating the player on a block display (sit mods, NPC plugins) took the vehicle for a minecart; none was
found, so riding was dropped and the player fell into the slab under the seat (y -61.0 where the server keeps
-60.1). The seat is the vehicle's riding position less the player's vehicle attachment (0.6, scaled).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
AbstractFurnaceBlockEntity.setItem resets the time; without the early reset a hopper-fed furnace would smelt its first item in one tick. Review feedback on #10170.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The knee eases HDR lighting's gain into the peak; light written as light (alpha 1) has no
gain, and on a float frame the knee would compress it before the clamp to the peak. On a
unorm frame such pixels never reached the knee, so nothing changes there.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Marked pixels (light sources, the sun, HDR lighting) clip at white before their lift, as on
a unorm frame; only unmarked ones show light over 1, which is how shaders will write light.
Without that, the float frame tinted the sun and its halo instead of lifting them white.
The native world-text pipeline targeted the swapchain's 8-bit format, which a 16-bit or
float frame's pass refuses.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Cache each baked face's shape for both collision-fullness states and pack
its own UVs once, preserving the original floating-point operation order.
Address neighboring blocks, AO samples and fluid samples from padded cell
indices instead of recomputing coordinate indices for each lookup.
Add asset-independent checks against coordinate lookups for padded steps
and all AO sample tables, plus literal shape bounds and packed UV values.
Co-Authored-By: pi coding agent (DeepSeek-V4.1-Flash) <[email protected]>
Co-Authored-By: Codex (gpt-6.1-sol) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01WZzq8KX3vDMZy53y1vXqeT
HDR design step 3, the frame and the display pass (docs/design/hdr-linear-light.md):
with the switch, HDR on and no shader pack, the renderer draws into Rgba16Float where
the surface offers HDR output and the device draws it, whatever Smooth Gradients is.
0.1 and shaders/ packs keep today's frame; HDR off is untouched.
The HDR pass keeps light over 1 where the world shows, up to the peak, and clips it at
white under the HUD and menus and with HDR Lighting off, as a unorm frame does. On
today's frames, which hold nothing over 1, the pass computes the same values.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
LocalPlayer.updateAutoJump reads Level.getCollisions, which returns the
collidable entities' boxes (EntityGetter.getEntityCollisions, query
inflated by 1e-7) before the block shapes. Horizon's auto-jump only saw
blocks, so walking into a boat, a shulker or a happy ghast never
auto-jumped onto it.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Document native_client_entities with its new fields,
native_client_items, native_client_texture and skins by UUID, which
entities the list leaves out, and an example loop.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Client mods such as radars, name tags and spectator ESP need the
entities the client shows, with what they look like, without reaching
into Game's internals. API 411's native_client_entities had ids,
positions and player profiles, but no type, babyhood or texture, and no
dropped items.
- NativeClientEntity gains kind, yaw, baby, spectator and the texture
its body draws with (mobs::render::body), plus position_at, the
renderer's interpolation. The struct is non_exhaustive, so existing
mods keep building.
- native_client_items lists the dropped items the client shows.
native_item_entities stays server-only, so server logic never acts on
a client's copies.
- native_client_texture returns TexturePixels by the name texture and
NativeClientProfile::skin give: a downloaded skin from memory, else
native_client_resources().texture(name).
Pending fragment client_entity_list. Tests: position_at between two
ticks, and a client fed the server's play packets reading a zombie, a
calf, a spectating player with its default skin and a dropped item
through Host, plus texture pixels and a refused path out of the packs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add the `native_client_resources()` helper. It returns `ClientResources`
over the resource tree the game loaded, which is vanilla's with the
resource packs turned on and the content mods' files over it. A client
mod fetches it on the game thread and can then use it on any thread.
`read(path)` returns a file's bytes for a plain path under `assets/`,
such as `assets/minecraft/textures/entity/cow/cow_temperate.png`.
`texture(name)` decodes a texture by name (`block/stone`,
`entity/cow/cow_temperate`) into `TexturePixels`. Paths outside
`assets/` return none, so Horizon's own prepared tables stay private.
Mods stop reading `options.resources` with `atlas_sources::texture_path`
and `prepared_files::read`. The tests build a folder resource pack over
a fake vanilla tree and check that the pack's texture wins, and that
paths outside `assets/` are refused.
Co-Authored-By: Claude Opus 5.5 <[邮箱]>
Add the `native_block_states()` helper. It returns `BlockStates`, a copy
of every block state keyed by its `BlockStateId`. A client mod fetches it
on the game thread and can then use it on any thread.
Each state's `BlockStateFacts` holds the block name, air, invisible,
`FluidState`, waterlogged, light emission, outline boxes, map colour and
whether the state can collide at all. `sprites(id)` returns what the
state's model draws on each side, as `native_block_sprites` does for a
block name. `texture(sprite)` names the texture behind a sprite, the
inverse of `native_texture_sprites`. `mod_api` now also exports
`FluidKind`, the type `FluidState::kind` returns.
The table copies the game's internal facts into stable public types.
Client mods that draw blocks their own way, such as minimaps, world maps
and radars, no longer need `res.facts()`, `res.collision()` or
`block_sprites::Lookup`, which change from commit to commit.
Tests run on real prepared resources (ignored without
HORIZON_TEST_RESOURCES): grass block facts, air and light, slab
outlines, and a grass block's face texture names.
Co-Authored-By: Claude Opus 5.5 <[邮箱]>
Without a shader pack the rain also pulls the fog in
(AtmosphericFogEnvironment's rain fog), which eases toward the rain only in
tick_particles, and a frozen client skips that. With the rain at its level,
a frozen scene showed full rain but no rain fog. Under HORIZON_STILL a frozen
client now puts the rain fog at its target, from the same inputs the eased
path reads; the easing itself is unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The rain and thunder levels fade 0.01 a tick and stood still in a frozen
world, so a scene that wanted rain had to step the world about a hundred
ticks, and its mobs walked differently in each launch. Under HORIZON_STILL a
frozen world now holds both levels at their targets, as STILL already does
for a shader pack's smoothed values: /weather rain shows full rain with no
step. A running or stepping world, and normal play, fade them as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Adopt the bounded String input before duplicate removal in the two Mob NBT load paths. Keep the canonical retained state visible to real tag commands and saves, and pin cap, duplicate, scalar and live rewrite behavior against Java.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
16.666667 steps a 60 fps clock. STEP_MS is a finite number more than 0,
and the reply gives it back as given.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Horizon pack's hot reload could rebuild the pack under the same name
mid-sequence, unseen by the scene check: it now waits for the reply. A
sequence doesn't start, or ends, while the level-loading screen shows,
which a new world keeps a moment after it is ready. The 30 s limit is on
the frames before the last: the last frame's PNG waits as screenshot does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
screenshot-sequence FRAMES STEP_MS PATH, under HORIZON_STILL with the world
frozen, makes the running pack start over and draws FRAMES frames on a
scripted clock: frame n is n frames and (n - 1) x STEP_MS milliseconds in,
STEP_MS after the frame before (0 on frame 1). The last is written as
screenshot PATH writes it, its -hdr.png too under HDR output, and the reply
names the pack and the last frame's clock. The same build, GPU and driver
write the same bytes every run, for shaders/ packs, Horizon's own and none.
Sequence frames are submitted inline and waited for on the GPU, so each
reads the centre depth the one before left, or the sequence fails; a frame
the surface refused is drawn again. It refuses to start with far terrain,
TAA, frame generation, chunk fade or the shadow cache on, refuses commands
that would change the scene while it runs, and ends with an error when the
camera, world, settings, pack or window size change. cancel stops it, quit
still quits. Documented in agent-control.md's STILL section, shaders.md and
api/shaders.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Since smooth() keeps going through teleports, a pack remembers whether it
has drawn a frame of the world. A reset pack now forgets that too, as a
pack just loaded has seen none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A pass that reads one of the pack's targets ([[post]] output) before the
pass that writes it got last frame's picture after a reset; a pack just
made reads it blank. The reset now drops the targets and their bind
groups, made again blank on the next frame; the pipelines stay.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Renderer::reset_shader_pack_history makes the next frame draw as a freshly
loaded pack's first, for either kind. A shaders/ pack's buffers, images,
storage buffers and shadow maps are made again when next drawn, its passes
get their declared uniform values back, and the frame uniforms, custom
uniforms and centre depth forget earlier frames; its programs stay. A
Horizon pack's post history goes black and its eased values, wetness and
frame count restart. Without a pack it does nothing. Nothing calls it yet.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The first install on Windows now checks SHA256SUMS.sig (a Nightly's
SHA256SUMS.v2.sig, for the version its title names) against the release
key, then the archive against SHA256SUMS, as install.sh does. OpenSSL is
looked for on the PATH and in Git for Windows. Without OpenSSL 3 it says
so and keeps the GitHub SHA-256 check.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The overlay formatted its lines and laid out its glyphs every frame. It now
paints at most 20 times a second and copies those vertices in between, and
repaints at once for another window size, GUI scale or font epoch.
- The refresh rate is read at most once a second: on Windows, winit calls
EnumDisplaySettingsExW each time. The Window line uses the surface size.
- Passes are timed only while the overlay lists their times, at most 10 frames
a second. gpu-passes and HORIZON_SHADER_PACK_TIMING keep every sample.
- The CPU name is read in the background when the window opens: on Windows it
starts reg.exe, which the first F3 frame waited for.
- DebugEntries::any_enabled and status no longer build a String per entry; the
game asks every frame, F3 closed or not.
- Closing the overlay stops the server thread computing its lines each tick.
- The Mem, Allocation rate and Allocated lines now show on Windows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Horizon took shadowDistance, sunPathRotation, shadowIntervalSize,
shadowNearPlane and shadowFarPlane from a program's vertex shader too, and
read nothing of shadow.fsh but its formats when the pack has no shadow
pass. Iris 1.11.7, with probe packs of ours, ignored those five declared
only in final.vsh, gbuffers_textured.vsh or shadow.vsh (its shadow
projection, shadow camera and sun stayed at the defaults), and applied
shadowDistance and sunPathRotation declared only in shadow.fsh of a pack
whose shadow buffers nothing read.
Part of #10490
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A villager's Brain.memories now keep golem_detected_recently, last_woken
and potential_job_site, and each memory's remaining ttl, through a save
and load (Brain.pack, MemoryMap.CODEC, ExpirableValue.codec). The brain
built at the first tick starts from the loaded memories.
last_worked_at_poi stays unsaved: the villager brain does not register it.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Use the shared mob home for spawn, movement restrictions and persistence.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reject conflicting overlaps between paintings and item frames.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reordering a long server list took many clicks on the icon's small arrows or
Shift+arrow presses. The press that selects a saved server now also holds it:
moving the pointer puts it on the saved server's row underneath, the others
close up, and the release saves the order. A press on the selected server
still joins, as in vanilla, and holds nothing.
Fixes#10060
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Mod block entities can replace, clear and atomically take an owner-controlled batch of client update fields without saving it or changing retained contents and revisions. The lazy queue is empty for new, decoded and cloned entities; normal loaded-chunk and block broadcast paths remain unchanged.
Validated with 3 real entity lifecycle/concurrency/poison tests and 3 reusable save/chunk/two-viewer Hub tests. Strict all-target Clippy passes for horizon-client-render and horizon-client-lib. No new dependency, third-party implementation or resource files.
Co-authored-by: Codex <[email protected]>
A client mod (battle music, a combat HUD) couldn't hear its own player's
fights on a remote server: the attack and damage hooks run where Horizon is
the server. native_client_attack(target, player) fires when the attack packet
leaves; native_client_hurt(attacker, player) when the server's damage event
for this player arrives, with the causing entity it names and whether this
client knows it as a player.
Fixes#10064
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An item's move takes getEntityCollisions: the entities whose
canBeCollidedWith(item) holds. AbstractMinecart overrides only
canCollideWith (for its own moves), so a cart keeps Entity's false and
an item dropped on it falls through; the item list chained the carts in.
Boats, living shulkers and a held happy ghast still hold items (#11091).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A client mod (timed or repeated screenshots, as fabrishot does) asks for the
next frame's screenshot through F2's own path: screenshots/, vanilla's file
name, the chat line. It returns the file it will write; an error without a
window, or while another screenshot waits for the frame, which it would
otherwise replace.
Part of #9606
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A client mod (a command macro such as CommandKeys) can open the chat screen
with text pre-filled for the player to finish, as T and / open it. Nothing is
sent. An error with no player in a world or while another screen is open.
Part of #10120
Co-Authored-By: Claude Opus 5.5 <[email protected]>
horizon-client-render forbids unsafe code, which the Windows ARM64 build
reported (IsWow64Process2 and sysctlbyname are FFI). The detection lives with
the other system probes in horizon_telemetry::system now.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Entity.collide adds getEntityCollisions: the boxes of the entities whose
canBeCollidedWith(mover) holds. Mob movement only had block collisions,
so a zombie or a dropped-on mob fell through a living shulker and
through a happy ghast a player holds still by standing on it (#10445).
Seen carries the happy ghast's still timeout and whether it carries
someone; the mob tick lists the slots that may block (shulkers, happy
ghasts) so other mobs pay nothing when there are none.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
packaging/server/amp/ holds a template for CubeCoders AMP's generic module.
Its update stage runs the egg's own install script (the release asset checked
against its SHA-256), the start runs the same horizon-start.sh, and its
settings are the egg's variables with the same defaults. Tests keep both in
step and check the console patterns against the lines the server logs.
Fixes#8238
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Each worker looks at the cancel flag before every chunk, not only before
every file, so Cancel stops within a chunk on large region files. A
canceled file is never swapped in by recreate_region_files.
- The writes of a region file reach the disk once, when the file is done,
instead of a sync_data after every chunk. On the ThinkPad (NVMe, ext4) the
per-chunk sync was half of a 1.21.1 world upgrade (187 s -> 89 s, one
thread); on macOS sync_data is F_FULLFSYNC and costs far more.
- The calling thread is worker 0 and helpers are named World Upgrader #N;
a helper that fails to start leaves its files to the others.
- Tag modifiers are no longer cloned per chunk; the recreate folder is made
once per file.
- Tests: the parallel/one-thread comparison also checks vanilla counts
computed from the fixture; a new test cancels from the first chunk with 4
workers and checks at most 4 chunks were looked at and that a canceled
recreate leaves the world unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Mods that speak QUIC (e4mc's relay) couldn't build: their dependencies must
already be in Horizon's lock. quinn joins horizon-mod-deps as an optional
dependency (feature quic: runtime-tokio + rustls-ring, so TLS stays on the
rustls + ring already in the lock). Nothing in the game builds it.
Fixes#9602
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The README and the hosting guide still said Intel Macs come "from the next
Nightly" and listed no Windows ARM64 launcher; both ship in every Nightly and
launcher release.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Auto already tries DirectX 12 before Vulkan on Windows ARM64, where Vulkan lost
the device (#7513, #6320 on Adreno), but it read the build's architecture: the
x64 game, which the x64 launcher installs on a Snapdragon PC, still started on
Vulkan. The order now follows the machine's architecture (IsWow64Process2), and
crash reports name the emulation ("x86_64 on aarch64"), on Windows and under
Rosetta 2 on macOS (sysctl.proc_translated).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A small render distance (8 chunks on a phone or an integrated GPU) could only
add far terrain out to 64 chunks, the slider's first stop. The slider now stops
at 16, 24, 32, 48, 64, 96, 128, 192 and 256 before doubling as before.
Fixes#9694
Co-Authored-By: Claude Opus 5.5 <[email protected]>
tools/test_non_mob_saved_flags_parity.py needs a fresh --work-dir that
parity.py never passes, so every selected run would fail; the unit test
covers the four writers.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A capturing server source is not silent: it takes every sendSuccess and
sendFailure line and informs no operator, so run it that way.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
They sat in the hook-point section, so the catalog test took them for hooks and hook_points_stay_hookable saw world_items!; now in the helpers' impl block of the same file (targets unchanged), the macro written out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The test maps each free hook to its module; the two world generation hooks are free functions in horizon-world.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Track MerchantResultSlot removal and quick-move counts, reset them at the achievement callback, and award through the actual trading player's stat API without a recipe ID.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
The join test's bot reads packets with the pinned readers; the world
packets the adapter now writes in 26.2's layout are counted (chunks) or
skipped instead.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Chunk columns (states down with stand-ins, repacked at 26.2's 15-bit
global palette, block entity types down, light masks as longs),
light_update, level_particles (26.2 field order, one speed, Int count),
explode (no trailing sound flag, silent blasts name intentionally_empty),
move_entity_pos/_pos_rot/_rot, entity_position_sync and animate in
26.2's encodings, swing_animation as 26.2's animate.
Tests write each in 26.2's layout and read it back with translate's
26.2 readers, made pub(super) for that.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
While ids are renumbered down, the values kept as pinned bytes that hold items
(pot_decorations, container, use_remainder, sulfur_cube_content) are read again
and written with that version's ids, pot sides as 26.2's four item ids, and a
tag slot display as 26.2's bare tag id (a composite of item displays for a list
of items). Quirks::{pot_decorations_are_items, tag_slot_display_is_id} written
the other way; ids::quirks_written gives the quirks of the version written for.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The test server keeps every packet the hub hands to the game; a 26.2 bot
confirms its teleports by id alone, walks, starts and stops destroying the
block below in 26.2's action numbers, and swings with each hand.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
layout_26_2 mirrors translate's 26.2 readers for a ClientSession: spawn info game
types back to bytes, the advancement tab position back inside its display,
argument types renumbered down with a string stand-in for 26.3-only parsers,
26.3-only synchronized registries not sent, map decorations and stats in 26.2's
numbering. Enabled with layout_26_2::set_enabled.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A 26.2 client confirms a teleport by its id alone: the session follows the
player_position packets it is sent and the moves it reports, and fills in
26.3's position and rotation from the teleport it accepts. Its digging
actions past 0 are one up (26.3 inserted CHANGE_DESTROY_DIRECTION at 1), and
its main-hand swing becomes 26.3's punch (an off-hand swing has no packet).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each entity's vertices are matched with last frame's by entity (Frame::entity_motion, now with
the GPU-posed start at each mark): last frame's pack buffer stays bound at set 1, binding 2, and
binding 3 gives each vertex its index there. at_velocity is this frame's view position less last
frame's (gbufferPreviousModelView joins the block when a pack reads it); a vertex with no match
reads zero, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
player_pre_login and player_profile name those paths in their hook signatures; the generated
dispatch code compiled in the test crate needs them, as the existing horizon_client stubs.
Part of #9996.
A chunk's noise volume is made of whole cells, and InterpolatedSampler
filled it one cell at a time: per cell eight corner loads and four Z
lerps, per column two X lerps and a bounds-checked index, then a scalar
loop of stores and additions (fill_cell alone was 6.4% of the Overworld
proto phase's instructions). For a volume aligned to its cells,
fill_whole_cells now walks the four columns of one Z row of a cell from
the bottom of the volume to the top. The X lerps at a level of corners
are computed once (they are the top of one cell and the bottom of the
next: the same lerps of the same samples), the four columns' repeated
additions run as four lanes, and four Ys of the four columns are stored
as four vectors. Each value comes from the same operations on the same
operands in the same order as vanilla's InterpolatedFunction fillCell;
only the order between columns changes. The top cell may be cut short
(the 16x114x16 volumes); a volume that is not aligned, or whose top
corners would clamp, keeps the general path.
fill_noise_inner spent about ten instructions on each air block above
the aquifer's skip height. A downward run of non-positive densities that
stays above it is now skipped in one step: nothing was written for those
blocks and note_skipped only clears a flag, so one call covers the run.
Exact: chunkgen_bench --snapshot files (terrain and decorated) are
byte-identical to main in 16 cases: the Overworld at seeds 12345, -1,
987654321 and -7, the Nether at seeds 12345 and 42, the End at seeds
12345 and 7, sizes 3 to 12. A new test compares sample_volume and the
new path with a transcription of vanilla's sampleWithBlockStep and
fillCell, on an input with NaNs, infinities, signed zeros and
subnormals, for 4x8, 8x4, 4x4 and 8x8 cells, and checks which volumes
the new path takes.
Proto-phase instructions (callgrind, 64 chunks, one thread):
Overworld 5.970 G -> 5.615 G (-5.9%), the Nether 2.868 G -> 2.853 G
(-0.5%).
A first draft by GLM-5.3 and DeepSeek V4.1 Flash, via pi, reviewed and
reworked (the air-run skip).
Co-Authored-By: pi coding agent (DeepSeek-V4.1-Flash) <[email protected]>
Co-Authored-By: pi coding agent (GLM-5.3) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
BrushItem.onUseTick aims with getHitResultOnViewVector, so a mob between the
player and the block is the nearer hit and the use releases without a stroke.
tick_brush used the block-only pick and brushed through the mob.
LivingEntity.stopUsingItem, reached when a use runs out or when the brush hits
no block, causes the ITEM_INTERACT_FINISH vibration. The brush and instrument
paths cleared the use directly instead, so only letting go of the button made a
sculk sensor hear them.
Merge main at 0873f24d2, retain its replacement shader screen, and route native selections and options through stable manifest IDs.
Co-Authored-By: GPT 6.1 Sol <[email protected]>
horizon-world cannot call horizon-client's dispatch, so the two generation hooks
call a NativeGenerateHooks table that entry.rs installs (as native_load_hooks
does for the loot hooks). mod_api re-exports NativeGenerate, NativeProtoChunk
and NativeLodColumn for the generated events. Without the table, and without
mods, both hooks do nothing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Since #6253 the mod API generator needs a mod_api entry for every pending hook
fragment. Cancel flags get {cancel, out: cancel}, menu_input_closed collects its
slots, every other hook listens. The placeholder bodies are filled by
`cargo run -p xtask -- mod-api`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- NativeEffect (player_effects) and NativeInventoryKind (set_inventory) are pub and re-exported in
mod_api: the generated mod API refused both fragments.
- native_teleport_player's cross-level teleports fill LevelTeleport's new cause (hooks-move) with
"unknown"; player_teleport's docs name that case.
- lan_vehicles: the vehicle_moved_v2 call closure is FnMut.
- cargo fmt (game.rs mod lines in order).
native_player_persistent_data(uuid) reads and
native_set_player_persistent_data(uuid, data) replaces the compound mods
keep on a player, as NeoForge's Entity.getPersistentData() and Bukkit's
PersistentDataContainer. It is saved in the player's file as
NeoForgeData, beside the keys Horizon keeps without modelling them, and
kept over a death and a level change; an empty compound takes the key
away. Nothing calls them without a mod.
Unproven: not yet compiled or probed live (build queue); the integration branch tests it.
native_item_dropped_v2(player, stack, cancel) is now cancellable, as
NeoForge's ItemTossEvent is: a canceled throw puts the stack back into
the player's inventory (Inventory.add, the held slot first); only what
does not fit is thrown, unheard, and native_item_dropped hears nothing.
The drop statistics and the swing still happen, as in NeoForge. Wanted
by the combat log, freeze and inventory-lock ports.
Unproven: this commit was neither compiled nor run (builds were stopped for
the integration test); `cargo run -p xtask -- mod-api` filled the dispatch.
The listen-only v2 drop hook before it was proven on a server.
Part of #10461.
native_projectile_world_border(dimension, entity, kind, pos, velocity,
collide) is heard each tick a thrown projectile (pearl, snowball, egg,
potion...) inside the world border would end its next step outside it,
before 26.3 stops it on the border like a wall (MC-310476). A mod may
change its position and velocity, or set `collide` false: that tick's ray
is then clipped without the border, so it flies through as before 26.3.
Arrows and tridents, which Horizon does not stop at the border, are not
heard. Static (no self), called from tick_projectiles.
Unproven: not compiled or run on a server on this branch (handed to the
hooks-all integration build).
Part of #10461.
native_trample(dimension, entity, kind, pos, fall_distance, cancel) is
heard when a player or mob that fell onto farmland is about to turn it to
dirt, once FarmlandBlock.fallOn's chance and checks have let it (NeoForge's
FarmlandTrampleEvent, Bukkit's PHYSICAL PlayerInteractEvent and
EntityInteractEvent). Canceling keeps the farmland and its crop; the fall
damage is taken either way. The landing rules ask through a new
BlockLandingWorld::trample_allowed (true by default), which the player's
and the mobs' landing worlds answer from a function the game sets once
(block_contact::TRAMPLE); there is no self.
Unproven: not compiled or run on a server on this branch (handed to the
hooks-all integration build).
Part of #10461.
native_player_respawn_position(player, name, dimension, pos, yaw, pitch)
is heard once per respawn, after the Respawn click and before the player
is placed (NeoForge's PlayerRespawnPositionEvent, Bukkit's
PlayerRespawnEvent.setRespawnLocation), with their respawn point: a bed
or anchor block's top, a forced point, else the world spawn. Left alone,
the point is resolved as before. A changed dimension or position places
the player exactly there, with no bed or anchor used and no free column
looked for; yaw and pitch set their look, which Hub::respawn now takes
instead of always 0, 0. An unknown dimension or a non-finite value
leaves that part unchanged.
Unproven: not compiled or run on a server on this branch (handed to the
hooks-all integration build).
Part of #10461.
native_player_teleport(player, name, cause, from_dimension, from,
to_dimension, to, relative, cancel) is heard before a server player's
teleport is sent (Bukkit's PlayerTeleportEvent, NeoForge's
EntityTeleportEvent): commands (/tp within a level and into another),
ender pearls, chorus fruit, the spectator teleport menu, a dismount and
other server moves of the player. A mod may move `to`, cancel (the
player and their body stay where they are), or set the look and motion
bits of the position packet's relative flags, so a pearl can keep the
player's speed (0x1f8, what 26.2 sent); the position bits are ignored.
The call sites name the cause in a thread-local (lan::hub::teleport_cause)
that Hub::teleport and Hub::teleport_keeping_motion read; a teleport
without a named cause (respawns, portals, the End gateway, vehicles
carrying riders, waking up, refused moves) is not heard. Command and
spectator teleports into another level carry their cause in
LevelTeleport. The hub reaches the hook through a function the game sets
once (lan::hub::PLAYER_TELEPORT); there is no self.
Unproven: not compiled or run on a server on this branch (handed to the
hooks-all integration build).
Part of #10461.
native_player_move(player, name, from, to, yaw, pitch, on_ground,
cancel) is heard for each position or look packet that changed either
and passed the server's checks (moved too quickly, moved wrongly, the
anticheat), before Hub::position_change applies it (Bukkit's
PlayerMoveEvent). Canceling sends the player back to `from`, as a
refused move does; a changed `to` puts them there and their client gets
a teleport. Riders, sleepers and moves while a teleport waits are not
heard. The hub reaches the hook through a function the game sets once
(lan::hub::PLAYER_MOVE), as the damage hooks do; there is no self.
Unproven: not compiled or run on a server on this branch (handed to the
hooks-all integration build).
Part of #10461.
The helper native_respawn_player(uuid) respawns a dead server player as
their client's PERFORM_RESPAWN does (Spigot's player.spigot().respawn()),
after the players' packets of the server tick, through the same
guest_respawn. Nothing calls it without a mod.
Unproven: not yet compiled or run on a server (builds stopped for the hooks-all integration).
The helper native_set_flight(uuid, allow, flying) lets a server player fly
in any game mode or not and sets whether they fly now, as Bukkit's
Player.setAllowFlight and setFlying: the guest keeps the mod's mayfly
(allow_flight) until their game mode changes or they respawn, their
abilities packet and the server's body carry it, and the flying check does
not kick them. Without a mod allow_flight stays false and every check is
the game mode's, as before.
Unproven: not yet compiled or run on a server (builds stopped for the hooks-all integration).
The helpers native_mount(entity, vehicle), native_dismount(entity) and
native_eject_passengers(vehicle) seat a server player or mob on a boat,
minecart or mob, take it off, or take every rider off, as /ride does with
its checks and native_entity_mount (Bukkit's addPassenger, leaveVehicle,
eject). /ride's mount checks move into ride_mount, which both use; the
command's answers are unchanged. Nothing calls the helpers without a mod.
Unproven: not yet compiled or run on a server (builds stopped for the hooks-all integration).
native_vehicle_moved_v2(player, dimension, vehicle, kind, from, to, cancel)
is heard right after native_vehicle_moved, with the same state swapped in,
and also for a mob a server player rides. A canceled move puts the vehicle
back at from with its riders, a changed to puts it there
(native_teleport_vehicle), as Bukkit's VehicleMoveEvent with a teleport.
v1 keeps its signature, firing point and vehicles. Without mods nothing
moves the vehicle.
Unproven: not yet compiled or run on a server (builds stopped for the hooks-all integration).
The helper native_teleport_vehicle(dimension, id, pos) teleports a boat,
minecart or ridden mob of the level in place with its riders, as /tp does
a vehicle (Bukkit's Vehicle.teleport): the riders stay seated and a server
player riding it is told, as their client drives it. Only where nobody's
state is swapped in. Nothing calls it without a mod.
Unproven: not yet compiled or run on a server (builds stopped for the hooks-all integration).
The helper native_teleport_player(uuid, dimension, pos, yaw, pitch)
teleports a server player as /tp does (Bukkit's Player.teleport): at once
when they are the acting player or nobody's state is swapped in, else
after the players' packets of the server tick, so a port no longer runs
execute in .. run tp from the console, which waits for the next tick and
does nothing inside a player's hook. Into another level it is a command's
level teleport. Nothing calls it without a mod.
Unproven: not yet compiled or run on a server (builds stopped for the hooks-all integration).
The helper native_resend_commands(player) sends a player their command
tree again, with native_player_commands_sent, its v2 and the permission
nodes of the mods' commands asked anew, as Bukkit's Player.updateCommands:
after a permissions mod changed what they may use. The tree goes out in
the next server tick, with no player's state swapped in, so the helper
works from any server hook, the player's own included. Nothing calls it
without a mod.
Unproven: not yet built or run (handed to the hooks-all integration branch for the build and proof).
native_player_command_level(player, name, command, level) is heard in
Game::guest_command after native_player_command, before the server checks
the command: level comes in as the player's own and becomes the level this
one command runs at (GuestCommand.permission), so a permissions mod can
grant /gamemode to a player of level 0 or refuse a command to an operator,
as Paper's VanillaCommandWrapper does with minecraft.command nodes. A mod
command with a permission node falls back to this level. Unchanged, the
command runs at the player's level, as before.
Unproven: not yet built or run (handed to the hooks-all integration branch for the build and proof).
NativeCommand::new(usage).permission(node) gives a mod's command a node, as
Bukkit's plugin.yml permission: does. The server then asks
native_has_permission(player, node, level), the command's level as the
default, both before it puts the command in a player's command tree and
before a player's typed command reaches native_perform_command, so a
permissions mod can grant it below that level or refuse it to an operator.
Without a permissions mod the level decides, as for a command without a
node; a dedicated server's console runs every registered command as before.
Unproven: not yet built or run (handed to the hooks-all integration branch for the build and proof).
native_player_commands_sent_v2(player, name, names, names_hidden) is heard
right after native_player_commands_sent, at the same times, with the root
commands the player's level hides (those a level 4 operator would get) in
names_hidden. A root a mod moves into names is sent with its subtree, as
for an operator of its level, so a command a permissions mod grants
completes and highlights in the player's chat box (Paper's
PlayerCommandSendEvent after a passed permission check). v1 keeps its
signature and firing point. Without mods nothing is granted and the tree
is unchanged.
Unproven: not yet built or run (handed to the hooks-all integration branch for the build and proof).
The hook native_permission_check(player, node, default_level, result) lets a
permissions mod (a LuckPerms port) say whether a player has a permission
node; the first mod to set result decides. The helper
native_has_permission(player, node, default_level) asks it, for every mod
and for the server, and falls back to the player's operator level against
default_level when no mod answers, as Bukkit's Permissible.hasPermission and
NeoForge's PermissionAPI.getPermission do. Without a permissions mod every
answer is the operator level, so nothing changes for unmodded servers.
Unproven: not yet built or run (handed to the hooks-all integration branch for the build and proof).
Written by hand from a scratch resolution seeded with this lock: rusqlite,
libsqlite3-sys, fallible-iterator, fallible-streaming-iterator, hashlink,
vcpkg, yaml-rust2, arraydeque, gif, weezl, color_quant, snap. Existing
entries keep their versions; chrono gains serde and mysql_common gains chrono.
To confirm: cargo metadata --locked must not want to change it.
The bundle's fragments carry no mod_api entry: session A adds them in one
step (add_mod_api.py, which takes a cancel: &mut bool parameter for the
cancel contract). So interact_entity_v2's claimed and
right_click_block_v2's canceled are named cancel; they act as before.
Not built yet.
native_living_incoming_damage_v3(victim, victim_uuid, kind, attacker,
attacker_uuid, direct, damage_type, amount, cancel) fires everywhere v1
fires (health::HURT_SERVER), right after it with the amount v1 left and
the same cancel flag. It adds a player victim's UUID, the causing entity
where v1 leaves 0 (a mob's attacker, a player's melee, the synchronous
attack in progress as living_knockback_v2 falls back to), a player
attacker's UUID and the direct entity. Players are noted by entity id as
each guest's turn starts (game/native_damage.rs, only while a native mod
is loaded), since the hit carries ids only and no Game.
Not done here, as the plan asked: a &mut self host (the victim is
borrowed out of the game inside Mob/Player/Dragon hurtServer, behind the
HURT_SERVER fn pointer) and Bukkit's modifier breakdown (the reductions
run after the hook in horizon-world's living_hurt and Mob::hurt_server).
Sentinel (attacker matched within 500 ms), Wanted, HuskSync and
CombatLog (an id-to-player map each tick) need the names.
Not built yet.
native_kick(player, reason) disconnects a player with a Text reason in its
colours and translations (Adventure's Player.kick(Component)), through
Hub::kick as /kick does, so the log line and native_player_kicked follow.
It is queued and runs at the tick's end with the mod messages
(flush_native_net), so it works from any server hook, the kicked player's
own included; today a port sends a raw Disconnect and then runs /kick with
one plain line.
Part of #10461.
native_resource_pack_status_v2(player, name, pack, status) is an additive
version of native_resource_pack_status with the answered pack's id
(Bukkit's PlayerResourcePackStatusEvent.getID), so a mod's own pack's
answers are told apart from the server's. In play it is heard just before
v1, which keeps its signature and firing point. The answers to the
server's own pack given during configuration, which the connection thread
handled alone, are kept (lan::Configured::pack_answers) and heard once the
player has logged in, right after native_player_logged_in. The connection
answers the packets exactly as before.
Part of #10461.
rusqlite 0.41 (bundled + cache, no default features) reads SQLite files such
as a Bukkit plugin's .db; yaml-rust2 0.13 (no default features, safe Rust)
reads and writes config.yml; gif 0.14 and snap 1 are small and pure Rust;
mysql gains chrono for DATETIME columns. All optional, nothing builds them.
Cargo.lock not yet updated.
tick_server_spans calls native_server_tick_timed with the pass's wall and CPU time and merged spans (TickSpan, re-exported from spans::Span) before handing them to the report. Spans are switched through tick_profile::want_spans by who wants them (HORIZON_SPIKES, horizon-report, a mod), so a mod and a report no longer turn each other's spans off. tick_profile keeps a mod's mob cost sums apart from the report's (two bits, two maps), so both can sample at once; calibration stays the report's. Fragment server_tick_timed.
While a mod turns counting on, the network threads keep each framed and read play packet's id and body size in the connection's Outbox (io_pool Conn::frame and Conn::read, behind one relaxed load of a static switch, capped at 65536 each way); the server tick's end hands them to native_server_packets per player and way. Off (the default) nothing is kept and the hook never fires. Fragment server_packets.
native_hurt_entity(dimension, target, amount, damage_type, attacker)
hurts a mob or a player of the level as LivingEntity.hurtServer with
damageSources().mobAttack(attacker) does, the way /damage <target>
<amount> <type> by <attacker> goes: cmd_damage's hurt is moved unchanged
into damage_target, which both call. Returns whether it hurt; false for
another dimension, a target that is neither a mob nor a player of the
level, a missing attacker or a non-finite amount.
Sentinel's own damage (damage set, workaround damage, lightning weapon)
today runs a damage command by UUID.
Not built yet.
native_send_resource_pack(player, id, url, sha1, prompt, required) sends
ClientboundResourcePackPushPacket with the mod's own pack id (Bukkit's
Player.addResourcePack), checked against the packet's limits (hash at most
40, URL at most 32767 characters); native_pop_resource_pack(player, id)
sends ClientboundResourcePackPopPacket for one pack or, with None, all
(removeResourcePack). Both go through native_send. A decline still kicks
only when the server's own pack is required, as vanilla's
handleResourcePackResponse does.
Part of #10461.
native_player_register_channels(player, name, channels, register) hears
the names a client registers (minecraft:register) or unregisters
(minecraft:unregister), NUL-separated as Bukkit's and Fabric's clients send
them; vanilla reads neither (Bukkit's PlayerRegisterChannelEvent). What a
client registered during configuration, which the connection thread
dropped, is kept (lan::Configured on Joined) and heard once right after
native_player_logged_in; a message in play is heard as it is handled, with
the player's state swapped in, and then goes on as before. The helper
native_player_channels(player) lists a guest's channels
(Player.getListeningPluginChannels). At most 128 names of at most 256
bytes are kept per client.
Part of #10461.
native_block_redstone(dimension, pos, old, new) is asked, as Bukkit's
BlockRedstoneEvent, when a button is pressed (hand or arrow) or let go by
its release tick, a lever is pulled, or a pressure plate's weight check
changes its signal. A mod may change new; one equal to old keeps the
component as it is, and a held button or plate is asked again at its
next scheduled check. The seam is LevelAccess::redstone_change in
horizon-world (default: leaves the signal), which GameLevel forwards on
an authoritative level. Wire, torches, diodes, observers and the other
sources are not heard yet.
CoreSMPEssentials part B holds its power rods' component on without the
2-tick re-set that leaks a 1-tick off pulse. Test (not run):
a_level_can_keep_a_lever_as_it_is.
Not built yet.
Bukkit's Player.setCompassTarget: Hub.compass_targets by UUID while the player is on; default_spawn sends the target in place of the world spawn wherever it is sent (join, respawn, level change). Without a target the packet is unchanged. Helper-only fragment compass_target.
Bukkit's BossBar.addPlayer/removePlayer: a NativeBossBar (mod_api) by player UUID in BossState, offered to that viewer by boss_events_for, so publish_boss_bars sends Add/Update/Remove as for the world's bars. Unmodded servers keep an empty map. Helper-only fragment boss_bar.
Vanilla's ServerLevel.sendParticles(player, ...) and Bukkit's Player.spawnParticle: within 32 blocks, 512 with force (overrideLimiter). Replaces the hand-built LevelParticles (dust id hard-coded) of plugin ports; same options and checks as GameLevel::native_send_particles. Helper-only fragment send_particles_to.
Bukkit's Player.playSound for plugin ports, which sent a raw ClientboundSoundPacket through native_send. Queued in game/native_player_fx.rs and sent at the tick's end with the hub back, so it works from any server hook; helper-only fragment play_sound_to.
Sentinel's native attacks (its default) call Mob.doHurtTarget for the
body. Horizon's do_hurt_target needs the goal's world, and its player is
whichever guest the mob sees, so the port injected a goal and wrote
LanHost.mob_players to bind the body to its target. native_mob_attack
runs do_hurt_target from any server hook with the target guest's view as
the mob's player (a new lan_guest_view, the single-guest form of
lan_guest_views), then hands the hits on that player to the guest and the
rest to handle_mob_events, as a mob's tick does. Helper only: unmodded
servers never call it. Part of #10461.
native_remove_entity(dimension, id) also removes projectiles (arrows,
tridents, snowballs, eggs, pearls, fireballs, wind charges, llama spit,
shulker bullets, firework rockets) as /kill does (kill_target), and a
server player's fishing bobber, as Entity.discard: the bobber of the
player in place, or outside a guest's turn any player's of the level
(remove_server_hook in fishing.rs). Displays, markers and interactions go
as before. Sentinel's arrow cleanup and CoreSMPEssentials part B's power
rods need it.
Not built yet.
Citizens' Navigator and Sentinel walk their bodies to points and
entities, stop them, ask whether they still walk and aim their heads.
Horizon offered this only to a goal (Mob::nav_move_to with the goal's
world), so ports injected a goal that read intents from a global map, or
moved bodies in straight /tp steps through walls. native_mob_navigate,
native_mob_follow, native_mob_stop, native_mob_navigating and
native_mob_look_at call the mob's own navigation and look control
(PathNavigation.moveTo, stop, isInProgress, LookControl.setLookAt) from
any server hook, for a mob of any loaded level, with the world its goals
see. Helpers only: unmodded servers never call them. Part of #10461.
native_move_entity turns only the entities Horizon keeps without
simulating them, and native_teleport_mob takes no rotation, so NPC ports
turned their bodies with execute .. rotate or tp .. facing every few
ticks. The helper is /rotate (Entity.forceSetRotation) for an entity of
any loaded level, with an optional head yaw (setYHeadRot) for a mob.
Helper only: unmodded servers never call it. Part of #10461.
native_entity_data_to(viewer, entity, values) is a typed shortcut of
native_server_packet for ClientboundSetEntityDataPacket: the entries one
guest is about to get for one entity, changeable for that viewer alone, so
a mod can show one player glowing to one viewer without touching the
server's own flags (TAB's and Essentials' per-viewer glow, which resend
their own packets on a timer today). Heard just before native_server_packet
for the same packet. Switched on by native_watch_entity_data, which decodes
that packet only. EntityDataValue is horizon_protocol's entity_data::Value.
Part of #10461.
native_fishing_hook_landed(player, hook, dimension, block, face) hears a
server player's flying fishing bobber meeting a block on its way, and
each tick it lies on one (on the ground as the tick began or ends: the
block under its centre, its top), as Bukkit's ProjectileHitEvent for a
FishHook. It is heard at the end of FishingHook.tick with the bobber back
in place, so a mod can remove it there. hook_block_hit keeps its result;
a sibling hook_block_hit_at also gives the block and face.
CoreSMPEssentials part B starts its power rods from the plate, button or
lever the bobber lands on; native_projectile_impact leaves bobbers out.
Not built yet.
NativeLevelStats counts a level's loaded and forced chunks, block entities and ticking ones (BlockEntities::ticking_len), and its entities (players included) and block entities by type. The entity list reuses the selector walk (level_candidates, now pub(in crate::game)) with a parked level swapped in as /execute in does; block entity types come from the block (lan::chunks::block_entity_type), which the enum's variants cannot tell apart, so this replaces the planned BlockEntity::type_id(). Fragment level_stats.
A stage plugin keeps its actors by UUID and moves, changes or removes them
in whatever level they are in; Horizon's entity handles reach the level in
place only, so ports ran tp/data merge entity by UUID on the console.
native_entity_by_uuid finds the entity (players included) in any loaded
level and returns its dimension, id and type; native_merge_entity_nbt is
native_entity_merge_nbt for a named loaded level. Helpers only: unmodded
servers never call them. Part of #10461.
native_lod_column(dimension, column, seed, flat, column_out) is asked
for each column GeneratorWorker::lod_grid, surface_sample and
surface_column sample. A mod whose native_chunk_generate builds that
chunk answers the column's blocks (and its biome) with
column_out.replace, and the far terrain shows them, with the top, the
water over it and the spans derived as vanilla's sample derives them,
instead of vanilla's noise until the chunk generates.
Vanilla's far sample is computed first and only replaced when a mod
answers: without a mod, one empty call per column and the same columns
(#105).
native_player_fish_v2(player, name, state, hand, cancel) is
native_player_fish with the rod's hand: the hand the cast or reel-in uses
(Bukkit's PlayerFishEvent.getHand), and for a bite the hand holding the
rod, main hand first as FishingHook.shouldStopFishing looks. Asked right
after the unchanged v1 hook with the same cancel flag. CoreSMPEssentials
part B sees off-hand stasis and power rod casts.
Not built yet.
native_server_packet(player, packet, cancel) hears each play packet the
server is about to send one guest, decoded, a broadcast once for each
player, where PlayerSender::send hands it over (ProtocolLib's and
PacketEvents' outgoing listeners; NeoForge and Bukkit have no event).
A changed packet is encoded again, a canceled one is not sent. Nothing is
decoded or copied until a mod calls the helper native_watch_server_packets:
until then each packet costs one relaxed atomic load. The hook is a free
function the game sets once (lan::conn::SERVER_PACKET), as player_move is.
Part of #10461.
Both helpers were refused for any level but the one in place, so a mod
spawning from native_server_tick (where no level is defined) or into
the Nether from the Overworld's tick could not, and Stage-style ports ran
`execute in <dimension> run summon` instead. The named level is now put
in place for the call and put back afterwards, through the same
enter_command_level that `execute in` uses (npc_in_level in the new
native_npcs.rs). A level not loaded is still refused. Unmodded servers
never call these helpers, so nothing changes for them. Part of #10461.
native_right_click_block_v2(pos, face, hand, canceled) is
native_right_click_block with the hand that clicks (NeoForge's
PlayerInteractEvent.RightClickBlock.getHand), asked right after the
unchanged v1 hook with the same flag, once per hand tried. A cancel does
what a v1 cancel does. CoreWG's guard refuses an off-hand flint and steel
or bone meal; today it sees the main hand only.
Not built yet.
Keeps both test modules (mixed_size_tests, undrawn_terrain_tests) and
has_terrain with #10502's doc and undrawn flag after sizes_differ.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
native_tick_times() returns the last 100 tick durations at vanilla's getTickTimesNanos indices (tick n at n % 100); native_tps() the ticks a second over one, five and fifteen minutes (Bukkit's getTPS), measured between recorded tick ends in FixedStep::record. Helpers only, fragment tick_times.
native_interact_entity_v2(entity, kind, hand, stack, claimed) is
native_interact_entity with the hand that clicks (NeoForge's
PlayerInteractEvent.EntityInteract.getHand), asked right after the
unchanged v1 hook with the same claimed flag: a click v1 claimed is not
asked again. Once per hand vanilla tries, so a mod claiming the main hand
hears one event per click (Citizens' click events and commandtrait
hands, CoreWG, CoreSMPEssentials part B).
Not built yet.
native_chunk_generate(dimension, chunk, seed, flat, chunk_out,
generate) is asked on the generation workers for every chunk, after its
biomes and structure starts and before the noise (Bukkit's
ChunkGenerator.generateNoise). A mod sets the chunk's blocks through
NativeProtoChunk::set_block and answers NativeGenerate::Replaced with
the shouldGenerate* flags: vanilla then runs only the surface rule, the
carvers (on a superflat level too), the structures and the features it
asks for. set_biomes and clear_structures work with either answer.
Left at Vanilla, nothing changes: the hook is one empty call per chunk
and every vanilla step runs as before. Replaces the cancellable HEAD on
VanillaTerrain::proto that generated the vanilla chunk and overwrote it,
and the coregen_glue module (#104).
The world's seed as /seed prints it (Bukkit's World.getSeed), for a
generator mod's preview that must draw the terrain the chunks get.
None off a server, where only a hash of the seed is known. No
behaviour change. Part of the world generation bundle (#98).
While the dragon fight's world fog leaves out the End's sky, Iris 1.11.7 still
runs a pack's gbuffers_skybasic (render stage SKY, vertex colour fogColor), and
the gl_Vertex values our probe packs read there (render distances 12 and 8) fit
a fan around the horizon: from (0, -16, 0) under the eye to eight rim points 16
above it, every 45 degrees, at the render distance. Horizon ran no sky program
in the End, so packs that paint their sky there left that band empty.
Frame carries the boss bar's world fog; PackSky draws only the fan then, in
the fog colour.
Part of #10491
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A saved chunk's zlib stream was inflated by a new Decompress, 8 KiB at a
time into a stack block, each block then copied onto the output, which
grew by doubling from empty. Now each thread keeps one inflater, reset
for every chunk, and a whole stream that fits is inflated straight into
the output, reserved at about eight times the stream (a chunk's usual
ratio) and never past one byte over the limit. Any stream that fails
(damaged, cut short, over the limit) is decoded again the old way, so the
error and what the output keeps are exactly as before. A new test
compares both on whole, cut and damaged streams around every limit.
A NativeChunkSnapshot of a loaded chunk of an open level (block states,
WORLD_SURFACE heights, noise biomes, the fuzzy biome zoom's cell) that
shares the chunk's sections copy-on-write and can be read on another
thread, with native_block_map_color, native_biome_name and
native_biome_tint to draw it. Replaces the engine internals a web map
reads today. No behaviour change. Part of #10461.
native_attribute(uuid, attribute) gives an attribute's base value and its
value with every modifier (Bukkit's AttributeInstance.getBaseValue and
getValue); native_set_attribute_base sets the base as
/attribute .. base set does; native_set_attributes loads a saved
attributes list onto the player as vanilla's AttributeMap.load reads a
player's file. A lower max_health caps the health, and the client gets
UpdateAttributes from the tick's sync. Nothing calls them without a mod.
Unproven: not yet compiled or probed live (build queue); the integration branch tests it.
native_effects(uuid) reads a player's effects as NativeEffect { id,
amplifier, duration, ambient, particles }; native_add_effect gives one as
vanilla's LivingEntity.addEffect (Bukkit's addPotionEffect), so a weaker
or shorter one waits under it; native_remove_effect takes one off as
/effect clear does; native_set_effects replaces them all, what a mod
restoring a saved player needs instead of writing PlayerEffects into the
hub's guests. The client gets UpdateMobEffect with the ambient and
particle flags from the tick's sync. Horizon keeps no hidden-icon flag on
an effect, so none is offered. Nothing calls them without a mod.
Unproven: not yet compiled or probed live (build queue); the integration branch tests it.
native_player_food(uuid) reads a player's food level, saturation and
exhaustion; native_set_player_status(uuid, health, food, xp) sets each
that is given, as Bukkit's setHealth, setFoodLevel, setSaturation,
setExhaustion, setLevel, setExp and setTotalExperience. The write goes
through the player's turn as /kill and /effect reach a player
(for_player), so their client gets SetHealth and SetExperience with the
tick's sync. Health is capped at the maximum; 0 or less kills as /kill
does, with "<player> died" (Bukkit's setHealth(0), LivingEntity.die with
the generic source). The mod's food is not heard as the player's own
change (native_food_level_changed) outside their turn. Nothing calls it
without a mod.
Unproven: not yet compiled or probed live (build queue); the integration branch tests it.
The helper native_player_compound(uuid) gives a player's compound as the
server would save it now (players/data/<uuid>.dat, playerdata::player_tag),
vanilla's ServerPlayer.saveWithoutId: what a mod keeping players in step
across servers (a HuskSync port) stores, instead of calling player_tag on
the hub's guests itself. None inside a player's turn, where their live
state is out of their record, and for a player not on. The new module
native_player_state.rs holds this group's helpers. Nothing calls it
without a mod.
Unproven: not yet compiled or probed live (build queue); the integration branch tests it.
native_profile_by_name(name) gives the id and name, and
native_profile_by_uuid(uuid) the name, of a player who is on, else one
in a dedicated server's profile cache (usercache.json): vanilla's
GameProfileCache, Bukkit's getOfflinePlayer without its network
lookup. They ask no profile service and make up no offline player;
UserCache::find_by_name reads without changing the cache. native_player_head
names a player who is off from the cache too. Ports parse
usercache.json or ask Mojang themselves today.
frame::contents made a new ZlibEncoder for every packet above the
compression threshold, which allocates and clears zlib's window and hash
tables (some 300 KiB) each time. A thread-local Compress, reset before each
packet and driven with the encoder's own calls (the input unflushed, then
finish), writes the same bytes. A new test checks them against a fresh
encoder for bodies from 256 bytes to 300 KiB, in both orders.
native_set_inventory_slot(uuid, kind, slot, expected, new) puts a stack
in any slot of a player's inventory or ender chest, armour and off hand
included, if it still holds exactly `expected`, and returns what was
there (Bukkit's setItem). native_take_stacks(uuid, slots) takes exact
stacks out of the given slots, all or none. Both are checked like
native_move_stack and work live or on the player's file. Wanted by the
InvSee, AdminKit and Wanted ports (armour slots, splitting, stakes);
Wanted edits LanHost guests directly for this today.
Unproven: not yet built or run on a server (handed to the hooks-all integration test).
Part of #10461.
native_set_inventory(uuid, kind, slots, selected) replaces a player's 41
inventory slots or 27 ender chest slots (Bukkit's
PlayerInventory.setContents), live or in their file like
native_inventory. For the inventory it sets the held hotbar slot, empties
the cursor and the 2x2 crafting grid, and sends a player on the server the
whole inventory at once. The HuskSync port writes Guest fields directly
for this today.
Unproven: not yet built or run on a server (handed to the hooks-all integration test).
Part of #10461.
native_block_place(player, name, stack, pos, state, cancel) is heard
where BlockItem.place has found the position and the state and checked
survival, before anything is set (Bukkit's BlockPlaceEvent, NeoForge's
BlockEvent.EntityPlaceEvent). A canceled placement is a failed one: no
block, no use, no swing, and the client gets its blocks and inventory
back. The position and state come from the existing dry run
(Behaviours::placement_state). Ports cancel placements today by claiming
native_use_item_on, which also stops doors, chests and the item's other
uses.
Unproven: not yet built or run on a server (handed to the hooks-all integration test).
Part of #10461.
native_inventory_drag(player, name, window, kind, slots, cancel) hears a
guest's quick craft in their own inventory or a vanilla menu once, at its
end, with the kind and the window slots the client dragged over (Bukkit's
InventoryDragEvent). A canceled drag's end is handled like a canceled
click: the drag ends empty and the client's prediction is answered with
the server's slots and cursor.
Unproven: not yet built or run on a server (handed to the hooks-all integration test).
Part of #10461.
native_inventory_click(player, name, window, slot, button, mode, cancel)
hears a guest's click in their own inventory or a vanilla menu they have
open before any handler applies it (Bukkit's InventoryClickEvent). A
canceled click becomes a drag start of no type, which every menu takes as
resetQuickCraft and nothing more, so nothing moves and the client's
predicted slots and cursor are answered with the server's, as for any
click the server refused. Drags and a mod's own menus are not heard.
Wanted by the HuskSync and Essentials ports (data lock, freeze).
Unproven: not yet built or run on a server (handed to the hooks-all integration test).
Part of #10461.
native_player_launch_projectile_v2(player, name, stack, cancel) is heard
where ProjectileItem.use creates a snowball, egg, ender pearl or a mod's
thrown item, before the projectile exists (Bukkit's ProjectileLaunchEvent
for a player). A canceled throw is a failed use: nothing is thrown, used
or put on cooldown, v1 hears nothing, and the guest's client, which took
the item at once, gets its inventory back. Wanted by the HuskSync port's
data lock (no ender pearls while locked).
Unproven: not yet built or run on a server (handed to the hooks-all integration test).
Part of #10461.
native_player_textures(uuid) gives the textures property (value and
signature) of a player who is on, as their player list entry carries
it (Paper's Player.getPlayerProfile). native_player_head(uuid) gives a
minecraft:player_head whose profile holds the player's id, and their
name and skin when they are on (Bukkit's SkullMeta.setOwningPlayer).
Ports look the current skin up at the session server today, or build
heads with a name-only profile.
native_player_pre_login(player, name, address, defer) runs on the
connection's own thread once the session service or the proxy has named
the player and before the server's own checks (Bukkit's
AsyncPlayerPreLoginEvent). A mod that needs time, a permissions or
inventory load, or may refuse, pushes a Receiver<Result<(), Text>> onto
defer and answers from any thread: Ok lets the player on, Err refuses
them with the reason. The login waits for every answer, 20 s at most in
all; a missing or dropped answer refuses with a plain Disconnected.
Today such loads start in native_player_login, where a database failure
cannot refuse the player.
native_set_profile_property(player, name, value) sets or removes a
property of an online player's profile (Paper's Player.setPlayerProfile);
native_refresh_player(player) shows them again at the end of their
level's pass: everyone who sees them gets their list entry anew and
their entity again, and the player is respawned in place into the same
level with all data kept (Paper's hidePlayer/showPlayer plus the
respawn), so their own client draws a new skin too. Both work from any
server hook, native_server_tick included. They replace the raw HEAD
injection on Game::flush_native_net that edits Guest.properties and
sends the player info itself.
native_player_profile(player, name, properties) runs on the
connection's own thread once the server has let a player in and before
it sends LoginFinished. A mod may change, add or remove the profile's
properties (a skin's textures): the player's own client and every
player list get the new ones (Velocity's GameProfileRequestEvent,
Paper's PlayerProfile at AsyncPlayerPreLoginEvent). It replaces the raw
HEAD injections on lan::conn::login_and_configure and
Game::lan_guest_joined that skin and Floodgate ports use today.
The helper native_player_permission(player) gives the permission level of a
player on the server or joining it (0 to 4, ops.json's for an operator,
PlayerList.getProfilePermissions). It works inside native_player_login for the
player that hook hears, whose level the hub set on admitting them, so a login
gate that lets operators through needs no ops.json of its own. The hook's
contract is unchanged.
The helper native_send_custom_payload_to_player(player, channel, data) queues
a ClientboundCustomPayloadPacket on any namespace:path channel outside
horizon: (at most ClientboundCustomPayloadPacket's 1 MiB) for one player's
client, vanilla or Horizon, sent at the tick's end with the mod messages, so
it works from any server hook. Bukkit's Player.sendPluginMessage.
native_custom_payload_from_client(player, channel, data) hears a guest's
ServerboundCustomPayloadPacket in play on any channel that isn't Horizon's own
(horizon:mod_net, horizon:quiet_commands, horizon:lod), which the server drops
today. Like mod messages it waits for the tick's end, with the hub back, so
every helper works; it is heard from a dead player too, as vanilla's
handleCustomPayload doesn't look at health. Horizon's own channels are handled
as before. The client side is #6774.
With proxy forwarding on (forwarding.secret, offline mode, not the integrated
server's own player) the handshake's host is read with Short.MAX_VALUE as its
limit, as Paper does behind a proxy, so a proxy's data after the host (a
Floodgate player's, often longer than 255 characters) no longer drops the
login. Without forwarding the limit stays vanilla's 255; it is the only string
read with that limit.
native_handshake(address, host, intent, cancel, reason) is a free function in
horizon_client_lib::lan::conn, called on the connection's own thread for each
network connection's handshake, before the server acts on the intent. A mod may
rewrite the host (a proxy's data after it, Floodgate's, included) or refuse the
connection: a login or transfer is disconnected with the reason, a status ping
is closed unanswered. Wired through NativeClientHooks like player_chat. Nothing
changes without a mod.
native_creative_slot_v2(player, name, slot, stack, cancel) is an
additive version of native_creative_slot with the whole ItemStack put in
the slot (none for an emptied one), components included. It is heard
just before v1, which keeps its signature and firing point and hears
nothing of a packet canceled here.
Part of #10461.
native_item_dropped_v2(player, stack) is an additive version of
native_item_dropped with the whole ItemStack thrown, components included
(enchantments, unbreakable, attribute modifiers...), heard just before
v1, which keeps its signature and firing point.
Part of #10461.
native_item_picked_up_v2(player, stack, thrower, position, canceled) is
an additive version of native_item_picked_up: the whole ItemStack with
its components, the item entity's thrower UUID (none for block and mob
drops) and its position. It is heard just before v1, which keeps its
signature and firing point and hears nothing of a pickup canceled here.
Part of #10461.
native_player_spawn_location(player, name, dimension, position,
rotation) is heard once per join after the saved data is loaded (or a
newcomer's spawn found) and before the level is chosen and the world is
sent (Spigot's PlayerSpawnLocationEvent). A mod that changes them places
the player there, in that level, with no teleport afterwards. An unknown
dimension or a non-finite value leaves that part unchanged.
Part of #10461.
native_player_hurt_by_player(attacker, attacker_entity, victim,
victim_entity, source, damage_type, amount, canceled) names both players
by UUID and network entity id, and how the hit came: melee, sweep, or the
projectile's entity type (arrow, spectral arrow, snowball, egg, a mod's
thrown type). It is heard where native_living_incoming_damage is, at the
victim's hurtServer past its guards (pvp rule and teams allow it, victim
not invulnerable or dead), and just before it: changing the amount
reaches v1, canceling skips the hurt and v1 alike. The PvP call sites
name the hit in a thread-local scope that hurt_server reads.
A snowball or egg does zero damage, where vanilla's Player.hurtServer
stops: it is heard there with amount 0 and nothing the mods set applies.
Thrown tridents hurt no players in Horizon yet and are not heard.
Part of #10461.
native_player_logging_out(player, name) is heard when a placed player's
connection is gone (lost, kicked, quit) but before their menu closes and
they are saved and removed (Bukkit's PlayerQuitEvent): they are still in
their level and listed in native_server_players. No state is swapped in;
the hub is back in self.lan for the call, so the UUID helpers and
native_run_command reach them. A death a mod causes there (`kill <name>`)
is died at once (death message, drops, native_player_died), since the
leaving player gets no further tick to die in; they are then saved dead.
Not heard when the server stops, where native_player_logged_out already
tells with them still listed. Unmodded servers are unchanged: the death
step only runs when the hook took the player from alive to zero health.
Part of #10461.
native_log_line(level, thread, message, line) hears each message
written to logs/latest.log, on the log's writer thread after the file
has it, so the thread that logged never waits on a mod and a Discord
console bridge needs no tailing of latest.log. line is the text as the
file got it. The logger calls it through logging::set_line_hook,
installed before the log opens; trait mods are reached through
NativeClientHooks. A callback that panics is not called again, so its
own panic line can't loop.
Refs #10461
native_console_line(source, line, cancel, replies) hears each line the
dedicated server's console is about to run, before vanilla's commands,
the dedicated-only ones and mods' registered ones: source is console
(typed on the console, or sent by horizon-server as the console) or
rcon. Set cancel and it does not run; replies are logged as the
console's feedback, so they also reach the RCON or horizon-server
caller. Until now native_perform_console_command only heard commands
a mod registered, so an audit mod could not see ordinary console
commands (Bukkit's ServerCommandEvent / RemoteServerCommandEvent).
Refs #10461
A mod limits one player's view distance below the server's: only that
player is told its radius (SetChunkCacheRadius) and is sent and tracks
chunks within it; native_player_view_distance reads it. The guest's
distance is ChunkMap.getPlayerViewDistance under the limit; with no limit
(every unmodded server) it is exactly as before. Part of #10461.
A level's world border bounds as the server enforces it, for a web map's
border line and a pre-generator's border selection. Vanilla 26.3 keeps a
border per level (ServerLevel.getWorldBorder, each level's
data/minecraft/world_border.dat); Horizon keeps one for every level, so
the helper answers the same bounds for each level until that is fixed.
No behaviour change. Part of #10461.
native_force_chunk only reaches the level in place, so a mod could not hold
a chunk of the Nether or the End from a server-wide hook, and a level not
open yet never ticked: a pre-generator for those levels never advanced.
native_force_chunk_in takes the dimension; a level held this way opens at
the next server tick and ticks while held. Unmodded servers hold no mod
tickets, so nothing changes for them. Part of #10461.
A composite program writing two colortex buffers was skipped whenever their
`size.buffer` lines differed, even when one relative size and one pixel size
came to the same size (`0.25 0.25` and `320 180` at 1280x720). Iris 1.11.7
draws it (a probe pack of ours: texel (100, 50) of both reads 100.5, 50.5 and
texcoord 0.314, 0.281).
Sizes of one kind that differ still skip the program as the pack loads. A mix
of the two kinds is kept, and the pass is skipped only at render sizes where
the buffers it writes resolve to different sizes.
Fixes#10454
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`RealmsSelectWorldTemplateScreen.Entry`'s 15x15 `icon/link` and
`icon/video_link` buttons beside each row's version, opening the
template's page or trailer; the drawn rectangles are kept for the click.
Co-Authored-By: ZCode <[email protected]>
Keep the command-block /say LAN regression and upstream legacy PvP checks,
with both registrations intact. Preserve upstream function bodies and the
original say regression unchanged.
Validation: Python syntax/AST and registrations checked; public-tree check
passed; Python unittest suite passed (41 skipped). pr_ready could not start
Rust checks because Cargo is unavailable in this restored environment.
Co-Authored-By: Codex <[email protected]>
Part of #9996.
BlockSection::set and BlockSection::fill_run are 7.0% of the proto phase
in the overworld and 9.4% in the Nether (callgrind on generate_proto).
Each write located its packed word twice, and fill_run rebuilt the word
mask for every block.
PackedStorage now hands out a Slot (word and bit shift) that read_at and
write_at take, so set locates the entry once; the palette-growth branch,
which re-packs the words, looks it up again. A run in one column moves
256 entries per Y, so while a word holds a whole number of those steps
(every width generation produces) the bit shift stays the same along the
run and WordRun writes each block with one load, mask and store. Widths
that do not divide the stride go one set per block, with the same palette
edits in the same order.
Live palette slots hold distinct states (sections loaded from disk are
rebuilt through set), so "already this state" is old_id == id and "was
air" is old_id == air_id, both resolved once per call. set on a uniform
section has its own small path.
Palettes, their order, the free list, counts, the chosen width and the
packed words are unchanged: the 8 chunkgen_bench snapshot files (terrain
and decorated; overworld seeds 12345 and -1, the Nether, the End) are
byte-identical to main, and a new unit test replays random set/fill_run
sequences over every palette width against main's fill_run.
Proto-phase instructions: overworld 6.497 G -> 6.340 G (-2.41%), Nether
2.974 G -> 2.859 G (-3.84%).
A first draft by DeepSeek V4.1 Flash (pi coding agent), reviewed by Claude.
Co-Authored-By: pi coding agent (DeepSeek-V4.1-Flash) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Fekd3xkgubE8VfXrALh2td
RealmsResetWorldScreen's tiles, RealmsSelectWorldTemplateScreen's paged
list, the local-world picker and RealmsUploadScreen with the tar.gz
packer and the upload body. Follows the 26.3 bytecode of those screens,
client.worldupload.RealmsWorldUpload/RealmsUploadWorldPacker/FileUpload
and RealmsClient.fetchWorldTemplates/resetWorldWithTemplate/
requestUploadInfo/putIntoMinigameMode.
Co-Authored-By: ZCode <[email protected]>
The Players tab's second button now opens RealmsInviteCodesScreen: the count in the
title, the subtitle, a row per code with its expiration (or Never), Copy / Edit / Delete
per row, and Create. RealmsEditInviteCodeScreen edits one code: its active flag, the
seven InviteCodeExpiration choices (a click on the slider track picks the nearest — this
screen has no drag event yet), Copy to the clipboard (a new Outcome::Copy the title
turns into Action::CopyText), Delete, Done and Back.
RealmsClient gains inviteCodes (GET api/v1.0/invite/codes?realmId={id}),
createInviteCode (POST .../invite/codes), updateInviteCode (POST .../update) and
deleteInviteCode (DELETE .../delete/{code}), all on the invite-code service with the
token in x-mojangauth, with InviteCode's DTO. The existing redeemInviteCode now goes
through the same helper. An existing expiration reads back as the closest of the seven
choices (InviteCodeExpiration.closestTo).
Co-Authored-By: ZCode <[email protected]>
A connection reported its world while still connecting, before a failed
login could drop it, and a resource-pack reload's loading screen still
counted as playing.
Co-Authored-By: Claude Opus 5.5 <[邮箱]>
Round-two review finding: a long invite's identity kept only the host's
addresses when it had any. Two friends on other networks behind the
same private address and the fixed direct port (25580), each reached
through a relay, shared one NativeWorldId. The relay's address and
nameplate now stay beside the addresses; neither secret takes part.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review findings on the session state helpers:
- NativeClientState.screen read remote_loading(), which leaves out the
portal loading screens because they draw no panorama blur, so a
minimap drew over them. A connection's screen is now one predicate,
remote_screen_up(), which remote_overlay draws by too.
- Every long friend invite showed as the same "Friend's world", so two
friends' worlds shared one NativeWorldId and a mod's per-world data.
A friend's world is now friend: and the invite's public part (the
host's addresses, else its relay nameplate), never its secret.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A minimap or waypoint mod can now read what it needs through stable
helpers instead of Game's fields, on a server's client and in single
player alike:
- native_client_state() gains screen (NativeScreenKind: playing, chat,
death or another screen), debug_screen (F3), world (NativeWorldId:
single player's save folder or the server's address, for per-world
data) and sleeping_pos (the bed, LivingEntity.getSleepingPos).
- native_biome(pos): the biome at a block as Level.getBiome picks it,
through BiomeManager's zoom.
- native_data_dir(mod_id) joins the helper catalog.
The window's connection remembers the save it joined through memory,
its own server thread's or P1's for a splitscreen seat, so the world
identity needs no mixin on open_integrated. A friend's world is named
as players see it, never by its invite secret.
Helper-only API fragment session_state.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Keep the map-crafting, transmute, mob-effect and frame-map groups and their
isolation sets. The merged game passes the focused 62-record Java replay.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
`Progress::done` on an advancement parsed from `{"criteria": {}}` (true, no
requirement groups to wait for) against one with a criterion (false), and
`Tree::automatic` over a tree holding both: only the empty one is listed, which
is what the load pass grants the rewards of.
Without the `done` change the first assertion fails at once
(`assertion failed: Progress::default().done(&empty)`), and `Tree::automatic`
does not exist to compile against.
`AdvancementProgress.isDone` loops over the requirement groups, so with no
groups at all it answers true: an advancement whose `criteria` map is empty is
finished from the moment the player's progress loads. `PlayerAdvancements.
checkForAutomaticTriggers` walks every advancement, and for those with no
criteria calls `award(holder, "")` - which finds no `""` criterion to grant, so
it neither toasts nor announces - and then `AdvancementRewards.grant`s it. The
rewards are granted again on the next load, because there is no criterion to
record them under.
Horizon required `!requirements.is_empty()`, so such an advancement was never
done and never paid out. `Progress::done` now keeps vanilla's shape (every group
satisfied, vacuously true with no groups), `Tree::automatic` lists the
advancements nothing can grant, and the progress load grants their rewards
through the new `grant_rewards`, which is the `AdvancementRewards.grant` half of
`award` split out so the toast and announcement stay with a real completion.
Scope, plainly: **no vanilla advancement has an empty criteria map** - all 1224
in the shipped data have at least one criterion - so nothing in the game's own
data changes. What changes is a datapack's: before this, an advancement it
declared with `"criteria": {}` could never be obtained and its rewards never
arrived, where vanilla hands it over on load.
`StatsUi::key` as `Game::stats_key` calls it: the cycle wraps both ways, a
number picks its tab and is clamped to the last one, a key without Control is
left alone, an empty Items or Mobs tab is skipped (and not switched to by its
number), and a switch scrolls the new list back to the top.
`StatsScreen.keyPressed` runs `MenuTabBar.keyPressed` first: with Control
held, `TabNavigationBar` cycles the tabs on Tab and Shift+Tab (skipping a tab
that cannot be chosen) and picks one on the number keys, where
`InputConstants.Key.getNumericKeyValue` reads the main row's digits and the
keypad's. Horizon's statistics screen had no keyboard handling at all, so only
the mouse could change tabs.
`StatsUi::key` is that tab-bar key press: it reports whether the bar took the
key, moves to the chosen tab when that tab has rows, and starts that list at
the top as a tab press does. `tab_key` ports `getNextTabIndex` (a digit is
`floorMod(digit - 1, 10)` clamped to the last tab; Tab walks to the next tab
that can be chosen, one full turn at most) and `numeric_key_value` ports
`getNumericKeyValue`. `Game::stats_key` gives it the tabs that have rows
(`stats_tab_active`) and plays the tab's click when the tab changes, and
`Game::key_pressed` hands the statistics screen its keys.
native_health_hud_state described left_height and right_height as the ones
that place the selected item's name and the action bar. They are gone, and
the text no longer reads them.
The two calls in hud_fixture/kinds.rs still passed the 39 the old signatures
took. The probe compares against the vanilla client, which reads neither, so
nothing else changes.
cargo clippy --examples -p horizon-client-lib -p horizon-client -- -D
warnings is clean.
The dedicated server and a LAN host decrypt each client's
ServerboundKey with the server's private exponent. That used
square-and-multiply, which multiplies only on the exponent's set bits,
with a data-dependent final subtraction in each Montgomery product, and
no blinding: a client controls the ciphertext and can time the reply,
the classic extra-reduction channel. The key is regenerated every start
(as in vanilla) and a recovered one still needs an on-path position,
but vanilla's JCE blinds this operation.
- Mont::mul subtracts n through a mask chosen from the overflow and
the borrow of t − n, never a branch (Big::sub_with_borrow,
Big::select). Every product takes the same steps.
- Mont::pow_secret: a square and a multiply for every bit of the
exponent's limbs, the product kept by a mask. RsaPublicKey::
apply_secret uses it; RsaPrivateKey::decrypt and ::sign call that.
Public-key verification and encryption stay on the faster pow.
- Big::from_be_bytes no longer skips zero bytes: it parses the private
exponent on every call, and the result is the same.
- Test: pow and pow_secret agree with a reference modular power, for
moduli near a limb boundary (the overflow case) and exponents with
leading zero limbs. The 11 existing RSA tests (OpenSSL signature,
known-key decryption, generated keys) pass unchanged.
No wire or parity change. Multiplicative blinding would be the more
complete fix once a modular inverse exists.
Found in yahya-nusseibeh's review of Horizon (L1).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
Hud.extractSelectedItemName is hud_overlays::ToolHighlight::draw and
Hud.extractOverlayMessage is hud_overlays::Titles::overlay_message; neither
row existed. map-check: 151 rows, no problems.
text_position_tests asks a ToolHighlight holding a stack and a Titles
holding an action bar for their draw calls at 320x240: the name's text top
is 240 - 59 (and 240 - 45 in a mode that cannot hurt the player), the action
bar translates to 240 - 68 with its text four pixels under that, and a
zeroed timer draws nothing. The font is built from no resources, as
chat_tests does, so the test needs no game assets.
Hud.extractSelectedItemName draws the name at guiHeight - 59, plus 14 in a
mode that cannot hurt the player, and Hud.extractOverlayMessage puts the
action bar at guiHeight - 68: both constants, which the health, food, air and
mount rows above the hotbar never move.
Horizon moved both up by max(leftHeight, rightHeight). That is 59 for a
survival player with armour, so the common case agreed by accident, but it is
69 with a second row of hearts and 81 for a player underwater on a mount, and
the two texts floated ten or twenty pixels above where vanilla draws them.
Both use the constants now, and HealthHud no longer keeps the heights: 26.3's
Hud has no such fields, only the locals of extractItemHotbar that the
contextual-info bar reads.
EnchantmentHelper.doPostAttackEffectsWithItemSource runs the victim's
equipment before the attacker's weapon: the player's melee hit and sweep,
arrow, trident and wind charge now meet the mob's own post_attack
effects. Thorns rolls from the level's random, hurts the player from
the wearer by 1-5 and wears the piece by 2 (Unbreaking, breaking with
its entity event); the player's own Thorns doesn't answer a thorns hit,
and a thorns hit that lands whole plays enchant.thorns.hit.
The mace harness gets armored targets with Thorns and Unbreaking; new
lan_e2e check mob-thorns.
Part of #9954
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The port ran 0.1's grade on linear values, which darkened midtones by about
nine 8-bit steps. The soft shoulder and the saturation are a look written for
display values, so the grade now runs them on the encoded picture, while the
golden tint, the glow and the vignette stay on light. The pack drops its own
[tonemap]: Horizon's curve clips at white in SDR as 0.1's clamp did, and light
above white survives the grade for HDR. The captures now match 0.1 within
about 0.3 steps on average; what's left is the glow around light sources.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Apply the existing leash writer after the boat's base command tag so live
holders and pending links override stale input data. Keep holder policy
and boat behavior in their existing implementations.
Add an actual Game getter regression and a guarded Java/Game fixture
covering owner clicks, repeated reads, pending UUIDs and detached links.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Through `ChatScreen::new` with `is_draft`, `key` and `draw` - the calls
`Game::open_chat` and `Game::chat_key_code` make: the draft's drawn text is
grey and italic, an edited draft and a box opened on ordinary text are not,
and the first Backspace on an untouched draft empties the box where an
ordinary box loses one character.
`ChatScreen` keeps the `isDraft` its constructor argument sets: `formatChat`
draws the text grey italic until `onEdited` runs, and `keyPressed` clears the
whole box on the first Backspace while it is still set. Horizon tracked the
draft (`Game::chat_draft`, `chat_draft_shown`) but opened the box as plain
text: `open_chat_method` knew whether the text was the draft and did not pass
it.
`ChatScreen::new` takes `is_draft` (`ChatScreen.init`'s `isDraft`), `edited`
(`onEdited`) clears it, `key` (`keyPressed`) clears the input on a Backspace
while it is set, `format` applies the draft formatter (`formatChat`, which
`ChatScreen.init` registers before `CommandSuggestions.formatText`) and
`resize` keeps it (`ChatScreen.resize` keeps the field).
`Game::open_chat` threads the flag from `open_chat_method`
(`ChatComponent.createScreen`); the in-bed wake-up
(`InBedChatScreen.onPlayerWokeUp`) and `Gui.openChatAndAddText` open plain
text, as vanilla does.
Broadcast unsigned say chat independently of feedback capture and keep the
command source display name. Add a two-client LAN regression for both
TrackOutput states and conditional-chain success.
Co-Authored-By: Codex <[email protected]>
Each frame picks the layers of the Overworld's or End's sky whose day,
weather, biome and height hold at the camera, eases them over their
transition, and draws them as 3x2 skyboxes with their blend mode, over the
vanilla sky's disc and sunrise (the End's box) and under the sun, moon and
stars. Packs without optifine/sky draw nothing new.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Parse optifine/sky/world<N>/sky<M>.properties into layers (source, fade
times, blend, rotation, days, weather, biomes, heights, transition), the
sun and moon_phases replacements, and the 3x2 skybox grid's faces.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
AgeableMob.isBaby is now a row of its own pointing at Mob::is_baby, which is
`can_be_a_baby && getAge() < 0`; canBeABaby points at
species::can_be_a_baby, and the six classes that override it to false carry a
row each so the false arms are not read as unported.
living.rs::a_negative_age_is_a_baby_only_where_can_be_a_baby_allows_it drives
Mob::new on the loading table's own types and sets Age to -24000, the
BABY_START_AGE a save or a /summon carries, then asks is_baby, dims and
voice_pitch - the three the game draws and hears from.
A parrot, a tadpole, a frog, a zombie nautilus, a camel husk, a slime, a magma
cube, a piglin brute and a creeper keep their type's box (the tadpole 0.4 by
0.3, the camel husk 1.7 by 2.375, the piglin brute 0.6 by 1.95) and voice at
1.0 ± 0.2. The control is a cow at the same age: is_baby true, Cow's
BABY_DIMENSIONS (0.45 by 0.7, eyes at 0.69) and 1.5 ± 0.2, and an adult box and
voice again at age 0. With is_baby back to `age < 0` the test stops at the
parrot.
entity_nbt's save test now runs a frog and a camel husk beside the slime, the
magma cube and the pig: the first four write no Age and keep none of a raw
compound's Age, the pig writes its modeled -37.
AgeableMob.isBaby is `canBeABaby() && getAge() < 0`, and canBeABaby is true
across the ageable tree but for the six classes that override it to false:
Frog, Parrot, ZombieNautilus, CamelHusk, Slime and MagmaCube (an
AbstractCubeMob is an AgeableMob). Everything outside that tree inherits
LivingEntity.isBaby's false, so a Giant, a Creeper, a PiglinBrute (it extends
AbstractPiglin and declares no isBaby of its own) and a Tadpole, whose own Age
runs up from -24000, are never babies either; only the zombies, the piglins and
the zoglin answer a synced DATA_BABY_ID instead of an age.
Mob::is_baby read `extra.age < 0` alone. The list of who can be a baby existed
already, but only as mobs::drops::can_be_baby, which gates the metadata baby
flag and the loot table: the box, the voice, breeding, growth and the goals
read the age and so called a frog, a parrot, a creeper or a tadpole with a
negative Age a baby. The list is now species::can_be_a_baby, on the species
table, and is_baby is `can_be_a_baby && age < 0`.
What that changes: a tadpole (Age -24000, its own DEFAULT_AGE) is drawn at
getAgeScale's 1.0 instead of 0.5 and speaks at 1.0 ± 0.2 instead of 1.5 ± 0.2,
as vanilla does; a parrot, a frog, a zombie nautilus, a camel husk, a slime, a
magma cube, a piglin brute, a creeper or anything else the table cannot make a
baby keeps an adult's box, voice, breeding, growth and drops. The Parrot arm of
voice_pitch was there for the same reason and is dead now: Parrot.getVoicePitch
is `getPitch(random)`, the plain range an adult already gets.
AgeableMob.addAdditionalSaveData writes Age only when canBeABaby, and the
branch that drops the values a raw compound carried named four classes by hand;
it asks the ageable table now, which also covers the frog and the camel husk
that were re-saving an Age vanilla writes for neither.
Record a marker only for Nightlies installed by automatic updates. Keep
manual and legacy Nightlies out of GUI and CLI cleanup, while retaining
the newest managed Nightlies separately from game releases.
Add regression coverage for manual, legacy, selected and pinned builds.
Co-Authored-By: Codex GPT-6 <[email protected]>
The Worlds tab's Backup button now opens RealmsBackupScreen: a row per automatic backup
with its date (mco.backup.entry), Restore per row, and Back. A row opens
RealmsBackupInfoScreen: the backup's metadata as translated key/value rows
(mco.backup.entry.<key> when the language has it). Restoring asks nothing (vanilla's
Restore button restores at once) and the list reads again when it is done.
RealmsClient gains backupsFor (GET worlds/{id}/backups) and restoreWorld
(PUT worlds/{id}/backups?backupId={id}, vanilla's 40 s/600 s timeouts), with Backup's DTO
(backupId, lastModified, size, metadata, changeList). The backup id goes into the query
percent-encoded, so it cannot add a parameter.
Download Latest still needs the download-and-unpack flow and is left for its own PR; the
rows show the date and the metadata screen the details (size included).
Co-Authored-By: ZCode <[email protected]>
These labels reach tr through a variable (the Launcher settings' slider rows, MODE_NOTES in
the server dialog), so the scan for tr("...") never asked for them and no table had them: they
showed in English in every language. Added to every table, in English, and translated in
fr.json.
The GUI runs the shared pruning path after saving its selection, but its
release-only filter excludes every dated Nightly. Include those builds
so the existing retention, selection, instance-pin and in-use protections
also apply to GUI Nightly updates.
Add regression coverage for following updates, selected and pinned
Nightlies, and retention across Nightly/release/local builds.
Co-Authored-By: Codex GPT-6 <[email protected]>
Use the level's backed maps for crafting recipe matching and run map
post-processing on the actual stacks taken by swap, pickup and quick-move.
Keep the original-copy callback used by quick-move and preserve cartography.
Add a real Game/Menu regression and a guarded connected-owner Java/Game
fixture covering previews, consumed inputs, transferred maps and saved backing.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
The Players tab's second button now opens RealmsInviteCodesScreen: the count in the
title, the subtitle, a row per code with its expiration (or Never), Copy / Edit / Delete
per row, and Create. RealmsEditInviteCodeScreen edits one code: its active flag, the
seven InviteCodeExpiration choices (a click on the slider track picks the nearest — this
screen has no drag event yet), Copy to the clipboard (a new Outcome::Copy the title
turns into Action::CopyText), Delete, Done and Back.
RealmsClient gains inviteCodes (GET api/v1.0/invite/codes?realmId={id}),
createInviteCode (POST .../invite/codes), updateInviteCode (POST .../update) and
deleteInviteCode (DELETE .../delete/{code}), all on the invite-code service with the
token in x-mojangauth, with InviteCode's DTO. The existing redeemInviteCode now goes
through the same helper. An existing expiration reads back as the closest of the seven
choices (InviteCodeExpiration.closestTo).
Co-Authored-By: ZCode <[email protected]>
The messages the Mods page shows after an install (a mod, a native mod, a data pack, a
modpack, a resource pack) and its "isn't set up" errors were built with format!, so they
stayed in English in every language: "Installed JEI for Horizon 0.1.0 into Horizon. Build to
play with it." on a French launcher. They now use trf with named holes. The 11 strings are
added to every table, in English, and translated in fr.json.
With the switch, the game draws 0.2 packs ([frame] blend = "linear" is
still refused). A renderer made with a 0.2 pack picked draws a float frame
where the device can, else the 8-bit one; a pack picked or dropped later
runs on the frame there is, logged once. The renderer's material, sky,
weather, vertex and shadow entries decode a 0.2 stage's colours and encode
its results; scene_color, opaque_color and the water mirror are decoded
copies, shadow_color is float, and Frame's sky and fog colours are
decoded. The post chain ends in a window-size picture, and the pack's
[tonemap] (or Horizon's clip) draws it onto the frame, encoded at alpha 1.
With HDR on, HDR output lifts nothing of a 0.2 pack's world.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The configuration screen's Settings tab (RealmsSettingsTab): the world's name and
description fields (the click moves the caret between them), the region preference line
and its Region Preference button, and the open/close toggle moved here from the footer
(vanilla keeps it on this tab). Leaving the tab — switching tabs or Back/Escape — saves
the changed name, description and preference through RealmsClient.updateConfiguration
(POST worlds/{id}/configuration with RealmsConfigurationDto's JSON: the active slot, its
settings, the preference and the description).
RealmsPreferredRegionSelectionScreen: the automatic choices and every manual region, a
click selects, Done writes the choice back to the tab and Cancel drops it.
RealmsClient gains getPreferredRegionSelections (GET regions/preferredRegions) and
updateConfiguration; RealmServer reads regionSelectionPreference; RegionSelectionPreferenceDto,
RegionDataDto and RealmSetting serialization join the DTOs.
The tier's render/simulation distance options and the region quality icons are the next
PRs (the icons need sprites the GUI atlas does not carry yet); the region rows say their
name for now.
Co-Authored-By: ZCode <[email protected]>
The example pack written for 0.2: its sky and post passes in linear light,
with the 0.1 pack's gamma constants decoded, and a [tonemap] stage of its
own that eases colours above three quarters of the headroom toward it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The panel takes the Glass look from docs/design-system.md: the sunset with pixel ridges, frosted
plates, Geist and Bricolage Grotesque Heading, the theme's Hugeicons. What it already did gets a page
each in a sidebar (overview, players, console, backups, crash reports), and confirm()/prompt() become
in-page dialogs. The supervisor serves the theme's fonts and the mark; the CSP gains font-src 'self'.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Jh5A2PzdusBChqE38VZ3zD
2026-11-04 23:29:16 +00:00
802 changed files with 57435 additions and 4443 deletions
@@ -51,7 +51,7 @@ Game workspace (root `Cargo.toml`); `launcher/` and `crates/horizon-telemetry-se
| `horizon-command` | Command parsing (Brigadier): the vanilla tree, argument types, selectors, suggestions, the tree's network form. Re-exported as `horizon_client::command` |
| `horizon-client-core` | The client lib's bottom layer: `text.rs`, `lang.rs`, `snbt.rs`, `nbt_path.rs`, `component.rs`, `scoreboard.rs`, `bossbar.rs`, `gamerules.rs`, `block_entity_nbt.rs`, registry/particle/sound tables. Re-exported under the old paths (`horizon_client::text`...) |
| `horizon-client-input` | Key bindings and key names, toggle keys, mouse options, controller settings and glyphs, the clipboard, window options (`keybinds.rs`, `gamepad.rs`, `mouse.rs`, `window.rs`...). Re-exported under the old paths |
| `horizon-client-render` | The client's world data, rendering and screens (chunks, meshing, light, streaming, `gfx`, HUD, menus, settings): ~80 modules that horizon-client re-exports under the same paths. Nothing in it reaches gameplay |
| `horizon-client-render` | The client's world data, rendering and screens (chunks, meshing, light, streaming, `gfx`, HUD, menus, settings), modules that horizon-client re-exports under the same paths. Nothing in it reaches gameplay |
| `horizon-world` | Vanilla world data with no gameplay loop: NBT, regions, chunks, `datafix/`, `worldgen/`, `level/` (server-side block behaviour, ticks, redstone), entity physics, items, effects, food, damage |
@@ -65,6 +65,7 @@ Game workspace (root `Cargo.toml`); `launcher/` and `crates/horizon-telemetry-se
| `horizon-mods`, `cargo-horizon-mod` | Content mods (`.hmod`: manifest, content fence, no code), the registry index and `.hpack`, and `cargo horizon-mod` (`new`, `build`, `run`, `check`, `registry`) ([docs/mods.md](docs/mods.md)). Code mods are native, built by the launcher ([docs/native-mods.md](docs/native-mods.md)) |
| `horizon-optifine` | OptiFine-format pack files below every feature that reads them: `.properties`, the name/range/block matchers, paths and `optifine/` folders, and custom entity models (`cem/`: `.jem`/`.jpm` parsing and baking) |
| `horizon-polytone` | Polytone-format pack files (`assets/*/polytone/`), read clean-room from the public wiki: targets, biome and dimension modifiers and their environment attribute layers (#11486) |
| `horizon-shader-api` | Horizon's own WGSL shader packs ([docs/api/shaders.md](docs/api/shaders.md)): `horizon-shaders.toml`, the `.hshader` package; shared by the game, the launcher and the registry |
| `horizon-shaders` | Shader packs (the `shaders/` folder format), rebuilt clean-room from a written spec of the format; no wgpu |
| `horizon-theme` | Design tokens and fonts for the launcher, the game's own menus and the website |
@@ -92,8 +93,8 @@ reads `README.md`, `launcher/links.json`, `docs/api/`, `roadmap/roadmap.json` an
```sh
python3 tools/pr_ready.py # the self-check: fmt, clippy -D warnings, touched crates' tests, harnesses; paste its block in the PR
python3 tools/pr_ready.py --quick # same without harnesses
cargo test -p horizon-client -p horizon-client-lib --lib --bins --tests # skips the 104 examples (pr_ready does this); `--doc` separately
cargo test -p horizon-client-lib --lib mod_content::tests:: -- --ignored # the 5 slow content-root tests, #[ignore]d; pr_ready runs them when mod content, assets, blocks or items change
cargo test -p horizon-client -p horizon-client-lib --lib --bins --tests # skips the examples (pr_ready does this); `--doc` separately
cargo test -p horizon-client-lib --lib mod_content::tests:: -- --ignored # the slow content-root tests, #[ignore]d; pr_ready runs them when mod content, assets, blocks or items change
cargo build --profile fast -p horizon-client # optimized, quick to link: use it to play or run lan_e2e
python3 tools/parity.py --changed origin/main # the harnesses your branch can affect (CI's parity job)
python3 tools/parity.py --changed-files paths.txt --list # which harnesses cover these files, without running them
@@ -105,8 +106,8 @@ python3 tools/agent_control_e2e.py # horizon-ctl / MCP end to end
- Harnesses and the game need the vanilla data in `.local/` (`minecraft/`, `horizon-client/26.3`, `datagen/`,
`tools/`): see [docs/parity-workflow.md](docs/parity-workflow.md). Without it, pr_ready lists the
harnesses for CI.
- A harness = `tools/test_<name>_parity.py` (324 of them) + a Java oracle (`mods/horizon/src/test/java/dev/horizonlab/parity/Vanilla*Oracle.java`)
+ a Rust probe: `crates/<crate>/examples/<name>_{probe,fixture}.rs` (horizon-world 175, horizon-client 104),
- A harness = `tools/test_<name>_parity.py` + a Java oracle (`mods/horizon/src/test/java/dev/horizonlab/parity/Vanilla*Oracle.java`)
+ a Rust probe: `crates/<crate>/examples/<name>_{probe,fixture}.rs` (mostly horizon-world and horizon-client),
or the game binary with a fixture env var (`game/dispense_fixture.rs`, `game/sculk_fixture.rs`,
`game/command_harness.rs`, `game/net_harness.rs`). Copy a nearby harness for a new one.
- Developing a native mod? Run `horizon-launcher settings native-profile dev` once. `dev` builds the game crate
@@ -176,7 +177,7 @@ state stays separate from the library tests. This compiles gameplay twice for ex
## How `Game` is organised
- `struct Game` (game.rs, ~290 fields) holds everything. Its methods are spread over ~180 files: game.rs keeps
- `struct Game` (game.rs) holds everything. Its methods are spread over many files: game.rs keeps
the core (tick, render, input, inventory, world open/close) and each `game/<feature>.rs` adds an
`impl Game<'_>` block for one feature, with its own state types. Fields stay private to the gameplay module and its descendants. Gameplay tests run in the gameplay library, separate from reusable-library tests.
@@ -410,16 +410,6 @@ The panel's later features (bans, worlds, server.properties import, logs and cra
</details>
#### Ely.by and other sign-in providers
Sign in with other account providers, such as Ely.by or any Yggdrasil-compatible one, beside Microsoft.
<details><summary>Technical</summary>
Issue [#1270](https://github.com/horizon-rust/horizon/issues/1270) asks for Ely.by and Yggdrasil-compatible servers with an account selector in the launcher. It builds on the Microsoft sign-in ([docs/microsoft-account.md](docs/microsoft-account.md)).
</details>
#### Join servers on older versions
Join servers that run older Minecraft versions, such as 1.21 or 1.8 PvP servers, from Horizon.
`crates/horizon-client-render/src/fluid_render.rs` translates the still-fluid face tiling and
UV rules of Catnip's `FluidRenderHelper` from Ponder 1.0.82 for Minecraft 1.21.1
(`net.createmod.ponder:ponder-neoforge:1.0.82+mc1.21.1`). Copyright (c) 2022 The Create Team,
licensed under MIT (`LICENSE_Ponder` in that artifact); the full license heads the Rust file.
The native renderer uses Horizon's own block-sprite arrays, pose and light packing. The
development-only oracle calls the unchanged helper from the player's local artifact. No
original classes, game shaders, textures or prepared resources are distributed here.
## wgpu-hal
`third_party/wgpu-hal` is [wgpu-hal](https://github.com/gfx-rs/wgpu) 30.0.1 by the gfx-rs
@@ -212,3 +222,35 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
```
## Just Enough Items
The Recipes settings icon is from [Just Enough Items 31.17.0.108](https://github.com/mezz/JustEnoughItems) by mezz and contributors. It is distributed under the MIT license in `crates/horizon-client-render/src/jei_recipes/LICENSE.txt`.
## Flywheel native model directional shading
The two-light diffuse expression and transformed-normal policy in
`terrain.wgsl`'s native model entries follow Flywheel 1.0.6
description="The dedicated server's anticheat: its [anticheat] settings, violation levels and the checks' rules, with no game state (docs/anticheat.md)"
description="The dedicated server's chat filter: its [chat_filter] settings, the slur list's matcher and the mutes, with no game state (docs/chat-filter.md)"
description="The client's lowest layer: chat text and translations, SNBT and NBT paths, the scoreboard, boss bars, game rules and the registry and particle tables commands name"
description="The client's world data, rendering and screens: chunks, meshing, light, streaming, the renderer, the HUD, menus and settings; no gameplay"
Loaded 100 of 802 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.