🤖 Agent: The HDR float frame follows HDR, Smooth Gradients and the picked pack at once by rebuilding the renderer; all of it is behind HORIZON_HDR_FRAME=float, so the default path only pays one bool check per frame.
🤖 Agent: The warden digging test ticks the warden at age 1, since the client moves tickCount on before it ticks; it had failed on main since #12110 wherever the vanilla data is prepared.
🤖 Agent: F3 shows the debug overlay on the title screen and F3+F6 opens the debug options over the panorama, closing back to the title without grabbing the pointer, as TitleScreen allows.
🤖 Agent: A piston push of primed TNT applies the blocks it moves through (powder snow, soul sand, slime), as the piston's Entity.move does. Stacked on #12290.
🤖 Agent: On the float frame the visibility buffer writes light itself; behind the HDR_WRITE_LIGHT override, false on the default frame. Stacked on #12282.
🤖 Agent: An option button's pointer cursor follows hover only, while its focus still highlights it (AbstractWidget.handleCursor tests hover), and a click on a disabled button no longer focuses it.
🤖 Agent: Screen drag and wheel get v2 hooks that lend the game mutably, heard just before v1; v1 keeps its signature and still hears the turn unless v2 cancels it or closes the screen.
🤖 Agent: A resting item in a lava cauldron burns: each lava cauldron its box meets (the cauldron's inside shape, not the air above) applies LAVA_IGNITE and its own lavaHurt, stopping once the item is gone; netherite stays immune, as the new oracle shows.
Villager.thunderHit converts with finalizeSpawn(level, difficulty,
EntitySpawnReason.CONVERSION, null); only Pig.thunderHit skips it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the batch-starting line now goes to the /test command's own source inside the command (TestBatchSummaryDisplayer); the test-instance block still uses the server source. Lifecycle stays 95/99: its 4 if-loaded records are an oracle artifact.
🤖 Agent: mob_died no longer resets scores at the death; vanilla does it in onDestroyed, so unloaded corpses keep them. The #12185 shulkers get fixed UUIDs so /scoreboard players list can match at all.
🤖 Agent: A new item's own random now makes its UUID (Mth.createInsecureUUID, the first two draws), then bobOffs, yaw and motion in that order, instead of a process-wide clock-seeded UUID; I merged main (the seed-sync gaps diagnostic this PR retires stays out).
🤖 Agent: Block contact scratch lists are kept on Game and restored on every Result path; I replaced extend(drain(..)) with append, the clippy error that failed verify (clippy -D warnings passes now).
🤖 Agent: `execute on passengers` finds the players riding a vehicle on a server (the player in place, then the hub guests by their vehicle); I merged main, whose new mob-children arm now follows this PR's player_passengers, so both players and mob riders are found.
Since #12110 a warden's client tick plays the heartbeat when its age is
a multiple of the beat delay. The test ticked a new warden at age 0, so
it heard a heartbeat. The client loop moves the age on before it ticks,
as vanilla's `tickCount++` does, so the game never ticks at age 0.
Fixes#12312
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: An export that fails on a missing lib/tzdb.dat now names the Java to repair, matched only on the FileNotFound/NoSuchFile line for that exact path; other failures keep their message.
🤖 Agent: /loot and pack functions take the world's packs' loot tables: the id list comes from the loaded tables (root plus pack overlay) and is refreshed on /reload.
🤖 Agent: On the float frame every shader writes its own light; all of it sits behind the HDR_WRITE_LIGHT pipeline override, false on the default frame, so the SDR path compiles it out. Stacked on #12220 and #12247.
🤖 Agent: Other crates' records, native mods included, now reach latest.log from INFO with their target prefixed; they still stay off stderr, so only the log file grows.
🤖 Agent: match_tool and loot predicates now test minecraft:custom_data as NbtUtils.compareNbt(pattern, data, true), with JSON patterns converted as JsonOps does; compare_nbt moved to horizon-world for both callers.
🤖 Agent: native_player_break_block breaks a block as a given player through the game's own player-break path (spectator, adventure can_break, spawn protection, native_destroy_block, loot, wear, stats).
🤖 Agent: Death protection and milk removal clamp a mob's absorption to its remaining max_absorption (onAttributeUpdated), instead of subtracting 4 per Absorption level.
🤖 Agent: CPU terrain occlusion tests each section's padded mesh bounds instead of the whole 18-block cell, so empty space no longer keeps a hidden section drawn; sections without bounds keep SECTION_BOX.
🤖 Agent: Realms world slots draw their own pictures and template pages arrive page by page. Cost to note: the four 1024-tall slot pictures now live in the HUD atlas for every player, about one 1024-tall shelf of GPU memory; a follow-up should load them only while Realms screens are open.
🤖 Agent: /loot now applies set_name and set_components to its drops, as a chest's roll already did, and names each drop by its display name in the feedback.
🤖 Agent: Ports vanilla's timelines, keyframe tracks and all 32 easings into horizon-world with Java's float/double steps; nothing in the game calls it yet, only the two new oracles' probes.
🤖 Agent: One hdr_mark_light function serves the display and resolve passes; a GPU test checks it against the old code for every 8-bit mark and both easing modes.
🤖 Agent: The recipe_sources native hook lets recipe placement into a mod menu type's grid take ingredients from containers the mod names; stacked on #12240 and #12233, lands after them.
🤖 Agent: An unbounded arbitrary @e walks the joined families in insertion order (EntityLookup.getEntities), falling back to the old order if the walk fails; it builds one count map per such query, other selectors keep their path.
🤖 Agent: #minecraft:tick functions now run before the levels tick, so they read the game time from before the tick, and /schedule'd ones run in ServerLevel.tickTime once the clock has moved, as MinecraftServer.tickChildren orders them.
With Improved Transparency on, the float frame's default lane no longer
stays marked: each see-through layer converts its own colour to light
(hdr_written) before it is weighted and accumulated, so the composite
lays light over the light behind it, as the forward passes do. Text,
shadows, weather and the border keep their source gain, as they do in
the forward passes; cracks keep their multiply blend outside the
accumulation, since it scales the light under them. Only a shader pack
keeps the frame marked now. The identity harness's float OIT session is
no longer an identity case; Improved Transparency with HDR off and on
the 16-bit frame are, and fail unless the option changed the picture.
Co-Authored-By: GPT 6.1 Sol <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Datapack-added or replaced biomes keep their minecraft:visual/ambient_particles, so ambient particles use the world pack's biome instead of the prepared vanilla table.
- Add touch::layout: control schemes, built-in controls, per-scheme placements and the saved settings file
- Visibility follows the scheme unless the player shows or hides a control; left-handed mirroring; D-pad jump/sneak swap
- Load falls back to defaults and sanitizes off-screen or non-finite values; save writes through a temp file
- Nothing uses it yet: the touch controls that read it come in the next pull requests
- Adapted from Starling (MIT, by Vasysik); credited in THIRD_PARTY_NOTICES.md
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
🤖 Agent: perf: unchanged ticket sources skip renormalisation, and item defaults load once per root; I fixed the fast path to compare the dragon arena too (it broke the watched-dragon-arena test).
🤖 Agent: update records that the cover was refreshed at its start, so each waiting chunk no longer compares the ~200-entry extra areas list every tick.
tests/test_game_home.py requires every script that starts the game to use it
(scratch folders, no audio) instead of setting HORIZON_NO_AUDIO by hand.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Vanilla's TestCommand.trackAndStartRunner adds a TestBatchSummaryDisplayer
holding the command's source; GameTestRunner.start runs the first batch
inside the command, and testBatchStarting sends
"Starting environment <env> batch <n>" with sendSuccess(.., true). Horizon
queued the line as a server console broadcast for the next tick, so `/test
run` answered only "Running 1 test(s)..." (world_test_def 94/96,
world_test_def_lifecycle 6 of its 10 differing records, as #11992 noted).
A runner a command starts now sends the line through that command's source
while the command runs (`feedback`). A test instance block's runner keeps the
server's source (TestInstanceBlockEntity.runTest), as do later batches, which
start from the tick.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Vanilla resets an entity's scores in ServerLevel.EntityCallbacks.onDestroyed
(Scoreboard.entityRemoved), when the entity is removed for good: a mob's body
twenty ticks after LivingEntity.die, never when it is unloaded. Horizon reset
them in mob_died, at the death. A mob killed in a frozen world and then
unloaded kept its scores in vanilla and lost them here, so a later
`/scoreboard players list` named 32 fewer holders (command_exec, after
#12185's shulker command damage case).
mob_died no longer resets them; score_destroyed_entities already resets every
removed entity's scores once a tick, and keeps an unloaded one's.
#12185's 32 shulkers had random UUIDs, which that list then named: Java and
Horizon could never print the same list. The case now gives them fixed
UUIDs, so the record is exact again.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`Entity.applyEffectsFromBlocks` runs the supporting block's `stepOn`
before the blocks the entity is inside, so slime slows TNT before a
bubble column beside it lifts it. TNT's own tick had them the other
way round, and the piston push shares the same steps.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
native_screen_mouse_dragged_pre and native_screen_mouse_scrolled_pre take
&self because their NativeScreenView borrows the game, so a mod can cancel
a drag or a wheel turn over a screen but can't click a slot there.
Mouse Tweaks-style mods (Mouse Tricks) inject into the private
inventory_drag_move and tweak_scroll instead and pin one Horizon commit.
Add native_screen_mouse_dragged_pre_v2 and
native_screen_mouse_scrolled_pre_v2 with &mut Game: the view's id and
pointer come by value, the rest as v1's. Each fires at v1's point, just
before v1, which hears the screen as v2 left it and nothing of an input
v2 canceled. Cancelling does what cancelling v1 does. v1 is unchanged.
Fragment tools/native-hook-fragments/screen_mouse_input_v2.json; native
bodies filled by `cargo run -p xtask -- mod-api`.
Fixes#12286
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KtNXhvTSpY15Rjzwuqh8Nf
With HORIZON_VISBUFFER=1, the float frame's default lane no longer stays
marked: the visibility buffer's resolve writes its solid pixels' light
as fs_opaque does (solid_light), so the frame is written whole. The
identity harness's float visbuffer session is no longer an identity
case; the visibility buffer with HDR off and on the 16-bit frame are,
drawn flat so that the buffer runs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Block entities and scheduled ticks wait until their chunk's entities have loaded, as ServerLevel.isPositionTickingWithEntitiesLoaded gates them; the entities_pending set is cloned once per call.
🤖 Agent: A spawner's delay counts down without building a level view when no player is in range, the common case, and builds it only when it may spawn.
🤖 Agent: Decoration ranks are unique, so only chunks without a rank are measured and the queue is a heap that pops until the workers are full, instead of a full sort per update.
🤖 Agent: An item pushed out of a solid block takes its gravity step before the escape push (#12018), and the new command_exec case pins two ticks whose height and motion are exact whatever the random push speed.
🤖 Agent: The Graphics page describes its rows and the shell draws and drives them; I merged main's three-state GPU Culling into the new test helpers (focus_on, key), graphics_ui tests pass.
🤖 Agent: /setblock and /fill notify neighbours after all shape updates (BlockInput.place, ServerLevel.updateNeighboursOnBlockSet), so a wire beside a block that pops off ends as a cross; I merged main (fill's note-block normalisation kept) and marked the two resource-backed tests #[ignore], as the other HORIZON_TEST_RESOURCES tests are, since CI has no resources.
The visibility buffer draws only frames drawn flat, which frames without
a pack are by default only on Metal and some Intel drivers, so elsewhere
float_visbuffer compared the forward passes. It now sets
HORIZON_FLAT_DRAWS=1 and fails unless the game built the buffer. The
module docs list the sessions added on this branch.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Once `limitPistonMovement` has used the tick's 0.51, `Entity.move`
returns at once, but `moveEntityByPiston` still runs
`applyEffectsFromBlocks`, so a second piston's push still lets slime
under the TNT slow it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The commands' minecraft:loot_table ids came from the resource root's
folders, so /loot refused a world data pack's table, and a pack's
function naming one was dropped at load. ResourceOrIdArgument looks the
id up in the server's registries, which hold the world's packs. The ids
are now the world's loot table map (Items::loot_table_ids): set when the
world opens and again before admit_store parses the packs' functions.
New command corpus group: loot world pack tables.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Studio's top bar and timeline are Horizon-only and off unless Studio is opened; the only gameplay touch is the fov/play_input hooks, which read the new state only while Studio is open.
🤖 Agent: A mod menu's crafting grid crafts through the crafting table's own recipe lookup (lan_crafting), so mod grids and the table agree on every recipe.
🤖 Agent: A replaced end gateway's cached server cooldown is discarded, so a new gateway at the same spot does not inherit its teleport cooldown; covered by the new gateway_replacement oracle.
🤖 Agent: A mob-owned arrow may break a block only with its own saved can_break predicate and mob_griefing on; predicates with nbt, components or predicates conjuncts fail closed instead of matching on blocks alone.
🤖 Agent: Rebased onto main: the hot-path buffers (command args, protocol buf, redstone, json folder) are reused instead of reallocated, with the args tests covering the reused path.
🤖 Agent: All geysers of one block-entity pass share one mob grid kept until the mob list's next write; the effect and push loops restamp it only because they change no mob box.
🤖 Agent: A cart's push and pickup read neighbouring carts and mobs from grids; any mutable access to the mob list bumps its stamp, and with native mods loaded both full scans stay, since a hook can move entities unseen.
🤖 Agent: A guest's effect and sting hits now credit the mob that swung, matching LivingEntity.hurtServer's attacker; stacked on #12204, lands after it.
🤖 Agent: Melee hits and game events read the mobs near them from a grid instead of walking every mob; the grid is rebuilt on the mob list's next write (JoinList stamp), so a mob spawned mid-tick is still found.
A watcher arriving at or leaving the End fight changes only Sources.dragon;
the unchanged-sources skip ignored it, so the arena never got (or lost) its
TicketType.DRAGON levels.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`Entity.move(MoverType.PISTON)` uses up TNT's pending stuck multiplier
(without scaling the push), reads its fall for powder snow's top, adds
to that fall and resets it on landing, and applies the block speed
factor. `moveEntityByPiston` then runs `applyEffectsFromBlocks`, so
powder snow it is pushed into makes it stuck, and slime under it slows
it. Horizon did none of this, so in a chunk that does not tick
entities pushed TNT kept its motion in powder snow and on soul sand.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After a fall over 2.5, `PowderSnowBlock.getEntityInsideCollisionShape`
is the 0.9-high top, so the snow only makes the TNT stuck once a step
reaches that top. A TNT passing 2.5 just above the snow used to be made
stuck early, lose its fall and sink through.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Block loot's `match_tool` (horizon-items) and the loot engine's
ItemPredicate (`item_matches`: `filtered`, entity `equipment`) ignored
`predicates."minecraft:custom_data"`, so any stack passed. They now test
it as `CustomDataPredicate` does (NbtPredicate.matches ->
CustomData.matchedBy -> NbtUtils.compareNbt(pattern, data, true)): a typed
NBT subset, lists matched element by element, absent custom data read as
`{}`, the pattern SNBT or a JSON object converted as DataFixerUpper does.
`compare_nbt` moves down to horizon-world (re-exported from nbt_path) so
the loot engine can use it, and a killer's equipment keeps its custom data
for entity loot predicates.
New harness loot_custom_data: a data pack's dirt table with a pool per
pattern behind match_tool, `loot give @s mine` with sticks carrying
different custom data, 66/66 records equal to vanilla. The loot engine's
`filtered` path gets the same vanilla matrix as a horizon-world test.
Fixes#12196
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KtNXhvTSpY15Rjzwuqh8Nf
The give branch of cmd_loot skipped every target but the player in
place, so the console's /loot give reached nobody on a dedicated
server, and a command block's /loot give @p gave nothing in single
player. Each target now goes through for_player, as cmd_give does
(LootCommand.playerGive gives to every target ServerPlayer).
New lan_e2e check: loot_give_guest.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A content mod's menu type may declare `"links": {"from", "count"}`: those
slots show the slots of block containers a native mod names each tick the
menu is open (`native_menu_links(player, menu, pos, links)`), in order, as
real slots of the menu. Clicks, shift-clicks and drags run on the server's
menu engine and move the containers' own items, a changed container is
saved and its comparators update, and the block entity never holds the
linked slots. Linked slots with nothing behind them hold nothing and are
left out of clicks, as a hidden slot is.
This is what a crafting station's side panel needs (Tinkers' Construct's
shows the chest beside it); a tab per neighbour is a menu button that
changes what the mod links for that player.
Part of #12198.
Claude-Session: https://claude.ai/code/session_01KtNXhvTSpY15Rjzwuqh8Nf
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Retain per-tick block contact, random tick, and entity-inside vectors on Game while restoring scratch across result exits and preserving vanilla ordering.
Co-Authored-By: Codex <[email protected]>
CI has no HORIZON_TEST_RESOURCES, so they panicked there; like the other
resource-backed game tests they now run with --ignored.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The items loot engine, which /loot rolls with, skipped both functions:
the item came out without its name, lore, potion or book contents,
while a chest rolling the same table had them. set_name sets the
component its target names, as a chest's roll does. set_components
applies the patch as ItemStack.applyComponents does and reads the
stack back as saved components are read, so a potion's contents make
it a potion. The feedback names each drop by getDisplayName.
New command corpus group: loot set name and components.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reuse resident section bounds for phase-one retests and deferred occlusion tests, retaining a one-block total pad and the existing full-cell fallback.
Co-Authored-By: Codex <[email protected]>
Under HORIZON_HDR_FRAME=float, with HDR on, no shader pack, and neither
Improved Transparency nor the visibility buffer requested, the world's
shaders write their own light (hdr_written) instead of a mark, and the
passes that only write marks, the hand's unmark and remark and the
resolve pass are skipped. A frame is all written or all marked. TAA's
history is dropped when that or the HDR parameters it holds change.
Co-Authored-By: GPT 6.1 Sol <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Fixed: `MobWorld` has no `end` field, so the fox village stroll now reads `dimension_type.has_fixed_time`, as `Level.isBrightOutside` does; it builds with main merged (lib and tests).
🤖 Agent: Requeued after a main merge (clean); the earlier clippy failure was in `horizon-core/src/density/interpolation.rs`, which this PR doesn't touch.
🤖 Agent: Requeued after a main merge (clean); the earlier failure was main's native mod API stamp (`native_level_play_sound`) at that moment, not the brake thresholds.
🤖 Agent: Requeued after a main merge; the earlier verify failure was a 60 s timeout in `test_entity_shadow_ownership` on a loaded runner, not this PR's launcher scroll change.
🤖 Agent: Requeued after a main merge: in report.rs, main's installed-data-pack branch comes before the carried-mod branch, which still marks 1.20 layouts Partial; port-pack checks clean.
🤖 Agent: Requeued after a main merge: the `graphics_ui.rs` conflict kept Opaque Ice's switch, and main's tri-state GPU culling no longer has a plain on/off arm there.
MobWorld has no end field. Level.isBrightOutside is false in every
dimension with a fixed time (the Nether and the End).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Primed TNT is an Entity in vanilla, so `checkFallDamage` adds each step
down, narrowed to a float, to its double fall_distance, which resets on
landing or in water and is saved and read and written by /data. Powder
snow uses that distance: over 2.5 it holds TNT on a 0.9-high top, and
otherwise TNT goes in and is stuck (0.9/1.5/0.9) like in a cobweb. The
stuck multiplier is used on the next move, which then zeroes motion.
Double and float lists in saved entity data now store -0.0 as 0.0, as
`DoubleTag.valueOf` does, so TNT resting on a block reads Motion 0.0d.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. The level in place reads its own saved `world_border` when the world opens, before any command, so a first `/worldborder set` is no longer overwritten later.
🤖 Agent: jukebox stop now captures was_playing before clearing j.playing, so the stop vibration fires once. On main merge 904b3036a, sculk, server_player_sculk, server_wool_vibration and mob_command_damage_vibration pass on playhorizon. command_exec's only 2 mismatches are main's gateway_item case (same 16819/16821 on bare main b13dbf068).
Ports net.minecraft.world.timeline and the keyframe machinery under it:
Timeline (clock, period, tracks, time markers, validateInternal),
AttributeTrack's codec (a modifier, override by default, and keyframes of
its argument), KeyframeTrack.validateKeyframes and validatePeriod,
KeyframeTrackSampler's looping segments and sampling,
argumentKeyframeLerp for every modifier, EasingType (cubic Bézier with
its Newton-Raphson and bisection solve) and all 32 Ease curves, with
Java's float and double steps (jmath, Mth's sine table). The layer stack
takes timelines as time-based layers that read their clock's ticks.
The new harness environment_timeline compares 3,464 cases with the pinned
client (every easing, the vanilla day, moon and early game tracks, and
synthetic and invalid tracks); environment_system now stacks the real
timelines between the biome and weather layers (36,000 lines).
Part of #11486
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The world slots' pictures follow RealmsWorldSlotButton.State: the
minigame image (or presets/isles.png when it has none), the empty
frame, a slot's own template image, then the slot's panorama. The
atlas now keeps the four slot pictures (the three 1024-tall faces and
isles.png) at their own size: they were excluded as title/background
and presets, and even once let through were dropped by the 512-tall
GUI texture cap.
- WorldTemplate/WorldTemplateList read like JsonUtils' getStringOr,
getIntOr and getUriNullable: numbers read as their JSON text (some ids
and player counts are numbers, some counters are strings), and an
empty link is no link — the row icons are not drawn and no empty URL
can be opened.
- The template list fetches one page per job and appends it as it
arrives, chaining the next page until isLastPage or an empty page,
instead of waiting for every page before drawing the first row.
- The minigame list (RealmsWorldsTab.switchToMinigame) returns to the
Worlds tab on Back and Escape, not to a reset screen.
Tests: 2250 lib tests pass (71 realms), scoped clippy clean.
Co-Authored-By: ZCode <[email protected]>
`native_recipe_sources(player, menu, pos, sources)` lets a mod name
block containers (chests and the like, mod containers) that recipe
placement into a mod menu type's crafting grid may also take
ingredients from, after the player's inventory, as a crafting station
takes them from the chests beside it. No answer keeps vanilla's
placement.
Part of #12198.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Vanilla's Entity takes its UUID from the first two draws of its own random
(Mth.createInsecureUUID); ItemEntity then draws bobOffs and the yaw from it,
and the constructor without a given motion draws the motion last. Horizon gave
every entity a clock-seeded UUID when first asked, drew a new item's motion
from a fresh random's first draws, its bobOffs from the particle random and a
loaded item's from the level's, and left the yaw at 0.
spawn_item_own makes all four from one own random in that order and replaces
dispenser::new_item_motion at its callers. A loaded or summoned item makes its
UUID (unless the tag has one) and bobOffs from its own random, so summoning no
longer shifts the level's random. Primed TNT from a block takes its UUID from
its own random too.
The seed sync proof now reads those UUIDs, the drop's rotation and motion, and
a TNT kick after a summoned item: 8/15 before, 15/15 after.
Fixes#12217
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Runs hand-built circuits and random "soups" of redstone components through the real
tick loop in vanilla 26.3 (the bench mod's client, no server, no EULA) and in Horizon
(horizon-ctl), in a frozen world stepped with `tick step 1`, and prints the first tick
where the blocks differ and which ones. Narrowing: --near/--drop reduce a difference
to a few commands.
- RedstoneDiff + ServerLevelTickMixin in the bench mod snapshot the box before the
first stepped tick and after each.
- `simulation blocks X1 Y1 Z1 X2 Y2 Z2` (horizon-ctl) reads the server's blocks, not
the window's replica, which lags the server by design.
- agent.rs: `blocks_json`, shared by `blocks` and `simulation blocks`.
Only blocks are compared; entities are recorded, not diffed.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
Vanilla places a command's block with UPDATE_CLIENTS | UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS
(2|256), so Level.setBlock notifies no neighbour. The shape updates run inside
setBlock; ServerLevel.updateNeighboursOnBlockSet follows afterwards (for /fill,
after every block is in). Horizon placed with UPDATE_ALL (3), so the neighbours
heard of the block before its shape updates ran.
That is visible when the notice makes a block pop off: a comparator with nothing
under it breaks at its first notice and takes an attached wall torch with it. In
vanilla the wire between them had already connected to the torch, so it ends as a
cross; in Horizon the torch was gone first and the wire ended as a dot.
Found by tools/redstone_diff.py (two of 48 random circuits differed on vanilla
26.3 vs Horizon, reduced to four commands); after the change 144 random circuits
over three seeds match vanilla tick for tick.
- Behaviours::update_neighbours_on_block_set ports ServerLevel.updateNeighboursOnBlockSet.
- place_state takes `command`: setblock and fill use the vanilla flags; clone keeps
its own flow. The strict variants are unchanged.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
🤖 Agent: queued. Piston restitution bounces the entity's own velocity and runs for server entities only; the ground flag comes from #12168 (queued first).
Carry the actual top-level projectile predicate through ArrowState load/save
and consult the existing matcher only for resolved Mob arrows under both
rules. Refuse unsupported conjuncts and preserve other projectile/drop paths.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
The recipe book's place packet and Horizon's viewer transfer channel go
to an open mod crafting menu (`lan_mod_craft_apply`) over its block
entity's grid, as a click does, with a ghost recipe when the items are
missing; the client's `native_place_recipe` and `native_transfer_recipe`
accept such a screen, so JEI ports and the Recipes mod fill it as they
fill a crafting table.
Part of #12198.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. A mannequin saves `profile`, `hidden_layers`, `main_hand` and `pose` always, and no `Mob` fields, since `Mannequin` is an `Avatar`, not a `Mob`.
🤖 Agent: queued. Literal `random_chance` predicates in commands now draw from the world's random, as `LootContext` gets `level.getRandom()`; main merged in.
🤖 Agent: queued. Realms screens follow vanilla's layout, state and input; the owner-UUID change was reverted (it would orphan tamed pets in Horizon-made worlds), and `under_world` skips parts with ':', with a test.
🤖 Agent: queued. Equippable assets for armor, wings, capes and camera overlays; the End flash now uses `random_source::create()` after main dropped `SoundEngine::unseeded`, with a skybox gate in `tick_end_flash`.
🤖 Agent: queued. An item a scheduled block tick drops ticks in that tick's entity pass, as `EntityTickList.forEach` runs after the block ticks; a boat's lily pad drop still waits.
🤖 Agent: queued. Health Boost and Absorption hooks run for every accepted effect and `add_effect` returns its result after them; after #11883, #11959 and #12108.
🤖 Agent: queued. Startup trace step stats (count, median, p95, RSS) with a test; the compression bench was taken out, since the zlib reuse it measures is not on main.
🤖 Agent: queued. GPU culling Auto turns it on where the CPU limits the frame and keeps it only if frame time improves; the client_lib.rs conflict kept both `autotune` and main's `random_source`.
🤖 Agent: queued. Styled container names stay as the `CustomName` tag in the live block model, so a styled chest name survives a break; main's spill test now compares tags.
🤖 Agent: queued. `/test pos` picks the block from a connected source player's view, as `TestCommand` reads `source.getPlayer()`; rustfmt fixed after the main merge.
🤖 Agent: queued. Undo, a row's cost and the greyed-row reason are generic shell features with tests; Extras gets no Undo, and Graphics moves onto them in the next PR.
🤖 Agent: queued. Arrows and snowballs breaking a chorus flower now drop it via `ChorusFlowerBlock.onProjectileHit` (`destroyBlock` with drops); main merged in.
🤖 Agent: queued. GLSL overload resolution now prefers an exact argument match over a conversion, as the GLSL spec ranks them, so packs that overload on int/float pick the right function.
🤖 Agent: queued. Environment attributes now come from the layer stack (dimension, then biome with blending, then weather) in `EnvironmentAttributeSystem`'s order; main merged in since the review.
🤖 Agent: queued. The remaining Russian strings in the launcher and the game are translated, and the keys match en_us, so no string falls back to English.
🤖 Agent: queued. A template start is read back only when every child decodes; one unreadable child sends the whole start to #11652's regeneration, so old saves get nothing worse. I couldn't rerun the ignored `saved_starts_match_generated_layouts` here (no prepared resources on this host); your 7/7 run and the parity run decide.
MinecraftServer.tickChildren runs the #minecraft:load and #minecraft:tick
functions before the world clocks and the levels tick; the Overworld's
ServerLevel.tickTime then raises the game time and runs the scheduled
functions due at the new time. Horizon raised the game time, the weather and
the day clock first, then ran the scheduled functions and the tick functions
after them, so a tick function read both clocks one tick ahead and a
`schedule ... 1t` from it waited a tick more.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A script that syncs runs on fixed seeds to its end, so in the full command-exec
corpus every group after the proof drew from them, at seeds fixed by whatever
came before. With one more proof case (#12217), "experience orb stuck", whose
orb rarely ends otherwise on random seeds, landed on such a seed and failed on
every run. The group is isolated now and runs as tools/test_seed_sync_parity.py;
the main corpus runs without the agent, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: keyring calls now give up after 20 s on a thread of their own; a call still stuck makes later ones fail at once, so the account lock is never held forever (tested with a keyring that never answers).
A Secret Service daemon that never answers (two keyrings racing for the
D-Bus name, a locked collection with no working prompter) held the
account lock forever: Play stayed on "Starting…" and printed nothing.
Every keychain call now runs on a thread and gives up after 20 s
(Bounded): on Linux the secrets file takes writes as before, and a
sign-in that can't be read says so instead of "no owner". While a
given-up call is still stuck, later calls fail at once. Play prints one
stderr line per step, and `account list` prints why a check failed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued first. Main's correctness job fails on this test since #12086 met the folder metadata gate (84f695992), and every PR that merges main fails on it; verify is green here.
🤖 Agent: queued. A dispatch over 65,535 workgroups per dimension (the wgpu limit) is now split into several dispatches with the base passed along, so packs whose compute pass sizes from the screen still run at 4K and up.
🤖 Agent: queued. LabPBR `_n`/`_s` maps now reach entities, items, block entities and the hand; `set_shared_material_maps` now runs after `set_custom_sky` in loading_window.rs, so a pack reload binds both.
#11794 made the item's water and air drag floats (0.99F, 0.98F), as
vanilla's are; the test still expected the f64 constants, so it fails
wherever the vanilla data is prepared.
Fixes#12221
Co-Authored-By: Claude Opus 5.5 <[email protected]>
folder_metadata_present (Pack.readPackMetadata) now requires a pack section
with a description, so the {} metadata in #12086's test hid both packs and
seed_create_world returned None. This failed every PR's verify on main.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: removing an entity's chunk now drops its area-effect clouds too (breath_clouds.retain), and saves keep entity_base_tag's Entity fields. On main merge 8b583a15e, save_roundtrip, entity_nbt_boolean, orb_saved_uuid, stand_fall_save, conversion_water_save, mob_pickup_save and potion_effect_icon pass. command_exec's only 2 mismatches are main's gateway_item case (same on bare main b13dbf068).
LevelChunk.isTicking and ServerLevel.isPositionTickingWithEntitiesLoaded
also need the chunk's entity section loaded (areEntitiesLoaded). It loads at
the end of a level tick, frozen or not (PersistentEntitySectionManager
.processPendingLoads), empty sections too. A chunk that loads now waits for
the end of the level's next pass before its block entities (bells, conduits,
gateways and sculk blocks included), moving pistons and scheduled ticks run.
The command harness's readiness waits mark their chunks loaded, as the
vanilla oracle's do.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every gassing geyser scanned every mob every 10 ticks, all on the same tick,
and every erupting one every tick. One grid of mob boxes is kept for the block
entity pass, until the mob list's next write; a geyser's own effects and
pushes move no mob, so the grid holds after them.
The mobs still get their effect and push in list order, and the gas reads
blocks only, so finding the reached mobs before any gets its effect changes
nothing.
(cherry picked from commit 2bad442a93, ported to the fields on Game)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A cave spider's poison bite or a bee's sting on a guest named the mob
nearest to where the hit came from in X and Z, ignoring height, so a zombie
standing under the spider took the credit (the kill message, the hurt-by
advancement, the guest's revenge target). The event names the mob that
swung, as a plain melee hit does since the previous commit.
(cherry picked from commit 3ab80dc9d0, without its deletion of tools/bench_*.rs)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each ticked cart compared its box with every cart, and a moving rideable cart
with every mob twice (and built the mob teams for each). A grid kept for the
tick answers in slot order, as the scans did. The cart grid follows each
ticked cart, is rebuilt when a cart is gone or added, and dropped after a
cart's command; the mob grid holds until the mob list's next write. With
native mods loaded, whose hooks can move either, the scans stay.
(cherry picked from commit 3613b40c6b, ported to the fields on Game)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. A shulker's hurt teleport now runs inside the hurt, before the bullet's levitation, and riders get off where it stood (`unRide`); NoAI shulkers never teleport, as `teleportSomewhere` requires.
A ListStamp names the list and how many times it was written through
`DerefMut` (every push, insert, retain or slot write). A cache built from
the list keeps the stamp and is rebuilt when the stamp differs; a swapped-in
list (another level's) has another name.
(part of commit 6b826d204a)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. Four components (`provides_trim_material`, `provides_banner_patterns`, `can_place_on`, `can_break`) are now encoded in outgoing stacks with the synced registry IDs; anything unsupported drops the whole buffer, as before.
A game event posted outside the mob pass (a block placed, a door opened, an
explosion) looked through every mob of the level for the wardens and allays
that can hear it. The listener slots are now kept until the mob list is
written again (the previous commit's stamp), or another level's list is in
place.
Mob id lookups for game events, thorns and a hit's source go through the
mob index instead of a scan. A guest's melee event names the mob that
swung, so the hit credits that mob rather than the one nearest in x and z
to the player.
(cherry picked from commit 6b826d204a without its join_seq part, ported to the fields on Game)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. The server now waits for the saved tickets' chunks before the first tick (`prepareLevels`), so their saved ticks unpack at the time they were saved at; the wait gives up after 600 s.
🤖 Agent: queued. Tools only: `world_compare.py` and `perf_bench.py --seed`, no generator change; the seed report it answers is documented with the two seeds compared.
🤖 Agent: queued. `--folder` on the CurseForge `Instances` folder now finds its instances, and a Prism `instances` folder (same name where case is ignored) is not mistaken for it.
A ListStamp names the list and how many times it was written through
`DerefMut` (every push, insert, retain or slot write). A cache built from
the list keeps the stamp and is rebuilt when the stamp differs; a swapped-in
list (another level's) has another name.
(part of commit 6b826d204a)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
push_from_ticked_cart searched the cart list for the ticked cart twice and
copied it in and out whole. The caller knows its slot: the ticked copy is
swapped into the slot for the push, and only the pushed cart is copied back,
over the copy it replaced (clone_from reuses its buffers). Lava hurting a cart
looks there first too.
Without mods a push changes velocities and riders, never the list, so the
cart is still in its slot after it (a debug assertion checks). With native
mods loaded a mount hook can change the list: the copies and the search by id
stay. The loot roll stays with the ticked copy, as #11526 made it: it comes
off before the swap and goes back after.
(cherry picked from commit 206905a2d5, without
the loot roll part of fb69aba537)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. Far terrain treats slabs as full cells like stairs, so a waterlogged slab keeps its own material instead of becoming water; the test walks every real slab state.
🤖 Agent: queued. An empty `shaderpacks/` (CurseForge and Sodium create it) no longer counts as a shader pack, for folders and archives alike; tests cover both.
🤖 Agent: queued. After every server push the ground flag is set to `verticalCollisionBelow` (`Entity.move` → `setOnGroundWithMovement`, which runs when `isLocalInstanceAuthoritative`), so a sideways push off a block clears it; #12167 builds on this.
main still makes a new ZlibEncoder per record, so the bench would compare
two fresh encoders. It belongs with the change that adds the reuse (#12189).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Fluids read Info::ice through is_ice_class (instanceof IceBlock, frosted
ice included), so excluding frosted ice there changed fluid meshing with
the option off. Regular ice now gets its own opaque_ice flag.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Restitution bounces the entity's velocity (getDeltaMovement), not the push.
It runs for server entities only, since a player's client moves the player.
It runs even when the step is too small to apply, as Entity.move does.
The ground flag is left to set_pushed_on_ground (#12168), which follows
setOnGroundWithMovement.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. `at_velocity` now covers boats, minecarts, armor stands, frames, paintings, TNT, falling blocks, block entities and moving pistons; the `falling_block_visible` doc comment is back on its function.
🤖 Agent: queued. A wildcard `data modify storage` that fails part way keeps the edits made so far, as vanilla's in-place NbtPath edit does; main merged in.
A helper-only fragment adds `native_player_break_block(uuid, pos,
into_inventory)` (`Host::player_break_block`), so a vein-mining mod can
break blocks as its player without injecting into the private
`Game::break_block`. It runs the game's own player break
(ServerPlayerGameMode.destroyBlock): the player's checks, the
`destroy_block` hook, loot for the held tool and its enchantments, tool
wear, stats, exhaustion and experience. With `into_inventory` the block's
drops go to the player's inventory, the rest dropped from them.
The break now reports whether it removed the block, and a break nested in
another's hook keeps the outer break's own-event position. The acting
guest's spawn protection is kept for their turn so the helper can check
it while the hub is out of reach.
Fixes#12195
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. Command reads keep the real bell block entity (and copper golem statue) instead of a synthesized one, so `data get block` matches vanilla's NBT; main merged in.
🤖 Agent: queued. A jukebox without a song now saves no `ticks_since_song_started`, as `JukeboxSongPlayer` writes it only while playing; main merged in.
A content mod's menu type may declare `"crafting": {"grid", "size",
"result"}`: those slots run vanilla's CraftingMenu logic on the menu
engine (live result, consumption and remainders on take, shift-click
crafting, recipe placement), over a grid the block entity keeps. The
result is the menu's own: the block entity never holds it, so hoppers,
comparators and a broken block can't dupe it, and the hub shows guests
the result the game made, again each tick the grid changed.
Part of #12198.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A Horizon-only option, off by default, that draws regular ice opaque in the solid pass and culls faces between adjacent ice blocks. It follows the public description of Luxium's Opaque Ice and the design note in #11563; no Luxium source was read or copied.
Fixes#12192
🤖 Agent: queued. Patch in vendored naga: copies of structs or arrays holding atomics go member by member, up to 64 scalars; larger ones stay whole copies, as noted.
🤖 Agent: queued. The added formats (RGB10_A2, RGB10_A2UI, 16-bit SNORM) map to the wgpu formats `image_format` already binds; formats a GPU can't store into are still left out with a note.
🤖 Agent: queued. A translatable with a fallback keeps the fallback as a literal, as `TranslatableContents` does, instead of looking it up as another key.
🤖 Agent: queued. The panel accepts LAN browsers when bound to 0.0.0.0 or :: only; a loopback bind still refuses them, so nothing new faces the network.
🤖 Agent: queued. Passing terrain's interpolants in 5 locations instead of 12; visbuffer reads `TerrainOut` only internally, so packs see no difference.
Measure startup step distributions and RSS, and compare reused versus fresh zlib streams with deterministic checksums.
Co-Authored-By: Codex <[email protected]>
🤖 Agent: queued. One cost: the stepped tracker paths add a Vec per tracked entity on the tracker tick; small, but it is the hot path, worth a look if MSPT moves.
🤖 Agent: queued. Batch of server click answers; each case matches `Mob.interact`/`InteractionResult` and is in the batch's harnesses, which run in parity.
🤖 Agent: queued. LT + A crafting a recipe repeatedly is new controller input; servers see the same clicks a mouse would send, so nothing server-side changes.
A test-only Java agent makes vanilla's RandomSupport.generateUniqueSeed step a
uniquifier of the calling thread's own, without the clock; HORIZON_TEST_SEEDS=1
does the same in Horizon's unique_seed. A command script's sync_randoms step sets
that uniquifier, the entity-id counter and the level's random in both engines,
and the "seed sync proof" group compares a drop's position, primed TNT's kick, a
dispensed item and the resting phase of four items exactly. The oracle cache
counts the agent jar as an input.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued. Comment settings end at their line, as the Iris probes in `read_stage` show it reads them line by line; live code still reads to the `;`.
🤖 Agent: queued. Bold is drawn as the shifted second pass and italic as the skew vanilla's `BakedGlyph` uses, and after the #11863 merge styled glyphs keep the HDR emission tier; the 102 sign cases pass.
🤖 Agent: queued. The Nautilus cooldown memory now exists only while it counts down and is erased at 0, as `Brain.setMemoryWithExpiry` does; its harness runs in parity.
🤖 Agent: queued. A one-block predicate now saves as the bare id and partial `custom_data` keeps only the keys given, which is how `BlockPredicate`'s codec writes them; the residual corpus documents what is left.
🤖 Agent: queued. A sideways push leaves `onGround` false, as `PistonMovingBlockEntity.moveCollidedEntities` only sets it for upward pushes; main merged in since my review, no other change.
🤖 Agent: queued. First of the 12086 → 12087 → 12088 series (each includes the one before): new worlds get the game folder's `new-world-datapacks`, copied at creation only.
🤖 Agent: queued. Ending the tick on the first level skips the dimension swap that did nothing, and the ray chunk cache is per raycast, so no stale chunk outlives one ray.
🤖 Agent: queued. One cost to know: the registry-wide cache has 8192 slots that are never reclaimed; past that it falls back to decoding, so it only stops helping, it doesn't grow.
🤖 Agent: queued. The SWAR check tests 8 palette indices per word and has an exhaustive test against the scalar loop, so a bad index is still caught at the same position.
🤖 Agent: queued. The trident attack continues while `alive && (holding a trident || !nav.done)`, which is vanilla's condition rewritten; a drowned that drops its trident now switches to melee.
🤖 Agent: queued. Tools only: one `git cat-file --batch-check` for the heads instead of a process per PR, and the diffs fetched in parallel; same notes out.
🤖 Agent: queued. `TradeWithPlayerGoal.canUse` also stops on hurt and on distance > 16; it reads `cx.w.player` like the existing LookAtTradingPlayer goal does, so guests on a server share that approximation.
🤖 Agent: queued. A ridden cube mob now skips its jump-move as vanilla's `SlimeMoveControl` does while it is a passenger, so a slime on a boat no longer hops off its seat.
🤖 Agent: queued. Matches `ClientPacketListener.handleMovePlayer`: a passenger keeps its seat when the server's position packet comes, instead of snapping out of the vehicle for a tick.
move_pushed applied the resolved movement to position only, leaving the
entity's velocity untouched. In a ticking chunk the entity's own tick
masks this (PrimedTnt::tick_among restitutes), but in a lazy chunk the
velocity survives: a primed TNT pushed by a piston into a wall, ceiling
or slime keeps 0.4 / -0.8 / -0.8 instead of vanilla's 0 / 0 / 0.64.
Add restitute_piston after apply_push: compare the post-piston-limit
movement (limited) with the post-collision result (resolved) and, as
Entity.move(MoverType.PISTON, …) does, zero blocked horizontal axes,
zero or slime-bounce the vertical axis, and update on_ground. Covers
Player, Guest and every Entity kind including PrimedTnt.
Add unit test a_pushed_tnt_loses_velocity_into_a_wall (needs prepared
resources) asserting the TNT's vx is zeroed by a wall.
Co-Authored-By: Nemotron <[email protected]>
Main moved build_renderer to game/loading_window.rs; the shared material maps
are set there now, after the custom sky as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Merge main.
- The owner switch from the offline UUID to the account UUID (integrated.rs,
lan/conn.rs, lan/playerdata.rs, net.rs, remote.rs) is left out: every world
Horizon made names its player by the offline UUID, so tamed pets and other
owner references would no longer know their player. It needs its own PR
with a migration.
- A tar entry part with a colon (a drive prefix or an alternate data stream)
would leave the world folder on Windows: skip it, with a test.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Retain top and bottom slab states in LOD chunk summaries instead of
discarding dry slabs or replacing waterlogged slabs with water. Use the
same full-cell approximation as the existing partial-block filter.
Check real summaries, coarse reduction and emitted faces for every
prepared slab state. All 72 LOD tests pass, including resource-backed
coverage of 606 slab states across 101 materials.
Refs horizon-rust/horizon#12180.
Co-Authored-By: OpenAI Codex GPT-6 <[email protected]>
🤖 Agent: queued. When `/tmp` is shared the socket goes into a private `horizon-<uid>` folder, so another user on the machine can't connect to or squat on the control socket.
🤖 Agent: queued at a3fd3597f (main merged in; the draw now uses `random_source::entity()`). Thorns rolls in EquipmentSlot order from the wearer's own random, as `EnchantmentHelper.doPostAttackEffects` does.
🤖 Agent: queued. The defender now gets the Cooldown packet the server sets on the shield (`Player.disableShield`), so its client greys the shield out for the 100 ticks instead of letting it block client-side.
🤖 Agent: queued at 14bfb672f (main and #11750 merged in). UPDATE/REMOVE_MOB_EFFECT ids are renumbered for older clients; effects they don't know are dropped instead of showing as another effect.
🤖 Agent: queued. One cost to keep in mind: `stab_players_for` builds a Vec for every guest using any item each tick (shields, food too); small, but a follow-up could return early when the item is not a spear.
🤖 Agent: queued. Same check as `Camel.canCamelChangePose` (the standing box against the blocks above), and the test uses a block at y+2 that blocks only the 2.375-tall standing pose.
🤖 Agent: queued. The data file is written off the UI thread after `complete_assets`, and a stale one is dropped when the assets change, so a repaired install doesn't read an old index.
🤖 Agent: queued. Matches `LivingEntity.dropExperience`/`dropCustomDeathLoot`: experience and equipment follow `lastHurtByPlayerMemoryTime > 0`, while LAST_DAMAGE_PLAYER still needs the player to be resolvable, and the new test checks both sides.
🤖 Agent: queued at fee1085be (main merged in). A 1.21.3 pick_item now becomes the same container swap a 1.21.4+ client sends, so older clients keep middle-click in survival.
🤖 Agent: queued. The count goes up at the one `NetEvent::Packet` send in `conn.rs` and down at the hub's drain, so the 2× inbox cap bounds what a flooding client can queue between ticks without touching the hot path otherwise.
🤖 Agent: queued. The stop waits only with `[transfer]` on, and its 90 s bound stays under the supervisor's 120 s kill, so a proxy that never answers can't hang a stop. Built with 14 other PRs on main (pr_ready --quick green apart from known NixOS-only tests).
🤖 Agent: I moved the distance blend per pixel (it was per triangle through the flat hdr_code). Before/after screenshots and frame time are in my comment: the pack's GPU frame is 121.1 vs 122.0 ms on lavapipe, same within noise.
ResourceOrIdArgument decodes an inline value with the registry's codec
while the command is parsed, so a feature the ConfiguredFeature codec
refuses fails the parse: the command never runs, and an execute store
around it keeps its value. Horizon's parse accepted any compound for
minecraft:worldgen/feature and ran the codec only when the command
executed, which then failed and stored 0.
Where the server has its live features, the command registries now carry
a check that runs the same inline decode placement uses (the live
features' catalog and block bindings, through LiveFeatures::decode_inline),
without placing anything. A value it refuses fails the parse with
argument.resource_or_id.failed_to_parse. Without live features (a client,
or a server without the worldgen bundle) the parse is unchanged. The loot
table and every other registry keep their decode.
The message is the native codec's own (for example "invalid world
generation field: to_place"), not DataFixerUpper's text.
Fixes#12171.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The End flash moved to tick_end_flash here, while main (0b23212f4) switched
the flash and the dragon fight to the dimension type: keep main's
has_ender_dragon_fight check, and gate tick_end_flash on the End skybox as
main did.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
main (3443abbde) kept NoAI Nautilus out of the needs_water branch; this PR
gives every Nautilus its own handler, which already refills NoAI ones.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: Follow-up to #8718. On main, lan_bot's older_clients_of_each_release_join_and_are_sent_chunks fails at 1.21.1 (the reverse step never adds 1.21.2's particle status to ClientInformation); CI misses it because the test returns early without prepared resources. With this it passes, as do all 440 horizon-protocol tests and, on the union, the particle harnesses block/emitter/entity/marker/native_client_particles, particle_providers, player_effect_amplifier and spell_ticks (8/8).
Each level reads its world_border SavedData when it is made
(ServerLevel.getWorldBorder), so a restarted server has the saved border
in place before its first command. #11016 gave each level its own
border. But world open reset the border of the level in place to the
default, and the saved file was read only later, in load_scoreboard (the
first tick's functions, or the first scoreboard use). A first
/worldborder get after a restart read the default, and a first
/worldborder set was put back to the saved size soon after.
World open now reads the in-place level's border with load_border, as
open_level does for the other levels, and load_scoreboard no longer
touches the border.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Shulker.hurtServer calls teleportSomewhere() and hitByShulkerBullet() inside
the hurt. Mob::damage has no world, so the flags it leaves now run right
after the hurt: in the mob's own tick (hurt_teleports, also without AI) and
in the game layer (settle_hurt_teleports: /damage, explosions, projectile
hits, the creature outbox), before a bullet's levitation. teleportSomewhere
also refuses a NoAI shulker, keeps inside the world border and unRides.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: I pushed 9bc110bae. The new second position_riders() call after the player moves rebuilt the whole mob index on every tick for every client; it now runs only when the riding graph has something on this player. I also added the missing passenger_vehicles field to main's studio Remote initializer (it did not compile on main) and drop passengers whose vehicle is removed. Both ignored tests pass with prepared resources, and so do the riding harnesses on a union with current main: boat_authority, boat_rider, boat_paddle_reset, mob_rider_navigation, mount_submerged, server_entities, vehicle_command_damage, dedicated_server (8/8).
Reads the seed, spawn, biomes, structure starts and blocks two saved 26.x worlds
share, with a run-to-run floor from a second world of one game. Run on seed 12345
and a large negative seed: the spawn, every biome and every structure start match,
terrain blocks match to 99.3 to 99.9%, and decoration sits at vanilla's own floor
except trees, a few points under it. perf_bench.py gets --seed to make the worlds.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
🤖 Agent: Checked against the 26.3 client jar: ServerboundSignUpdatePacket's codec is pos, fixedSizeList(4) of stringUtf8(384), then SignTextSlot as an idMapper VarInt (BACK=0, FRONT=1), so reading the side last and writing it as the old boolean after the position is right. It only runs in the older-server adapter, so 26.3 servers are untouched. I merged main (the #8718 conflict in translate.rs: kept #8718's arms plus this one); 440 horizon-protocol tests and clippy pass.
The partial reason of a carried 1.20 mod leaves out the recipe clause
when no result names `item`, and says "1 recipe result names" for one.
docs/modpacks.md says how the game treats each 1.20-shaped file, as an
offscreen run of a carried synthetic 1.20 jar showed: one file at a
time, never the whole start.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A missing comma after the horizon-server line made ru.json unreadable, so
the whole launcher fell back to English (the i18n tests failed on it).
The {mods} placeholder had been translated, so the count would never be
filled in. Also "подключиться" for two "Couldn't connect" lines.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
datapacks::READ named four kinds, so a pack of only recipes, loot tables, advancements or
worldgen was refused as changing nothing, and the install note listed them as not read. READ
now lists the kinds the game loads (worldgen by folder, so world presets still count as not
read), the refusal goes through trf(), and docs/datapack-upgrade.md no longer says worldgen is
not applied.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
GPU Culling pays where the CPU is the slower of CPU and GPU and costs where the
GPU is, and a machine's parts don't say which. Auto watches the frames' median CPU
and GPU times, tries it where the CPU is clearly the slower one, and keeps it only
if the frame's busier side dropped under 97%; a trial that didn't help is undone and
waits 5 minutes, then 20, then for good.
gpuCulling in horizon-graphics.txt is now false|true|auto (older files read as before);
Settings > Performance steps Off/On/Auto; horizon-ctl gpu-culling takes auto. Off stays
the default and no preset sets it.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
Carry admitted Component tags through name state, saves and drops; let modeled name removal override stale source names.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Main already carries the 1.21.1 and 1.20.6 attribute tables (consolidated in
d79a36101); kept main's generated tables and this branch's mob effect renumbering.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Under HORIZON_HDR_FRAME=float, when HDR, Smooth Gradients or the pack picked call for another
frame than the renderer's, the renderer is made again on the same device before the next frame
draws, and the world is meshed again, as after a lost device. Without the switch nothing changes:
the frame stays until the next start.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
MinecraftServer.prepareLevels waits for the chunks the restored tickets hold
(ChunkLoadCounter) before the first tick, and the first tick unpacks their
saved block and fluid ticks at the game time they were saved at. Horizon's
dedicated and integrated servers started ticking while the forced chunks were
still streaming in, and the saved ticks were unpacked from the tick the chunk
arrived, one tick or more late.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
move_pushed applied the resolved movement to position only, leaving the
entity's velocity untouched. In a ticking chunk the entity's own tick
masks this (PrimedTnt::tick_among restitutes), but in a lazy chunk the
velocity survives: a primed TNT pushed by a piston into a wall, ceiling
or slime keeps 0.4 / -0.8 / -0.8 instead of vanilla's 0 / 0 / 0.64.
Add restitute_piston after apply_push: compare the post-piston-limit
movement (limited) with the post-collision result (resolved) and, as
Entity.move(MoverType.PISTON, …) does, zero blocked horizontal axes,
zero or slime-bounce the vertical axis, and update on_ground. Covers
Player, Guest and every Entity kind including PrimedTnt.
Add unit test a_pushed_tnt_loses_velocity_into_a_wall (needs prepared
resources) asserting the TNT's vx is zeroed by a wall.
Co-Authored-By: Nemotron <[email protected]>
The CurseForge app, and mods such as Sodium, create `shaderpacks/` in an instance
even when the player never installed a shader pack. Import counted the folder
itself as one "shader pack, conversion pending" file and listed it under "not yet",
so a client-only instance such as one with Sodium, Lithium, Jade, malilib and
Litematica reported 4 "not yet" rows for 3 mods. A folder instance now reports
shaderpacks/ only when it holds something, and a zip only when it has a file
under its shaderpacks/ (a bare directory entry no longer counts).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Mannequin extends Avatar extends LivingEntity; it is not a Mob.
Mannequin.addAdditionalSaveData always writes profile, hidden_layers,
main_hand and pose after LivingEntity's fields, and none of
Mob.addAdditionalSaveData's. Horizon left out the four unless they were
loaded, and wrote PersistenceRequired, CanPickUpLoot, LeftHanded and NoAI.
A mannequin now reads main_hand and hidden_layers into the main-arm and
hidden-layer state it already had (a list with an entry that is not a part
hides nothing, as the codec's failed read), and writes all four from its
live state: the empty profile when it has none, the hidden layers in
PlayerModelPart order, its arm and its pose. It neither reads nor saves
Mob.readAdditionalSaveData's flags, and its save drops the Mob-owned keys
over a stale loaded compound. Every other kind is unchanged.
Fixes#12135.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Resolve the overlap with #11863 in the styled span and glyph loops. Keep its emission tier on glyphs, outlines and decoration quads, with regression coverage.
Co-Authored-By: OpenAI Codex GPT-6 <[email protected]>
The fade-in's 16-to-32-block distance blend was computed per vertex and
carried in the flat hdr_code, so it was one value per triangle (the
provoking vertex's) and near faces faded in steps. The flat bits now hold
only the section's own visibility (the same for the whole section), and
horizon_material_faded blends it by the interpolated spherical distance,
as terrain.wgsl's interpolated visibility does.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The reverse step for 1.21.1 clients passed ClientInformation through, so a
host read past its end. The older-server adapter now writes 1.21.1's real
layout (no particle status), which made lan_bot's
older_clients_of_each_release_join_and_are_sent_chunks fail at 1.21.1;
real 1.21.1 clients were refused the same way. The step appends
ParticleStatus.ALL in configuration and play.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
NeoForge lets mods add item rarities (Immersive Engineering's masterwork) and some blocks carry a
map colour id past 26.3's 61 (Mekanism's gas blocks: 63). The game refuses the whole content
root for either, so one such file left every block of the mod out. They now keep the template's
and the report names them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The oracle reports can_occlude per state, but the carry ignored it: a
full-cube block made with noOcclusion() (one its block entity draws) took
its template's occlusion and hid the faces behind it, leaving a hole.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ItemEntity.tick applies gravity (or the fluid movement) first and then
moveTowardsClosestSpace, whose 0.75 scales the already-fallen vertical
speed. The push ran before tick_move_before_block_effects, so gravity
came after it. Pass the escape speed into the move and apply it right
after gravity; the native block-contact pipeline is unchanged.
Fixes#12018
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The orb kept its own vertical-only on-ground rule for a piston push. It
now takes the server's end-of-move rule that items, primed TNT and armor
stands use: on ground only when the push's downward part was blocked.
Fixes#12100
Co-Authored-By: Claude Opus 5.5 <[email protected]>
wgpu caps a dispatch at 65535 work groups per dimension and panics past it;
GLSL packs may ask for more (131072). A larger workGroups now runs as the
same number of groups in a shape within the limit, and main works out each
group's place in the declared dispatch, so gl_WorkGroupID,
gl_GlobalInvocationID and gl_NumWorkGroups read what they would there. The
shape uses every group exactly, so no group leaves before a barrier. A
count no shape holds skips the program with a note; an oversized
workGroupsRender dispatch is not run instead of panicking.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
naga's GLSL frontend turns a uint/int that an atomic*() call touches into
an atomic type after parsing. A load or store of a whole struct or array
holding such a member then had the upgraded type: `Cell c = buf.cells[i];`
and `uint copy[4] = shared_counts;` failed validation. A whole struct or
fixed-size array of integers in a buffer or shared memory is now loaded
and stored member by member, so each copy keeps its declared type.
Vendored-naga patch: third_party/naga/src/front/glsl/context.rs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An image declared RGB10_A2, RGB10_A2UI or a 16-bit SNORM format was made
as an RGBA8, RGBA8UI or 8-bit SNORM texture, so a shader binding it as
layout(rgb10_a2) etc. was refused ("is rgb10_a2 but its texture is
Rgba8Unorm"). Each now gets the wgpu format of its GLSL qualifier; one the
GPU can't store into is left out with a note, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
naga's GLSL frontend never compared an exactly matching argument with the
previous best overload's conversion there, so greaterThanEqual(ivec3, uvec3)
found the uvec3 overload no better than the vec3 one and reported
"Ambiguous best function". GLSL 4.60 §6.1 ranks an exact match above any
implicit conversion: the uvec3 overload wins. Zephyr Starlight's
shadowcomp1-4 failed on it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Packs list their const settings in a block comment one per line with no
';' (`const vec4 colortex6ClearColor = vec4(0.0, 0.0, 0.0, 1.0)`). The
reader looked for the next ';' and took every following line as the value,
so each such setting was refused as 'not a vec4' (Zephyr Starlight logged
85 multi-line notes) and its clear colour was lost. In a comment a
declaration now ends at the first ';' or line end; live code still reads to
the ';'.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
tick_blocks applies the entities' block contacts (a boat breaking a lily
pad) before the scheduled ticks; vanilla runs them inside each entity's
own tick, so their drops still wait for the next tick.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With the blur at 0 the plates still read their backdrop bilinearly between
texel centres, and a texel is 3 to 6 logical pixels of pixel art, so the
scene behind the glass stayed slightly soft. With no blur, read the texel
under the point instead.
Fixes#12062
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The render crate read a trail duration of -1 as the pre-1.21.4 marker, and
the client read particle ids -2/-3 as the old entity-effect providers, for
any server: a 26.3 server sending those values got the legacy behaviour.
The client now decides from the connection's release (received_particle)
and passes an explicit legacy_trail_lifetime option; a current server's
-1 stays a lifetime of -1.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A content mod jar whose data files are mostly in the folders 1.21
renamed (recipes/, loot_tables/, structures/, advancements/,
predicates/, item_modifiers/, functions/ and tags/blocks, items,
entity_types, fluids, game_events, functions) was refused by `carry`.
Now the carry moves each such file to the singular name 26.3 reads.
A file the jar also ships under the new name keeps that copy, and a
1.21 jar's stale plural folder is left as it was.
The JSON stays 1.20's: a recipe whose result names its item as `item`
(before 1.20.5), which 26.3 skips, is kept as it is instead of counted
as upgraded, and NBT in loot functions, ingredients and predicates is
not upgraded yet. The report therefore marks such a mod partial
whatever its harness says, and says why. `check` still refuses 1.20
jars until the JSON steps of #10262 land.
Part of #10262.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The browser's data pack note still said recipes, loot tables, advancements, other tags and
worldgen were not read. The game reads them now. The note goes through tr() and is in all
86 language tables (translated in 14, English elsewhere).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Two words, like the guide's other rows, and the same name as Quick Craft on
Servers. The exact rule (single player, LAN worlds from the server list) stays
in docs/controllers.md.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ServerLevel.tick runs the block ticks and block events before
entityTickList.forEach, so a dropper's item ticks on the tick it is
dispensed. The item cutoff was taken right after the tick functions;
take it just before the entity pass instead. Primed TNT and falling
blocks keep their own packet cutoff (#10063).
Fixes#12017
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Terrain's vertex stage exported 20 scalars in 12 interpolant locations. A GPU's
vertex stage pays per location (AMD gives each a parameter slot): on an RX 580 the
12 cost 3.2 ns per quad and the same values in 5 cost 1.6. TerrainOut stays the type
the shader code uses; TerrainWire crosses the stage boundary, each component
interpolated on its own and the integers flat, so a pixel gets the same values.
RX 580, Vulkan, 1080p, render distance 16: village 523.7 -> 590.8 FPS (+12.8%),
forest 267.6 -> 323.7 (+21.0%).
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
The second position_riders() call after the player moves rebuilt the mob
index every tick for every client; run it only when the riding graph has
something on this player. Also forget passengers whose vehicle is removed,
and add the new field to main's second (studio) Remote initializer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: the client keeps only the server's attribute modifiers, so Speed now ends one tick after RemoveMobEffect as in vanilla (ServerPlayer.doTick runs after sendDirtyEntityData); Paper+GrimAC bench 7-17 Simulation flags at expiry -> 0, 8 selected harnesses pass.
The component parser translated a missing key's fallback as if it were another key, so a fallback that named a
real key showed that key's translation. It now builds the text through Text::translate_with_fallback, as
TranslatableContents.decompose does (Language.getOrDefault(key, fallback)), which also keeps the fallback in the
component's JSON. Fixes#12042.
Bound to every interface, the panel's Host check accepts any IP address with the bind's port (and localhost),
which a rebound DNS name can never be, and the login link names this computer's network address instead of
0.0.0.0. Fixes#12039.
Every spawn start measured its distance to every player to keep those
within reach of its attempts. With more than 8 players, the level keeps them
by the 256-block square they stand in, built once a pass first asks, and a
start reads the 3×3 squares around it: every player within reach is in
them, and the nearest one is the same in any order. The spawn draws are
untouched.
(the player-square part of commit e3b70aace7; its region spawning is left
out)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: verifies the per-mod-set build signature with its own Ed25519 key (separate from the release key) before the launcher runs a prebuilt binary; independent review found it sound; opt-in stays off until the rollout steps.
🤖 Agent: the default build resolves the same features as before (390 third-party units with identical fingerprints); every new gate is cfg!(), so CI's existing --no-default-features clippy already compiles the no-extras side and no stub can drift.
TestCommand.showPos takes the source player (getPlayerOrException) and
that player's own pick: its eyes and view, not the execute position. So
`execute as <connected player> run test pos` from another player answers
with the block that player looks at, and the issuer is told. Horizon
only accepted the player in place, so the command did nothing.
For a connected player as the source, /test pos now picks the block with
that player in place (for_player) and only the pick runs there; the test
is looked up in the command's level and the issuer told afterwards. Every
other source keeps its current rule, and the highlight packet is
unchanged.
Fixes#12102.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
VehicleEntity.destroy spawns the cart item through spawnAtLocation, with an
ItemEntity's own motion (0.2 up, random sideways). A container cart's contents
go through Containers.dropContents: a spot in the cart's block, pieces of 10 to
30 with the level random's scatter and no pickup delay, every slot drawing its
spot, and chestVehicleDestroyed drops the emptied container again. Fixes#12099.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ShulkerBoxBlockEntity.moveCollidedEntities pushes through Entity.move too,
so it ends in the same setOnGroundWithMovement as a piston's push.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
EnderEyeItem.use now fires the minecraft:used_ender_eye criterion with the squared horizontal distance to the located structure, matched against the distance bounds squared as MinMaxBounds.Doubles.matchesSqr does.
The End flash state now ticks on every client, so servers, LAN guests and single player show the sky flash and its light. Its weather.end_flash sound plays 30 ticks later ten blocks from the camera toward the flash, follows the camera, and is skipped under the credits.
Getting off a boat, minecart or mount whose block is in #minecraft:portals now takes the free-position branch straight up from the rider, as for a removed vehicle, instead of the vehicle's side dismount spot.
Shears now take the saddle off any #can_equip_saddle mob, not only pigs, and play the saddle's shearing sound item.saddle.unequip unless the mob is silent. A mount wearing horse or nautilus armor is left alone, since vanilla shears that body armor first.
A survival player's hit that breaks a chest boat with entity_drops on angers the piglins that see it, as AbstractChestBoat.destroy does through chestVehicleDestroyed.
A mob writes invulnerable_time while it has hurt immunity left and reads it back with a default of 0, so /data get shows it, /data merge sets it, and the immunity survives a save and reload.
time_since_rest now counts every tick the player is not sleeping, dead or alive, as ServerPlayer.doTick does; time_since_death still counts only while alive.
Items a server or datapack gives a camera_overlay now draw it in first person when worn in their equippable slot, and a carved pumpkin whose equippable was removed no longer blurs the screen.
Throwing a snowball, egg, ender pearl, splash or lingering potion or bottle o' enchanting awards the used statistic, as SnowballItem, EggItem, EnderpearlItem, ThrowablePotionItem and ExperienceBottleItem.use do.
A chestplate a server gives elytra wings now shows them and hides the cape, and an elytra whose equippable was changed or removed no longer shows elytra wings.
A frame broken by a player, an explosion or losing its support posts block_change after its drops, as ItemFrame.dropItem(level, causedBy) does, on top of the entity_die it already posted.
Armour a server or a command gives a different or removed equippable now renders with that asset, or not at all, and only in its own slot, on players, mobs and armour stands. The head layer draws a helmet item when its equippable has no asset.
EndermanTakeBlockGoal.tick posts block_destroy and EndermanLeaveBlockGoal.tick posts block_place, each with the enderman and the block state as context.
The first golem to open a chest half posts container_open with the golem as source, the last to close it posts container_close (incrementOpeners / decrementOpeners), and the openers recheck posts a sourceless container_close when the golems have walked off (recheckOpeners).
Filling a bottle from water now plays the fill sound at the player and posts fluid_pickup at the water block; both the water and the dragon's breath branch award the used statistic (BottleItem.turnBottleIntoItem), and collecting dragon's breath fires player_interacted_with_entity on the cloud.
Entity.move(MoverType.PISTON) ends in setOnGroundWithMovement on a server,
for every push: a sideways push clears the flag, and so does a free push
down. The ArmorStand-only repair in move_pushed becomes a trait method that
items, primed TNT and armor stands share.
Fixes#12029
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`replace_word` lowercased the text to find the user's name in any case, and
used that copy whenever it had the same byte length as the text. Equal
lengths don't imply equal boundaries: `İ` (U+0130) grows from 2 bytes to 3
and `K` (U+212A) shrinks from 3 to 1, so a text holding both keeps its
length while its offsets move. A match then sliced the original text
mid-character and panicked.
Use the lowercased copy only when the two share their character boundaries.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
The Device page gets a row beside Button Glyphs that toggles
PadSettings::nintendo_letters, so players no longer edit
horizon-controller.txt to make A confirm and jump on a Nintendo pad.
Its description says what A and B do in each mode. Always shown, like
the DualSense rows. docs/controllers.md points at the row.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
with_vault built the list of every player (lan_present: eye heights, effects,
omens) for every loaded vault every tick. VaultBlockEntity.Server.tick reads the
players only when the vault's state may change (stateUpdatingResumesAt, once a
second), so the list is now built at the first ask, from the same game state.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A resource pack's core/sky now draws the overworld's sky disc and its
core/stars the stars, as vanilla's SkyRenderer does with the SKY and
STARS pipelines: the disc of buildSkyDisc(16) with the sky colour in
ColorModulator and the sky's fog, the stars with their brightness, both
turned with the camera only. Without such a pack the sky draws natively.
Packs use this to tint the night sky or add a haze to the horizon. With
Visual Effects+'s sky shaders alone, Horizon's sky now reads within a few
units of the Java client's at noon, sunset and night.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Part of #11487. The graphics screen now describes its pages and rows
(`GraphicsScreen`), and the shell draws them, takes the clicks, the wheel,
the stick, the keys and the pad's focus, as it does for Extras. What each
setting does, its saved value, its cost and its description stay in
graphics_ui as they were; Undo puts back the same settings.
What changes beyond what the shell gives every page: the HDR shortcut row
shows its key on a key button, as Extras' Sort key does, instead of between
arrows. The screen's own keyboard, scrolling and drawing code goes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Main moved key_pressed to game/play_keys.rs and inventory_press to
game/inventory_input.rs; the craft's stop on a key or mouse press moves
with them.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: item components read by reference instead of cloned per frame (frame prepare 217→193 µs); earlier parity failures were harness bugs since fixed on main; parity-main covers it after merge.
🤖 Agent: HUD glint sprite looked up once per item instead of per quad (HUD 84→72 µs); render-only, packet harness 16/16; parity-main covers it after merge.
saveConvert pushed one UTF-16 unit at a time and marked a surrogate half with
a NUL sentinel, then ran fix_surrogates over the whole value, which split every
NUL in the result. A \u0000 that is part of a value (legal in a .properties
file) was therefore taken for a marker: "k=\u0000\uD83D\uDE00x" came back from
store with the NUL and both halves of the pair replaced, and since
server.properties is rewritten on every start the value could not survive a
round trip. Walk chars() instead: a Rust str's UTF-16 units always pair up, so
Java's per-char loop needs no marker, and a character past U+FFFF is still
escaped as the two units Java escapes it as.
🤖 Generated with Codebuff
Co-Authored-By: Codebuff <[email protected]>
LT + A on a crafting book recipe crafts it into the inventory: a tap makes
one, each further LT + A while one is in flight makes one more, and held past
the repeat delay it keeps going until a button comes up, the items run out
or the inventory is full. One request at a time, each waiting for the
server's answer; a result moves only with room for all of it as the client
last saw the inventory. A result that isn't the recipe's stops the craft with
nothing moved, and two seconds with no answer stops it with nothing more
sent. A refused LT + A (a carried stack, another recipe still finishing, not
allowed here) is plain A.
It's free in single player and on LAN worlds joined from the server list's
LAN entries; everywhere else, joins by address included, it waits for Quick
Craft on Servers. On any server but single player's own, placements keep
250 ms apart, under Paper's default limit on recipe placements (5 a second
over 4 seconds); on servers that need the option, every request also keeps
a tick apart.
On the crafting book LT changes the category as it comes up unless A was
pressed meanwhile; RT, and both triggers on the furnace books, change it as
they go down, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On Metal, vanilla terrain drew flat only past 1600 listed sections. Cave
culling can drop the listed count below that on a normal view, and the
per-section path then drops cutout leaves on Intel Metal: the canopy above
trunks vanished. Flat draws keep them, so Metal now always draws flat.
Co-Authored-By: Claude Haiku 5.5 <[email protected]>
Part of #11487. What the graphics screen has that the shell couldn't say
yet, for any screen that wants it:
- Undo over Done once a row changed something, putting back what the
screen says it changes (`Screen::undoes`, `Screen::undo`), from the
settings as the screen found them. Tab and the arrows reach it.
- A row's cost, a square and the cost in its colour after its name in the
strip.
- Why a greyed row can't be used, in the strip, muted, instead of what it
does.
- `Shell::show` opens a screen at one of its pages.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Part of #11487.
- The shell keeps the rows it built and builds them again only when what
they read changes: the screen's stamp of its settings, a press or slide,
the search, the page, the size or the language. On Extras, a pad session
of 727 frames built them twice instead of 751 times.
- On a screen with no page list, Down from the search field goes to the
first row instead of Done, for the shell's keys and, through a declared
focus link, for the pad and the keyboard's focus.
- A row the list hides part of scrolls fully into view when the pad or the
keyboard focuses or presses it (AbstractSelectionList.setSelected), so A
presses it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A server's client applied its effects' attribute modifiers itself, so it
slowed down as soon as ClientboundRemoveMobEffectPacket arrived. Vanilla's
server removes a Speed that runs out in ServerPlayer.doTick, after the
tick's sendDirtyEntityData, so the movement speed without the effect's
modifier reaches the client a tick later and vanilla's client runs fast for
that tick. GrimAC's Simulation flagged the Horizon client on that tick
(0.051 off with Speed II, decaying over the next ticks).
LivingEntity.onEffectAdded/onEffectUpdated/onEffectsRemoved change
attributes on a server only; a server's client now keeps the modifiers
ClientboundUpdateAttributesPacket sends.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Inserting "Found on this computer" before the last entry of each language
table, instead of after it, adds one line per file and leaves the last line
alone, so other pull requests that append strings don't conflict with it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`let mut keepalive` was only assigned under
`#[cfg(not(any(target_os = "windows", target_os = "openbsd")))]`, so on
Windows and OpenBSD clippy reported `unused_mut` and `-D warnings` failed.
Shadow the binding under the same cfg instead; behaviour is unchanged.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The "Found on this computer" heading says enough on its own, and the caption
cost a string in every language table. The test now checks that each found
instance's slug is the one importing it gives, so the Open button keeps
pointing at the pack an import made.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Imported.datapacks counts the packs in a profile's home/new-world-datapacks,
`can_play` and `apply_profile` accept a profile with only data packs, and
`outcome` and `modpack list` count them.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
A mod jar with no classes, a pack.mcmeta and a data/ folder is a data pack.
For a 26.2 or 26.3 modpack, `port` now upgrades such jars to 26.3 (the
upgrade-datapack code; 26.3 ones are copied) into the profile's
home/new-world-datapacks/ instead of carrying them as content mods, which
dropped their data/minecraft overrides. The game copies that folder into
each new world it creates.
Before its jar is read, a mod whose Modrinth project also ships as a data
pack counts as data tier, so `fetch` gets the jar; the jar then decides.
The coverage report lists such a jar as partial.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
horizonDynamicLightCount and horizonDynamicLight0..15 give a pack Horizon
Extras' dynamic lights, nearest first (camera-relative position, light
level). horizonTier gives terrain, entity and hand programs a face's HDR
tier: 0 none, 1 to 4 low to very bright, plus 4 on terrain when the whole
face shines. Opt-in: a pack that declares none of them sees no change.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Entity.collidedWithFluid sweeps the box along the whole move, so water reaches
the boat down to the lower of its start and end bottoms in every step.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Modpacks page now opens with "Found on this computer": the modpack
instances the CurseForge app, Prism Launcher, MultiMC, the Modrinth App,
ATLauncher and the official launcher keep in their usual folders, read with
`other_launchers::scan` on a thread. Each row shows the app, Minecraft
version and loader, and imports the instance with one click through the
same path as "Import an instance folder…" (a Prism instance from its own
folder, so its `mmc-pack.json` still names the version). A pack already
imported opens instead.
Only instances with a `mods` folder of their own are listed: the official
launcher's profiles that play in its own folder are the player's everyday
game, not a pack. The two new strings are in every language table.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
StorageDataAccessor.getData returns the stored compound itself, and
DataCommands.manipulateData edits it in place. A wildcard set on a
non-empty byte, int or long array clears the array before it rejects a
string, so the command fails with nothing counted and the storage keeps
the cleared array. Horizon edited a copy and dropped it on the failure.
On that failure, a /data modify on storage now puts the edited compound
back with CommandStorage::set_unsaved: only an id that is stored is
replaced, and the storage is not marked for saving, as vanilla calls no
setData. The error, the result, the success path and the entity and
block copies are unchanged.
Fixes#12057.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
VehicleEntity.destroy spawns the boat item through spawnAtLocation, so it
leaves with ItemEntity's default motion (0.2 up, random sideways), not
straight up at 0.1. Chest contents keep their own scatter. Fixes#12026.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Where folder names ignore case (Windows, macOS), a Prism or MultiMC root's
`instances` folder is also `Instances`, so `in_folder` took it for the
CurseForge app's modding folder and found nothing. It now reads that folder
as CurseForge's only when none of its instances has an `mmc-pack.json`.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Entity.checkInsideBlocks meets the blocks of the box a move starts in before
the ones it reaches, each once, and each step lights before it puts out: a
boat that leaves water for fire in one move ends up burning.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: stamps the existing chunk_tickets fragments (API 412/413, NATIVE_HOOKS_API 413); tests.test_stamp_native_hooks 10 OK, verify green, and this exact head built into the SMP engine on playhorizon (rtp2), whose /rtp proof passes with no slow tick. The SMP 0.35.1 PR in horizon-mods needs it.
Decoration scheduling measured, sorted and ranked every proto chunk each update.
Ranks are unique, so only chunks without one need their distance; the rest
pop from a heap until the workers are busy. A server with 200 players spread
over the map held ~57,000 protos and spent a fifth of its tick here.
The heap pops in the order the full sort gave; unranked chunks at the same
distance now go by position instead of where the unstable sort left them.
(cherry picked from commit d20e7bf74e)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`instance others --folder` and `copy-from --folder` now accept a folder whose
children are CurseForge instances, the `Instances` folder the app shows the
player, as well as the modding folder above it. Before, that folder found
nothing.
When nothing is found, the message says where it looked (the folder named, or
every default folder the scan reads) and which folders it expects. The
CurseForge folder is joined one component at a time, so its path no longer
shows mixed separators on Windows.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Windows users cannot create symlinks without a privilege (WinError 1314), and NeoForge's
unix_args.txt gives "Illegal char <:>" there. Link the server's libraries folder with a
junction and the mod jars with a hard link, copying when that fails; use win_args.txt on
Windows. Linux and macOS keep the symlinks and unix_args.txt. Adds tests/test_neoforge_oracle.py.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
🤖 Agent: notices list the three cases (Iris, OpenJDK math, OpenJDK HashMap); both clean-room rewrites (#11944, #12043) are merged, so the notice now describes main accurately.
Reuse the existing machine menu cache, shared placement and authoritative
slot writeback for ordinary PlaceRecipe packets. Preserve inventory/table
placement and the separate Horizon recipe-transfer route.
Fixes#12009
Co-Authored-By: Codex (gpt-6.1-sol) <[email protected]>
bees_stay_in_hive built the mob world (every mob and player) for each occupant
whose time was up. The night and rain it reads hold for the hive's turn.
(cherry picked from commit 3430283af0)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every in-range check compared the cover's copy of the extra list (about 200
entries on a spread server) with the live one, for each waiting chunk each
tick. The cover is refreshed at the start of update; update now says so and
the checks trust it, with a debug assertion that they still match.
(cherry picked from commit f61e6ed98d)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A heart built the mob world (every player, the teams, the weather) each tick,
but reads it only in the update about once a second. server_tick is split at
that point; the game builds the world for the update only. The heart's
creaking, asked for a few times a tick, is found through the mob index
instead of a scan.
(cherry picked from commit a1cf6491dd, with the mob index
in place of its own slot map)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
BaseSpawner.serverTick only decrements a running delay. The level (every
player, the mob world) was built for each spawner near a player every tick
first.
(cherry picked from commit 1f06b720c9)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Players spread far apart read more start chunks than the cache held, so
outposts' and trial chambers' jigsaws were built again on the server thread
whenever natural spawning asked about them.
(cherry picked from commit da2c8d2d48)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A game folder can hold a `new-world-datapacks` folder. Create New World now
seeds its temporary data pack folder from it and selects those packs, and a
world opened without the Create screen (`--world`, the control socket) gets
a copy when it has no level.dat yet. Each world keeps its own copy. Without
the folder nothing changes.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
AbstractBoat.tick decays the damage before Entity.baseTick burns the boat, also
for a boat its rider's client moves, and the block effects follow the whole
move, so water the boat started in still puts it out.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: clean-room java_hash_map (CLRS tree, slab); java_hash_map and java_identity_map oracles identical over 1.18M lines plus 3,028 edge fixtures; 11/12 caller harnesses pass (structure_layout lacks oracle data on the desktop).
🤖 Agent: clean-room jmath; JVM-identical on ~2.5M inputs per function (log/log10/cbrt), 5.9M hypot pairs, 3.05M sin/cos, all 2^32 floats for round_f32; parity-main covers the ~420 dependent harnesses after merge.
DiodeBlock.canSurviveOn asks isFaceSturdy(level, pos, UP, RIGID) of the
block below, in canSurvive and in updateShape with the neighbour's new
state. A retracting piston base is a moving_piston whose block entity
shape holds the extended base, so its top face is rigid. The diode checks
read the raw state's exported facts instead, saw no support, and broke the
repeater or comparator. Both paths now go through the contextual rigid
support, which takes the state a shape update hands over.
The redstone catalog harness gains fixed lever/piston/diode contraptions
for both piston and both diode kinds (#12019).
Fixes#12019
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SulfurCube.mobInteract ends with Bucketable.bucketMobPickup, and a sulfur
cube is picked up with an empty bucket (canBePickedUpWithBucket), not a
water bucket. Horizon's bucket pickup only knew the water bucket's mobs,
so a click with a bucket left the cube where it was.
After the cube's own baby, primed, shears and swallow branches, an empty
bucket now plays the sulfur fill sound, fills a sulfur_cube_bucket, fires
filled_bucket and player_interacted_with_entity, drops the leash, discards
the cube and gives the bucket through ItemUtils.createFilledResult, as the
other bucketable mobs do. The native bucket hook hears it.
The filled bucket carries SulfurCube.saveToBucketTag: the cube's age and
age_locked in bucket_entity_data, and its body block as
sulfur_cube_content when it has one. Other species keep their bucket data.
The command harness gets a mob_interact op: the connected player's real
Interact packet on a mob, on both sides.
Fixes#12044.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The archetype table keeps knockback resistance as a decimal double for
the physics code. The data's modifier amount is the float widened
(0.4000000059604645 for slow_bouncy), so /attribute read 0.4 and 0.7
where vanilla reads 0.4000000059604645 and 0.699999988079071.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A boat now keeps Entity.remainingFireTicks: Entity.baseTick burns it down a
tick at a time and hurts it every 20 ticks, and the two applyEffectsFromBlocks
of AbstractBoat.tick light it on fire blocks, hurt it once per fire block,
let water, powder snow and rain put it out and clear the cooldown. Saves,
/data, portals and the root vehicle write the live Fire; loads and /data
merge read it. Fixes#12025.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of 512 lines (Loading, App, run_event_loop, the start checks) to game/loading_screen.rs; the Windows-only hwnd stays in game.rs and stays visible to the moved code.
🤖 Agent: pure move of 506 lines (keys and clicks over a container screen) to game/inventory_input.rs; click_inventory stays because an open draft changes it.
python3 tools/stamp_native_hooks.py --only chunk_tickets chunk_tickets_in_level.
The SMP's /rtp moves from /forceload (which waits on the server thread
until its chunk is generated) to native_force_chunk_in, so it needs a
number to declare.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
SulfurCube.collectEquipmentChanges runs in LivingEntity.tick
(detectEquipmentUpdates) when the body item changed. It removes the
previous body's archetype modifiers and adds the new body's as transient
modifiers: knockback_resistance, explosion_knockback_resistance and
bounciness (add_value), friction_modifier and air_drag_modifier
(add_multiplied_total). /attribute read the defaults for these five on a
cube with a body.
A sulfur cube now has its own attribute map from its type's defaults.
The five attributes go to that map, and the tick adopts the body's
modifiers there with the existing AttributeInstance operations. A
transient modifier shadows a permanent one with the same id, and the
removal takes both, as in vanilla. The archetype table also gets its
explosion knockback resistance, from the 26.3 data.
The save writes the base and the permanent modifiers of these instances,
not the transient ones. A command change of the knockback base or of a
permanent modifier also goes to the store that the bodiless physics
reads, so that effect stays as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The same check for #6741 found that java_hash_map.rs was translated from
OpenJDK's HashMap.java. List it with the other two, and say so in
how-horizon-is-built.md.
Refs #6741
Co-Authored-By: Claude Opus 5.5 <[email protected]>
execute if|unless block, fill ... replace and clone ... filtered tested only
the block state; BlockPredicateArgument also compares the {...} part with the
block entity's full saved data (NbtUtils.compareNbt), and a block without a
block entity fails it.
Fixes#12030
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of cauldron_outcome and use_on_cauldron (96 lines) to game/cauldrons.rs; cauldron_at and cauldron_extinguish stay because an open draft changes them.
🤖 Agent: pure move of 117 lines (GLFW key/button codes, follow_named_keymap, the key routing rules) to game/key_codes.rs; game.rs re-imports the names other game modules reach through super::.
A blended draw over a surface of the other kind leaves one mark for both, so a frame that mixes
marked and written light would clip the written light and lift it again, or leave the marked part
unlifted. Step 4 now goes path by path: the frame-case rebuild first, then every marking shader on
the default path in one pull request, then Improved Transparency and the visibility buffer.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: moves 575 lines of game.rs's inline tests to game/core_tests.rs with unchanged bodies; the horizon-client test count stays 1159, and the tests open PRs touch stay put.
🤖 Agent: pure move of spawn_player (117 lines) to game/player_spawn.rs; test_world_input_capture's loading-completion check reads it from the new file and still sees the player set before the mouse capture.
The previous implementation was translated from OpenJDK's
java/util/HashMap.java (GPL-2.0), which Horizon's MIT licence cannot
carry. This one was written in a two-person clean room: the implementer
worked from a behaviour spec, the Java SE API documentation, CLRS
chapter 13 (red-black trees) and runs on a real JVM, and never read
OpenJDK's source or the old file.
Entries live in a slab of nodes addressed by u32 index. A bucket holds
the first node of its bin's list and, for a tree bin, the root of a
red-black tree over the same nodes, whose links sit in a side table the
map allocates with its first tree bin. The public API is unchanged.
Unit tests pin iteration orders printed by java.util.HashMap on OpenJDK
25: a doubling from 16 to 32 buckets, a tree bin made at 64 buckets
after two doublings below 64, a tree-bin insertion after its tree
parent, movable and cursor removals from a tree bin, copies, and the
cursor's error order.
Refs #6741
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of 190 lines (startUseItem, interactOn, the placement/bucket cancel hooks) to game/item_actions.rs; the Player actions docs rows now point fn use_item and fn attack at their files.
`MeleeAttackGoal.canContinueToUse`'s follow branch returns
`isWithinHome(target.blockPosition()) && !(target is a spectator or
creative player)`. Horizon's `MeleeAttack::can_continue` only refused a
creative player: a mob with a home kept chasing past its radius, and a
player who became a spectator was still chased.
`DrownedTridentAttackGoal` overrides `canUse` with the trident test and
inherits `RangedAttackGoal.canContinueToUse`, which calls `canUse()` again
(and falls back to a target it is still walking towards). Horizon's
`DrownedTridentAttack::can_continue` asked only the base ranged goal, so a
drowned whose trident was gone kept the trident attack running once its
path was finished.
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
fs_oit_cull writes the nearest opaque see-through depth into the depth
buffer, and the later OIT stages test GreaterEqual against it, so an opaque
fragment is compared with its own depth. On Metal, position.z can differ from
the rasterizer's depth by an ulp, which dropped random pixels of players and
mannequins (horizontal dashes, text behind showing through). Write the depth
4 ulps farther: the fragment still passes, anything visibly behind is culled.
Checked on macOS (Metal, Apple M1) with a mannequin and a text display; not
yet on Linux (Vulkan).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of 90 lines (autosave interval, tick-rate change, player autosaves, level.dat metadata) to game/autosave.rs; save_game_with and save_level stay because open PRs change them.
🤖 Agent: pure move of 109 lines (renderer_for, apply_settings, translucent layers, far terrain distances) to game/render_settings.rs; prepare_lod and all_sections_changed stay because an open draft changes them.
🤖 Agent: pure move of 234 lines (title actions, open_world, load_world, save_for_exit, close_world) to game/world_session.rs; finish_open_world and finish_close_world stay because an open draft changes them.
After a close blast a mod can now dull and quieten the world's sounds while
its own ringing plays clearly: native_muffle_sounds(amount, lowpass, ticks,
exempt) turns every sound down by `amount` and low-passes it by `lowpass`
(down to 300 Hz), both fading back over `ticks` client ticks and held while
the game is paused. The sound events in `exempt` play through untouched,
those already playing too.
The mixer mixes the muffled sounds into a bus of their own, runs two
one-pole low-pass stages and the duck over it, and adds it to the exempt
ones. The level glides over 20 ms so tick-sized steps don't click. While no
muffle is set or once it has faded, sounds mix straight into the output as
before. The sound engine marks a channel exempt as it starts, and marks the
playing ones again when the list changes.
Helper-only fragment muffle_sounds. Fixes#12006
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`CatTemptGoal` overrides `canUse` with `!cat.isTame()` but does not
override `canContinueToUse`; `TemptGoal.canContinueToUse` ends with
`return this.canUse()`, so taming a cat mid-tempt ends the goal. Horizon's
`CatTempt::can_continue` ran only the base `Tempt` continue, which never
asked whether the cat was tamed, so a cat tamed while following a fish
kept following it.
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857): the tests no open PR changes go to their own test module; the ones open PRs touch (the module header, the scaffolding fall test, the integrated save test) and the tests next to them stay. Test bodies are unchanged; rustfmt re-indented them one level.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The HUD atlas reserved `horizon/modimage` (native_mod_image, API 355) only
when the launcher's native-mods.json listed mods, so a bundle applied with
horizon-mixins by hand or by a mod's own script built a game whose
native_mod_image always returned false.
horizon-mixins leaves .horizon-mixins-stage.json at the root of every
workspace it changes, naming each injection's and module's mod. The build
script turns a record that names mods into HORIZON_MIXIN_MODS, and
built_with_native_mods counts it and installed trait mods as well as
native-mods.json. A release or Nightly has none of them and keeps no slot.
Fixes#12005
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of 110 lines (block face and particle sprites, honey particles, destroy particles) to game/block_particles.rs; particle_sprite and crack_particle stay because open PRs change them.
🤖 Agent: pure move of 186 lines (attack strength, crits, sweeps, Player.attack) to game/melee.rs; melee_damage and attack_mob stay because open PRs change them.
🤖 Agent: pure move of the game window's ApplicationHandler (295 lines) to game/window_handler.rs; the input tests read window_event/device_event from the new file first, since Loading and App have handlers with the same names.
🤖 Agent: pure move of 359 lines (slot layout, hit test, item and decoration drawing) to game/inventory_slots.rs; slots/slot_count/draw_inventory stay because open PRs change them.
One Cargo feature, on by default, holds the entry points of Horizon's own
extras so a fork can build the game without them
(`--no-default-features --features audio,controllers`) or delete them:
cosmetics, emotes and the "Horizon player" presence (one gate,
game::cosmetics::enabled), the official and partner servers added to
servers.dat, the Options screen's Horizon Settings and Mods buttons, and
the existing discord, friends and heartbeat features it now turns on.
The default build resolves the same features as before and every gate is
cfg!(), so both sides compile in every build. A build without the extras
names the official build on Credits & Attribution. docs/forking.md lists
the extras, their files and their gates.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
After the post effects and before the camera overlays, ITU-R BT.2390's EETF in PQ rolls the
light off into the peak, on its brightest channel so it keeps its hue. It takes over the HDR
lighting's easing and the light sources' cap at the peak, which the resolve pass no longer
applies. The knee stays where the lighting's was, 60% of the peak.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An absolute /tp zeroes the player's speed, as vanilla's does
(TeleportCommand's relatives carry no DELTA), so the check's teleport
right before the hit left the guest at walking speed and the check
failed on main. The guest now runs 19 ticks from 8 blocks out and
clicks in reach. The slowdown assertion is tightened: the first
post-hit sample must be under 0.75 of the sprint speed and then climb,
which fails with the client's 0.6 cut removed (the old 0.9 bound did not).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of 237 lines (an item in use, tick by tick) to game/using_item.rs; apply_consume_effect and drop_bundle_content stay in game.rs because an open draft changes them.
The parity work after the per-screen PRs (#9714, #9836, #9909, #9926,
#9968, #9982, #10032, #10391, #10448, #11640), as verified in the local
playtest builds:
- Main list: RealmsMainScreen's header and footer geometry, row content
and status light, scrolling, notices, selection narration, and the
60 s/10 s/300 s data-fetcher periods.
- Configuration screen: the four tabs (MenuTabBar), GridLayout columns,
slot options, region preference, invite codes and their edit screen,
the players tab (invites, operators, removal), the subscription tab
and the tab keyboard.
- World screens: RealmsResetWorldScreen's tiles, the template list, the
upload selection and upload screen, and the PDP rows and keyboard.
- Backups: RealmsBackupScreen/RealmsBackupInfoScreen on the current wire
(lastModifiedDate, metadata as strings, a local changeList), restore
as RestoreTask (retries, abort), and the complete Download Latest flow:
DownloadTask, RealmsDownloadLatestWorldScreen's size check, bar, speed
and cancel, and FileDownload's tar.gz unpack (name cleanup, world/
entries only, level.dat renamed, resource pack SHA-1 checked).
- Settings: RealmsSettingsTab's GridLayout/ScrollableLayout, the
distance sliders, the name validation, and tooltips drawn after every
widget.
- Service: the ops/invites endpoints, the invite answer's players, the
backup age on the wall clock, null gameMode, and news persistence.
- Integrated server: the owner and the memory connection's login use the
account profile (IntegratedServer.setSingleplayerProfile,
ServerboundHelloPacket), so a world without singleplayer_uuid (a Realms
download) loads the owner's players/data file; saveAllChunks'
saveDataTag(profile.id()) is mirrored by save_owner.
Tests: 2166 lib tests pass (65 realms), scoped clippy clean.
Co-Authored-By: ZCode <[email protected]>
/test only knew the test instances of the game's own data: read_test_def
read options.resources, and the selector ids were the exported built-in
ones, so a test a world's data pack defined was "No matches for
selector". Vanilla 26.3 loads Registries.TEST_INSTANCE from the packs the
world enables when it loads: later packs override earlier ones and the
built-in data, and /reload or /datapack do not change it.
When a world opens (the end of load_world_state), the built-in pack's and
the initially enabled packs' test_instance files are read once, in pack
order, through the existing test instance decoder. They become the
world's test definitions (test_defs) and its minecraft:test_instance ids,
replacing the previous world's. /reload and /datapack leave them as they
are. A game with no world opened reads the game's own data as before.
Fixes#11976
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The coverage view is chosen once, when the shader is wrapped; nothing reads the field after.
Also lets a test fill the coverage with its own f64.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CI has no prepared resources, so the assert on the test world failed
there. Do what the other test-world tests do: check nothing when the
data is missing.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: requeued after merging main: the only conflict was the module list in game.rs (`pointer_capture` from main beside `portal_ticking_tests`), resolved by keeping both; no code change since the checks.
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: queued: spans only, no behaviour change; pr_ready --quick green. Point: the guards wrap the mods' hooks outside every level span, so the SMP's 1-2 s ticks get a name in horizon-report.
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The workflow's clone is blobless, so `git cat-file -e` on a head it
does not hold fetches it from the server, one round trip per check.
On busy days the job ran into its 10-minute timeout. Check every head
in one `cat-file --batch-check` with lazy fetching off, and run the
diffs on 8 threads so their blob fetches overlap. A note for #11723
now takes 13 s instead of 170 s; `show` over every open pull request
29 s instead of 785 s, with the same notes.
Closes#11984
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of 390 lines (toggled keys, mapped keys, hotbar wheel, play_mouse) to game/play_input.rs; the two hook tests that compile mapped_key_event and the wheel branch read the new file.
🤖 Agent: pure move of 170 lines (title panorama, menu blur, border wall, the screens' render-post hooks) to game/screen_backgrounds.rs; regenerated on main after #11939 since the blocks were adjacent.
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`TradeWithPlayerGoal.canUse` refuses a trader that `wasHurtRecently` or
whose trading player is more than four blocks away, but Horizon's
`TradeWithPlayer::can_use` checked neither. A wandering trader that took a
hit, or whose customer walked off, stayed in the stand-still trade goal
where vanilla ends the trade.
`HappyGhast.HappyGhastFloatGoal` overrides `canUse` with
`!isOnStillTimeout() && super.canUse()`. `FloatGoal` does not override
`canContinueToUse`, so `Goal.canContinueToUse` asks that same overridden
`canUse` on every tick: a player standing on the ghast makes it stop
floating. Horizon's `HappyGhastFloat::can_continue` re-ran only
`Float.can_use`, so the goal kept hopping in water under a player who was
holding it still.
#7820 gave the entity pipeline a constant depth bias of 128 so entities win the tie with
coplanar terrain. HDR output marks what it draws by drawing the same quads again, alpha only,
GreaterEqual against the depth it left, but without the bias: every mark tested a hair behind
that depth and failed. Mobs lost their HDR lighting, light sources in the hand stopped shining,
and the empty hand only looked right by reading the ground's mark behind it.
The marks after entities, the hand (unmark and mark) and translucent particles now draw at the
same bias. Translucent items, additive draws and the OIT stage follow unbiased pipelines and
keep the unbiased mark.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On the float HDR frame the last pass guessed which pixels still showed the world
(`shows_world`) and clipped everything else at white, so the world's light dropped to
SDR under the chat's see-through background, a menu's backdrop and the coloured camera
overlays. Where the device records HUD coverage (#11896) the frame is now drawn in
layers, as the HDR note's step 3 specifies:
- After the world and the hand, `Hdr::resolve` turns the frame's marks into light once.
The camera overlays, post effects and entity outlines draw on light after it.
- `keep_world` becomes a pass: the world clipped to white, alpha 0, into a HUD target in
the frame's format, which the HUD and menus draw on with their unchanged blends.
- The last pass lays the HUD over the world's light by its coverage, in gamma as the HUD
blends (H = P - (1 - c) B, then H ui^(1/2.2) + (1 - c) W). UI Brightness scales the
HUD's own part.
- The menu blur blurs that composite, light included, and makes it the world the menu
draws over.
Until the tone curve, a screenshot is the HUD target and the world icon the world, both
clipped at white; the -hdr.png is the full picture. The legacy path, the 16-bit frame and
HDR off are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A dedicated server's slow tick lines on the SMP show 1-2 s ticks whose
time is in no span: it is spent outside every level, where only the mods'
ServerTickStart/End hooks, the config poll and the idle-level close run.
Each gets a span, so a slow tick line and horizon-report name it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
/clone placed the blocks but not the block ticks pending in its source
box, so a strict clone of fresh sand never fell, while vanilla 26.3's
CloneCommands.clone ends with LevelTicks.copyAreaFrom over the whole
source box.
cmd_clone now calls the existing LevelTicks::copy_area with the source
box and the destination offset, after the placements and block
entities and before the empty-count failure, as vanilla does. The core
copy (the box filter, trigger times, priorities, sub-tick reordering
and dedup) is unchanged. Block ticks only, in the same level.
Fixes#11963
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`AbstractCubeMob.CubeMobKeepOnJumpingGoal.canUse` is
`!this.cubeMob.isPassenger()`, but Horizon's `CubeKeepJumping::can_use`
returned true unconditionally. A slime or magma cube riding a boat, a
minecart or another mob therefore kept hopping and steering its vehicle
every tick, where vanilla leaves the vehicle alone.
#7820 gave the entity pipeline a constant depth bias of 128 so entities win the tie with
coplanar terrain. HDR output marks what it draws by drawing the same quads again, alpha only,
GreaterEqual against the depth it left, but without the bias: every mark tested a hair behind
that depth and failed. Mobs lost their HDR lighting, light sources in the hand stopped shining,
and the empty hand only looked right by reading the ground's mark behind it.
The marks after entities, the hand (unmark and mark) and translucent particles now draw at the
same bias. Translucent items, additive draws and the OIT stage follow unbiased pipelines and
keep the unbiased mark.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pure move of 415 lines (first renderer, start_game) to game/loading_window.rs; the Windows-only hwnd stays in game.rs, so no cfg'd re-import is needed.
Entity.handlePortal runs in the entity's own tick, and ServerLevel.tick
ticks only the entities in entity-ticking chunks. handle_portals and
find_portals walked every loaded entity, so a mob in a nether portal past
the simulation distance went through it and its cooldown counted down.
They now skip entities outside entity-ticking chunks and keep the portal
state of the ones waiting there.
Fixes#11960
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857). Private methods and items become pub(super), the same reach they had in game.rs (the game module and its children). Port map rows, source-reading tests and the docs tables follow the code.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Keep active tuples authoritative and carry only fallback EffectInstance subtrees through Mob load/save and the audited mutation boundaries. Reuse the existing pure duration operation after server work.
Co-Authored-By: Codex <[email protected]>
🤖 Agent: pure move of the world entry impl block (367 lines) to game/world_entry.rs; the world_items!/world_recipes! macros stay defined before the mod list, so the moved code still sees them.
🤖 Agent: merging #11948 (smp-200). The one risk was the filter cutting block contact for entities that do tick: it keys on the same entity_ticking_area test the mob and item ticks use. The new test shows a zombie inside simulation distance 4 still presses its plate while one past it doesn't (and fails without the change). server_entities, mob_ai, cauldron, raid and dedicated_server harnesses pass locally; CI parity covers the rest.
🤖 Agent: pure move of 324 lines to game/pointer_capture.rs; test_world_input_capture's method() now also finds pub(super) methods, so the capture fixture still compiles the real code.
An Allay loaded with a finite minecraft:liked_player memory kept it
forever and saved it without its ttl: the loader read only the memory's
presence and value, the save wrote the value, and nothing counted the
memory down.
- The memory's time to live is kept next to the existing liked-player
state (None for a memory that never expires), read on load and
written on save as MemorySlot.visit does.
- The Allay's AI step, which NoAI skips like customServerAiStep, ticks
it as MemorySlot.tick does: it counts down, and is cleared on the
tick after it reaches 0.
- Giving an item sets the memory without a time to live
(Brain.setMemory), taking it back erases it, and the /data path that
keeps the memory keeps its time to live too.
Fixes#11936
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The shared atlas's textures (items, entities, block entities) now bring
their _n and _s maps to shader packs as paged maps: 16x16 pages of the
atlas, only those holding a mapped texture stored, found through a page
table at the map's start. The programs drawing that geometry read
normals and specular through hz_pagedTexture.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
JavaHashMap reproduced java.util.HashMap iteration order by translating
OpenJDK's java/util/HashMap.java (GPL-2.0 with the Classpath Exception):
its tree-bin routines, their signatures and their quirks (#6741). This
keeps the public API and removes the body, which panics with todo!() until
the next commit writes it again from a behaviour description, the Javadoc
and the real JVM's results, by an agent that has not read either source.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: kacpherek's Dir point is fixed (from3DDataValue wrap, Steps unclamped, tested); on top of MSedgeMC's main merges I only moved the no-target doc comment back onto its test; shulker_space and the save harnesses pass on the union of this gate's PRs on main 1d63e0a5a (playhorizon: fmt, clippy -D warnings, client + client-lib tests, 26 harnesses all pass).
🤖 Agent: port_ledger map-check on the four changed TSVs reads 519 rows, no problems, and kacpherek's PackSelectionScreen.Watcher row is restored (rdbtCVS's fix); TSV-only, no game code.
jmath.rs held ports of OpenJDK code: fdlibm::{log, log10, cbrt, hypot}
from OpenJDK's FdLibm.java (GPLv2 with the Classpath Exception),
aarch64::{sin, cos} from HotSpot's AArch64 trig intrinsics (GPLv2), and
round_f32 from OpenJDK's Math.round(float). Horizon is MIT.
Rewritten in a two-person clean room from Sun's fdlibm 5.3 C sources, a
behaviour-only description of the AArch64 sin/cos, and the Java SE API
documentation:
- jmath/fdlibm.rs (new, Sun's notice at the top): log, log10, cbrt and
hypot ported from e_log.c, e_log10.c, s_cbrt.c and e_hypot.c; sin_fma
and cos_fma, fdlibm's sin and cos (s_sin.c, s_cos.c, k_sin.c, k_cos.c,
e_rem_pio2.c) with fused multiply-adds, as Math.sin/cos on AArch64.
- jmath.rs: round_f32 from the Java SE specification of
Math.round(float); the module doc says what each platform computes;
jmath::fdlibm and jmath::aarch64::{sin, cos} keep their paths.
- geometry/trig.rs: Sun's notice above its fdlibm asin/atan/atan2 ports.
- THIRD_PARTY_NOTICES.md: an fdlibm section.
Bit for bit against JDK 25: StrictMath log, log10, cbrt and hypot on
2.5M, 2.5M and 2.7M inputs and 5.9M pairs; Math.sin and Math.cos on an
Apple M4 on 3.05M arguments, compiled and -Xint; Math.round(float) on
all 2^32 floats.
Refs #6741
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Absorption adds 4 per level to max_absorption (an ADD_VALUE modifier),
and LivingEntity clamps the absorption amount to getMaxAbsorption: when
the effect starts (setAbsorptionAmount) and when it goes
(onAttributeUpdated). A mob's max_absorption lived only in the command's
per-target attribute map, without the effect's modifier; /effect clear
left the amount, and expiry subtracted 4 per level, so a mob with a
raised base lost absorption it keeps in vanilla.
The mob's max_absorption now uses the attribute map it already has for
armor; its effective value counts the effect in the ADD_VALUE phase. The
add clamps the amount to it, /effect clear and expiry clamp to what is
left. The command-set base and modifiers stay unsaved, as before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
THIRD_PARTY_NOTICES.md gets a "Code removed for its license" section for
anyone holding an older version: the shader pack code written from Iris's
source (LGPL-3.0, removed in #1561) and the math functions translated from
OpenJDK and HotSpot (GPL-2.0, #6741). In those versions that code is not
covered by Horizon's MIT license.
CONTRIBUTING.md says OpenJDK's own source is GPL too, so it is off limits
like any GPL project; Java's behaviour comes from its documentation, a JVM's
results, or a permissive original such as Sun's fdlibm.
docs/how-horizon-is-built.md no longer says Horizon holds no Minecraft data
and never downloads it: the datafix tables come from vanilla's code, and the
launcher downloads 26.3 from Mojang for a signed-in account that owns it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without a skin the launcher can read (signed out, offline), the Shop's pictures
and its 3D view drew the plain grey figure. They now use the stand-in the
website's shop draws (its standInSkin, original art painted from rows, not
Mojang's Steve or Alex), so both shops show the same player.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
In a frozen level, water a command placed in newly forceloaded chunks
before the next world tick never flowed: the chunks' tick containers
were only registered by tick_blocks (a running world tick) or by
postprocessing, so the water's flow tick found no container and was
dropped. Vanilla registers them when the chunk becomes a full chunk
(LevelChunk.registerTickContainerInLevel), before any command sees it.
update_world now runs the existing sync_tick_chunks once the streamed
chunks are resident and the unloaded ones are saved with their ticks,
so registration comes before the tick's commands and world tick. Only
an authoritative game (not a server's client) registers.
Fixes#11873
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A block entity's faces were baked with `minecraft_mix_light` and their
normal left zero. A shader pack's `gbuffers_block` (or its fallback)
receives the unshaded colour and the face normal from Iris, then applies
its own lighting, so Horizon's baked shade was applied a second time:
chests, shulkers, banners and the rest came out darker than the terrain
around them (#8390).
`draw_part` now emits the tint alone and the face's world normal
(`normal.w` = 127), and the entity shader applies `minecraft_mix_light`
from it, as it already does for mobs. Lights that are not the level's (an
item preview's `Lighting.Entry`, which the entity shader cannot know)
stay baked into the colour with `normal.w` 0, which is the path
`game/item_draw` gives block-entity items in inventories and hands.
The parity harness compares the tint and the face normal; the loom's GUI
preview keeps its baked `ITEMS_FLAT` colour. The chest cache test now
checks the nether flag reaches the shader instead of changing a baked
colour, so the cached geometry serves both dimensions.
Fixes#8390
Co-Authored-By: opencode (DeepSeek V4.1 Flash) <[email protected]>
Its vertices fell in the unmarked run that closes the mobs', so in third
person (and in a pack's shadow pass) its at_velocity read zero.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
🤖 Agent: pictures are drawn on background threads and cached by item hash and screen scale, so the Shop never renders on the UI thread; prices and the checkout call are the same rules as before (two live checkouts cancelled via Paddle).
Entity.applyEffectsFromBlocks runs in the entity's own tick, and
ServerLevel.tick ticks only entities in entity-ticking chunks, so a mob or
item past the simulation distance presses no plate and is not hurt by a
cactus. tick_blocks swept every loaded entity: 2.5 ms a tick on the SMP,
where ~2,900 of 4,600 overworld mobs sit outside entity-ticking chunks.
Fixes#11881
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Carry the already selected mob through an optional creation-only level hook. Preserve existing entries and the generation initializer and RNG order.
Co-Authored-By: Codex <[email protected]>
A pack that adds font providers over the game's own can leave the overlay without the vanilla font sheets its merged font/default.json names. Every glyph then became the missing box. Read a bitmap sheet, TrueType file or Unihex file the overlay lacks from the game's own root, as vanilla's resource stack keeps the game below every pack. Font definitions stay the overlay's, so a pack filter can still hide one.
Fixes#8875
Co-Authored-By: opencode (DeepSeek V4.1 Flash) <[email protected]>
ClientPacketListener.handleMoveEntity decodes a move's delta from the
entity's position codec base, which AddEntity, a move and a position sync
set and a TeleportEntity leaves alone. Horizon counted it from the
tracked position, which the teleport had moved: when a command teleported
a vehicle, its rider was told the teleport and then the tracker's delta,
and saw the vehicle (and themselves) as far again past where it was.
lan_e2e gains `vehicle-teleport`: a strider the guest rides, teleported
3 blocks, is where the server has it in the guest's game.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A pack text shader may declare colour outputs past location 0. The HUD's
pipelines have one target, so they went nowhere, but the coverage wrapper kept
them, and one at location 1 collided with the coverage and dropped a shader
that works today.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Ludovic (2026-11-07): "In the launcher you just don't see the items." The Shop
now opens on the season pack across the top (its looks drawn side by side, name,
item count, price, Buy), then rows of big tiles: Looks, each collection's items,
the founder set and the Supporter badge. A tile is the item drawn, its name and its
price; it lifts under the pointer. A tile opens it big on the account's own player
in 3D (an emote plays), with a look's items, Buy and Gift.
- Pictures: the Wardrobe's CPU renderer, framed on the item (preview::fitted_view),
at device pixels supersampled 2x, on background threads, kept by item hashes.
- Buy and Gift are one checkout of that offer through the same web checkout;
prices still come from the catalogue and the service's set rules.
- Cut: the page's line, the basket and its notes; three new strings (Buy, Gift,
Looks) in all 86 tables, nine unused ones removed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
items.rs tests: keeps this branch's tool-rules test and main's command
loot zero-count test, which both landed at the same place.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
end.rs: keeps this branch's live gateway items; main's only change there
(item_entity_to_nbt_with_items) was inside the saved-item path this
branch removes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857): defer_world_entry to join_world_rest (the deferred world entry, world creation and rest polling, the world's item/recipe/station tables). Private methods become pub(super), the same reach they had in game.rs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857): the creative screen's tabs, pages, tooltips, trash, search and clicks. Private methods become pub(super), the same reach they had in game.rs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Pure move (#11857): look_options to regrab_on_refocus (mouse look, pause when inactive, held keys, the pointer grab and cursor icon). Private methods become pub(super), the same reach they had in game.rs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A bitmap provider's file may be any texture, and HUD packs keep sheets outside textures/font. HudAtlas already received the sheet from the font commit, but only if the texture was packed: WALKED_TEXTURE_FOLDERS did not include custom paths such as textures/lumiere/hud, so those sheets still fell back to per-glyph run-time copies. Collect every bitmap provider's file while building the atlas, deduplicated against the walked textures, so its glyphs use the sheet's atlas rect.
Co-Authored-By: opencode (DeepSeek V4.1 Flash) <[email protected]>
A bitmap provider may name any texture, and server HUD packs keep their glyph sheets under their own namespace or a path such as textures/lumiere/hud. The HUD atlas already holds those textures, but Font only looked up sheets under font/, so every glyph was decoded from disk and copied into the 512-row run-time region one by one. When that region filled, a glyph became the 6-pixel missing glyph, shifting the rest of the HUD line.
Look the sheet up by its atlas name for every namespace and keep every atlas sprite available to the font. Glyphs now use the sheet's atlas rect, so their advances come from the original texels and the run-time region is not involved.
Co-Authored-By: opencode (DeepSeek V4.1 Flash) <[email protected]>
Prepare starts for eligible actual sources before executing their owned function calls. Keep the existing body, callback, store and fallback paths, and consume or clear invocation slots around leaf execution.
Add frozen Java/Game command parity and causal store, return, error and fallback controls.
Fixes#11824
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
In PQ output the debug view mixed toward PQ (1, 0, 1), so any covered pixel
blew out. Mix toward BT.709 magenta at the paper white instead, encoded like
the picture.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Translate still-English UI strings in launcher/lang/ru.json and the game's
crates/horizon-client/assets/horizon/lang/ru_ru.json (particle culling
settings). Keep proper nouns, acronyms, key names, paths and env vars in
English.
Co-Authored-By: Nemotron <[email protected]>
horizon-release-sign gets `native_build`: a build's request (target, base commit, each mod's
id, version and registry file SHA-256, sorted) and its key, the release and asset names in
horizon-mods, and the signed message `horizon native build <key> <target>\n<sha256> <asset>\n`,
which no release or Nightly signature can pass for. The keys are their own
(native-build-keys.txt): horizon-mods' `native-builds` environment holds the private one,
because the registry compiles other people's code there.
tools/ci/sign_native_build.sh signs with OpenSSL as sign_release.sh does, refuses a key whose
public half isn't listed, and checks its own signature; tests sign with it and verify as the
launcher will.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ClientPacketListener.handleMovePlayer applies a position packet only when
the player is no passenger, and still accepts the teleport. Horizon's
client applied it to a rider too, look included: a map whose datapack
teleports the vehicle every tick (`tp @s ~ ~ ~ 0 0` on a strider) snapped
the rider's view back 20 times a second. A rider now keeps its own
position and look and answers the teleport with them.
lan_e2e gains `rider-look`: a tick function teleports the strider the
guest rides, and the guest's view stays where they turned it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
/place feature refused every inline feature value with "An unexpected
error occurred", although vanilla 26.3 places it like a named one
(PlaceCommand.placeFeature over a ResourceOrHolderArgument value) and
reports commands.place.feature.success.inline.
The inline value is read with the existing SNBT-to-JSON pair, decoded
by the same feature codec as a named feature (feature_with_blocks,
uncached), and placed through the same Feature.place path with the
level random. A feature that does not place reports
commands.place.feature.failed; a value that does not decode keeps the
plain failure.
Fixes#11843
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Entity.getPassengers holds players as well as mobs. Horizon's `on
passengers` named the player in place when a mob had a rider flag, and no
server player: a map's datapack, running with no player in place, never
found the player riding its strider, and a command the host ran named the
host instead of the guest riding. It now gives the player in place when
they ride the entity, and each other player of the level riding it.
Mob and boat passengers are left to #11810.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Health Boost is a max_health attribute modifier. The attribute getter
already counted it, but Mob::heal clamps to the cached attrs.max_health,
which the effect paths never refreshed, and nothing lowered Health when
the effect went (LivingEntity.onAttributeUpdated). Adding the effect,
/effect clear and its expiry now refresh the cache and lower Health above
the new maximum; the add still heals nothing.
Sulfur cubes and wolves write their maximum straight into the cache
(sulfur_cube::set_size, tame::set_tame), outside the attribute model,
and keep their current behavior. Other effect removal paths are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
HORIZON_COSMETICS_CATALOGUE=<file of GET /v1/catalogue> gives the development preview
real prices, sets and collections, so Shop screenshots show what players see.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With HORIZON_HDR_FRAME=float, HDR on and no shader pack, on devices with independent blending, each
HUD and menu pipeline gets a variant with a second output into an R8 target: how much of the world
behind it the draw hides (docs/design/hdr-linear-light.md, The HUD and menus). The fragment shader
is wrapped as it is, so the primary colour, its blend and alpha are unchanged, and the coverage
follows the draw's final colour and blend: layering by its alpha, additive draws not at all,
darkening ones by how much they darken, inverting and replacing ones fully. HORIZON_HDR_DEBUG=coverage
tints it magenta in the last pass; nothing else reads it yet. A resource pack text shader without a
colour output is dropped as an unusable one is, instead of panicking.
Co-Authored-By: GPT 6.1 Sol <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Their overflow_y_scroll lists lacked the on_scroll stop_propagation every other
inner scroller has, so a wheel turn the list could not use (up at the top, or
down at the end) scrolled the whole page behind it.
Fixes#11853
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
PvP & HUD, Replay Studio, the music page, Browse Mods, the resource pack
browser, cosmetics and emotes, moderation and tempban, the controller page,
graphics tooltips and recipe transfer. Placeholders, leading and trailing
spaces match en_us; durations use abbreviations (хв, год, дн.) so one form
reads right for every number.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The GL backend reported MAX_TEXTURE_SLOTS (16, WebGL2's minimum) as max_sampled_textures_per_shader_stage even on desktop GL, where drivers usually offer 32. A native shader pack's sky pipeline layout declares the terrain's 10 sampled textures and the frame's 9, so it was refused at 16 although the driver accepts it (#10095).
Query GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS and GL_MAX_TEXTURE_IMAGE_UNITS, take the lower, and cap at the arrays' new 32 slots (MAX_SAMPLERS likewise). On WebGL2 the query still yields 16.
Co-Authored-By: opencode (DeepSeek V4.1 Flash) <[email protected]>
A layout or pipeline the backend cannot take (a stage's bindings past its sampled-texture limit, say) reaches the device's error scope. A native pack built its frame, stages and passes outside one, so wgpu's validation error hit the default handler and crashed the game (#10095). Build the pack in a scope and return the first error, which the shader pack screen already shows as a refusal.
The test asks for OpenGL's minimum 16 sampled textures per stage and builds a sky-only pack whose layout needs 19: the pack is an error, not a validation panic.
Co-Authored-By: opencode (DeepSeek V4.1 Flash) <[email protected]>
Glow ink text was full-bright in the lightmap only: HDR output and shader packs that light
with their own GI (and read the HDR tier, horizonTier) showed it as dark as plain text.
Its glyphs now carry the low HDR tier, like other things that glow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
CI's stable Rust deprecates AtomicU64::fetch_update in favour of
try_update, which the workspace's minimum Rust (1.95) doesn't have, so
clippy -D warnings failed. A compare_exchange_weak loop does the same
on every version.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
AbsorptionMobEffect.applyEffectTick, asked on every tick, answers false
when the absorption amount is 0, so MobEffectInstance.tickServer drops
the effect before its duration counts down, finite or infinite. A mob's
effect tick had no Absorption case: a used-up Absorption stayed until it
expired. It now ends through the existing removal path; other effects,
infinite ones included, are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A client mod asks once a frame (`hold.hold()`, or `hold.hold_within(x, z, radius)` for circles):
sections already on screen that are meshed again are uploaded as ever but wait beside the meshes
drawn, and letting go swaps them all in on one frame with nothing left to upload. Smaller circles
let go of what they leave. In single player, far terrain waits too for the columns the server
changes beyond the view inside the hold, and takes them in when it lets go of them. Bounded: 30 s,
1 GiB of waiting meshes, the arena running out; ended so, it stays off until no mod asks for a
frame. Clearing the sections (another world or dimension) and unloading columns drop what waits;
arena compaction moves waiting meshes too. Renderer::upload keeps its order on the normal path
(before_mesh / upload_mesh / after_mesh). Tests in gfx/hold.rs; docs in native-mods.md; fragment
world_hold.json.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Part of #11126. Builds on #11652.
Decode shipwreck, ocean ruin, igloo, ruined portal, woodland mansion,
nether fossil and end city pieces from saved starts. Rebuild template
boxes and placement settings like vanilla's tag constructors, retaining
the whole-start regeneration fallback for unsupported or broken pieces.
Cover every new type with resource-free round trips and extend the
prepared-resource generation/read comparison across all three dimensions.
Piece ids go through #11652's saved_piece_id first; any other id is
matched with or without the minecraft: namespace, not lowercased, as
Identifier.parse reads it.
Co-Authored-By: Codex (gpt-6.1-sol) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Older packs write expressions in the legacy syntax: uppercase variables
(AGE, DISTANCE_SQUARED, RAIN...), pi and e, and bare rand(), gaussian()
and noise(). Each variable now runs as the scripting call the wiki maps
it to, and a legacy expression keeps the old argument order of lerp,
smoothstep and color. The color functions (red, color, withAlpha,
lerpColor, hsb, lerpHsb...) follow vanilla's ARGB channel rules, and
dist/distSquare join the math table. All expressions of Visual
Effects+ now compile.
Part of #11486
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A moving block (and an extending or retracting head) is drawn with the entities while it
moves, and read zero at_velocity: flying machines and piston contraptions smeared under a
pack's TAA. Each marks its own run, (6 << 32) | the cell it moves to, which stays the same
while it moves; a finished one keeps that key until the chunk mesh shows the block.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Expressions such as `o.age() * 0.1`, ternaries, `&&`/`||`, strings,
local variables, `if` blocks and calls on context objects are parsed
once when the pack loads, with every name resolved, so a typo is a load
error. A Host answers the context objects (o, p, c, g, r, v), their
methods and the game's global functions; the math functions follow
Java's Math. 1461 of the 1520 distinct expressions of Visual Effects+
compile; the rest use the legacy uppercase names, which come next.
Part of #11486
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An attributes_modifiers object with a group (`base`...) no longer refuses
the file over a key that is none of the modifier's groups: it is left
alone, so a pack Polytone loads isn't rejected over an entry neither reads.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without XDG_RUNTIME_DIR (containers, some WSL and minimal setups) the
control socket was /tmp/horizon-control.sock, a fixed name in a folder
every user shares. Another user who bound it first made --control fail
to start, and horizon-ctl then talked to their socket.
When the temporary directory is shared (world-writable, as /tmp), the
default endpoint is now horizon-<uid>/horizon-control.sock in it. The
game makes that folder 0700, and both the game and horizon-ctl refuse
it unless it is a real folder, owned by this user, that no one else may
enter. A temporary directory of the user's own (macOS's $TMPDIR) is
used as before, so nothing moves there. The launcher's copy of the
default endpoint (game_tools.rs) follows the same rule.
Separately, `bench -1` or `bench nan` panicked the game in
Duration::from_secs_f64; bench now takes 0 to 86400 seconds and refuses
anything else.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
A mob loaded or summoned with FallFlying:1b kept the flag even when it
could not glide, and a gliding mob's elytra never wore. The flag was
only the raw tag's value: the loader did not read it, the save only
supplied a default, and the mob tick had no updateFallFlying step.
- mob_from_nbt reads FallFlying into the mob, and mob_to_nbt writes the
mob's current flag (LivingEntity.read/addAdditionalSaveData).
- The mob's aiStep, with or without AI, runs updateFallFlying while the
flag is set: canGlide (ground, vehicle, levitation, a glider whose
next damage does not break it) clears the flag; every 20 glide ticks
Util.getRandom picks a glider from the mob's own random.
- The game applies that wear with the existing durability path
(Unbreaking from the level's random) to the mob's kept stack.
- LivingEntity.tick counts fallFlyTicks after aiStep.
Glide travel, checkFallDistanceAccumulation and the ELYTRA_GLIDE game
event are not part of this change.
Fixes#11809
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The launcher's es.json still held 91 English values: the mod-port status sentences, release notices, look-and-feel settings, the SSH hosting panel and several install/update results. Translate them, and fix Back from the body part (Espalda) to Atrás.
es_es.json was 20 keys short of en_us.json: every /studio replay message, Play in Background and the controller-focus note. Add them so Spanish screens no longer fall back to English.
Closes#11814.
Co-Authored-By: DeepSeek V4.1 Flash <[email protected]>
tools/parity.py runs every harness with no arguments, so the harness's
required --work-dir made it fail on every CI run before reaching the
oracle. Without the flag it now captures into a fresh
.local/function-tags-parity; an explicit --work-dir must still be new.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
game.rs: keeps this branch's wax_on_sound (honeycomb) beside main's
transform_copper_chest_neighbour (axe/hoe/shovel transformers, #33ec06ab12).
test_use_item_on_parity.py: keeps both the honeycomb double-chest cases
and main's block transformer cases.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
view_distance and chunk_center come straight from Login,
SetChunkCacheRadius and SetChunkCacheCenter, any i32 a server sends.
remote_chunk_in_range added 3 to the radius and subtracted the centre,
and the streaming radius and sent_radius added 1: a server sending
i32::MAX or i32::MIN panicked a debug build, and in release (no
overflow checks) the wrapped range dropped every loaded chunk.
The storage range is now in_storage_range: the same ClientChunkCache
rule (radius at least 2, plus 3), with a saturating add and abs_diff,
so nothing changes for any radius a real server sends. The +1s
saturate too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
Keeps both imports in command_exec_corpus.groups(): this branch's
player_food_predicate_corpus and main's treasure_map_copies.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each distinct pack selection gets its own overlay folder under
.horizon-cache/resourcepacks, holding a full unpacked copy of its zip
packs, and nothing removed old ones. Every server pack, and every new
version of one, left a permanent copy the player can't see: ten
servers with 100 MiB packs kept about a gigabyte.
resource_root now marks the overlay it returns as used by this process
(a shared lock on <overlay>.overlay-cache.use, held until the process
exits, whose time is the last use), then keeps the 8 most recently used
overlays in the folder and deletes the rest. An overlay another
process uses or is building is skipped, so two games sharing a game
folder never pull one out from under each other; the marker goes before
the folder, so a half-deleted overlay is never reused. The lock files
stay, as build() already requires.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
After the encrypted join, the guest offered the session on a random
127.0.0.1 port for 30 s and handed it to the first loopback connection.
Any other local user or process that found the port first got the
authenticated session to the host's server, and a web page aiming
fetch at local ports could send HTTP into the tunnel and make the
player's own join fail.
serve_loopback now returns a Loopback with the port, a random 16-byte
token and the thread. A connection must send the token within a second
of connecting (compared in constant time); anything else is dropped and
the port keeps waiting until its deadline. The client's join_friend
passes the token on, and run() writes it raw before the handshake.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
Light written into the frame can't be taken back out from under the HUD, and without the tone curve it clips at
the peak, so step 3 now holds the HUD's own layer and the tone curve, and step 4 the shader groups. The look
target names the differences allowed (rounding, light behaving as light under glass, water and anti-aliasing)
and the artifacts that aren't.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The shield check now also asks each player's window what its hotbar
draws over the shield (a new `shield_cooldown_shown` in the control
state: ItemCooldowns' percent for minecraft:shield). Right after the
vindicator's axe disables it the cooldown shows, and after the 100 ticks
it is gone. On main both players' games showed none (#11805).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
parity.py and parity-main call tools/test_*_parity.py without --binary or --work-dir, so the harness failed in 0 s there. Use harness_args.ensure_work_dir, as a98ba0273 did for the dolphin and spell-ticks harnesses.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
In PCP and NAT-PMP a lifetime of 0 means the mapping is deleted, but a
success reply carrying 0 was taken as a mapping. Mapping::due() was
then always true, so the host renewed on every 100 ms wake-up of its
loop (a UDP exchange each time, under the reach mutex) while
advertising a direct address that no longer existed.
pcp_map and natpmp_map now refuse a zero lifetime when they asked for
one (a delete, which asks for zero, still expects zero), and due()
never fires sooner than 30 s after the last renewal, so a router that
grants a few seconds isn't asked again ten times a second either.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
Applying packs builds an on-disk overlay: every file under assets/ in
every zip pack was inflated (up to 1 GiB each, whole in memory) and
written out, on every worker thread at once. A gigabyte of zeros
deflates to about a megabyte, so a server pack at the 250 MiB download
limit could write some 240 GiB to the player's disk and hold a 1 GiB
buffer per thread, once the player accepted the pack prompt. Vanilla
never unpacks a pack.
Zip::copy_to now streams an entry to its file 64 KiB at a time, taking
each piece from a byte budget shared by the whole build: 16 times the
zips' size on disk, at least 64 MiB, at most 8 GiB. Past it the build
fails with FileTooLarge (so the reload drops the server packs, as for
any failed build) and the half-written overlay folder is removed. An
unreadable entry still costs only that file. Memory per thread is now
the compressed entry plus one piece.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
BlocksAttacks.disable adds the item cooldown (ItemCooldowns.addCooldown
on the shield's group), which ServerItemCooldowns sends a player as
Cooldown(minecraft:shield, 100) and ends with 0. Horizon only set its own
shield counter, so a guest's client never heard of it: no cooldown on the
hotbar, and a held right click could show the shield raised while the
server had it down. The disable now also goes through the item cooldowns.
Fixes#11805
Co-Authored-By: Claude Opus 5.5 <[email protected]>
They were drawn in runs marked only by type, so a pack's at_velocity read zero on them even
while they or the camera moved (TAA smeared them). Each now marks its own motion run:
(3 << 32) | entity id, (4 << 32) | id for an item frame's held item, (5 << 32) | position for a
block entity, with a closing mark after each group; maps stay unmarked (keyed by map id).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Chunk sections already put their solid quads in the arena as one 24-byte
record each instead of four 24-byte vertices (`mesher::packed`). Far tiles
never could: that record's coordinates are a block grid of 16, and a far
tile's quads are merged rectangles of a voxel column's exposed side,
hundreds of blocks wide. So they stayed at four vertices a quad, and at
`lodDistance` 256 they were by far the largest thing the renderer held.
A far quad is much more regular than a chunk's, though -- a rectangle in an
axis-aligned plane, one colour, one light and one layer across all four
corners, with UVs `lod::mesh::emit_quad` derives from the positions -- so a
record of its own holds it in the same 24 bytes, and terrain.wgsl's
`far_corner` does what that function did. The two layouts share the arena
and the pipeline; a draw's base vertex says which it is (`FAR_BASE`).
Measured on a settled LodTest at `lodDistance` 256, render distance 16, the
same binary with `HORIZON_FAR_PACK=0` and without, each run settled on the
same 4424 tiles and the same quad count to within four:
far vertices arena pages GPU allocated
four vertices 492.5 MiB 960.0 1241.7 MiB
a record a quad 127.4 MiB 640.0 921.8 MiB
365 MiB less vertex data, and 320 MiB less video memory actually allocated
rather than merely accounted for. 24.8 bytes a quad: a record each, and the
few quads that spill. Both figures repeated exactly over two pairs of runs.
Frame time is unchanged. Over the same flight, the mean of two pairs:
avg FPS 1% low GPU avg
four vertices 171.0 95.8 5.390 ms
a record a quad 170.3 94.9 5.420 ms
The pairs disagree on the sign -- the compact layout was 1.5% slower in one
and 0.7% faster in the other -- so the difference is run variance, not the
cost of expanding a record. Worth stating because one pair alone read as a
regression.
Nothing here reasons about which record is right for a quad: `pack` builds
one, rebuilds the four vertices from it, and keeps it only if they come back
bit for bit. A quad no record reproduces is kept as its four vertices, as
`mesher::packed` keeps one; in practice that is the fluid faces, whose
surface sits at 8/9 of a block, and about 1% of quads.
`lod::mesh`'s `every_quad_the_mesher_emits_packs_and_comes_back` holds the
layout and the mesher to each other over real terrain, which the layout's
own tests cannot: they check its records against its own copy of what
`emit_quad` does, and would pass just as well if the two had drifted.
A same-build control says the picture does not change: two launches of the
identical build differ in 50.7% of pixels (16.0% above the animation noise
floor, worst channel delta 76), because far-tile refinement does not settle
identically between launches. Packed against unpacked differs in 40.5%
(12.8% above the floor, worst 77) -- less than the control.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_011paghWkrnyQRBhJRodZpG2
NeoForge 26.1 has neither. A tooltip is drawn from a style's textures, picked
in RenderTooltipEvent.Texture, and the anvil take is AnvilCraftEvent, whose
.Pre comes before the inputs are used and the anvil is damaged (no break
chance). `events` now looks the 1.21.1 names up under those, so ATM10's 6
Color and 4 AnvilRepair users are answered by the hooks that exist.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Vanilla Entity.getAirDrag is 0.98F. PrimedTnt, FallingBlockEntity and
ItemEntity scaled motion by the f64 0.98, so after four ticks a TNT's
Motion x was 0.092236816 instead of 0.09223682318…. Item underwater
and lava drags, LlamaSpit air drag, and hurting-projectile inertia
were the same class of float-widened constants.
Fixes#6814
Co-Authored-By: OpenCode <[email protected]>
Co-authored-by: monkeycode-ai <[email protected]>
Co-authored-by: monkeycode-ai <[email protected]>
Two camels in love in a built world: the breed goal starts, then one loses a
point of health and is refused, and a rider also refuses it. Without the
canParent guard the wounded camel still starts the goal.
`Camel.canMate` is `partner instanceof Camel && this.canParent() &&
camel.canParent()`, and `AbstractHorse.canParent` requires no vehicle or
passenger and full health (a camel's `isTamed` is always true). The shared
`Breed` goal applied those checks only to horses, donkeys and llamas, so a
camel stayed free to breed while hurt or carried.
Camels still pair only with camels: the `camel` flag feeds the canParent
guard only, not the partner species rule.
KineticWeapon.damageEntities and PiercingWeapon.attack hit every entity
along the view, players included (PiercingWeapon.canHitEntity:
canHarmPlayer, not the same vehicle). Horizon's stab targets were the
mobs only. A guest's spear now also reaches the other players of its
level; each stab is worked out as the attacker in their turn and lands
through the PvP path once the hub is back, with the spear's damage type,
the 0.4 push and the weapon's Knockback, the dismount, thorns and the
kill score, then the attacker's wear, stats, exhaustion and hit feedback.
The kill score and thorns of pvp_land move into pvp_aftermath, shared by
both. lan_e2e gains a `spear` check: a jab and a sprinting charge hurt
the host, and with the pvp game rule off neither does.
Fixes#11790
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The test builds a world with a stone ceiling cell inside the standing box
but above the sitting box, then asks RandomSitting, the arm the goal
selector calls: 200 asks must all be refused. A second camel with open sky
fires within the same 200, so the refusal is the pose check and not the
state setup. Dropping the can_change_pose conjunct fails the first loop.
`CamelAi.RandomSitting.checkExtraStartConditions` ends with
`body.canCamelChangePose()`, which is
`wouldNotSuffocateAtTargetPose(isCamelSitting() ? STANDING : SITTING)`.
RandomSitting did not ask, so a camel sitting under a ceiling that blocks
its 2.375 tall standing box (its sitting pose is 0.945 tall) stood up into
it. The `can_change_pose` helper already existed for the elastic leash pull.
The fade-in rides the high 16 bits of the flat hdr_code varying (no new
location); solid and translucent colours mix toward the fog colour after
the pack's material, alpha kept.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The atlas holds 256 map pictures. Every framed map in range claimed one, seen or not, so past 256 the maps took slots from each other within one frame and showed each other's pictures. Now a frame's outline is culled before its map claims a slot, the nearest claim first, and a slot shown this frame is never given away: past the slots, the far maps go undrawn.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Drives the real goal selector on a built test world: hurt the frog with a
PANIC_CAUSES damage, tick the goals once, and assert the panic goal started
and left vanilla's 2.0 in `Navigation.speed_modifier`, the value
`SmoothSwimmingMoveControl` multiplies. Reverting the constant fails it with
1.25 against 2.0.
`FrogAi.initCoreActivity` builds the brain's core activity with
`AnimalPanic(2.0F)`, but the goal list used 1.25.
`PanicGoal.start` hands the multiplier to `PathNavigation.moveTo`, and
`SmoothSwimmingMoveControl.tick` computes
`speedModifier * getAttributeValue(MOVEMENT_SPEED)` and then multiplies by
0.1 on land or 0.02 in water. A frog's movement speed is 1.0, so a fleeing
frog should move 0.2 blocks a tick on land and 0.04 in water; at 1.25 it
moved 0.125 and 0.025, 60% slower.
26.3 ends sign_update with the side as a VarInt; 26.2 and older read it as a
boolean right after the position. Forwarding the packet unchanged made the
server misread it and kick the player on Done.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
push_from_ticked_cart cloned the ticked cart into the list and back each tick,
and with it a chest cart's rolled loot: its whole Items list as NBT. The stored
cart already goes without the loot for the tick (tick_carts_now takes it), so
the copies do too and the loot stays with the ticked cart.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A saved area resolves the same dozen block states in every section of every column, but decode_column built a format!-ed String key and parsed each entry through BlockState::from_nbt into a per-column HashMap. The registry now memoizes palette entries by their NBT in an FxHashMap keyed by the tag itself, so a repeated entry costs one hash and one comparison and allocates nothing; the per-entry resolution rules move with it unchanged. Loading the 529-column saved area drops Persistent::load from 1,467.8M to 1,284.3M instructions.
Part of #9996.
A first draft by DeepSeek V4.1 Flash, via pi, reviewed and reworked.
Co-Authored-By: DeepSeek V4.1 Flash <[email protected]>
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Loading a generated area spends 16% of Persistent::load (the worker's
chunk load) in PalettedContainer::from_packed, and nearly all of it is
check_indices: a scalar bounds check of every entry of every section
(~10.6 instructions per entry, 22M entries per 529-column area).
check_indices now rejects a whole word at once. Adding 2^bits - palette
to a word lifts every field by that much, so a field carries out of its
own bits exactly when its index is past the palette; the carry out of
each bit is computed for the whole word with (a & b) | ((a | b) & !sum).
A field can only appear bad where a genuinely bad field precedes it in
the same word (a carry adds one to it), which never turns an accepted
input into a rejected one, and the offending word is rescanned entry by
entry on the error path so the reported index is the same first
offending entry as before. The last word's padding fields are biased by
zero, so they cannot look bad, as the entry-by-entry scan ignored them.
The same set of inputs is accepted: the early return when the palette
holds exactly 2^bits entries was already there and stays.
Part of #9996.
A first draft by DeepSeek V4.1 Flash, via pi, reviewed and reworked.
Co-Authored-By: DeepSeek V4.1 Flash <[email protected]>
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The saved sky and block nibble layers are already the resident Light::Sky and Light::Block layout. Choose the resident variant from the layers and copy the stored layer when only one kind of light varies, so only sections where both vary still expand their light into a full array. Missing layers still inherit the bottom row above and dimensions without sky light still ignore saved sky layers.
Part of #9996.
A first draft by DeepSeek V4.1 Flash, via pi, reviewed and reworked.
Co-Authored-By: DeepSeek V4.1 Flash <[email protected]>
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add a direct Unbreaking and Mending durability comparison over the real
enchantment registry: item_damage remove_binomial (small and large paths)
and repair_with_xp, with --no-mending reproducing the Unbreaking corpus.
position_riders re-seated every mob whose vehicle is not a mob through
passenger_riding_position, which puts every boat rider in seat 0 from
the boat's position. seat_boat_mobs had already placed them, so remote
players on a moving boat jumped between the two seats each pass.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The ring got stuck at its smallest on servers whose normal tick sits
between 25 and 40 ms, since it only grew back under 25 ms.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Vanilla LivingEntity.dropExperience and Mob.dropCustomDeathLoot key on
lastHurtByPlayerMemoryTime, not whether that player is still in the
world. Horizon folded both into Death.player, so a guest who left
within the 100-tick memory made drops::die skip experience and
equipment.
Give die a player_killed flag from the timer. LAST_DAMAGE_PLAYER stays
None when killer_uuid cannot resolve them.
Fixes#11674
Co-Authored-By: OpenCode <[email protected]>
Co-authored-by: monkeycode-ai <[email protected]>
1.21.2 dropped the generic./player./zombie. attribute prefixes, so the id tables of 1.21.1 and 1.20.6 (matched by
name) had no attribute runs and every update_attributes value was left out for those clients. They are regenerated
with the renames. Mob effect ids were never renumbered: update_mob_effect and remove_mob_effect now go through the
client's table, and an effect added after its release is not sent.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
1.21.3 and older clients send pick_item with the inventory slot that holds the picked block's item; 26.3 has no
such packet and the adapter dropped it. It now becomes a container_click swap of that slot with the held hotbar
slot (tracked from set_carried_item and set_held_slot), state id 0 so the server resends the inventory.
Fixes#11722
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The 1.21.8 and 1.21.1 explode steps re-read a payload the renumbering had already put in the client's ids with
the pinned codec, so the particle's layout was chosen by the wrong id and sending any explosion failed. They now
read and write through the client's ids. 1.21.1's step read the knockback as three doubles where 1.21.4's step
leaves a double and two floats. 1.21.1's strength is 26.3's radius instead of a fixed 3.0.
Fixes#11721
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A version's first start read its thousands of small files one by one, about
100 s under Windows Defender here, because only the game wrote
horizon-data.bin, and only after that slow start. The launcher now writes it
in the background once an install or import has its resources, reading on up
to 8 threads (43 s against 281 s on one, on a fresh copy); the start then
loads in 1.2 s. The game stays the fallback, on its one idle thread.
A reused version no longer copies the data file, which names its own root.
complete_assets removes the file when it adds something, as the file answers
missing for what it lacks. A write removes what a stopped writer left.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Evaluate the supported damage-only subpredicate against modeled damage and
existing effective component presence. Preserve removal precedence and leave
unsupported conjunctions on their existing path.
Fixes#11715
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Plugins seat players on text displays, interactions, armor stands or area
effect clouds that ride another player. Resolve the seat through the whole
chain of vehicles, keep the packet riding graph, move the local rider with a
moving seat, and use the Avatar attachment height for player vehicles.
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
tools/test_warden_memory_full_diagnostic.py is not a test_*_parity.py harness, so nothing runs it, and it is 816/839 by design. The canonical owned-memories harness stays. Drops the docs/parity run logs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Thorns on the player answered a mob's hit with generic damage that had no
attacker, so the mob was not knocked back, did not turn on the player and
dropped no experience when it died of it. It now hurts the mob by
`minecraft:thorns` damage the player caused (`DamageEntity` with the
wearer as owner), rolled from the mob's own random, and the held weapon's
Breach applies as on any hit by the player.
The whole equipment counts, in `EquipmentSlot` order: the main hand, the
off hand, then feet up to head (Thorns' slots are `any`), so a Thorns
piece held in a hand answers too. Each piece rolls its chance, hurts the
attacker and wears in that order, also when the attacker is already dead.
A piece that takes no wear, or a creative player's, is left alone.
A mob hit by thorns damage plays `enchant.thorns.hit` where it stands
when the hit lands whole, silent or not (`playSecondaryHurtSound`).
Part of #9954.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The pose test asserted that Temper lands in the legacy variant3 byte. #11483/#11659 move every equine's temper to the signed equine_temper field; the assertion was outside this test's subject (LastPoseTick), so drop it rather than pin the storage.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Both PRs added a signed AbstractHorse temper: #11483 for the seven horse and llama species, this one for camels. Keep one field, equine_temper, for every equine: the loader reads Temper unclamped for all of them, temper()/modify_temper() use it for camels too, and the camel saver writes it. The camel_temper field and the camel byte-clamp path are gone; tests follow.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
FogState now holds the torch flicker's LegacyRandom, so main's rain fog
test (settled_rain_fog_is_its_target_and_easing_is_unchanged) moved
`start` twice; verify failed on E0382.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
#11631 adds saved_piece_id (PiecesContainer.load's RENAMES) to the same file; read_jigsaw_start now asks it instead of its own copy of the four old ids. The upper-case id case is dropped from the test: Identifier.parse refuses upper case, so vanilla never reads 'MINECRAFT:JIGSAW' as a jigsaw piece. Land #11631 first.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Main's #11661 added LevelAccess::container_open and its test for the open copper chest; this branch keeps that name and its test and drops its own container_has_viewers, folding the copper golems' chests into GameLevel::container_open. The recheck_container_openers doc comments #11661 left stranded above container_open are back on their function.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
load_guest_records read stats/<uuid>.json directly, and on the server thread (the default single player) and on LAN and dedicated servers every player joins through it, so locateStatsFile only reached the in-frame path. The guest's own name goes to locate_player_json.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Identifier.parse reads 'nvi' as minecraft:nvi, so RENAMES applies to a bare id too. The new saved_piece_id had been inserted between tag_json and its doc comment.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Since a pack with no program for terrain or translucent terrain gets the
notes "no program for terrain; nothing draws it" and "no program for
translucent terrain; nothing draws it", standard_pack_gpu_entities and
entity_shadow_ownership failed their no-notes assertions: their packs had
entity programs only. Both packs now also have gbuffers_terrain and
gbuffers_water. The fixtures draw no terrain, and every other assertion is
unchanged.
Fixes#11710
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Add a direct parity suite for the landing branch of
FallingBlockEntity.tick: real falling entities into air, solid blocks,
water, plants, slabs, cobwebs and moving pistons, with concrete powder's
water contact and the anvil landing event behind --no-contact.
Keep main's fall_step(entities) signature with the air guard first;
keep main's vibration_tests module beside the new test module.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With [transfer] on and players on, a stop (/stop, a stop signal, the
supervisor's stop or restart) now waits stop_wait_seconds (10 by default,
0 to 60, 0 stops at once as before) so a proxy told server/stopping can
move the players out first. The log says "Stopping in 10 s: a proxy may
move the players out first", which horizon-server takes as the start of
the stop. Every login is refused as server_shutdown, and the server ticks
on and answers the proxy's requests.
The wait ends as soon as no player is placed, at its time when no
hand-over is frozen, and at the latest a freeze's longest time later, so a
hand-over started in time finishes; the whole wait is cut at 90 s, within
the supervisor's 120 s for a stop, so stop_wait_seconds and
max_freeze_seconds may add up to 90 at most when the stop waits. No freeze
starts that this limit would cut: a player its source saved after the
target placed it would be on both servers. A freeze lasts ticks, so its
length is reckoned at the tick length of the moment (the rate's, or longer
on a server slower than its rate). A second stop ends the wait. Then the
stop goes on as vanilla's.
Refs #7484
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Main's thorns check counts events from before the hurt; the PR's helmet
path runs after that count like the plain one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The portal parity suite only sent entities through portals: the PortalShape
search that turns an obsidian frame into portal blocks was never exercised.
Add a shape family over real frames and a --no-shape switch that reproduces
the previous corpus byte for byte.
HoneycombItem.useOn gives a double chest's other half its game event and
particles and plays a second sound from the clicked block, on the server and
in the client's prediction. The wax sound leaves out the acting player and
goes out in the blocks source.
WeatheringCopperChestBlock.randomTick skips the attempt, and its draw, while
getEntitiesWithContainerOpen has anyone: a player's menu, or a copper golem
(CopperGolem.hasContainerOpen), a double chest counting for both halves.
Fixes#11378
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Exclude actual crafters from automatic dropped-item name capture while
preserving chest/shulker names, contents and the other copy policies.
Cover the real destruction route against a frozen fresh Java reference.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
A connection's network thread hands every play packet to the hub over
an unbounded channel. The hub's inbox caps (INBOX_LIMIT packets,
INBOX_BYTES bytes) apply only as it drains that channel at the start of
a tick, so while one tick runs long (an autosave, chunk generation) a
client can queue tiny packets without bound, each a boxed Serverbound
many times its size on the wire.
Each connection now counts what it handed the hub and the hub hasn't
taken yet (Undrained, shared between the network thread and the
guest's PlayerSender). Past twice the inbox caps the network thread
disconnects the client with the same message as the packet-rate kick
(disconnect.exceeded_packet_rate) and logs a warning; the hub
uncounts each packet as it takes it. A client within the inbox caps
never comes near.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01SJ3stK8iXVDXqoi7midcqd
The row said the name-based stats file is not moved to the uuid file. It names the function that
does it, and the segment rule that decides whether vanilla would look for it at all.
The test works in a temporary world folder: nothing is written when there is nothing to move, the
`players/stats/<name>.json` file lands on the uuid name and is not left behind, a uuid file that
already exists wins and leaves the old file alone, and the segment check refuses capitals and
separators while accepting a lowercase name with dashes, dots and digits.
PlayerList.locateStatsFile: the stats file is `stats/<uuid>.json`, but a version that wrote
`stats/<player name>.json` left its numbers under the old name. When the uuid file does not exist
yet and the name is one vanilla would write as a path segment
(FileUtil.isValidPathSegment: `[-._a-z0-9]`, and not `.` or `..` — the check is on
`<name>.json`, so a capital letter in the player's name refuses it), the old file is moved to the
uuid name and that is the file that is read; a failed move leaves the old one in place.
Horizon always resolved the uuid path, so a world opened after such a version started with empty
stats. The 26.3 advancements file has no name-based form (`PlayerList.getPlayerAdvancements`
resolves the uuid directory directly), so only stats move.
A cushion is an ordinary entity in vanilla (a BlockAttachedEntity), but
Horizon had no command target for it: @e never matched one, so execute
if entity, tag, data get, tp and kill missed every cushion. Target::Cushion
joins the level's candidates like a painting; /kill removes it with no
drop and lets its rider go, /tp moves it with its block, data get writes
it through cushion_to_nbt.
Fixes#11626.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
PiecesContainer.load now names saved_piece_id, where the rename table lives,
and StructureStart.loadStaticStart spells out that a jigsaw start (the renamed
old ids included) comes back from its saved pieces while another structure's
start comes back from the generator's rebuild of its chunk.
A piece tag named minecraft:nvi, pcp, bastionremnant or runtime comes back as
jigsaw, as does minecraft:jigsaw itself; a monument, stronghold or jungle
temple piece (OMB, SHCC, TeJP) comes back as nothing so the start keeps the
generator's rebuild, and so does a tag with no id or one that is not a
compound. With the table narrowed to minecraft:jigsaw alone the first four
fail.
PiecesContainer.load maps four piece ids - minecraft:nvi, minecraft:pcp,
minecraft:bastionremnant and minecraft:runtime - to minecraft:jigsaw before it
looks the type up: a save from before those pieces were jigsaw wrote them under
the older names with the same pool-element tag. The saved-start reader only
took the literal minecraft:jigsaw id, so such a start fell through to the
generator's rebuild of its chunk, and where that rebuild makes a different
layout (a world whose structure data or version moved on) the start came back
with no pieces at all: nothing placed, nothing bearding the terrain.
saved_piece_id now holds that rename table for the reader; a piece of another
type (a monument's or a stronghold's, whose NBT is not a pool piece's) still
sends the start to the rebuild.
Encode bare item templates using the primary record form while preserving
compound counts and components. Cover actual canonical load/save and the
real command route against a frozen fresh Java reference.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
RideCommand.mount forces the ride (startRiding(vehicle, true, true)), so
any entity takes a rider. Horizon only took boats, carts and mobs. A rider
now sits at the vehicle's default passenger attachment (its box height,
less the rider's own vehicle attachment) and gets off on sneak on top of
it, as Entity does for a vehicle that overrides neither.
Fixes#11603.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
RideCommand.mount and dismount work on any ServerPlayer in vanilla. Horizon's
/ride reached only the player in place, so the console, a function, a command
block or another player could not seat a guest (every player of a server or a
LAN world, and in single player too): the guest's turn now runs through
for_player, as native_mount does. A guest as the vehicle answers
cant_ride_players, as any player does.
Fixes#11602.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Propagate required dependency failures before expanding function tags so
invalid tags cannot run partial members. Skip whole invalid optional children
while preserving the existing ordered, deduplicated expansion.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Ports EnvironmentAttributeSystem: every attribute is its default run
through layers in the order they are added, leading constant layers folded
into the base (bakeLayerSampler) and the result sanitized. The dimension
type's attributes are constant layers; the biome layer reads the biome at
the position, or for spatially interpolated attributes the
SpatialAttributeInterpolator mix that GaussianSampler accumulates; the
weather layers (WeatherAttributes.RAIN and THUNDER) blend by the rain and
thunder levels with each type's state-change lerp. Timelines plug in as
time layers and come next; nothing in the game uses the stack yet.
Entries of types not ported yet (append, overlay) are kept as no-ops so a
biome's other attributes still load.
The new harness environment_system compares 900 stacks with the pinned
client (36,000 lines), with the vanilla biome and dimension type data.
Part of #11486
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Reject folders without parseable metadata, an object pack section and a non-null description before exposing their contents. Preserve existing parser and format behavior.
Co-Authored-By: Codex <[email protected]>
Ports vanilla 26.3's environment attribute registry (EnvironmentAttributes:
types, defaults, ranges) and one EnvironmentAttributeMap entry: a value or
a {modifier, argument} object, parsed as its codec does (hex, list and int
colors, FloatWithAlpha, BlendToGray) and applied as AttributeModifier.apply
with float math in Java's order (ARGB.alphaBlend, addRgb, subtractRgb,
multiply, greyscale, scaleRGB, srgbLerp; Math.min/max). The layer stack
that sums these comes next; nothing uses the module yet.
The new harness environment_attribute_modifier compares 8,835 cases with
the pinned client, the registry included.
Part of #11486
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Validate candidate functions at actual compilation permission before exposing them, using refreshed world content and candidate registry IDs.
Co-Authored-By: Codex <[email protected]>
ItemModels::components cloned the item's whole default component map (an
enchanted sword's: a few dozen JSON values and strings) for every stack
with a patch, and the model resolver did it on every draw: the hotbar,
each held item of every player in view, the first-person hand twice. A
PvP fight with enchanted gear paid it a dozen times a frame.
ComponentsView reads the patch, then the defaults, with no copy; the
resolver, the swap animation scale, attack range, use duration and the
use animation read through it. components() stays for what needs the
merged map (bundle fullness, still built only for bundles).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
submit_quads hashed the quad's sprite name into the glint atlas for every
quad of an enchanted item drawn in the GUI (a generated sword: hundreds of
side quads, one sprite). It now keeps the last sprite's glint rect, as it
already does for the item atlas rect: the same uvs, one lookup a sprite.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without the change the first reads `Err(InvalidData, "external chunk stub has
inline data")` and the second `Err(Os { code: 2, kind: NotFound })` instead of
the 4000 bytes in `c.-32.0.mcc` and an absent chunk.
`RegionFile.getChunkDataInputStream` never trusts the stub's length once the
external flag is set: `streamLength != 0` only logs "Chunk has both internal and
external streams" and the stream comes from the `.mcc`. When the `.mcc` is not a
regular file, `createExternalChunkInputStream` answers `null`, so the chunk is
absent. Horizon refused both: a stub that also declared inline data failed with
"external chunk stub has inline data", and a stub whose `.mcc` was gone failed
with a `NotFound` io error, which aborts a reader where vanilla skips the chunk.
Use the existing air predicate and Gone result at the start of the actual falling-block tick. Cover admitted command actors with a real Game tick regression and frozen Java parity controls.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
PlayerPredicate.matches calls FoodPredicate.matches on the player's
FoodData: the food level in `level`, then the saturation, a float
compared as a double, in `saturation`. The player facts carried no food
and the matcher never read `food`, so any food bounds passed.
PlayerFacts now holds the food, always filled (two numbers), for the
player in place and for a server's other players, and player_facts tests
it after the level, as vanilla does.
The command_exec harness gets a group that fills and drains the player's
food in player loops and tests named food predicates, a non-player, and
a tick advancement whose player condition asks only for food.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Left in the air at y 163, the player fell into the sea below while prepare() ran, which unlocked the boat
recipes: their toast showed over the first shots at a different moment each run, so main against itself
failed in the top right corner.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The command-execution furnace family could not give a checkable gap control
on this host, so this is a direct AbstractFurnaceBlockEntity.serverTick
harness instead: a real RecipeManager and a furnace, blast furnace or smoker
in a block-update level, compared with FurnaceCooking::tick. It pins the
cooking_fuel burn table for every fuel item, the 200/100/100 cook times and
speed multiplier, fuel consumption and LIT state, RecipesUsed experience, a
full output slot, timer decay, and an input changed mid-cook. --no-change
reproduces the corpus before that family.
757/757 cases match the pinned client.
DropperBlock.dispenseFrom finds its target with HopperBlockEntity.getContainerAt:
the block's container, else a container entity (chest boat or raft, chest or
hopper minecart) whose box meets the space in front, picked at random. Horizon's
dropper only asked for a block container, so items fell out next to a chest boat
instead of going in. It now falls back to the entity lookup hoppers already use.
The dispenser harness gains 48 dropper cases with chest boats, rafts and chest
and hopper minecarts centred in front, reaching in from beyond, just past the
space, two at once, behind a chest block and non-container vehicles. The oracle
and the fixture print the container entity's items (Tinv lines).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`tools/port_map/game2-002.tsv` recorded `gap: minor: anchor_pos is not read` and
`not written` for `Phantom.readAdditionalSaveData` and
`Phantom.addAdditionalSaveData`. Both now carry the anchor and say how: the key
only for a phantom that has an anchor, absent or unreadable as no anchor.
`mob_from_nbt` and `mob_to_nbt` are the pair `Game::load_mob` and
`Game::mob_tree_nbt` go through, so the test loads and saves a phantom by its
compound. A phantom at (0.5, 75, 0.5) whose save names `anchor_pos: [100, 64,
-40]` loads with that anchor, circles to (109.66, 62, -37.41) - the anchor,
`distance*cos(angle)` across and `-4 + height` up - and a phantom that moved its
anchor to (9, 70, -3) saves that position, not the one its compound came in
with. A phantom with no anchor loads with none and saves no `anchor_pos`.
Without reading the anchor the load gives the phantom (0, 80, 0), its own
position five blocks up, and the circle point (9.66, 78, 2.59); without writing
it the save keeps the stale (100, 64, -40).
`Phantom.addAdditionalSaveData` writes the anchor the phantom circles around
beside its size, and `readAdditionalSaveData` takes it back:
protected void readAdditionalSaveData(final ValueInput input) {
super.readAdditionalSaveData(input);
this.anchorPoint = input.<BlockPos>read("anchor_pos", BlockPos.CODEC).orElse(null);
this.setPhantomSize(input.getIntOr("size", 0));
}
protected void addAdditionalSaveData(final ValueOutput output) {
super.addAdditionalSaveData(output);
output.storeNullable("anchor_pos", BlockPos.CODEC, this.anchorPoint);
output.putInt("size", this.getPhantomSize());
}
Horizon models that anchor in `extra.home`, which
`PhantomCircleAroundAnchorGoal.selectNext` centres the circle on
(`anchor + (distance*cos(angle), -4 + height, distance*sin(angle))`), but
`mob_to_nbt` wrote only `size` and `mob_from_nbt` read only `size`. A phantom
saved and reloaded therefore kept the anchor `register` gives every new phantom
- its own position five blocks up, vanilla's `finalizeSpawn` value - instead of
the one in its save: a phantom at (0.5, 75, 0.5) loaded with
`anchor_pos: [100, 64, -40]` came back anchored at (0, 80, 0), and its first
circle point was (9.66, 78, 2.59) rather than (109.66, 62, -37.41).
`TagValueOutput.storeNullable` writes the key only for a non-null value, and
`read(...).orElse(null)` gives null for a save without one, so a phantom with no
anchor now writes no `anchor_pos` and loads back with none - the circle then
fills it with its own `blockPosition()`, as vanilla's does.
Write back live menu slots before spilling chest boats, chest rafts and
container carts. Reuse the container split/motion algorithm through its
actual double-origin path, preserving integer callers and empty-slot draws.
Keep ordinary hurt, hull, lead and pending-loot policies separate.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
NaturalSpawner.getRandomPosWithin samples up to chunk.getHeight(WORLD_SURFACE)+1,
and ChunkAccess.getHeight is firstAvailable-1, so the bound is firstAvailable.
Horizon already stored firstAvailable and added one more. Also correct the
oracle's fake LevelChunk.getHeight.
Fast lava, water evaporation, bed and anchor rules, the raid-bright check, the
day timeline, ambient light and the default clock of /time read the level's
dimension type facts instead of matching on the level, as vanilla reads
DimensionType and its attributes. No behaviour change for the three built-in
levels.
Part of #11282
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
The fight tick, end crystal fire and respawn, the boss bar, the fight dragon
check, the exit portal and the DRAGON chunk ticket read the dimension type's
has_ender_dragon_fight, and the End flashes read its End skybox. No behaviour
change for the three built-in levels.
Part of #11282
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
MobWorld carries the level key and its dimension type facts instead of nether
and end flags, and the spawn light test samples the type's
monster_spawn_light_level as vanilla's DimensionType.MonsterSettings does.
Fast lava, snow golem melting, piglin zombification, fixed-time and day
timeline checks read the type's facts. No behaviour change for the three
built-in levels; random draws are unchanged.
Part of #11282
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
Part of #11126.
Load complete jigsaw starts from the owning chunk's structures.starts into
its shared generator cache, retaining regeneration for unsupported or
undecodable starts. Read pending saves in every dimension and preserve
reference counts alongside the original piece layout.
Cover resource-free round trips, generated layouts across two seeds and
pending-record loading from a different referencing chunk.
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
Level.updateSkyBrightness is `(int)(15.0F - skyLightLevel)`: the float
truncated. Horizon's environment::sky_darken (and the End copy in
Game::sky_darken) rounded, stepping the darkening up half a level early
at dusk and late at dawn, which moves bed eligibility and the light-
dependent spawns with it.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
LivingEntity.travelFallFlying stops the glide on a climbable, and a
server simulates its players' travel (AUTHORITATIVE_SIDE_AND_SERVER).
Horizon's server travel cleared the flag only on the guest's temporary
body; as_guest now copies it back, so the glide ends on the server and
the client, which takes the flag from the server, stops gliding too.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
handleClientCommand(PERFORM_RESPAWN) for a player who won the game
calls restartClientLoadTimerAfterRespawn, as the death respawn does: the
player's movement and actions wait, and isInvulnerableTo spares it, until
its client reports the level loaded or 60 ticks pass. Horizon restarted
the timer only after a death.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Leashable.setLeashedTo goes through LeashData.setLeashHolder, which
clears the delayed holder a save named. Horizon kept it, so 100 ticks
after loading the forgotten holder still dropped a lead item next to a
mob the player was leading, or a holder that loaded late took the mob
from the player. Mobs and boats both.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Piglin.finishConversion cancels the admire, then drops every non-empty
stack of its inventory with spawnAtLocation (whole stacks, components
kept), then converts; the converted sound comes before all of it. Horizon
dropped only the admired off-hand item, before the sound, and the
inventory was lost with the piglin.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
AbstractMinecart, MinecartTNT, MinecartFurnace and MinecartHopper read
their fields through ValueInput's numeric getters, which take any
NumericTag: getIntOr (fuse, DisplayOffset), getShortOr (Fuel, the low 16
bits), getDoubleOr (PushX, PushZ) and getBooleanOr (FlippedRotation,
HasTicked, Enabled: byteValue() != 0). Cart::load took only byte, short
and int, and only double for the pushes, so {fuse:20L} left a TNT
minecart unprimed and {PushX:1} gave a furnace minecart no push.
Command harness group "minecart numeric fields": 3/18 -> 18/18.
Fixes#11146
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Match vanilla's column order and fallback when choosing a breathing destination.
Co-Authored-By: Codex (gpt-6.1-terra) <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
entity_chunks(remove = true) is the unload path: every other kind it
writes is also taken out of the level, and the potion clouds were not,
so a chunk loaded again got a second cloud on the same spot. The new
test unloads and reloads a cloud and expects one.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
JukeboxSongPlayer.stop posts the event inside `if (this.song != null)`:
taking a disc out (by hand or by a hopper) after its song ended is silent.
The test takes the finished disc out and expects only the end's event.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The merchant fixture compares strict command and menu records, so the raw
packet trace (net/command_trace.rs in the client receive loop, the Java
trace reader, the oracle's serialized channel) and the per-operation
receipts were diagnostics only. The production client no longer writes or
joins anything for them; tools/test_merchant_gate_parity.py is a thin
wrapper like the other command_exec fixtures.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ThrownTrident inherits AbstractArrow.lerpMotion, which restarts life even
when the motion is zero. The remote motion test compiles remote_motion out
of remote.rs, so its fixture needs the arrow fields; it now also checks
that zero motion keeps a stuck arrow in place and non-zero motion frees it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Keep the completed ordered request list for the current atlas so an
unchanged overflow does not regenerate and upload every trim each frame.
Preserve final LRU residents, retry failures, and invalidate the cache
when equipment, atlas contents or the graphics device changes.
Fixes#10539
Co-Authored-By: Codex GPT-6 <[email protected]>
`build_surface_shared` applies the compiled rule at every solid block of a
column. In the Overworld most bands specialise to "the ore veins, then one
block" (deepslate), so 1.7M rule applications per 64 chunks produce ~1M block
writes, each walking the rule tree from the top and going through
`set_block`/`set_block_worldgen_column`.
A band variant that is a block rule, or ore veins followed by one, gives that
block at every position where no vein places one. The walk now writes the whole
run of solid blocks at once: the top y through the single-block path, the rest
through `TerrainChunk::fill_solid_run`, stopping at the first y where a vein
can place a block (each vein's first test, its density), at the band's bottom
and at the write bounds. Only blocks that are opaque for the stage's heightmaps
and carry no fluid take this path, so one update at the run's top leaves the
maps as a write per block does.
`CompiledRule::apply` on a sequence also tests its conditional children in the
loop instead of through a recursive call.
Instructions (callgrind, proto phase, 64 chunks, seed 12345): Overworld
6.49 G -> 6.03 G (-7.0%), surface stage 1.98 G -> 1.53 G (-23%), rule
applications 599 M -> 329 M; Nether 2.97 G -> 2.73 G (-8.1%).
A first draft by DeepSeek V4.1 Flash, via pi, reviewed and reworked.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Skip only the Nautilus custom air handler under NoAI, preserving the existing Breath/drown path and normal custom behavior. Cover actual Game and full Mob ticks against fresh Java.
Co-Authored-By: Codex <[email protected]>
A ledger build from today's upstream/main reports seven map gaps, all rows
whose where cell names a function that no longer exists: PrimedTnt.explode
points at Game::tick_tnt (it is tick_one_tnt), FlatLevelSource.createState at
FlatTerrain::structure_settings (from_settings), PacketProcessor's hand-off at
conn::read_loop (io_pool::Pool::push), three ServerCommonPacketListenerImpl
keep-alive rows at conn::write_loop (that loop is wait_alive), and
PackSelectionScreen.Watcher is a class row written as a port.
AreaEffectCloud.addAdditionalSaveData was listed missing, which was right: only
the reader existed. game/potions.rs::potion_cloud_to_nbt writes it now, from
entity_chunks.
A cloud with a poison and a slowness effect, one of them hidden and one ambient,
written with potion_cloud_to_nbt and read back with potion_cloud_from_nbt: the
position, age, duration, radius, the four use settings, the duration scale,
RadiusPerTick, the effect list and the colour all come back. The test also
checks that the compound carries the field names vanilla's
readAdditionalSaveData looks for, and that it is saved as
minecraft:area_effect_cloud.
AreaEffectCloud.addAdditionalSaveData writes the cloud's age, duration, radius,
the four use settings, RadiusPerTick, potion_duration_scale and its
potion_contents, and the client already read every one of them back
(game/potions.rs::load_potion_cloud): a cloud that was loaded from a save came
back whole. Nothing ever wrote one, though. entity_chunks writes the dragon, the
crystals, the item frames, the fangs and the rest kind by kind, and the
lingering clouds -- which live in breath_clouds, not in entity_lists! -- were in
none of those, so every replacement pass dropped the cloud a thrown potion or a
creeper left behind.
The save side now writes them: game/potions.rs::potion_cloud_to_nbt builds the
compound on the entity fields entity_nbt::entity_base_tag gives it, and
entity_chunks adds one tag per potion-bearing cloud of the chunk. A dragon's own
breath carries no potion and is not written.
The reader moved out of the Game method into potion_cloud_from_nbt, so the pair
can be tested against itself. The client keeps no potion id, so the effects are
written as custom_effects with the colour the cloud was drawn in, and the custom
particle and owner reference the vanilla cloud also saves have no counterpart
here.
A windowless game with a jukebox at (0, 64, 0): a disc put in and popped back
out posts one G 0,64,0 minecraft:jukebox_stop_play, a song ticked to its end
posts one, a broken jukebox posts none from this path (the world's spill posts
it) and a jukebox that never held a disc posts nothing.
JukeboxSongPlayer.stop posts GameEvent.JUKEBOX_STOP_PLAY at the block (and level
event 1011), and both ways a song ends reach it: popOutTheItem, when the disc is
taken out or a hopper pulls it, and tick's own hasFinished branch.
Horizon posted that event only from the world's removal path, so a broken
jukebox was heard but a jukebox whose disc was pulled out by hand, or whose song
ran out, was silent to a sculk sensor beside it. The paths that keep the block
now go through stop_song_heard; jukebox_removed keeps the plain stop_song,
because the world's spill already posts the same event once for it.
Resolve an absent saved show_icon from show_particles while preserving
explicit icon bytes and the remaining potion fields. Keep this in the
saved item encoder, without changing the general component view.
Add resource-free decoded/modelled codec coverage and a guarded,
zero-tick chest command regression against the pinned Java behavior.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Retain the real last trader on the villager when a trade is accepted,
then deliver the coalesced reputation event after Brain.tick. Keep it
across menu close and live NoAI edits, and refresh current offer prices
when the event actually runs.
Add an actual-owner merchant regression with complete raw capture and
separate strict-all byte-order diagnostics.
Co-Authored-By: Codex gpt-6.1-sol <[email protected]>
Preserve the existing adapter and maintainer history while including the
upstream removal of stale Menu Navigation translation keys.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Select attribute and carpet registry names from the actual server release.
Upgrade incoming pre-component block entities on both update and chunk paths,
retaining tag identity and vanilla typed-field remainders.
Add independent registry and vanilla DFU regression fixtures, including
missing IDs, removed types and unchanged current-version tags.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Preserve published compatibility commits and newer maintainer changes before addressing the protocol-764 review. This merge alone claims no new compatibility or parity result.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Preserve the published older-server contribution and current upstream changes as separate parents before the next tested extension. No new compatibility or parity result is claimed by this merge.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Translate protocol 766 attributes, food, horse inventories, projectile power,
chat holders and item use into the pinned protocol. Supply the old static
enchantment and painting names, and normalize legacy wolf variant holders.
Reuse verified recipe and boat layouts and cover changed codecs with
independent vanilla wire fixtures, including full chat packet translations.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Translate protocol 767 packets, recipes, item components and generic boat
variants into the pinned protocol. Preserve old movement and interaction
layouts, and correct simple spell particles across the older adapters.
Add independent vanilla wire fixtures and document the experimental scope.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Translate protocol 768 particles, player-list defaults, vehicle movement and
integer model data through the existing older-server pipeline. Preserve old
creaking heart states and transient entities through explicit registry aliases,
and keep legacy trail lifetimes in the client particle provider.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Translate legacy chunk framing, item patches, recipe previews, chat and
entity metadata into the pinned client representation. Preserve old click
operations through authoritative server resynchronization and cover older
stack/profile layouts with independent vanilla wire fixtures.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Map protocol 770's packet and registry ids, remove vanilla's unused chunk
padding, renumber command parsers before reading their properties, and
translate player actions, crouch transitions and hanging-entity metadata.
Convert pre-1.21.6 attribute and equippable components in both directions,
with independent vanilla wire fixtures and protocol regression coverage.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Reuse the packet layouts shared with 1.21.7/1.21.8, map the item and sound
registry ids before the lava chicken insertion, and generate inherited tables
only where the vanilla reports differ. Cover shifted discs and sound holders
and exercise the shared metadata and packet conversion cases on both protocols.
Co-Authored-By: Codex (GPT-6) <[email protected]>
Generate vanilla packet and registry mappings, translate the changed layouts
and player metadata, preserve renamed chain states, and extend the offline
protocol probe to fail on decode errors and follow dimension changes.
Co-Authored-By: Codex (GPT-6) <[email protected]>
2026-11-03 22:18:41 +00:00
1069 changed files with 119669 additions and 19649 deletions
// ArrowRight does not change ChunkUpdates when can_change_value is false
ui.key(&muts,&atlas,K::ArrowRight,None);
Loaded 100 of 1069 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.