Repository navigation
feat(launcher): the Hookshot Launcher, one app to build, update and play every version - #2739
Draft
hckrman101 wants to merge 7 commits into
Draft
hckrman101 wants to merge 7 commits into
hckrman101 wants to merge 7 commits into
Conversation
docs/launcher-upgrade.md is the contract between a 2.2/2.3 Builder that hands an install over to the Hookshot Launcher (3.0) and the launcher that takes it: how a launcher release is told from a Builder release (hookshot-launcher.txt, never the version name), where the launcher is installed and how it is swapped in whole, the import file dropped for the game folder, what changes in the folder's stamp, and the bridge that brings Builders older than 2.2 to the same screen in one update. tests/fixtures/launcher/ holds sample files that both sides test against, so the upgrader and the launcher read and write the same thing.
A nightly was named by git describe (v2.1.0-rc.3-251-gee29dd55), which reads as a release candidate and says nothing a player can compare. CI now names every build that is not a release nightly-<commit count>-<commit> (scripts/nightly_name.sh), a checkout the same way (nx2/version.py), and players see it as "Nightly 2029 · ee29dd5" in the menu, the start-up screen, Discord, both builders and the update question. Nightlies are ordered by their count, and every new name is newer than the old git describe names. A nightly and a release or release candidate are not ordered: a builder does not build one over the other unless that track is chosen in the Track menu, and it asks first. Updater.cs is now checked against the shared track vectors too (tests/test_update_tracks_cs.py).
launcher/Core holds the Hookshot Launcher's logic with no UI and no package: versions and channels (the Updater.cs/linux_update.py rules, nightlies named by their commit count as docs/versioning.md now says), the newest version per channel, both parts lists, per-version build tools with checked and reused parts, game folders, builds.json, the 2.x hand-over (docs/launcher-upgrade.md; an import file is deleted only once what it brought is saved), remembered settings, make_game.py's command line and @@ lines as one overall progress, update offers per channel, the 2.x command line, and saves (a port of scripts/oot_saves.py: discovery, summary, copy with a backup). tests/launcher_core checks it with the shared version vectors, the hand-over fixtures and test_oot_saves.py's cases.
launcher/App: the window (960x600, the Builder's Slate palette and Inter), home with Build/Play, the list of builds (play, set default, delete, update offer), the channel for new builds, placeholder wizard steps, the settings page and a saves page. The 2.x command line is handled (--play starts the game with no window). Avalonia 12.1.6, pinned, with lock files and nuget.org as the only source; Avalonia's build telemetry target is switched off. CI installs .NET 10 beside the Builder's 8 on Linux and Windows, so the core's test runs there, and compiles the solution with warnings as errors.
…files found, checked, built and timed The core's side of the screens: the game files on this PC (a port of the Builder's Finder) and make_game.py --check's answer, make_game's run with its whole process tree stopped on cancel, the build's clock and time left, the builds folder and an update taking its build's row, disk space and what Free up space may let go (only what make_game made in the build files folder), the saves' refusals and summaries, the Linux shortcuts and the GPU makers. Deleting a build refuses a link, the home folder and a folder that holds another build or the launcher's home. ScreenChecks.cs checks each.
The mockup's screens on the app shell: home (empty, one or several versions, updates, the hand-over's notice), the channel picker, the build wizard (the tools download while the game files are found and checked; settings with saves, DLSS and FSR 4 behind their licences and the disk space; the build with one bar), Ready and the problem screen, the launcher's settings and the Saves page. Nightlies show as "Nightly 2029 · c544828" (docs/versioning.md). A styling pass: focus rings for the keyboard and a controller, quick transitions that stop when the desktop asks for less motion, tabular digits, and paths on one line with the home folder shown as ~. Closing the window during a build asks first, a delete runs off the window's thread and not while the game runs, and --update with no --tag offers the build's newest. launcher/README.md lists the calls made and what comes next.
This was referenced Nov 12, 2026
Contributor
|
A PR for this branch: hckrman101#20 (one commit on top of fabb12c). It adds plain-language problem screens with the fix, finds a game crash in the last run's log at the next start, and a Send report button with consent asked once. Sending needs a report inbox; docs/launcher-reports.md there has the API, and until one exists the button copies the report and opens the issue form. Could magicbean.dev host the inbox? (_trash.panda's agent) |
…art, and Send report (#20) A build that stops shows make_game.py's message as it is, and a crash leaves only a log a player has to find and attach to a GitHub issue. Now: - launcher/Core/Problems.cs: make_game.py's and the game's own messages mapped to a plain headline, what to do and a fix (antivirus removed the tools, another game version, an incomplete dump, a full drive, the build out of memory, a lost GPU, no mesh shaders); a crash's signature (exception code and module+offset, no folder) for grouping. A message it doesn't know stays "The build stopped". - ProblemReport.cs: the report under the issue form's headings, as the game's Bug report writes them, the log's end with the home folder and user name taken out by the game's own rule; CrashCheck finds a crash in a build's last run (or the run before) at the launcher's next start, once per log. - ReportSender.cs: one https POST to a report inbox (docs/launcher-reports.md); none is set up yet, so Send copies the report and opens the issue form. HOOKSHOT_REPORT_URL for trying an inbox. - The report page: what happened, the fix (the driver page for the card's maker), consent asked once (Always send, Just this once, Never send) with "What gets sent"; the build's problem screen uses the plain words and has Send report; the settings have Problem reports. tests/launcher_core/ProblemChecks.cs covers the mapping, the redaction (the game's cases), the crash found once, the report, the settings and the inbox rules. Built with warnings as errors; 480 checks pass. The crash page checked on Linux with a crashed build's log. Co-authored-by: Joey Magnus <joey.magnus@cxalloy.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Windows and Linux each have their own Builder today, and a PC keeps only one built version of Hookshot at a time. Hookshot 3.0 replaces both with one launcher that builds, updates and plays several versions side by side (release, release candidate and nightly), and copies saves between them and the emulators.
Depends on: #2733 (nightly naming) and #2722 (the 2.x hand-over contract and upgrader). This branch carries their commits until they merge. Please review only the four launcher commits on top of them:
feat(launcher): the launcher's core library, with its testsfeat(launcher): the Avalonia app shellfeat(launcher): what the launcher's screens need from the core: game files found, checked, built and timedfeat(launcher): the launcher's screens, from the design mockupRelated:
What changed
Core (
launcher/Core, no UI and no packages):Updater.csandlinux_update.py, checked againsttests/test_update_tracks.py's own vectors. Nightlies use feat(release): name a nightly by its commit count on main and its commit #2733'snightly-<count>-<commit>names, and nightlies and releases are never ordered against each other. Each build follows its own channel, and an update is offered only when it is newer on that channel.<home>/toolchains/<tag>, installed from that release's parts list (hookshot-setup.txtorhookshot-linux.txt). Every part is checked for size and SHA-256 before it is unpacked. Zip entries that would land outside the folder are refused, and so are links that escape it. A part another version already has is hard-linked rather than downloaded again. Downloads are https only, also after redirects.builds.json: the list of builds, with exactly one default.docs/launcher-upgrade.md): reads the import files and, on a first start, the Builder's own settings. An import file is deleted only once what it brought has been saved.Runner.InfoandAppRunbuild them. Its@@lines become one progress bar that never goes back. Cancel kills make_game's own process tree and nothing else.scripts/oot_saves.py. It finds saves in Hookshot and the emulators, takes a backup before every copy, and refuses while a game or emulator runs.Finder), disk space, shortcuts, the GPU makers on Linux.App (
launcher/App, Avalonia 12):INotifyPropertyChanged, with one compiled-binding view per page.--launch,--update [--tag] [--fetched]and--play, so the game's own update prompt and the shortcuts keep working.Screens (from the design mockup):
~on Linux.Safety calls worth checking:
~/Games/Hookshotholds the new builds for now);scripts/ngx_sdk.pyandscripts/ffx_sdk.pyfetch.Why Avalonia, and the first NuGet dependency
One UI codebase has to run natively on Windows and Linux, and WPF and Tk each cover only one. Avalonia is MIT-licensed, mature and draws the same on both, with native Wayland. This is the repository's first NuGet dependency, so it is kept tight:
Directory.Packages.props, withpackages.lock.jsonfor everything they bring in. Those are SkiaSharp, HarfBuzzSharp, Tmds.DBus, NWayland and MicroCom, all MIT.NuGet.Configallows nuget.org only.launcher/Corehas no packages at all: the tests compile its sources directly.UseWaylandWithFallback()uses native Wayland where there is a compositor and falls back to X11 otherwise. On Windows it does nothing (Avalonia's docs).How it was tested
python -I -B tests/run_all.pyon Linux: 425 passed, 67 skipped. The only failures are the two known local ones,test_bake_portability(no zstandard here) andtest_builder_log_read_only(no Tk here).tests/test_launcher_core.py: 453 checks over temporary folders, with a UTF-8 scratch path (ゲーム Zélda 🎮). They cover:test_oot_saves.py;It also passes at each of the four commits.
dotnet build launcher/HookshotLauncher.slnx: 0 warnings, with warnings as errors.An end-to-end run on Linux, using a fake parts list (
file://) and a stand-in make_game: download, check, build, Ready, and the row inbuilds.json.CI:
source-regressions.ymlnow installs .NET 10 next to the Builder's 8 on Linux and Windows, so the core test runs in "Every test". Part 1 of each system compiles the solution withRestoreLockedMode.Not tested:
CreateHardLinkW), the PowerShell shortcuts, the reduced-motion check, process detection, and%LOCALAPPDATA%paths. CI's Windows compile with SDK 10 has not run yet.bin/hookshotdirectly. It does not go throughplay-diagnostics.shor its own systemd scope as the 2.x Linux Builder does.Planned next (not in this PR)
~/Games/Hookshot/versions/<version>and saves and settings in~/Games/Hookshot/profiles, with an importer that moves a 2.x install into it.hookshot-launcher.txtand the per-platform tools lists;docs/launcher-upgrade.md, sections 1 and 5).Open design decisions
The calls this PR makes are listed in launcher/README.md, "Open design decisions". Each one can be changed:
Screenshots
These are headless renders (Avalonia.Headless and Skia, 960x600 at 1x) with fake builds, saves and game files, and a fake home folder shown as
~. Headless rendering draws blurred shadows poorly: the glows under Play and the main buttons show as dark corners here, though they are soft glows in the app.Before and after the styling pass
Every other screen