Commit Graph

36 Commits

Author SHA1 Message Date
Ludovic Marie 6c995bfa15 Merge remote-tracking branch 'origin/main' into feat/mod-api-level
# Conflicts:
#	crates/horizon-mods/Cargo.toml
#	crates/horizon-mods/src/index/tests.rs
#	launcher/Cargo.lock
2026-10-26 11:09:19 +01:00
Ludovic Marie 8cc8c6180d Mod API levels: refuse a mod made for a newer Horizon, list each version's level
Every function the game offers mods has the API level it arrived at
(crates/horizon-mods/src/api_level.rs): level 0 is the contract of
Horizon 0.2.67 and 0.2.68, level 1 adds render.set-minimap. A mod needs
the level of the newest function it imports, read from its component.

- The game refuses a mod that calls a function it doesn't have with
  "made for a newer Horizon (it uses ...): update Horizon to run it"
  instead of wasmtime's linking error.
- The registry index lists each mod version's level (api_level, read
  from the file by CI); check_file compares it when the entry has one,
  and the moved extras' installer takes the newest version this game
  can run.
- cargo horizon-mod check prints the level.
- The api_reference test fails until a new WIT function has a level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 10:39:47 +01:00
Ludovic Marie a1f1501305 launcher: Cargo.lock follows horizon-mods' shader check
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 07:11:30 +01:00
rdbtCVS 4be53ee28a Reduce resource preparation filesystem work
Share the asset index and verification state, stage checked imports while hashing, preserve indexed replacements, and clone independent resource copies where supported.

Co-Authored-By: Codex (GPT-6.1) <noreply@openai.com>
2026-10-25 22:58:00 +00:00
Ludovic Marie 1920106445 Merge pull request #1793 from socksprox/launcher-dark-titlebar
🤖 Agent: thank you! Merged after the contribution gate (checked against vanilla 26.3, tested in the modes it touches; pr_ready on main + the six gate PRs: fmt, clippy, 1327 tests, 112/112 affected harnesses) and this head's verify.
2026-10-25 17:23:29 +01:00
socksprox 3740de5116 Launcher: pin the macOS title bar to the chosen appearance
The launcher's Appearance setting darkened the page but not the window's native chrome: NSWindow keeps the system appearance, so a launcher set to Dark still drew a light title bar on a light desktop. apply_appearance (titlebar.rs) pins the NSWindow's appearance to the choice at startup and when the setting changes; System clears the pin so the chrome follows the computer again.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-25 13:40:07 +01:00
Ludovic Marie 7e513d2d5c Launcher: Horizon's registry as a mod browser source
A browser source for Horizon's own registry (docs/api/mod-index.md): it reads index.json
(HORIZON_MOD_INDEX for a test registry, cached in the game folder for when the registry can't
be reached), searches it by kind and words, lists versions with the SHA-256 the index gives,
and blocks the versions it says were taken down. Downloads accept the registry's release host
and check SHA-256; file:// files only from a local index.

Installing, the takedown check before play and the CLI come next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:57:54 +02:00
Ludovic Marie 850354ca28 Merge pull request #1337 from ludovic111/feat/telemetry-hardware
🤖 Agent: merged after verify passed. Ludovic asked on 2026-10-23 for every hardware fact players send (opt-in). The end-to-end run against a local server is in the description: breakdowns need ≥3 installs, with no install ID or exact value. The consent version goes up to 2, so players who opted in are asked again, as the privacy page promises.
2026-10-23 03:35:06 +02:00
Ludovic Marie a57efd4fa0 Launcher lock file: horizon-bench now depends on horizon-telemetry
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 02:27:54 +02:00
Ludovic Marie 3e99730b98 Merge remote-tracking branch 'origin/main' into sw1132 2026-10-23 01:43:49 +02:00
Ludovic Marie 784e8f0648 Mod browser (1/3): Modrinth and CurseForge sources, checksum-checked downloads
The launcher's core gains `browser`: search Modrinth for what Horizon loads
(resource packs, Iris/OptiFine shader packs, modpacks; never Java mod jars),
say how well each result runs, pick the newest version that runs, and
download a file only from Modrinth's or CurseForge's file servers, with a
plain file name, kept only when its size and SHA-512 (SHA-1 on CurseForge)
match. CurseForge is a pluggable source that stays off until an API key
exists (HORIZON_CURSEFORGE_API_KEY at run time or build time; the launcher
workflow passes the repository secret). docs/mod-browser.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 03:17:25 +02:00
Ludovic Marie 6127790456 Switch releases to 0.2 (merge only when Ludovic says go)
.github/release-line.txt = 0.2: the release train's next runs publish v0.2.0 (a beta, as always)
and launcher@0.2.0 at once, "the first release of 0.2" (docs/versioning.md). Merge it only after
the bridges (the last dated game release, made stable, and launcher-v0.17.0) are out: the go-day
steps are in docs/versioning.md.

Also, because they say 0.2 is out: the site's FAQ explains the numbers, Install says an old launcher
updates twice, hosting.md gives the one command for a server that says it is up to date, and the
launcher's Cargo.toml says 0.2.0 so builds from source (packages) are on the 0.2 line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 01:24:30 +02:00
Ludovic Marie fd9eb84bea Merge pull request #356 from ludovic111/feat/version-consts
🤖 Agent: merged (version-port tooling, rebased on main). Ludovic asked on 10-21 to clear the PR queue.
2026-10-21 16:16:03 +02:00
Ludovic Marie 9f385280ef Version facts in one generated file: crates/horizon-core/src/version.rs
tools/gen_version_consts.py writes what the pinned client jar's version.json
states (name, id, protocol, data version, pack formats, Java, stable, build
time) into horizon_core::version, and every Rust site reads it from there:
PROTOCOL_VERSION, GAME_VERSION, DATA_VERSION, the save's and the datafixer's
version name, /version, the crash report, the legacy ping, the dedicated
server's start line, the title screens, the default resource folder, the
resource pack format, and the launcher's and horizon-ctl's fallbacks. CI
checks the file against the pinned jar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:22:20 +02:00
Ludovic Marie 36908858a3 Launcher and horizon-server update: install only releases whose signed checksums match
The launcher (game installs and its own updates) and `horizon-server update` fetch a release's
SHA256SUMS and SHA256SUMS.sig, check the signature against the keys built in from
crates/horizon-release-sign/release-keys.txt and the archive against the signed SHA-256, and
refuse anything unsigned, signed for another tag or by another key, or that doesn't match, with a
message saying why. Merge only once release-keys.txt lists the key and a signed stable release
exists: until then every download is refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 12:20:36 +02:00
Ludovic Marie 4768d3f65b launcher: a Servers page that creates, runs and manages horizon-server servers (WIP)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 14:09:09 +02:00
Ludovic Marie bff5891073 Launcher: Cargo.lock has horizon-port-pack's horizon-mods dependency
horizon-port-pack now depends on horizon-mods (1f1d5a5a), which the
launcher's own lock file didn't have, so every --locked build of the launcher
failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 14:46:16 +02:00
Ludovic Marie 206b91e8aa Launcher: Import modpack, its coverage report, port and play it as a profile
A Modpacks page between Versions and Patch notes (docs/modpacks.md, the
working version's launcher step):

- Import a pack file (.mrpack, CurseForge zip) or an instance folder (Prism,
  MultiMC, CurseForge): horizon-port-pack's import, inventory (Modrinth by
  file hash) and report into ports/<slug>/ in the game's home.
- The coverage report: ported, partial, not yet and not needed, with every
  mod's class, tier, licence and reason, filtered by status.
- Port: the portable mods' jars (content, data tier) the player's own way
  (their folder, the pack's links, Modrinth's CDN; what only CurseForge has
  needs its key and is said so), carried over for the selected version's
  prepared tables; the pipeline's log shows while it runs.
- Play: the game as the pack's own profile, ports/<slug>/home/ (its saves,
  options.txt copied from the player's the first time) with the carried-over
  mods as dev mods. The session flow is shared with Play (start_client).
- The same from the command line: horizon-launcher modpack list | import |
  port | play | remove.

Nothing is uploaded or shared; jars and ports stay in the game's home.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 07:00:45 +02:00
Ludovic Marie b2e9fafbe0 launcher: prepare from Prism, MultiMC, Modrinth App, ATLauncher or GDLauncher
The official launcher crashes on some Linux systems (rdbt's Arch), and
Horizon only read its layout. Preparing now finds the pinned version in the
chosen folder (or a folder above it, so an instance folder works), then in
every launcher's default folder on Linux (Flatpak included), macOS and
Windows:

- the official launcher: versions/<v>/<v>.jar
- Prism Launcher, MultiMC: the jar their meta/net.minecraft/<v>.json names
  (a Maven name in libraries/), libraries/com/mojang/minecraft/<v>/...
- the Modrinth App: meta/versions/<v>/<v>.jar, meta/libraries
- ATLauncher, GDLauncher (data/, Next's datastore/):
  libraries/net/minecraft/client/<v>/client-<v>.jar

The client jar is checked against the lock's sha1. horizon-prepare reads the
official layout and pins the version JSON's own sha1, so files found
elsewhere are linked into that layout in the data folder
(linked-minecraft/), with the pinned version JSON from piston-meta (by its
sha1, checked) when the launcher keeps only its own format or a rewrite.
Nothing else is copied or downloaded. Every preparer, old Python ones
included, reads the linked folder as an official one.

When nothing is found, the error lists the chosen folder and every installed
launcher's, with what each lacks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 00:40:31 +02:00
Ludovic Marie bb1c292a40 Launcher benchmark reports go to the telemetry outbox with the benchmark-upload consent; tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:01:02 +02:00
Ludovic Marie c78f2d3e27 Launcher measurements: a session performance log and horizon-launcher benchmark
Each session rewrites benchmarks/launcher-<time>-session.json in the data
folder (report format v1): start to first frame, release list fetch,
install/update and prepare durations, Play to the game's first frame (the
game now logs its first frame), RSS, UI frame times, binary size.
horizon-launcher benchmark starts the launcher N times (cold, then warm),
switches pages, measures idle CPU and memory and the release list fetch,
and writes one report with means and 95% CIs. Nothing is uploaded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:01:02 +02:00
Ludovic Marie c9b3a06cf7 Launcher lockfile: horizon-friends' if-addrs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:08:07 +02:00
Ludovic Marie 5d29f31663 Play with friends: Join a friend in game and in the launcher
The game joins `friend:CODE` addresses: the network thread resolves the code
through the relay, agrees on the key, settles on a direct or relayed stream
and connects the client to the tunnel's loopback port; everything after is
an ordinary join. Errors (unknown or ended code, wrong secret, host not
answering, version mismatch, rate limits) end on the Disconnected screen in
plain words. Shown and logged addresses hide the code's secret half.

Horizon's multiplayer page gains "Join a friend" (a code field); the classic
screens keep vanilla's buttons. The launcher's Play page gains a "Join a
friend" row (paste works) that starts the game straight into the join;
`horizon-launcher play --join CODE` does the same.

tools/test_friends.py runs a relay, a host Horizon sharing a world and a
guest Horizon, over plain LAN (--baseline), the direct path and the forced
relay path: chat, movement and blocks both ways, then the ended invite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 03:08:07 +02:00
Ludovic Marie 86149f7da6 Restyle the launcher in the new design language
A top bar (the mark, Horizon, the pages as text) replaces the sidebar; a mono footer carries
the launcher's version, its update and the community links. Every page is a display title, a
short muted line, then an index list: Play's resources and folders, the versions with their
names large and their size and state in mono, the patch notes, the settings. Buttons are pills,
the main action is the one violet thing on the screen, selection is an ink pill, the console,
notices and the crash report sit on soft plates without borders. The Play page ends with the
faint wordmark, half set below the window's edge.

The sunset photograph and its veils are gone (and the `image` dependency with them): the
window is the page colour, light or dark with the system. The opening draws the wordmark
letter by letter, then the launcher rises into place. HORIZON_LAUNCHER_APPEARANCE=light|dark
overrides the system's appearance, for screenshots and tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 00:08:40 +02:00
Ludovic Marie fcbbff26a7 Give Horizon a mark, and its icon everywhere
The mark is an H whose crossbar is the horizon: a violet line edge to edge, the stems above it
and their reflection below at a third, on whole units of a 16-unit grid so it stays sharp at
16 px. docs/media/brand holds the SVG masters (square icon, macOS tile, avatars, the mark for
light and dark backgrounds), the two alternatives that were shown with it, the GitHub social
preview and the Discord banner, and a README of what goes where and how to upload what only
Ludovic's accounts can.

`cargo run -p xtask -- icons` renders the masters (rsvg-convert) and packs, in plain Rust:
packaging/icons/horizon.ico and horizon.icns, Linux's hicolor icons 16–512 and the scalable
SVG, the game's run-time window icon, the avatars and the website's favicons.
packaging/linux has .desktop entries for the launcher and the game.

On Windows, build.rs embeds the .ico as icon resource 1 in horizon-client (and its tools) and
horizon-launcher (winresource, a Windows-only build dependency); gpui loads that resource as
the launcher's window icon. The game window sets the icon itself (X11, Windows) and names
itself `horizon` on Wayland so the desktop entry matches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 00:08:40 +02:00
Ludovic Marie 82bee83dc5 Launcher: Microsoft account sign-in, keychain and the game's session
Device-code OAuth, Xbox Live, XSTS, Minecraft login, ownership and profile;
secrets in the OS keychain (oo7 on Linux, keyring on macOS/Windows), accounts.json
without tokens; Settings account rows, 'Playing as' on the Play page; CLI
'account' and MCP account tools; the game gets a 0600 session file it deletes.
Client ID is configuration (HORIZON_MSA_CLIENT_ID), empty = not configured.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:07:47 +02:00
Ludovic Marie 2edc69ef99 Launcher 0.1.5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:59:23 +02:00
Ludovic Marie a995fd3b46 Prepare with horizon-prepare: launcher, release folders, messages and docs
The launcher prepares a version in-process when it ships the preparer sources
the launcher was built with, with the version's own horizon-prepare when they
differ, and with the Python script only for versions made before it. Release
folders ship horizon-prepare and its sources; resources are reused between
versions with the same preparer. The game's messages, PLAY.txt, the docs and
release builds name horizon-prepare, and tools/prepare_native_client.py is a
wrapper of it for the development tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 16:29:33 +02:00
Ludovic Marie 8fa3eef809 Launcher 0.1.4: a new version reuses resources another installed version prepared
After installing a version (CLI install and update, the window's automatic install, MCP),
when it is not prepared and another installed version with the same Minecraft version is,
the launcher copies that version's prepared resources. Players who auto-updated to
v2026.10.13.1 no longer prepare again, and can play it without their Minecraft folder.

The prepared output depends on the Horizon version as well as Minecraft's: the manifest
records the hashes of the vanilla exporter sources it was made with, and
prepare_native_client.py writes every file and the manifest's schema. So resources are reused
only when both versions have the same pinned jars in research-lock.json, the same
prepare_native_client.py and the same exporter sources the manifest lists. They are copied
(prepare rewrites files in place, so hard links would tie the versions together), into a
partial folder renamed when complete. install and the MCP tool say when it is prepared already.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:08:06 +02:00
Ludovic Marie 1c0a47e012 Website: playhorizon.dev in the README, docs, PLAY.txt and the launcher (0.1.3)
links.json gains a website key; the launcher shows a quiet Website link under
Join Discord and lists it in the links CLI command and MCP tool.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 20:35:22 +02:00
Ludovic Marie 929fa4ec5a Launcher 0.1.2: Discord link, auto-install on by default, menu style setting, smoke tests
- links.json on main gives the Discord and sponsor links (built-in fallback offline,
  empty hides the button); 'links' command and MCP tool.
- Install new versions automatically is on unless explicitly turned off (new settings
  key, so 0.1.1's written default does not count as a choice).
- Settings: Horizon / Classic menu style, the horizon.txt menuStyle line the game reads;
  'menu-style' command and MCP tool.
- launcher workflow: install latest into a temp home, check imports (Windows, Linux client)
  and that the client starts, on all three platforms; also runs on launcher-* branches.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 17:27:31 +02:00
Ludovic Marie eb18bb42af Menus: Horizon style for the title, worlds, create, multiplayer and pause screens
One theme for the launcher and the game: crates/horizon-theme holds the
palette, type scale, spacing, radii, control sizes, motion and the bundled
fonts; launcher/src/theme.rs and the game's ui_theme both read it. ui_theme
ports the launcher's components (button with hover layer and press state,
sidebar list with the sliding selection, segmented control, switch, rows)
and draws Geist and Instrument Serif through the HUD atlas.

The title is laid out like the launcher (sidebar, page header, rows) over a
live backdrop: Horizon generates an ice-spikes landscape for a fixed seed,
orbits a camera over it with falling snow, then blurs and cold-grades the
frame (post.rs). The launcher's opening (wordmark, then the interface) plays
once per session and a click or key skips it. Menu style Horizon / Classic
lives in horizon.txt, never options.txt; Classic keeps vanilla's drawing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 15:19:14 +02:00
Ludovic Marie 2e4c4d1861 Launcher: run the game from the saves folder, never a version folder
The client ran in the version's own folder, so saves and options.txt would have
landed there and been deleted with the version, and the saves in
~/.local/share/horizon (where the release folders' horizon.sh keeps them) were
not seen. It now runs in HORIZON_HOME, else horizon in the platform's data
folder, with --resources pointing at the version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:12:16 +02:00
Ludovic Marie 6c51322171 Launcher: opening screen over the prismal background, violet accent from it, opening setting, docs frames and clip
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:27:30 +02:00
Ludovic Marie 55ecf5a59e Launcher core: update checks for Horizon and the launcher, self-update, install by tag, notes as plain text, tests
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 13:13:57 +02:00
Ludovic Marie 57257ae231 Launcher: a GPUI app that installs, prepares and starts Horizon builds
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:45:05 +02:00