Every documented key: particle, fog and sky colours, potions, spawn eggs,
map, sheep, collar and dye colours (with their old names), text and its
colour codes, the loading screen, clouds, xporb.time, palette.format and
palette.block.* assignments.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Each frame picks the layers of the Overworld's or End's sky whose day,
weather, biome and height hold at the camera, eases them over their
transition, and draws them as 3x2 skyboxes with their blend mode, over the
vanilla sky's disc and sunrise (the End's box) and under the sun, moon and
stars. Packs without optifine/sky draw nothing new.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Parse optifine/sky/world<N>/sky<M>.properties into layers (source, fade
times, blend, rotation, days, weather, biomes, heights, transition), the
sun and moon_phases replacements, and the 3x2 skybox grid's faces.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Main's new rows go on the pages: Find BlueMap and BlueMap Radius under
General → Distance, Frame Generation under Quality → Image, the seven
block-entity caches as Performance's Block Entities group after Culling,
and Translucent Batching under Advanced → Frames (greyed where the device
can't, hidden on macOS). The shortcut row keeps main's one translated
"Shortcut: %s" label and shows its key apart. With "Scroll wheel changes
settings" on, a hovered stepped value takes the wheel as CycleButton does;
sliders, tick boxes and a clicked or focused row leave it to the list.
Enter does nothing on a row greyed out while focused. split_ui and
split_profiles draw their titles with graphics_style::title. Translations
drop the keys of the three old screens; main's translations win where
both sides added a key.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A filled bucket that the clicked block refuses (a sneak-click on a
slab) pours into the block beside the clicked face, which
`native_fill_bucket` and `native_bucket_empty` never saw, so a claim
mod could be bypassed. Both now hear that block too when it isn't the
target, as vanilla's `mayUseItemAt(blockPos2)` checks it next to
`mayInteract(blockPos)`. Powder snow places like a block and never
pours beside, so it isn't asked twice.
Follow-up to #6783, which merged before this commit.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
`BucketItem.use` targets the clicked block only when it is a liquid
container that `canPlaceLiquid`. A double slab, kelp, seagrass or a
survival barrier send the bucket to the block beside from the start, so
a fish bucket's mob spawns in the poured water and kelp no longer
swallows the click. `canPlaceLiquid` moves to `Behaviours` so the game
can ask it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review round 1 on #6783:
- `Behaviours::bucket_empty` takes `beside: Option<BlockPos>` and returns
where the contents went; Shift only counts with a hit result, as in
vanilla's `!shiftKeyDown || hitResult == null`.
- The client leaves waterlogging to the server wherever the water lands,
not only on the clicked block.
- `Hit::beside` replaces three copies of `pos + face`.
- The click cases get their own test, with a case where both blocks refuse.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
BucketItem.emptyContents retries at the block beside the clicked face
when the clicked block refuses the fluid. Without it, a water bucket on
a double slab did nothing, and a sneak-click on a waterloggable block
failed instead of pouring next to it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- No import starts while a removal runs: it could take the folder of the
same program imported again.
- `import-build --version` on a program imported before on other game files
says so, instead of selecting the old version as if it were made anew.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Windows refuses to rename a folder while a scan holds the program just
written in it: the move and the cleanup are retried for a second, as
backend::remove is. A folder at the version's place without the installed
mark (a removal that stopped at the running game) is cleared instead of
failing every import of that build. The .importing folder stays, so two
imports never remove it under each other.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The copy is made under versions/.importing, where no version is listed,
with its installed mark already in it, and renamed into place: no other
import (or the window's list) ever sees it half made, and none deletes a
version another one finished. A damaged zip says so instead of "not a
Horizon game", and an artifact holding a .tgz is read too.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- An Actions artifact is a zip around the release's archive: the game is
found in a zip or .tar.gz inside it, and in a picked .tar.gz.
- Two imports of one program at once (window and CLI) each copy into their
own folder, and the second leaves the first's version as it is.
- A second file picker's pick starts no second import.
- A native build for an instance on an imported build starts from the
imported build's base, not from a tag GitHub doesn't know.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The module is build_import, apart from `import` (dropped files and packs).
- `import-build --version`, as native-build and prepare name the installed
version a command works on; its help says a Nightly works too.
- launcher/README.md: the Versions page's import, the CLI line, and that an
imported build stays selected through updates.
- fr uses "compilation" as the table does, tr "yükleyin", ja its half-width
colons.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- native_mods::copy_base is the base-folder copy both a native build and an
import start from; carried_over stays private.
- is_game (platform magic and the mod API mark) and base_for (with --base)
are pure and tested: a program without the mark, a --base that is not an
installed release or Nightly, and a failed import that must leave nothing.
- One reader for a plain file and a zip entry, refused past 1 GiB.
- install_as runs the same after-install step as an import.
- The Versions page draws native and imported builds with one helper.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The new field clashed with `importing` (import_page): it is
`importing_build` now.
- The partner banner's 10 s timer is back; a formatting revert had put a
second copy of the drain loop in its place.
- A picked file must be named as the game is (the launcher and horizon-ctl
carry the mod API mark too), and no larger than 1 GiB, checked before it
is read; a zip entry past that is refused instead of cut off.
- The same program imported on another day selects the version made then.
- A failed last step removes the version folder it had renamed into place.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The first layer of joining older servers: a ServerVersion type and, per
connection state, how a 26.2 server numbers each packet, generated from
both releases' packets reports. Nothing calls it yet.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A horizon-client built on a fork's Actions can't start on its own: it has
none of the prepared game files. The Versions page gets "Import a game
build…", which takes the program (or a zip holding it), checks that it is a
Horizon game for this platform, and installs it as imported-<date>-<sha>: a
copy of the selected (or newest) release or Nightly's folder with the picked
program in place of the game, so it plays with that version's prepared
files. It is listed apart as unsigned, can be used and removed like any
version, and no update replaces it, prunes it or moves the selection off it.
The CLI gets `import-build <path> --trust-unsigned`, kept out of MCP: running
a program nothing signed is the player's decision.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Far terrain on a server without horizon:lod draws only the chunks
Horizon kept of it. A player who also plays with Bobby has a cache of
the same server's chunks, often of areas Horizon never saw.
Far terrain now finds Bobby's folder of the level in the game folder
(HORIZON_MINECRAFT_DIR, else the official launcher's): .bobby/<address
as typed, ':' as '_'>/<hashed seed>/<namespace>/<path>, names compared
without case and with or without the default port. A chunk Horizon's
store doesn't have (or holds as an old record) is read from Bobby's
region file, decoded as a vanilla chunk with its stored light
(save::decode_lit, handed in from game.rs), summarized as a loaded chunk
is (lod::summarize) and kept in server-terrain/, so Bobby's folder is
read once per chunk and never written. Bobby's region headers count for
the wide cells too. A chunk of another DataVersion, one that doesn't
decode or has no light gives nothing. A Bobby chunk is not kept over one
the server sent while it was decoded. Friend worlds and splitscreen
guests still keep no server terrain.
Tests: folders by address (port typed or not, ':' as '_', case), another
seed, another dimension, another port; a Bobby-shaped region counted for
wide cells, read, summarized and kept, its file left unchanged, another
version's chunk ignored; with prepared resources, a chunk with both
palette shapes (names, and compounds with {"": name} wrappers) decoded
with its light and kept; and with HORIZON_TEST_BOBBY, every Overworld
chunk of a .bobby folder Bobby really wrote, found by address and seed.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- is_blocking, is_using_item: the synced living entity flags (and a player's
blocking item), as the client keeps them
- is_swimming, is_crawling: the swimming pose (a player's or mannequin's
avatar pose, another mob's DATA_POSE, a drowned's swimming flag), crawling
out of water
- is_holding_item_right/left: the held items on each arm, by the main arm
- is_swinging_right_arm/left_arm: the current swing's arm, the main arm when
the swing names no hand
- is_ridden: the rider SetPassengers names
- is_sitting: foxes, pandas and camels from their own flags
- anger_time, height_above_ground, fluid_depth_up and is_wet's rain stay as
they were, with comments naming what the client lacks
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Block overlays come before tiles under the shared cap, so a pack's many
tiles can't crowd out vanilla blocks'; any tile named by its file pairs,
not only those under ctm/; the doc comment is back on load_optifine.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A tile with <name><suffix>.png beside it (0.png and 0_e.png) pairs with
its overlay as block sprites do, so emissive glass, lamps and ores keep
glowing when connected textures replace their sprite.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A plant's 45° plane snapped its texture's right along the face normal;
directions now need to lie within about 25° of a block axis.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Panes, whose tiles draw with the cube's UVs, read those as their texture;
plain overlay keeps the standard directions (Continuity's orient leaves it
out); Axes::texture refuses NaN and quads turned off the block's axes.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A conduit.jem that reshapes the eye alone no longer paints the shell,
cage and wind, whose vanilla geometry keeps its texture's layout. The
part-name test gives every entry a box of its own, so swapped names show.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Main's far terrain samples only the tiles its plan draws, nearest first,
with no coarser tile under the near ring to keep drawn: while BlueMap's
finest level (one pixel a block) was still downloading, the ring past
the near chunks stayed empty.
A cell up to a chunk wide whose finest map tile is on its way is drawn
from the finest coarser level that has it (BlueMap::coarser_column, the
first to arrive); the tile is sampled again when the finest arrives
(Lod::refresh). LodSampler::waited now means a hole: a cell on its way
with nothing coarser to stand in (a void there is a void, not a hole).
Only a sample with a hole is thrown away (what it would replace stays
drawn); one stood in for is drawn. Columns whose finest tile is out of
the download reach take the coarser levels too, which reach farther
(BlueMap::chunk), and chunk detail with them. A coarser level arriving
names chunk-wide cells among those it changes, and every arrival drops
the cached chunks over it (and a cell around, for a coarser level), so
they are built and sampled again with it.
Tests: coarser levels stand in for a waiting finest one (the finest of
them), and past level 1's reach; a hole without any is waited; a waited
sample is thrown away and a stood-in one drawn; in the scheduler
scenario (prepared resources, ignored), a source standing in coarse
columns keeps every chunk past the near terrain drawn while the finest
loads, and the finest replaces them once in.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With BlueMap on, a tile arriving re-sampled every far-terrain tile over it
up to the roots, and a coarse one also cleared the detail of thousands of
chunks, each re-requested and re-meshed.
- Arrivals name the cell widths their level is drawn in (Lowres::widths):
Lod::refresh samples only those tiles again. Changes are handed over at
most every 250 ms, so a burst is sampled once.
- A sample, or chunk detail, equal to what is drawn meshes nothing. A tile
refreshed while its sample was in flight is sampled again.
- Tiles looked up out of reach are remembered and wanted when the player
comes near or the server is tried again.
- A level-1 arrival drops only the chunks it covers from the cache. The
cache size and pruning walk the disk outside the lock the frame takes.
- Columns end 8 blocks under their lowest neighbour's top, not at the
bottom of the world: the edge of the map's data drew a stone wall along
the horizon.
- The map's settings, once a visit: a network error backs off and keeps
the cached map; only an answer without this level's map drops it. When
the lowres settings changed, the map's cached tiles are deleted before
the new settings are kept (if they can't be, no map this visit), and
lookups or chunks built under the old settings meanwhile aren't kept.
- A map dropped or replaced redraws everywhere, not only in the download
radius (announce_all). A failed cache write no longer hides the tile
just downloaded.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On a server the player names in horizon-bluemap.txt (blueMap: lines, off
otherwise), far terrain fills what Horizon didn't keep from the server's
BlueMap web map: its lowres tiles (a colour, height and block light a
column) are downloaded around the player, one at a time and at most ten
a second on their own thread, kept on disk, and drawn as the natural
block nearest each colour. A tile that arrives refreshes the far-terrain
tiles over it (Lod::refresh).
The maps have a file of their own beside horizon-graphics.txt, which
shared instances and modpacks carry: the map is asked for tiles around
where the player stands, so only the player names one. BlueMap Radius
stays a graphics setting. Tiles are cached up to 256 MiB a server
(oldest first), fetched again after three days, written whole (temp file
and rename), and an error page never replaces a cached tile; a server
that stops answering is left alone longer each time, up to ten minutes.
The settings: Graphics holds them, horizon-graphics.txt keeps
blueMapRadius:, and the blueMap: lines are read from
horizon-bluemap.txt.
Chunk detail read before a refresh over its chunk is dropped and asked
again, never kept over the newer data (Lod::refreshed_since).
The launcher's import from other launchers leaves horizon-bluemap.txt
and server-terrain/ out, so an instance another launcher made can't name
a map for the player.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The first half of far terrain from a server's BlueMap web map, nothing
wired yet: the player's settings and their formats (blueMapRadius:, and
the blueMap: lines of their own file, horizon-bluemap.txt; the next
commit reads and writes them), the web app's settings.json and a map's
lowres layout (tileSize, lodFactor, lodCount, validated so a hostile map
can't overflow or index past a tile), where a block's pixel is at every
level (LowresLayer.saveTile), the tile paths (pathFromCoords), decoding
a lowres tile's PNG (colour, height clamped to the world's reach, block
light; gzipped too), and the palette that turns a colour into the
natural block nearest it, tinted by its biome as the game draws it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With a BlueMap, a far-terrain cell up to a chunk wide whose map tile was
still downloading was sampled empty, and that tile, ready, replaced the
coarser one drawn there. A band of nothing stood past the near chunks
until the map's tile came and the tile was sampled again, ring by ring
outward.
- A sample with data still on its way (LodSampler::waited) is thrown
away: what it would replace stays drawn. The tile waits in the retry
map for 10 s (WAIT_RETRY: a shorter wait let near retries take every
sample slot), or is sampled again as soon as the data comes
(Lod::refresh), at once if it came while the sample was taken. A
refresh also marks first samples still in flight.
- A finished mesh job's tiles go up finest first, then nearest: a tile
re-meshed clipped around a finer one no longer shows its hole frames
before the finer one is drawn.
- On a server, far terrain's finest tiles stop where the server stops
sending chunks (its view distance and a ring), not at the render
distance: thousands fewer chunk-sized tiles to fill in.
Tests without resources drive the scheduler by hand: a waited sample is
retried when its data comes, a tile refreshed mid-sample is sampled once
more, the same samples mesh nothing, and kept chunks re-sample the wide
tiles over them. With nothing coarser under the near ring yet, the ring
stays empty while the finest level downloads (the ignored scenario test
added here fails here; the next commit replaces its source with one that
stands in): the next commit's coarser levels fill it.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With Find BlueMap on (Video Settings > Advanced > Rendering, off by
default), joining a server no blueMap: line names asks a few likely
addresses for BlueMap's settings.json on the BlueMap worker, one at a
time with short timeouts, and keeps the answer per server (a map found
is checked again after a week, none after a day). BlueMap Radius sits
next to it. A blueMap: line always wins.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A tile sampled before the server sent a chunk of it kept that sample
while it stayed in the plan. Cells up to a chunk wide take the chunk's
detail when it is observed, but wider ones (past 4 km, and the coarse
fallback drawn while finer tiles load) never did, and a sample in flight
while the chunk was being stored overwrote it with what it read before.
When a source keeps the chunks it observes (LodSource::keeps_observed:
server terrain), the tiles over a newly kept chunk whose wide cell there
is empty, or whose sample is still being taken, are made stale and
sampled again, their old samples drawn meanwhile; an unchanged sample
meshes nothing. Local worlds are unchanged.
Tests: resampled_over's choice, without resources; with prepared
resources (ignored), a player flying past 1800 chunks with a view distance
of 10 keeps every dropped one drawn, and 32 x 32 chunks kept after their
far tiles were sampled fill in (all 1024 stayed empty before).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With no BlueMap, a far-terrain cell wider than a chunk took the column
of the kept chunk nearest its middle, however few the cell held. On a
fresh cache the coarse fallback tiles, sampled first, drew a few chunks
around the player as flat slabs hundreds of blocks wide (floating ones,
from a sky build), which finer tiles then replaced with the nothing they
know of, nearest first: terrain that appeared, then vanished outward.
A wide cell now takes a kept chunk only where at least half of its
chunks are kept, as Column::reduced keeps what fills half a cell.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
A rule with orient=texture finds a face's left, right, up and down from
the quad's own texture (Axes::texture: the UV gradients of its corners,
snapped to the block's axes), so turned or mirrored models connect along
their texture. OptiFine's defaults are unchanged.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
On a server that doesn't offer horizon:lod (any vanilla, Paper or Spigot
server), every chunk it sends is kept as its far-terrain summary, on
disk per server, world and dimension (server-terrain/<server>/<hashed
seed>/<namespace>/<level>/, Anvil region files), and drawn beyond the
server's view distance on this visit and later ones: a server never
sends its seed, so the client draws what it was sent. A chunk the client
forgets (ForgetLevelChunk, or a moved or resized storage) is observed
one last time first. A server's offer (horizon:lod) still comes first,
and a friend's world keeps nothing (its code changes at every share),
nor a splitscreen guest's (the player's own world, its other guests
writing the same folder).
The far terrain is rebuilt when the level changes; a proxy's next server
comes through reconfiguration. Cells wider than 128 chunks aren't
searched for kept chunks, and a failed write is logged once.
tools/server_lod_e2e.py's server without an offer now expects these kept
chunks (source "server-terrain").
server_terrain lives in horizon-client-render beside lod and streaming.
The launcher leaves server-terrain/ out when it duplicates an instance:
it is a cache, rebuilt as the instance plays.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Only rules before the one that picked were tried on the face's sprite;
later ones naming the picked tile chain as any other.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Trapped and ender chests read only their own files (cem_model.txt lists them with no
fallback); copper chests have no CEM name and always draw vanilla's chest.
- An entry whose name isn't OptiFine's for the model reaches the vanilla part of that name,
as mobs do; it is skipped only when neither exists.
- Tests: every OptiFine part name of the block entity tables lands on its vanilla part,
book.jem reaches the lectern's book, a copper chest draws vanilla's geometry and UVs beside
a chest.jem, and only entries no model has a part for are warned about.
- jem_files' precedence test moves next to it in horizon-optifine; read_small is private.
- docs/resource-packs.md names both test modules.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The second pass skips rules tried on the face's own sprite (an overlay
naming both drew twice), and doesn't run when the picked tile is that
sprite itself (tiles=stone).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
ctm.properties: "Tiles output by CTM rules can also be matched by another
rule" (matchTiles=optifine/ctm/mygrass/1.png). After a rule picks a tile,
the tile rules naming that tile (and this block, if they name blocks) are
tried once more; <default> there keeps the first pick.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Solid beveled panels made the screen read heavy. The plate and the strip
are now the page colour, see-through, with a 1 px edge; groups a lighter
tint on them; buttons, tick boxes and the value stepper flat; a slider a
thin accent track under its value.
Co-Authored-By: Claude Opus 5.5 <[email protected]>