Commit Graph

72 Commits

Author SHA1 Message Date
Ludovic Marie e93c99a4c7 Merge remote-tracking branch 'origin/main' into fix/pr751-merge
# Conflicts:
#	crates/horizon-client/src/options_horizon.rs
2026-10-18 09:44:42 +02:00
LookImEcho-Rick b9d61e5701 Make pr_ready portable to Windows 2026-10-17 15:48:49 -05:00
Ludovic Marie bf1cd95868 Merge main into feat/server-command-tree
tools/lan_e2e.py: the check lists keep #700's effects and portals and this PR's commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:33:18 +02:00
Ludovic Marie 1c79addec1 Input capture test: on a server the chat is ChatScreen (#708)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:05:28 +02:00
Ludovic Marie 9240aeb9ff Input capture test: the mock Game has lan_acting (#687)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 22:05:16 +02:00
LookImEcho-Rick cde0703645 Merge main and preserve capture and mining release behavior 2026-10-16 20:44:16 -05:00
LookImEcho-Rick 3230b6b88d Fix mouse capture when world loading completes 2026-10-16 19:24:05 -05:00
Cosmic b27e4b9323 Skip Rust verify steps for content-only pull requests 2026-10-16 11:46:49 -04:00
Ludovic Marie 08c4805e10 Release train: pad the same-day counter to two digits
GitHub's releases page sorts a day's tags as text, so v2026.10.15.9 showed
above v2026.10.15.18 (reported by rdbt on Discord). Tags now count .01, .02...
Every reader (release_train.tag_key, the launcher's compare_versions) compares
the parts as integers, so older unpadded tags still order correctly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 23:17:09 +02:00
Ludovic Marie d53a366718 Opt-in data collection (2/5): the launcher asks, and Settings changes it
- First run (and when CONSENT_VERSION changes): a question over the launcher
  with four switches, all off, what each sends in plain words, what is never
  sent, "View what would be sent" (the exact JSON), and "Send nothing" /
  "Save choices".
- Settings → Data collection: the same switches any time, the install ID and
  what waits, "View what would be sent" and "Delete my data" (server deletion,
  outbox cleared, new install ID).
- The outbox is sent at start and on close (at most 3 s), only when there is a
  server (none yet) and only what the player allows.
- CLI `horizon-launcher data status|allow|deny|show|delete|send|bug|benchmark`
  and MCP tools data_collection, set_data_collection, data_preview,
  delete_my_data, report_bug. Bug reports and benchmark uploads are printed for
  the player to confirm; logs are scrubbed and lose chat lines.
- horizon-telemetry joins the launcher's paths (launcher.yml, release_train,
  pr_ready).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 18:52:45 +02:00
Ludovic Marie 7c0d3ceea6 Port report: ranked work items from a version diff
tools/port_report.py turns a diff into work items: rank 1 blocks the switch (facts,
packets, DataFixers, data component types, report formats, tables to regenerate),
rank 2 and 3 one item per Horizon file that ports changed vanilla code (with or
without a harness), rank 4 new content, rank 5 summaries. version_port.py report
writes report.md and report.json; port prepares, diffs and reports in one go.
The class map now resolves names of both versions. The doc records the 26.2 -> 26.3
replay against Horizon's own code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:51:39 +02:00
Ludovic Marie 93bb113e0d Diff vanilla classes method by method and map them to Horizon
tools/class_index.py hashes every method's bytecode with constant-pool references
resolved and branch targets as instruction indices, lambdas folded into their
declaring method; class diffs list moved classes. tools/vanilla_map.py maps vanilla
classes to the Rust files whose comments name them and the harnesses whose oracles
or probes reach them. version_port.py prepare indexes, diff reports both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:44:26 +02:00
Ludovic Marie 58ad81674b version_port.py: detect, prepare and diff Minecraft releases
detect reads Mojang's manifest; prepare downloads and verifies a version into the
version store (outside the repository), runs its data generator and reads the
jar's own version facts; diff compares facts, packets, registries, blocks, item
components, commands, data packs, assets and lang.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 13:38:37 +02:00
Ludovic Marie 49b6367d2e perf_bench: a load ends when every streaming stage is idle
Builds that report status.streaming are loaded once chunk counts are
stable and no stage (generation, decoration, light, meshing) has held work
for half a second. Meshing going quiet also waited through the remeshing
that follows a load (fluids settling, random ticks: 1500 falling to 60
meshes/s for ~150 s at RD16), which is simulation, not loading. Older
builds keep the meshing-rate rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:01:03 +02:00
Ludovic Marie 2a91612928 perf_bench.py: repeats, and the benchmark report format v1
Each game runs --repeats times (3) per render distance and writes one v1
report per distance (benchmarks/perf/<label>-rd<N>.json): figures as means
with standard deviation and 95% CI, per-flight frame entries with the fixed
histogram, the hardware without the host name. frames() and summary() match
crates/horizon-bench value for value (tested on the same inputs).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:01:03 +02:00
Ludovic Marie b691b5ebaa Benchmark Horizon against vanilla 26.3 and vanilla with performance mods
tools/perf_bench.py runs Horizon, the vanilla 26.3 client and vanilla with
Sodium, Lithium, FerriteCore and ImmediatelyFast through the same steps:
fresh world (seed 1234, creative) per render distance, start to title, start
to in-world, then per scene teleport, chunk load, settle and a fixed 192-block
flight. Every frame time is kept and one function computes average FPS, 1% and
0.1% lows, p50/p95/p99/p99.9 and a frame-time histogram for every game; RSS is
sampled from outside the process for every game. One options.txt (vanilla's
keys, Fancy preset values with a Custom preset so the render distance holds)
is written for all of them.

Vanilla is driven by a dev-only Fabric mod (mods/horizon/src/bench) compiled
against the user's own client jar: it creates the world with
WorldOpenFlows.createFreshLevel, teleports with /tp and places the player on
the path every frame as horizon-ctl fly does. Nothing of the game is
redistributed and the performance mods are fetched from Modrinth with the
hashes pinned in packs/mods.lock.json.

Horizon side: `fly ... PITCH raw` also replies with every frame time, and
--offscreen drives frames from the event loop, so a window nobody sees (a
locked macOS session) still renders.

docs/benchmark.md describes what is measured, how fairness is kept, the
caveats and how to reproduce it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:40:12 +02:00
Ludovic Marie 48c85fa19f Add tools/pr_ready.py, CONTRIBUTING.md and a pull request template
pr_ready is the one self-check to run before asking for review: from the
files a branch changes it runs rustfmt, clippy with warnings denied, the
tests of the touched crates (mapped through cargo metadata), the launcher's
own workspace when it is affected, the tokens.css check, the Cargo.lock
rule, the Python tests and the public-tree check, and the vanilla harnesses
the change can affect, picked by tools/parity.py exactly as CI's parity job
picks them. It runs locally or on a fleet machine through ~/fleet/rbuild;
on the Mac mini the harnesses use the parity runner's own workspace and
scripts. It prints one Markdown block for the pull request body and exits
non-zero on any failure.

tools/parity.py gains --changed-files for copies without Git history, and
tools/ci/vanilla-data.sh accepts the preparation key from outside.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:13:39 +02:00
Ludovic Marie 86e0e122f4 Release train: ship every settled player-facing change, no 3-hour gap
Ludovic wants releases as often as changes land. A kind now releases when
player-facing pull requests merged since its last release and the newest of
them merged 15 minutes ago (a burst of merges becomes one release), or the
oldest an hour ago when merges never pause; release-now still ships at once.
There is no minimum time between releases. The scheduled checks run every 10
minutes (game at :02, launcher at :07) so a settled release goes out without
another merge.

Release titles keep proper nouns (Horizon's mark, Microsoft, LAN, ...) when
they lowercase the clauses after the first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:01:26 +02:00
Ludovic Marie beac11eaa5 Nightly parity audit on the Mac mini, with an issue per regressed harness
nightly.yml runs every harness from scratch on main at 03:00 Paris time on the Mac mini's runner, after
preparing the resources again if the preparer changed. tools/ci/parity_issues.py compares the results with
the previous audit: a harness that passed and now fails gets one issue labelled parity-regression (an open
one is commented on), and an issue closes when its harness passes again. The bugs session picks them up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:18:31 +02:00
Ludovic Marie 6d759dc7bb release_train: name a feature split over several pull requests once in the title; no leading Fix
Today's main would have been titled "Controllers, controllers, controllers and 17 more changes".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00
Ludovic Marie 8aacb48798 release: automatic game releases after main's green checks
release.yml now releases the game by itself: after every green checks run of a push to main, every hour
(a release the cooldown held goes out without another merge) and by hand. tools/release_train.py decides
from the pull requests merged since the last published game release: a release when one changed what
players run and the last release is 3 hours old, at once for a pull request labelled release-now;
skip-release pull requests don't count. RELEASES_PAUSED=1 stops it. Tags vYYYY.MM.DD, then .1, .2 the
same UTC day.

Linux (ubuntu-22.04) and Windows build on GitHub with Cargo caches, macOS on the Mac mini's runner
(MAC_RUNNER=macos-latest sends it to GitHub). Each builds horizon-client, horizon-ctl and horizon-prepare
(all three named: --bin selects only the named binaries), checks all three exist, checks their libraries,
starts each one, packages with xtask and archives; the Mac also runs the game offscreen for 300 frames on
a new world with resources prepared from its own Minecraft. The notes: New (label feature), Fixed (the
rest), Known issues (docs/known-issues.md). A hand-started run off main, or with dry_run, builds
everything and writes the notes but publishes nothing.

docs/release-flow.md describes the flow, the labels, pausing and rolling back; the launcher gets a test
that a release turned back into a draft is never offered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00
Ludovic Marie 874d111d60 checks: cached verify, and a parity job on the Mac mini for the harnesses a PR can affect
verify keeps its dependencies and workspace build in a cache saved by main's runs (Swatinem/rust-cache).
The new parity job runs on the Mac mini's self-hosted runner (label horizon-game), which holds the
maintainer's own Minecraft files: tools/ci/vanilla-data.sh links .local/ to the runner's copy and prepares
the resources with the pull request's own horizon-prepare, once per preparer version; then
tools/parity.py --changed <base> runs only the harnesses whose traced dependencies changed since the merge
base and that have no pass for their current inputs. A fork's pull request runs it only when a maintainer
adds run-parity, once per label, on the commit it had then.

parity.py: --changed REF, and linux_only in tools/parity_hosts.json for harnesses that differ on other
platforms for a reason that is not a Horizon bug (skipped there with the reason, unless named).

docs/release-flow.md starts with the pull request checks and merging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 22:17:20 +02:00
Ludovic Marie a96580aa2e Tests: the supervise trial-merge test sets its own git identity (CI runners have none)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:06:28 +02:00
Ludovic Marie 6d7f11feb2 Prepare: require only this platform's libraries; accept republished 26.3 manifests
lab.prepare imported every client library in the version JSON, ignoring
its OS rules, so a Linux install failed on the macOS-only
java-objc-bridge the official launcher never downloads there. The rule
evaluation moves from runtime.py into lab.py and one selector,
client_library_artifacts, now serves preparation, the runtime and the
model baking harness: OS rules as the launcher applies them, and the
natives for this architecture (classifier suffixes, plain natives being
the x64 build only when an ARM variant is listed). Required libraries
are still checksummed and a missing one still errors.

Mojang republished 26.3.json twice (702fe591, then 11f1aefe, the current
one) with only assetIndex changed: non-English translations, same jars
and libraries. The lock pins the current file and accepts both earlier
ones; preparation takes the launcher's copy so it matches the asset
index installed with it, and an unknown republish errors clearly.
Fixes #41

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 20:20:17 +02:00
Ludovic Marie 6fab5eb279 Merge branch 'main' into parity-base
# Conflicts:
#	README.md
#	crates/horizon-client/src/game.rs
2026-10-12 19:02:09 +02:00
Ludovic Marie cdd05f8e8b Trial-merge branches before integrating them
supervise.py check trial-merges every candidate branch into the integration
branch with git merge-tree, without touching a worktree: each alone, every
pair, then the largest set that merges cleanly together, in about a second.
integrate starts from that set (or every parity/* branch with new commits,
with --ready), gates it once, and if the gate fails tries each half, so the
branches that pass still land.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:43:58 +02:00
Ludovic Marie f5ab40d415 Shard harnesses, narrow their Rust sources, keep the desktop light
parity.py:
- A harness declaring PARITY_SHARDS = N runs as N entries, NAME#1..NAME#N,
  each told its share through PARITY_SHARD; NAME selects them all.
- PARITY_RUST_SCOPE lists the Rust sources a harness's probes can reach.
  It applies only if closed (nothing in it names a module, crate-root
  re-export or macro outside it, and nothing outside implements anything for
  its types); crate roots then count without their other module declarations.
- Bytecode caches no longer count as dependencies.
- With other hosts, this machine runs 3 harnesses at once and only those
  under 800 MB, admitted against the memory free at each start; hosts can
  exclude harnesses and run at a nice level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 16:41:51 +02:00
Ludovic Marie 6fdd7f3bd7 Add the Horizon logo, banner and in-game screenshots to the README
An original logo (a sun setting behind a blocky horizon), a banner and a social
preview card, and six screenshots captured in the Rust client. The README opens
with badges, a short pitch and a gallery; a test keeps the parity badge in step
with the subsystem table. Packaged builds use the logo as their menu icon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:47:52 +02:00
Ludovic Marie 37898e856b Build from the user's own copy of the game instead of downloading it
Horizon no longer fetches anything of Minecraft from Mojang's servers. The
client jar, version JSON, libraries and assets come from the user's official
launcher install, and the server jar is one they downloaded themselves
(--server-jar or HORIZON_SERVER_JAR). Every file is checked against checksums
pinned in research-lock.json, and the prepared .local/ layout is byte-identical
to the old downloads, so the harnesses and the native client are unchanged.

CI keeps the offline checks on GitHub's runners; the vanilla comparisons and
the mod build need the game, so they move to a manual job on a self-hosted
runner that holds the maintainer's own copy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 02:00:18 +02:00
Ludovic Marie d061bdf2fb Share harness passes between worktrees and cap harnesses machine-wide
A pass is keyed by the content of the harness's dependencies, so it is
also valid in any worktree with the same files. Passes are published to
parity-shared/ beside the shared vanilla data, and a worktree adopts one
whose key matches its own files instead of running the harness: an
agent's gate only runs what its own changes affect, instead of the whole
suite from scratch. Every runner on the machine also shares
PARITY_MACHINE_JOBS slots, so several agents' gates can't overload it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:42:52 +02:00
Ludovic Marie 13759ef16b Reuse unchanged vanilla oracle runs, and keep test games silent
tools/parity_shim.py stands in for java and javac in harness runs: it
records each oracle run's outputs and the content of every file under the
paths its arguments name, and replays the run while those are unchanged.
After a Rust-only change a harness only pays for its Rust side
(block_update 53 s -> 5 s). A harness that fails while reusing oracle
results is rerun fresh, and stops reusing them if that passes; --all and
--fresh never reuse them.

Harness probes built in the dev profile are optimised (overflow checks
and debug assertions kept), and the chunk generation harness runs its
vanilla and Horizon sides concurrently.

HORIZON_NO_AUDIO keeps the game off the speakers; tools/parity.py sets it,
since the save round-trip harness launches the game on a virtual screen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:43:26 +02:00
Ludovic Marie 6396e8c161 Run the vanilla harnesses in parallel, only when their inputs change
tools/parity.py traces each harness (its Python, Java oracle sources and
the Rust sources of the probes it runs, from Cargo's dep-info) and reruns
it only when one of those, the vanilla artifacts or the toolchains change.
Harnesses run in parallel within the available memory, and their probes
build without LTO and with 16 codegen units into .local/parity-target: a
one-line change rebuilds every horizon-world probe in 3 seconds instead of
122. The full suite takes about 20 minutes instead of an hour.

tools/gate.py is the check a branch passes before integration, and
tools/supervise.py manages the per-area worktrees: status, new worktrees,
saving unfinished work, and integrating a branch only when the gate passes.

Harnesses find probes through lab.cargo_target() instead of a hard-coded
target/, the save round-trip harness builds the binaries it launches, and
the mob AI harness now fails on divergences like every other harness
(--report-only keeps the old behaviour). Debug builds keep line tables
only, so worktrees stay small.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:05:36 +02:00
Ludovic Marie 34f4722e42 Show audited Java parity in the README and correct stale status
Score fourteen subsystems against the code (55% overall) and test that the
README percentage matches the table. Fluids, redstone, crop growth and the
Nether already run in the client; the native-client status still described
the old test-generator terrain without mobs or containers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 11:21:53 +02:00
Ludovic Marie 18d0c44e8d Retain vanilla biomes during below-zero regeneration 2026-10-06 19:49:00 +02:00
Ludovic Marie b1505adc54 Enforce generation write heights during below-zero upgrades 2026-10-06 15:28:26 +02:00
Ludovic Marie e9a082517b Enforce generation write radii before feature mutations 2026-10-06 14:38:35 +02:00
Ludovic Marie 0a63903b91 Connect generation-time destruction to resident region writes 2026-10-06 14:20:49 +02:00
Ludovic Marie 619f319924 Keep decoration fixtures independent of region tick and query probes 2026-10-06 14:03:31 +02:00
Ludovic Marie 895af92d12 Implement ordered overlay feature composition 2026-10-06 12:13:34 +02:00
Ludovic Marie 4998765408 Accelerate large differential trace comparisons 2026-10-06 12:07:41 +02:00
Ludovic Marie cb19660fbe Build Horizon Rust parity foundations and differential validation
Add version-bound core, world data, geometry, generation and feature implementations with native client/server comparison tools and scoped evidence reports. Apply Horizon project branding.

Workspace tests, Clippy and 147 Python tests passed. Full 51-family survival and expanded provider comparisons are still running; complete gameplay parity and a playable Rust client remain unfinished. Private game artifacts and the isolated simple-block placement work are excluded.
2026-10-06 11:24:51 +02:00
Ludovic Marie b3dda5152c Merge pull request #33 from ludovic111/integrated-tick-context
Capture shared graphics context in integrated-server tick reports
2026-10-04 14:24:03 +02:00
Ludovic Marie 357cc90326 Capture shared client graphics in integrated-server reports 2026-10-04 14:09:19 +02:00
Ludovic Marie ed416c91cf Retain shader failures and private graphics timeout stages 2026-10-04 13:45:29 +02:00
Ludovic Marie 99d6aae5e0 Verify actual cinematic shader activation in private world probes 2026-10-04 13:09:45 +02:00
Ludovic Marie 213498c77e Separate failed readback assertions from independently logged Vulkan errors 2026-10-04 12:07:25 +02:00
Ludovic Marie a77476b54e Locate failed Mac Vulkan probes without exposing private logs 2026-10-04 11:59:57 +02:00
Ludovic Marie a5000edf11 Check recommended Mac profiles in demo worlds and retain separate Vulkan research 2026-10-04 11:26:47 +02:00
Ludovic Marie 02d7a5189d Capture sampled tick-manager changes without altering simulation 2026-10-04 08:43:00 +02:00
Ludovic Marie 470022f10b Add core performance profile for matched view-distance comparisons 2026-10-04 07:14:50 +02:00