🤖 Agent: merged after verify passed; cloud_render, environment_render, weather_render and 923 client tests pass, with before/after screenshots of Complementary Unbound. Java shader packs get real eye brightness, custom uniforms, Iris's sun path and tilt, and clouds=off/sun/moon flags. Part of #944 (reported by brabus_); shadows and gbuffers still to come.
🤖 Agent: merged after the verify rerun passed (the first run's failure was a flake; horizon-server tests pass 39/39 locally) and CI parity passed. Old releases are pruned automatically after an update.
🤖 Agent: merged after verify and CI parity passed (modpacks-10, reviewed with local harness proof in the agent's report). Carries Formations' structures.
🤖 Agent: merged after verify and CI parity passed (modpacks-10, reviewed with local harness proof in the agent's report). Carries the mods' pillar and fluid-fix processors in ported packs.
Complementary Unbound drew clouds and light shafts inside terrain and a
second sun because the full-screen passes got wrong inputs:
- eyeBrightness was a constant (0, 240), so packs saw open sky
underground. It is now the block and sky light of the camera entity's
eye block (CommonUniforms.getEyeBrightness), and eyeBrightnessSmooth
follows it with the pack's eyeBrightnessHalflife (SmoothedFloat).
- Custom uniforms (uniform.* / variable.* in shaders.properties) were all
0: Complementary's eyeBrightnessM, isEyeInCave, starter, framemod8 and
the rest. horizon-shaderpack's uniforms.rs parses and evaluates them
each frame like Iris (stareval priorities, IrisFunctions, SmoothFloat).
- sunPosition, moonPosition, shadowLightPosition, sunAngle and
shadowAngle now follow CelestialUniforms: the sky's sun and moon angles
turned by Y -90, the pack's sunPathRotation about Z, then the angle. The
old sun vector pointed north-south instead of east-west.
- While a Java pack runs, sunPathRotation tilts Horizon's sun, moon and
stars as Iris tilts vanilla's, and clouds = off | fast | fancy, sun and
moon = false apply. Complementary and Sildur's set clouds = off; the
vanilla clouds Horizon drew anyway wrote depth the packs' light shafts
and fog took for terrain.
Fixes#944
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An install kept every release it ever unpacked in releases/. Once the game is up on the
new release (or a stopped server is switched), the update now deletes the old ones,
keeping the release in use, the one `previous` names (rollback's target), the one the
running horizon-server came from, and the `updates.keep` newest of the others (1 by
default, a new server.toml setting). A release that can't be deleted is reported and
left for the next update.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its formations:simple becomes the game's horizon:formations (its pool and
placement), and its single pool element vanilla's with its ground_level
under horizon:ground_level. Its pieced structure (one) stays out; its
processors are still dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 Agent: merged after verify passed. The site's nav links Host a server; README gains a hosting paragraph; tools/server.py and docs/server-quickstart.txt retired.
🤖 Agent: merged after verify and CI parity passed. horizon-server update (SHA-256 check, world backup, switch, health wait, automatic rollback) and rollback; 6-hourly update notice in log, status and panel. Proven end to end on the Mac mini and the ThinkPad (healthy update, crash rollback, timeout rollback, manual rollback, real GitHub download); 37 unit tests.
Repurposed Structures' and Moog's pillar_processor, Repurposed's
bottom_pillar_processor and close_off_fluid_sources_processor, YUNG's
pillar_processor (Better Fortresses, Better Jungle Temples) and Better
Fortresses' stair_pillar_processor become the game's horizon:pillar,
horizon:bottom_pillar, horizon:close_off_fluid_sources,
horizon:yung_pillar and horizon:stair_pillar. A pillar's own processor
list is written in, with the Java processors in it dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 Agent: merged after verify and CI parity passed, and a combined check with rdbt's #796 on current main (fmt, clippy, 359 tests, 14 world-gen harnesses). YUNG/Moog terrain adaptation around structures (Better Fortresses netherrack inside bridges 39–41 % → 2.5–7 %), its carry, and YUNG modifiers.
🤖 Agent: merged after verify and CI parity passed, and a combined check with rdbt's #796 on current main (fmt, clippy, 359 tests, 14 world-gen harnesses). YUNG/Moog terrain adaptation around structures (Better Fortresses netherrack inside bridges 39–41 % → 2.5–7 %), its carry, and YUNG modifiers.
🤖 Agent: merged after verify and CI parity passed, and a combined check with rdbt's #796 on current main (fmt, clippy, 359 tests, 14 world-gen harnesses). YUNG/Moog terrain adaptation around structures (Better Fortresses netherrack inside bridges 39–41 % → 2.5–7 %), its carry, and YUNG modifiers.
🤖 Agent: merged, thanks Online5277! Terrain LOD lands off by default. With LODs off it matches main (0 differing pixels in 6 RD16 views, same FPS/GPU ms on the HD 620, same time to full render distance); CI verify and parity pass. We merged main in three times and adapted it to #905/#939 and the packed texture coordinates. The follow-ups from the review (edge band, integrated-GPU cost, lighter far meshes) we're taking on ourselves.
tools/server.py and docs/server-quickstart.txt are gone (horizon-server replaces them);
build_release.py builds only the client starter, and the docs point to docs/hosting.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
YUNG's API and MoogsStructureLib add their own kernel to the Beardifier:
a size and falloff of its own, carving or burying above and below a piece's
floor, around rigid pieces and junctions. Better Strongholds buries its
stronghold, Better Fortresses carves the netherrack over its bridges and
Moog's mega fortress carves a band around its corridors. A structure or a
pool element holding `horizon:enhanced_terrain_adaptation` now gets that
kernel (horizon_core's TerrainKernel), summed after vanilla's density as
the libraries' mixins do; YUNG's also grows the start's box by its radius.
Vanilla data never has the key, so vanilla worlds are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The soft 4 ms yield per frame (also with Unlimited budgets) made a saved
RD32 world reach full render distance about 1 s later on the Mac mini
(streaming time after launch, 4 alternating pairs: main 1.9 s mean, yield
2.8 s; t100 from a first batch of 3 pairs: 6.1 s vs 7.1 s) without better
loading frame times (max 133-210 ms vs 31-150 ms). Without a budget every
finished mesh uploads at once again, as on main. The budgeted path keeps
this branch's fixes: only loaded chunks' sections wait, and sizes count the
24-byte terrain vertex.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
processors.rs tests: keep both #926's trial spawner and vault tests and
#920's Repurposed rule-test test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Keeps #905's single command buffer and #939's occlusion culling: distant
tiles draw in phase 1, so phase 2 stays exactly the deferred sections the
culling test reads back, and the flat path's origins include the tiles in
main's relocated upload block.
Adapts to main's APIs: block sprites and models now live in RenderAssets
(WorkerContext::render), BlockFacts behind Resources::facts(), and
terrain vertices hold the UV as 16-bit fixed point (#df58a474). Merged
distant faces repeat their sprite once per block, which the [0, 2) section
UVs cannot hold, so tile vertices now store UVs from the face's corner in
1/32-block steps (TerrainVertex::tiled, faces split at 1024 blocks) and the
instance's origin.w carries the UV step: 1/32768 for sections (decodes
exactly as before), 1/32 for tiles.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 Agent: reviewed; verify passes. Release dry run on this branch (run 39167763008): linux, macos, windows builds and publish all succeed; horizon-server-linux-x86_64.tar.gz (28 MB) built and import-checked (no libasound/libudev). Agent's proof: 880/879 client tests in both builds, dedicated_server/server_entities/lan on the server build, install → init → start → bot join on the ThinkPad without the libraries.
🤖 Agent: reviewed; verify passes. Equal-work server benchmark: fixed exploring speed, 300 s warm-up, chunk-work comparison per run. Horizon 1.8/4.7/11.7/23.8 ms per tick at 1/10/25/50 bots vs vanilla 11.3/34.3/50.5/stopped by its watchdog; single runs on the ThinkPad, caveats in the README.
Explore bots paced at 0.8 blocks a second and a 300-second warm-up, so both servers serve the same
paths; the work table shows it. Horizon ran as horizon-engine (the server build, from its archive).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ChoiceTheorem's Overhauled Village swaps blocks in its templates through
Lithostitched's block_swap processor (137 lists): a map of block to
block, the new one taking Block.withPropertiesOf the old state, and the
block left alone when the new one is not registered. Carry dropped it.
- Game: horizon:block_swap, the same map; the new block's default state
with each property of the old one it has too (by name, when the value
is one of its own).
- Carry: lithostitched:block_swap becomes it.
ATM10: processors carried 136 -> 273 of 1,427 (dropped 1,291 -> 1,154).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Better Dungeons' small dungeon templates hold several chests; its Java
chest processor keeps the first, then each other one on nextFloat <= 0.2
until two have stayed, and turns the rest into cave air. Carry dropped it,
so every chest stayed.
- Game: horizon:count_cap. Of `blocks`, the first `min` a placement meets
stay, then each stays on settings.getRandom(pos).nextFloat() <= chance
until `max` have; the others become `replacement`. Counted per
placement (PlaceSettings, reset in process_infos), like the mod's
DungeonContext, set up per StructureTemplate.placeInWorld.
- Carry: betterdungeons:small_dungeon_chest_processor becomes it with the
mod's config defaults (1, 2, 0.2; chest and trapped chest).
Seed 1234, five small dungeons decorated: 12 chests -> 6.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Better Desert Temples builds its temples with placeholder blocks (orange,
red and white glass, blue and yellow concrete, red wool, polished diorite,
blank pots) that its Java processors turn into suspicious sand, sand and
sandstone, upside-down stairs, cobwebs, terracotta, TNT and pots with
sherds. Carry dropped them, so the placeholders stayed in the world.
- Game: a horizon:pick processor. A block of `block` takes the first
pick whose `below` is past one settings.getRandom(pos).nextFloat(),
else `default`; an outcome is a state or one of several by nextInt
(Util.getRandom), a new block entity or the block's own, and NBT lists
of picked strings (one float each).
- Carry: the fixed swaps become vanilla's rule; the random ones
horizon:pick with the mod's bounds (BlockStateRandomizer's sums as
floats). The processors that read or change the world around (pillars,
Diorite's waterlogging) stay dropped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moog's trial spawner and vault randomizing processors keep the block and
change its block entity. The templates were saved with state that breaks
them in a new world: a vault's server_data holds
state_updating_resumes_at far in the future (it never opens) and a
spawner's spawn_data an empty entity (it spawns nothing of its own).
- Game: a horizon:edit block entity modifier takes `remove`'s keys out,
then merges `data` in. append_static now merges as CompoundTag.merge
does (compounds in both are merged), not a shallow replace.
- Carry: each processor becomes vanilla's rule processor, one rule per
block state (12 trial spawner, 32 vault), with horizon:edit: the
spawner's normal and ominous configs by id, the vault's loot table and
key (the ominous ones on ominous vaults). trial_spawner/ files get their
mobs' 1.21.1 ArmorItems/HandItems as 26.3's equipment.
ATM10: 6 more processors carried (125 of 1,427).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tool picks this machine's natives (macOS arm64 or Linux), adds -XstartOnFirstThread on macOS
and turns vsync off there: with it on, an unseen window (a locked session) blocks in
SDL_GL_SwapWindow before the client joins. docs/server.md records the join.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two things kept Repurposed Structures' mineshaft processor lists from
loading in 26.3, and with them the mod's 5 Nether mineshaft pools:
- Its three rule tests written in Java. Carry renames them to the game's
horizon:matter_phase, horizon:piece_origin_axis_aligned_linear_pos and
horizon:y_value (213 in 75 lists), counted in the report's
rule_tests_carried.
- 1.21.1's minecraft:chain, which 26.3 calls minecraft:iron_chain (its
datafixers rename it in worlds). Carry renames it in recipes, loot
tables and worldgen files: 112 files in 5 of ATM10's mods.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
horizon-client's cpal output and its controllers (gilrs, the DualSense's HID output) move behind the
audio and controllers features, on by default, so the game is unchanged. Built with
--no-default-features it links neither libasound.so.2 nor libudev.so.1. release.yml packages that
build as horizon-engine in horizon-server-linux-x86_64.tar.gz (xtask package-server, checked by
check_imports.py --server); install.sh installs it on Linux (and --archive installs a local one);
horizon-server already prefers horizon-engine. The Dockerfile uses it too, and CI lints it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
🤖 Agent: reviewed; verify passes. Parity: 298/298 harnesses on the integration commit 234bd101 (main + #907/#908/#910); vanilla structure-start oracle 35/35 with all five modpacks-7 changes. Merged in the required order #907 → #908 → #909 → #910 → #911.
🤖 Agent: reviewed; verify passes. Parity: 298/298 harnesses on the integration commit 234bd101 (main + #907/#908/#910); vanilla structure-start oracle 35/35 with all five modpacks-7 changes. Merged in the required order #907 → #908 → #909 → #910 → #911.
🤖 Agent: reviewed; verify passes (client --help exits 0 without loading the game, so the start check only catches a program the system can't load). Linux installs now name the missing ALSA/udev libraries; services run through the installer's current link.
The first published benchmark for the working version (docs/server.md
section 10, item 4). It ran the v2026.10.18.15 release, as install.sh
installs it, against vanilla 26.3 with 1, 10, 25 and 50 mixed bots. The
servers ran on the ThinkPad (quiet, under nice 19) and the bots on the
Mac mini over the tailnet. Vanilla's default game rules applied on both;
Horizon's server runs the locator bar now.
Both servers kept 20 TPS throughout. Horizon used 3.7 to 5.2 times less
memory. Its average tick was shorter with 1, 10 and 25 bots and 18 %
longer with 50, while it generated about twice the new terrain a second
(the exploring bots fly as fast as each server sends chunks). Its P99
was lower at every count.
benchmarks/server-mixed-2026-10-18/ holds the README, the report and the
machine's load; docs/server.md links it from sections 9 and 10.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a Linux without ALSA's library (most minimal server images), the
game exits at once with the loader's "error while loading shared
libraries: libasound.so.2". The supervisor took that for a crash and
restarted it five times over two and a half minutes before giving up
with "see logs/latest.log", which holds nothing about it.
Now:
- `run` starts the game with --help first (it loads no game files) and,
if the system can't load it, stops at once with the loader's line and
the packages to install on Debian/Ubuntu, Fedora, Arch and NixOS;
- `init` prints the same as a warning after "Game:";
- install.sh lists the libraries `ldd` can't find, with the same advice;
- docs/server.md says so in the install section.
Also, a service installed without the installer's launcher on the PATH
ran `<dir>/releases/<tag>/horizon-server`: Linux reports the running
program with links resolved, so the unit pinned that release and an
update never reached it. The fallback now goes through `<dir>/current`
while it points at that release.
Found by the end-to-end install check on the ThinkPad (NixOS, no
nix-ld libraries) with v2026.10.18.15.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>