Skip to content

feat(builder): the launcher, one window to play, build and update (Windows and Linux) - #2373

Open
madebyjordan wants to merge 17 commits into
NotRexed:mainfrom
madebyjordan:builder/launcher
Open

madebyjordan wants to merge 17 commits into
NotRexed:mainfrom
madebyjordan:builder/launcher

Conversation

@madebyjordan

@madebyjordan madebyjordan commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Players have to juggle three things today: the builder window, the game folder's launcher, and the update dialogs. Nothing tells them which version they have or what an update will do (it rebuilds the game, ~30 minutes). This replaces all of that with one launcher window, on Windows and Linux, drawn from the "Hookshot v2" design.

Status: planned for 3.0.0, per review. Up to date with main as of 55dd641 (#2367, #2447, #2449, #2141, #2160 merged in). Waiting for hands-on testing. The open scope questions for @NotRexed are at the end.

What changed

hookshot.exe in the game folder (and the AppImage) opens the launcher. It shows the wordmark, then Play (Build while there is no game yet, which opens the build). Update stands beside Play while a newer release is out. Under Play are the version, then Credits / GitHub / Discord. Menu opens a side panel with three tabs. The window is the page colour behind the wordmark. No picture of the game ships, and original art will come in its own PR.

  • Build: the game file and the install folder, then Build Game.
    • While it builds, the button becomes the build's bar, with its step and the time left ("Compile 31 of 58 · about 18 minutes left").
    • If the build stops, a red Build stopped button says why and retries.
    • Language, Workers and Memory sit under it.
  • Releases: every published release, newest first.
    • The one in the folder is ticked. The five newest stay in sight, and older ones fold into one row.
    • Pressing another release asks first. It then installs that release's builder beside the one in use (setup.c --engine, no window) and rebuilds the game with that release's own make_game.py.
    • With pre-releases on, release candidates appear among the releases in semver order (rc.10 after rc.9).
  • Settings:
  • Foot of the panel:
    • Check for updates, or Update Available when a newer release is out.
    • During an update, its bar and Cancel. If it fails, Update stopped with the reason, Show details, Copy error log and Dismiss.
    • Before anything is pressed, it says that installing a version rebuilds the game from the player's files, and about how long that takes on their PC (from build_times.json).

Linux (builder/linux/hookshot_launcher.py): the same screen on one Tk canvas, painted with numpy from the XAML's own path data. The art test checks the paths are byte-for-byte the same. The old Tk pages remain, for a Python without numpy and for the builder's own update.

Smaller changes:

  • Shaders step: the last step, preparing the shaders, shows its own percentage and time left. It has the bar's last 8% but can take as long as the compile.
  • Updater.ReleaseList: every release with a builder to download. Tags that are not versions (nightly, ultrawide, low-vram) are skipped.
  • setup.c --engine --tag <tag>: installs a release beside the one in use and reports progress on stdout. It hard-links the parts the two versions share (copying where links can't be made), in a portable home too.
  • Clean-up after a build: it also keeps the builder of the release the game folder holds, so switching back and forth downloads nothing.
  • docs/versioning.md: the update-track section describes the switch.

For reviewers

Where to start:

  • builder/HookshotBuilder/Launcher.cs: the launcher's state and its update flow (Bring → StartWork → PullEngine → Runner).
  • LaunchList.cs and LaunchWork.cs: the pure rules (which versions show, what the bar and the words say). The Linux side mirrors them in launch_list.py / launch_work.py, and both have tests.
  • MainWindow.xaml: the screen itself.
  • setup.c: the --engine mode (g_engine).

Already settled in review (no need to raise again):

  • Game art: the background picture is gone (launcher.jpg, the Linux picture.png, make_picture.py). The only new asset is the Marcellus font, with its OFL licence.
  • Engine mode security: https parts with SHA-256 checked before unpacking; list names through list_name_ok; --tag limited to [0-9A-Za-z._-]; never over the version in current.txt; staged in <tag>.part. The 127.0.0.1 exception applies only to a list that itself came from localhost (the rig).
  • Portable homes (feat(builder): add relocatable Windows portable installations #2367): the engine setup and make_game.py get the home through BuilderHome.PassTo. Storage.Base is BuilderHome.Base, so the clean-up after a build stays in the portable home.
  • fix(setup): recover incomplete self-contained Builder runtimes #2449's runtime check: the engine mode goes through the same work() path, so builder_ready runs before @@installed.
  • Hard-linked parts: nothing writes a part's file in place. Compilers, Python, headers and libraries are only read. Python's bytecode caches are written to a new file and renamed over. emsdk is used for its clang only. This rule is written beside link_tree.
  • CI: the launcher's checks run on the first Windows shard only, like builder_workers.
  • Back after a stopped or cancelled update (After an update, enough times the game won't build #2129/fix(builder): the first page offers the update again after one that stopped (#2129) #2141): one function, BackFromBuild, returns to the launcher, which offers the update again.

Checks to run:

  • dotnet run -c Release in tests/builder_launcher: list, work and settings rules.
  • python -I -B tests/run_all.py builder launcher portable setup update.
  • On Linux: tests/test_linux_launcher_{lists,work,art}.py and tests/test_linux_update*.py.
  • The full flow on Windows: tests/builder_launcher/rig/run.ps1. It uses fake releases from a local server, a scratch install (HOOKSHOT_SETUP_BASE) and a stand-in make_game.py. The real install is untouched.
  • Screens without a display: HOOKSHOT_UI_SHOT=<file.png> draws the window to a PNG and exits. HOOKSHOT_UI_PREVIEW picks the state: tab=build|releases|settings, older, pick=…,go, or found,build / found,stopped / update,menu for the build pages. HOOKSHOT_LATEST_TAG=v2.2.0,v2.1.0 fakes GitHub's answer. The Linux launcher takes HOOKSHOT_UI_PREVIEW=menu,tab=…,building|stopped|updating|update-stopped.

How it was tested

  • tests/builder_launcher (new, in CI on Windows): the version lists, release-candidate order, and the progress and rebuild wording.
  • tests/builder_launcher/rig/run.ps1, run locally against the real Windows build. It covers:
    • install beside the builder in use, and switch back;
    • cancel, stop and retry;
    • a builder that cannot be downloaded;
    • a build from the Build tab, and --update;
    • that the real install is untouched.
  • tests/test_linux_launcher_{lists,work,art}.py (new): the Linux list rules, the progress and rebuild wording, the launcher's state handling (cancel during a download, a second exit, a stuck pipe), and the drawing.
  • tests/test_builder_details_hitbox.py hit-tests the Show details / Cancel row: the words take clicks, and the blank beside them does not.
  • Updated tests from main:
    • test_portable_setup_transaction.py also runs the engine mode's part reuse: linked, in a standard and a portable home, with the version in use left whole.
    • test_builder_update_back.py checks the launcher's way back.
    • test_linux_update_cancel.py compiles the Linux builder's message_in beside pump.
  • After the merge with main:
    • Windows tests/run_all.py: 387 passed. Five failed (test_lift_cache, test_nup_paths, test_vram_budget_row, test_fpx_sticky, test_bake_portability), and the same five fail on a clean checkout of main on the same machine.
    • On Ubuntu 22.04: the Linux checks above, test_linux_update*, test_release_scripts and the launcher's screens, in each state.
  • On Windows, a real update (v1.0.0 to v2.1.0 from GitHub, real game files, about 30 minutes) went through this launcher before the hardening commits.
  • Not tried: a real game build, or a real downloaded release, through the new screens on Linux.

Open for @NotRexed

  • Older releases: the Releases tab can install any older release that has a builder to download, as well as newer ones. Keep that, or newer only?
  • Nightly: it is no longer a choice in the launcher. The nightly's own setup or AppImage installs it, and it stays Nightly until Enable pre-releases is turned off. If you want it selectable, I'll add it as a third choice.

Screenshots

960 × 640. The Windows ones are drawn off-screen from this branch, the Linux ones taken in Ubuntu 22.04. The paths shown are placeholders.

Windows

Launcher
Launcher
Update Available
Update Available
Building
Building
Build stopped
Build stopped
Updating
Updating
Settings
Settings
Linux
Update Available
Update Available
Updating
Updating
Building
Building
Update stopped
Update stopped
Settings
Settings
Build
Build

Linux draws the same screens, with Inter in place of Segoe UI.

🤖 Generated with Claude Code

madebyjordan and others added 6 commits November 11, 2026 16:22
…ndows and Linux)

The game folder's hookshot.exe (and the AppImage) now opens a launcher drawn
from the Paper design "Hookshot v2": the picture, the wordmark, Play, and a
side panel with three tabs.

- Build: the game file and the install folder, Build Game, which becomes the
  build's bar with its step and time left, or "Build stopped" with the reason;
  the language, workers and memory options under it.
- Releases: every published release, newest first, the one in the folder
  ticked; pressing another installs it beside the builder in use (setup.c
  --engine) and rebuilds the game with that release's own make_game.py,
  after asking. Older releases fold into one row.
- Settings: Prepare Shaders, DLSS, FSR 4 and Enable pre-releases (the
  Release / Release candidate update track).
- The panel's foot: Check for updates, Update Available, the update's bar
  with Cancel, or "Update stopped". Installing a version says it rebuilds the
  game and about how long that takes on this PC (build_times.json).

Release candidates are ordered as semver orders them (rc.2 after rc.1, rc.10
after rc.9), so the next candidate is an update, not a switch back.

The Linux launcher (builder/linux/hookshot_launcher.py) is the same screen
on one Tk canvas, painted with numpy from the XAML's own path data.

Tests: tests/builder_launcher (the version lists and the progress words, and
a whole-flow rig with fake releases and a stand-in make_game.py),
tests/test_linux_launcher_*.py, and the details hit-box test moved to the
new Show details / Cancel row.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tes in order

- Cancel during an update's download stops it, and nothing from the
  cancelled work starts a build afterwards; a program that will not stop is
  killed 8 s after SIGTERM; a work whose program is gone but whose pipe
  stays open ends after 10 s; its end runs once.
- Tk is only touched from the Tk thread; an exception in a message no
  longer stops the builder's message loop (pump), and the launcher's own
  messages are guarded the same way.
- Timers are cancelled when the window closes; an @@eta of inf or nan, a
  damaged picture.png, a version file that is not UTF-8 and a settings file
  that cannot be written no longer break the screen.
- Long errors are cut to a few lines with an ellipsis instead of running
  over the buttons; one panel image instead of one per window height.
- Release candidates are ordered as semver orders them (rc.10 after rc.9),
  as on Windows; a greyed-out release says why when the folder cannot be
  rebuilt at all.
- The game version check reads make_game.py's output as UTF-8 and says
  when it cannot run, instead of "Checking the game version..." for good.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d cannot get stuck

- Any close during a build or an update (Alt+F4, the taskbar, its own
  button) asks first, and the hidden programs stop with the window; the
  builder's own exits (Play, Repair, the updater) go through Leave() and ask
  nothing. A Cancel pressed before the build's program started is kept.
- A release with only the zip (no setup list) installs through the
  builder's own update instead of failing in the setup's engine mode.
- Half-installed (.part) and half-removed (.gone) versions, and versions
  with no installed.txt, are not taken for installed builders; the clean-up
  after an update counts as busy and never deletes a .part folder.
- Play, the folder link and Repair say when the game or setup cannot be
  started (an antivirus that took it) instead of closing the app; an
  unexpected error is written to launcher-error.txt and shown, and the
  window stays.
- "Update stopped" can be dismissed; long reasons are cut to a few lines
  with the whole text as a tooltip; the setup's engine mode sends its
  reasons on one line; "Copied" resets for the next failure.
- Installing a release keeps the saved DLSS and FSR 4 choices, and passes
  an older release's make_game.py only the options it knows.
- The switch-back question no longer says the builder goes back on the
  path where it does not; greyed-out rows say why; Check for updates gives
  up at once when there is no network at all.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The last step, preparing the shaders, has 8% of the bar (92-100%), but on
some GPUs it takes as long as the compile: the bar sat at 94% for half an
hour and looked stuck. Under the bar (an update's and the Build tab's) the
step now says its own percentage and a time left from how fast it has gone,
and the build's usual time, which has no shaders in it, is not used for it.
Windows and Linux.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… of releases that are out too

The Releases tab listed a pre-release only while it was ahead of the latest
release. Hookshot's release candidates come before their release
(v2.1.0-rc.1 to rc.3, then v2.1.0), so with pre-releases on the list showed
nothing more. Now the pre-releases among the releases in sight are listed in
version order (below their release), and all of them once the older releases
are unfolded. Windows and Linux.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@nullifyshot

Copy link
Copy Markdown
Contributor

A suggestion for the new Settings tab: an option for #2206's RX_VK_PCACHE_LOAD=0.

Why. #2206 (merged) lets the game skip loading and saving mkw_vk_pipelines.bin. That file can reach 2.5 GB, and the driver holds it in memory for the whole session. Skipping it saves about 3 GB of memory and commit, which matters for players near the commit limit, like the RTX 3060 player in #2206 who crashed at 30.7 GB. Right now it's only reachable by setting an environment variable or editing hookshot_env.txt, which most players won't do.

It fits what's already here. StartGame in MainWindow.xaml.cs already starts the game with a variable of its own (si.Environment["HOOKSHOT_NO_UPDATE_CHECK"] = "1"). The Linux launcher's play() passes its own environment too. So it would be:

Two things to decide:

  • Starting the game without the launcher. Starting bin\hookshot.exe directly, or from a shortcut, wouldn't get a variable set at Play. If it should apply to every start, the launcher could instead add or remove an RX_VK_PCACHE_LOAD=0 line in the game folder's hookshot_env.txt, which the game reads at every start (it takes RX_ names). The Play variable is simpler and matches what StartGame already does.
  • Don't delete the file when it's turned on. Turning it off again should pick up the existing cache. It might be worth showing the file's size next to the toggle, so players can see what it saves.

@Arkex-ai

Copy link
Copy Markdown
Contributor

I'm investigating #2352, claimed here: the game-folder wrapper can select an older installed Builder over the recorded AppImage, and the legacy default update mode can offer older tools even offline. Both are reproduced with authored fixtures on main.

This overlaps linux_update.py here, although hookshot-folder is outside this PR's file list. My scope would protect the wrapper/default action while preserving your explicit release selection and downgrade confirmation. Are you already addressing those two paths, or is there a preferred integration point? I haven't edited them yet.

madebyjordan and others added 2 commits November 11, 2026 16:59
…CACHE_LOAD=0

Suggested in review. NotRexed#2206 lets the game skip its pipeline cache file
(mkw_vk_pipelines.bin, up to 2.5 GB, held by the driver for the whole
session: about 3 GB less memory), but only through an environment
variable or by editing hookshot_env.txt.

The switch writes or removes that one line in the game folder's
hookshot_env.txt, so it holds however the game is started (Play, a
shortcut, bin\hookshot.exe). The rest of the player's file and its line
ends stay as they are, the file goes when nothing is left in it, and the
cache file is never deleted: switched off again, the game uses it as
before. A note under the switch gives the file's size here. Off by
default, as in NotRexed#2206. Windows (GameEnv.cs) and Linux (game_env.py), with
tests of both.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… an older one

The game's own update offer starts the launcher with --update <folder> and
no --tag: the target is then the update track's, which can be older than
the folder's version (a newer release candidate's or nightly's, NotRexed#2352). It
was built with no question; now it asks first, as a row of an older
release does. Raised on the PR by Arkex-ai.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@madebyjordan

Copy link
Copy Markdown
Contributor Author

@nullifyshot Thanks, added in 96eaeef: Lower memory use in Settings, off by default. It writes RX_VK_PCACHE_LOAD=0 as one line of the game folder's hookshot_env.txt (so it holds however the game is started) and removes only that line when switched off; the cache file is never deleted, and the note under the switch shows its size. Windows (GameEnv.cs) and Linux (game_env.py), with tests of both.

@madebyjordan

Copy link
Copy Markdown
Contributor Author

@Arkex-ai Thanks for checking. This PR doesn't touch hookshot-folder, and its only change to linux_update.py is a ValueError guard in game_version, so both of your paths are yours. The PR's new launcher (hookshot_launcher.py) shows Update Available only for a newer release and always asks before an older one, in semver order (launch_list.newer, which also orders rc.N). Its --update <folder> start with no --tag (the game's own update offer) now asks before an older target too: a14058e. For choose_mode, fixing it in linux_update.py as you suggest, compared by release_key, sounds right, and I'll rebase on whichever lands first.

@hckrman101

Copy link
Copy Markdown
Collaborator

Going to do very deep testing on this, plus will have a few design feedback. Few to start:

  • Currently it only shows 'Play' (at least in screenshots). When nothing is built, it should maybe have 'Build' as the CTA?
    • When clicking Build, instead of a sidebar menu, let's make this a full-screen modal
    • The Build modal should be broken down into 3 steps: NUP/ROM, options (DLSS/FSR/memory/CPU/etc), and a build progress
    • Also this is minor but maybe the Play/Build CTA could be redesign a bit, personally I'm not a super big fan of this slanted styled
  • When there's an update available, it should have 2 buttons seen (make sure to include screenshots for this case). 'Play' launches last version, 'Update' basically is like build
  • Also, when the user clicks 'Build' or 'Update', we should start downloading the builder package while they select their ROM. In the future, I see a world where the options list actually comes from the builder (so it's dynamic), so it's best to download the builder early

@madebyjordan

Copy link
Copy Markdown
Contributor Author

Thanks @hckrman101, deep testing is very welcome.

On the design side (a full-screen build window, the three steps, the style of the Play/Build button): I'll take those as suggestions. The design here isn't final. I have bigger plans for it that I kept out of this release so the launcher could be ready sooner, and I'll weigh your ideas in that.

The other points are fair, and I'm addressing them in this PR:

  • Build when there's no game: the main button will say Build and open the build, instead of a dimmed Play.
  • Play + Update when an update is out: two buttons side by side. Play starts the version in the folder, Update brings the new one (it asks first, as it rebuilds the game). I'll post screenshots of that case here.
  • The builder downloaded early: an update already starts downloading that release's builder the moment it's confirmed (the setup's --engine mode, hidden), before the rebuild, and a first build uses the builder already installed, so nothing waits on a download after the game file is picked. When the options come from the builder later, they can be read from that download, so the order already fits.

…en a newer release is out

From hckrman101's review. With no game in the folder the main button says
Build and opens the build's choices (the menu's Build tab), instead of a
faint Play. While a newer release is out, an Update button stands beside
Play: Play plays the version in the folder, Update brings the new one,
asked first as Update Available does. It is Play's torn strip in the
panel's dark with the accent's words, at Play's right so the menu's panel
never covers it. Windows and Linux.

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@madebyjordan

Copy link
Copy Markdown
Contributor Author

@hckrman101 The two cases are in (4ee39e2), on Windows and Linux:

  • No game yet: the main button says Build and opens the build (the menu's Build tab), instead of a faint Play.
  • A newer release is out: Update stands beside Play. Play plays the version in the folder; Update brings the new one, asked first (it rebuilds the game, and says about how long that takes on the PC). It sits at Play's right so the menu's panel never covers it.
Windows Linux
Update out Windows: Play and Update Linux: Play and Update
No game yet Windows: Build Linux: Build

@hckrman101 hckrman101 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this, it's a big step up from three windows, and the two cases I asked for (Build with no game, Play + Update side by side) are in with screenshots on both platforms. I'm still doing the deeper hands-on testing I mentioned, so this is a first pass on the code, not a sign-off.

The one thing that has to be sorted before anything else: the background picture. builder/HookshotBuilder/Images/launcher.jpg is a picture of the game (the XAML calls it that: "the picture of the game, dim behind everything"), and builder/linux/launcher/picture.png is the same image dimmed by make_picture.py. CONTRIBUTING.md is fine with screenshots of the game in issues and PRs, but this goes further: it is committed to the repo and embedded in every HookshotBuilder.exe (<Resource>) and copied into every AppImage (cp -a builder/linux). That's redistributing the game's art with every release, and the PR doesn't say where the image comes from. That's a project and legal call for @NotRexed, not something to slip in with a launcher; until he's OK'd it, I'd drop both files and use something we own (a flat colour or gradient, or original art), keeping make_picture.py only if there's an image that can ship.

Updater / setup security looks right to me:

  • setup.c --engine keeps the existing checks: parts must be https with a 64-hex sha256 that's verified before unpacking, list names go through list_name_ok, the --tag is restricted to [0-9A-Za-z._-], and a list whose tag differs from the one asked for stops the install. Unpacking still goes into <tag>.part and is renamed when complete, and it refuses to install over the version in current.txt.
  • The http://127.0.0.1: exception in part_url_ok only applies when HOOKSHOT_SETUP_LIST itself points at localhost, so a list downloaded from GitHub can't use it. Fine for the rig.
  • link_tree hard-links the shared parts (or copies them), doesn't follow reparse points, and removes the half-made folder and downloads it again if it fails. Good.
  • On Linux the launcher uses the existing linux_update.install path (https only, redirects too, sha256 per part) and checks manifest.tag == tag before execve.
  • ReleaseList drops tags that aren't versions, and the tag goes into the API URL with Uri.EscapeDataString and to the setup through ArgumentList. No quoting problems that I could see.

Process launching: Windows StartGame is unchanged apart from the error box (working directory is the folder, HOOKSHOT_NO_UPDATE_CHECK=1). Linux Play still goes through hookshot_builder.play(), so the socketpair stderr from #2279 is kept and the game still writes hookshot_log.txt. The Lower memory switch only edits RX_VK_PCACHE_LOAD lines in hookshot_env.txt, through a .new file and a replace, and never touches the cache file. Thanks for doing it that way (@nullifyshot's suggestion).

Saves / install folders: I found nothing that moves or deletes saves or the game folder. Storage.Freeable now skips <tag>.part, which is needed now that an engine install can run next to a build. One thing to be aware of: after switching to an older release or a pre-release, FreeAfterBuild deletes that version's builder again (it isn't current.txt), so switching back and forth downloads it again each time. That's probably intended, just confirming.

Things for @NotRexed (product, not code):

  • The Track menu goes away. A saved Nightly stays Nightly, but nobody can choose Nightly any more from either launcher, while docs/versioning.md still lists it as a track. Either keep a way to opt in or say in the docs that Nightly can't be chosen in the UI any more.
  • The scope (a new main window on both platforms, the Releases tab that can install any older release) is his decision.

Overlaps: this conflicts textually with #2367 (portable installs: Storage.Base, setup.c, Updater.cs, Runner.cs, App.xaml.cs, the asset-digest test), #2201 (MainWindow.xaml(.cs), Updater.cs), #2141 (MainWindow.xaml.cs) and #2150/#2159/#2160 (hookshot_builder.py, linux_update.py). The one that's more than a rebase is #2367: whichever lands second has to make sure the launcher's hidden --engine setup and FreeAfterBuild use the portable home rather than %LOCALAPPDATA%. #2352's two paths are Arkex-ai's, as agreed above.

CI: the new tests run (Windows builder_launcher: 37 + 33 + 13 checks passed; test_linux_launcher_* are picked up by run_all, and _art declares numpy). The two red jobs aren't from this PR: Windows part 2/2 fails test_native_astc_texture_count.py (a scanf deprecation in its fixture under clang -Werror), and appimage timed out installing system packages. Re-run the AppImage job once the runner's network is back, since appimage.sh changed here.

Small things inline.

Comment thread builder/linux/launcher/make_picture.py Resolved
{
StartGame(launchFolder);
Application.Current.Shutdown();
chosenTrack = PreTick.IsChecked == true ? (Track == Updater.Nightly ? Updater.Nightly : Updater.Candidate) : Updater.Release;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the Track menu gone, this is the only switch, and it can't select Nightly (only keep a saved one). Turning it off and on again moves a Nightly player to Release candidate for good. Fine if that's the intent, but docs/versioning.md's table still presents Nightly as a track you choose.

Comment thread builder/HookshotBuilder/Storage.cs Resolved
…ys, and review nits

From hckrman101's review:

- The launcher's background was a screenshot of the game (launcher.jpg, and
  the Linux launcher's dimmed picture.png made from it by make_picture.py),
  built into every HookshotBuilder.exe and copied into every AppImage: the
  game's art, shipped with each release. All three are gone; the window is
  the page's colour behind the wordmark, until original art takes its place.
- The clean-up after a build or an update kept only the builder in use
  (current.txt) and the one running: after switching to an older release,
  its builder was deleted, and switching again downloaded it again. The
  builder of the release the game folder holds stays too.
- The launcher's .NET check ran on both Windows shards: now on the first only,
  as the builder's workers check beside it.
- docs/versioning.md: Nightly is not chosen in the launcher (its own setup or
  AppImage installs it, and it stays until Enable pre-releases is turned off).

Assisted-by: Claude Code
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@madebyjordan

Copy link
Copy Markdown
Contributor Author

Thanks @hckrman101, that's a thorough first pass. Fixed in d1a4155:

  • The background picture: you're right, it's the game's art, and it shouldn't ship with every release. launcher.jpg, the Linux picture.png and make_picture.py are gone; the window is the page's colour behind the wordmark for now. I'll replace it with original art I'm making from scratch (nothing traced or taken from the game), and I'll say where it comes from and its licence in the PR that adds it.
  • Switching builders: not intended. The clean-up after a build now also keeps the builder of the release the game folder holds, so switching back and forth doesn't download it again. Other old builders still go.
  • CI: the launcher check now runs on the first Windows shard only, like builder_workers.
  • Nightly: docs/versioning.md now says it isn't chosen in the launcher: the nightly's own setup or AppImage installs it, and it stays Nightly until Enable pre-releases is turned off. @NotRexed, if you'd rather keep Nightly selectable, I'll add it as a third choice.

#2367: agreed. If this lands second, I'll make sure the hidden --engine setup and the clean-up use the portable home.

CI reds: agreed, neither is from this PR. Could someone re-run the AppImage job once the runner's network is back, since appimage.sh changed here?

Looking forward to the hands-on testing.

@Arkex-ai

Copy link
Copy Markdown
Contributor

#2449 is ready for review: one runtime-readiness check before installed reuse, staging publication and fast launch. Hosted CI passes, and an actual disposable installation recovered a missing coreclr.dll from the pinned official release and ran the managed CLI successfully.

For your --engine route, keep that check before reporting @@installed, while preserving your current-version/pruning policy. I checked the history: the parts-based installer already used directory-based self-contained publishing when it was introduced. The required-file contract will need an intentional update if the launcher moves to Avalonia or single-file publishing. Whichever lands second should retain both contracts.

@madebyjordan

Copy link
Copy Markdown
Contributor Author

Thanks for the heads-up @Arkex-ai. The --engine route goes through the same work() path, so it gets builder_ready before @@installed with no extra change here, and I'll keep both contracts whichever of the two lands second.

One thing to know: I have a small follow-up planned that lets a later release rename the builder (the setup records the list's run path, and the game folder's launcher opens that). I'll base it on #2449 once it's in, and derive the .dll / .deps.json / .runtimeconfig.json names from the exe's own name rather than HookshotBuilder, so the check holds for a renamed builder too. Noted on Avalonia / single-file: that contract will change with the standalone launcher anyway.

hckrman101 pushed a commit that referenced this pull request Nov 11, 2026
## Summary

On Linux, the game-folder launcher could select an older installed
Builder and offer to rebuild a newer game backwards, even offline. This
keeps the recorded Builder associated with its game and prevents an
implicit downgrade, while preserving confirmed version changes.

Fixes #2352.
[Claim](https://discord.com/channels/1568810484818378804/1569019786766188665/1570119562655961089).
Scope coordinated with #2373; this does not redesign its launcher.

## Technical changes

- Prefer the game's recorded Builder; retain installed-Builder and
direct-game fallbacks when it is unavailable. No candidate execution or
extra interpreter needed to choose one.
- Default to Play for a known older update target. Confirm older
rebuilds at the common update entry, including DLSS changes and direct
launcher updates.
- Carry accepted downgrade consent once across an exact-version handoff,
bound to the unchanged game version. A legacy fetched flag alone grants
no consent.
- Require the download list and fetched tools to match the selected
target before rebuilding; report release-list lag instead of silently
using another version.

## Validation

Current tested head **`04ea74bb`**, rebased onto main **`34f05785`**.
The worklist conflict preserves main’s damaged-file row and places this
package’s row in score order. Focused version-admission tests pass again
on Windows and Linux; Windows skips the POSIX executable-permission
case, which passes on Linux. No fresh game rebuild or native Linux GUI
run is claimed. Fresh [hosted
checks](https://github.com/NotRexed/Hookshot/pull/2406/checks) are
separate exact-head evidence.

<details>
<summary>Earlier validation evidence and limits</summary>

Tested **`2dcca138`** on main **`7759a6f3`**:

- **14 focused methods pass on Linux**; Windows passes the 13
handler/version methods and skips the POSIX-wrapper method. Tests
execute the actual shell wrapper, version selector and complete
production Tk handlers with authored UI/thread/download boundaries.
- Original main fails the regression. **11 broken variants rejected on
Linux / 10 on Windows**, including lost or stale consent, wrong fetched
tools, mismatched release lists and bypassed confirmation.
- Full Linux: **374 passed / 0 failed / 23 skipped**, versus exact
base373/0/23.
- Full Windows: **364 passed / 2 failed / 31 skipped**, versus exact
base363/2/31. The same recorder negative-control and NUP link-privilege
failures remain; no new failure.
- Existing updater30 and launch6 tests pass; shell syntax passes. The
screenshot is the actual Tk launcher on Windows using authored version
metadata.

No game files, real downloads or builds were used. Native Linux GUI
rendering, a real AppImage upgrade and gameplay remain untested. Unknown
hashes/differing nightlies retain the existing ordering policy; version
labels are not commit-ancestry proof. [Behavior and
limits](docs/linux-builder-version-selection.md).

![Tk launcher preview on Windows with authored game v2.2.0 and Builder
v2.1.0
metadata](https://raw.githubusercontent.com/Arkex-ai/Hookshot/2dcca13886cb8f4dd22126fba45c0090d1300fcb/docs/images/linux-builder-newer-game.png)

</details>

<!-- pr-ci-status -->
| Current-head workflow | Result |
|---|---|
| [Game-free source
regressions](https://github.com/NotRexed/Hookshot/actions/runs/43090325870)
| Passed |
| [Linux
AppImage](https://github.com/NotRexed/Hookshot/actions/runs/43090325735)
| Passed |
| [PR
title](https://github.com/NotRexed/Hookshot/actions/runs/43090324841) |
Passed |
| [Windows
builder](https://github.com/NotRexed/Hookshot/actions/runs/43090325627)
| Passed |
<!-- /pr-ci-status -->

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It now conflicts with main in MainWindow.xaml.cs, builder/linux/hookshot_builder.py, builder/linux/linux_update.py and tests/test_updater_asset_digest.py (#2141, #2150, #2159).

The art blocker is fixed: only the Marcellus font is new, with its OFL licence. I spot-checked the risky paths and they hold: --engine still requires https parts with sha256, the localhost exception only applies when the list itself came from localhost, and RX_VK_PCACHE_LOAD is rewritten through .new plus rename.

Minor: link_tree hard-links shared parts between versions, so nothing may ever write those files in place.

Merge order: I'd take #2367 first (clean), then this rebased with BuilderHome.PassTo on the hidden --engine setup and the portable home in FreeAfterBuild. The scope (Releases tab installing any older release, Nightly no longer selectable) is NotRexed's call.

hckrman101 pushed a commit that referenced this pull request Nov 11, 2026
## Summary

Windows Builder could not stay portable on an external SSD: setup, app
storage and the game updater selected different locations, while saved
absolute paths broke after a move. This adds opt-in `--portable` setup
with one marked, relocatable Builder home. Standard installs retain
their existing location.

Fixes #2258.
[Claim](https://discord.com/channels/1568810484818378804/1569019786766188665/1570073925902270599).

## Technical changes

- Share the home across setup, WPF settings/work/SDKs, child builds and
self-updates, using process-scoped context.
- Keep same-volume paths and the game's Builder association relative.
Invalid portable associations fail instead of selecting AppData.
- Stage setup/ZIP repair publication, retain installed parts until
success, and reject truncated native paths before file operations.
- Add source-owned native, .NET, archive and published-app regressions.
The startup fixture now compares directory identity, including DOS
aliases, and executes the shipped self-contained Release package.

## Validation

Current tested head **`f75c156f`**, rebased onto main **`355453de`**. A
successful standard rebuild now removes the former portable association
before publishing standard Builder metadata. The transition regression
rejects the previous production source; it also checks repeated standard
builds and denied removal preserving old metadata. All eight focused
Windows fixtures pass, including native setup/transactions,
archive/relocation and actual published self-contained WPF CLI startup.
ZIP fallback repair limits and portable setup discovery are documented.
Physical removable-drive relocation and a complete public-release/game
repair remain untested. Fresh [hosted
checks](https://github.com/NotRexed/Hookshot/pull/2367/checks) are
separate exact-head evidence.

<details>
<summary>Earlier validation evidence and limits</summary>

Earlier tested head **`6affe74b`**, rebased onto main **`7759a6f3`**:

- All eight focused portable checks pass on Windows/Python 3.12, plus
the worker fixture's **104 assertions**.
- Published Release app: **seven startup/child/relocation/error
scenarios**, including an owned DOS alias, with installed-runtime
discovery isolated. Restoring the old textual path comparison reproduces
the hosted false failure; the rejected-home startup mutation is still
caught.
- Fresh hosted checks will run on this head. Earlier hosted failures
include the missing `env_on` fixtures now fixed on main by #2380; the
old WPF failure was a short/long path assertion, not selection of a
different home.

At `64856415` on `b744b1ec`: Windows **359 passed / 5 failed / 31
skipped**; Linux **362 passed / 2 failed / 31 skipped**. Both hit the
two missing-`env_on` fixtures; Windows also retained the documented
recorder negative control, NUP link-privilege and compute-guard fixture
failures. These are earlier-head results, not current-head green checks.

At `9e3aa287`: complete native setup/host-update compilation and
self-contained WPF publish passed; ten broken variants were rejected.
Full Linux was 351/0/31; Windows349/3/30 had the same failure identities
as its exact main baseline.



Tests use authored packages/files, with no game data, downloaded SDKs or
built game. Actual removable-media/drive-letter changes, live release
download/repair and gameplay remain untested. #2373's new launcher
shares setup/storage paths and needs the portable contract preserved
whichever lands second.

![Portable folders in an isolated preview, captured at 9e3aa28 and
cropped without changing
pixels](https://raw.githubusercontent.com/Arkex-ai/Hookshot/9e3aa287381926d8d80e007cf7565af7cdd18c47/docs/images/windows-portable-folders.png)

</details>

<!-- pr-ci-status -->
| Current-head workflow | Result |
|---|---|
| [Game-free source
regressions](https://github.com/NotRexed/Hookshot/actions/runs/43095610848)
| Queued |
| [Linux
AppImage](https://github.com/NotRexed/Hookshot/actions/runs/43095610874)
| Queued |
| [PR
title](https://github.com/NotRexed/Hookshot/actions/runs/43095609270) |
Queued |
| [Vulkan back end with NVIDIA
DLSS](https://github.com/NotRexed/Hookshot/actions/runs/43095610937) |
Queued |
| [Windows
builder](https://github.com/NotRexed/Hookshot/actions/runs/43095610860)
| Queued |
<!-- /pr-ci-status -->
hckrman101 pushed a commit that referenced this pull request Nov 11, 2026
## Summary

Starting a Windows build could stop Python or compiler processes
belonging to another work folder. Cleanup now requires positive
work-folder or output ownership and preserves processes whose ownership
cannot be established. Fixes #2142.

## Technical changes

- Parse complete Windows arguments and compare normalized absolute paths
at component boundaries. Unrelated orphans, sibling-prefix outputs and
include/input paths no longer qualify.
- Protect the full ancestor chain, including non-Python launchers.
Recognize this installation's default work and a proven make_game
parent's matching build.py child; preserve unknown orphans and unrelated
descendants.
- Stop eligible processes individually, without `taskkill /T`; report
successful stops only and retain best-effort cleanup.

## Validation

Current tested head **`40321e87`**, rebased onto main **`355453de`**.
Default-work cleanup now recognizes previous packaged Builder versions
under the same home, including their proven build child. Other homes,
sibling prefixes, unexpected layouts and custom-work mismatches remain
excluded. Eleven tests pass on Windows and Linux, including 500
quoted-argument controls; the previous production source fails the two
new cross-version/query-diagnostic regressions on both systems. All
process queries and termination calls are intercepted. No real process
was stopped. Fresh [hosted
checks](https://github.com/NotRexed/Hookshot/pull/2250/checks) are
separate exact-head evidence.

<details>
<summary>Earlier validation evidence and limits</summary>

Earlier tested head **`e14e3a8c`**, rebased onto main **`6df80a8d`**.
The selector, test and documentation patch is unchanged by range-diff;
the worklist conflict preserves both main's existing row and this PR's
row. **Ten focused tests pass on Windows and Linux**, including 500
deterministic quoted-argument round trips. All process queries and
termination calls are intercepted; no real process was stopped. Fresh
[CI checks](https://github.com/NotRexed/Hookshot/pull/2250/checks) run
on this head.

At `6a3af365` on base `ebbba2ea`, the unmodified helper selected
unrelated synthetic processes as well as the intended control. Four
causal mutations removing work ownership, path boundaries, ancestor
protection or individual termination were rejected. Full Windows: **342
passed / 3 failed / 30 skipped**, versus exact base **341 / 3 / 30**,
with the same failure names. Full Linux: **352 / 0 / 23**; a separate
release-script run with jq supplied its missing internal coverage (**24
cases passed**). These full/causal results refer to that earlier head,
not the current rebase.

This is a synthetic selector regression, not a real termination or
PID-reuse guarantee. Unknown orphan build.py processes and outputs known
only through relative paths or response files remain untouched. Linux
cleanup behavior is unchanged; gameplay does not exercise this Windows
process selector.



[Dev-work
claim](https://discord.com/channels/1568810484818378804/1569019786766188665/1570037297234051113).
Coordination with #2324 and #2373 preserves this engine fix
independently of retained-lift and launcher UI changes.

</details>

<!-- pr-ci-status -->
| Current-head workflow | Result |
|---|---|
| [Game-free source
regressions](https://github.com/NotRexed/Hookshot/actions/runs/43095862413)
| Queued |
| [Linux
AppImage](https://github.com/NotRexed/Hookshot/actions/runs/43095862343)
| Queued |
| [PR
title](https://github.com/NotRexed/Hookshot/actions/runs/43095862104) |
Queued |
| [Vulkan back end with NVIDIA
DLSS](https://github.com/NotRexed/Hookshot/actions/runs/43095862318) |
Queued |
| [Windows
builder](https://github.com/NotRexed/Hookshot/actions/runs/43095862349)
| Queued |
<!-- /pr-ci-status -->
Conflicts with NotRexed#2141, NotRexed#2160, NotRexed#2367 and NotRexed#2447, resolved for the launcher:

- NotRexed#2367 (portable homes): Storage.Base is BuilderHome.Base. The launcher's
  hidden --engine setup gets the home (BuilderHome.PassTo), and the setup's
  engine mode opens it before it installs (it ran before init_home and would
  have stopped on every install). In engine mode a reused part is linked, in
  a portable home too, so the version in use stays whole; otherwise a part is
  moved, or copied in a portable home, as NotRexed#2367 has it. The post-build clean-up
  is Storage's, so it already uses the portable home. A portable home makes no
  shortcuts by default, as NotRexed#2367's portable install.
- NotRexed#2447 (Save my pipeline list): its button was on the launch page the launcher
  replaces. It is a row of the menu's Settings now, on Windows and Linux, with
  the short of what it did under it and the whole in its tooltip.
- NotRexed#2141 (NotRexed#2129, back from an update that stopped): the launcher's one way back
  is BackFromBuild, for Back and for a cancel alike: to the launcher of the game,
  which lets the update go and offers it again, else to the build's choices.
  test_builder_update_back.py's Windows half now checks that.
- NotRexed#2160 (Cancel during a Linux update download): its checks are in the
  launcher's message_in, which pump hands each line to;
  test_linux_update_cancel.py's stand-in compiles both.

The hard-linked parts (link_tree): nothing writes a part's file in place
(compilers, Python, headers and libraries are read; Python's bytecode caches
are written to a new file and renamed over), now said where the links are
made. test_portable_setup_transaction.py also runs the engine mode's reuse:
linked, in a standard and a portable home, the version in use whole.
test_portable_builder_home.py's fixture compiles LaunchList.cs with Storage.cs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@madebyjordan

Copy link
Copy Markdown
Contributor Author

Thanks @Joey-Magnus. #2367 was already in, so I merged main (55dd641) and followed your order:

Agreed that the scope (installing older releases, Nightly not selectable) is @NotRexed's call.

madebyjordan and others added 5 commits November 11, 2026 22:55
A reason from the pipeline list tool can run to five lines (no list yet, and
why), which pushed the rows under it, and the foot, out of the menu's panel.
The note under Save is cut at three lines now, whole under the pointer, on
Windows and Linux, as the panel's other long reasons are.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Build tab is drawn from the top down and its build options from the panel's
foot up. A long reason under the game file (three lines) with the time line
under Build Game ran into "Build options" on a 640 high window: the button and
the label were drawn over each other. The reason is one line now, whole under
the pointer, and the line under Build Game takes only the lines there is room
for above the options.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DLSS and FSR 4 take effect at a build, and the launcher had no way to build
the game in the folder again: a player who forgot to switch FSR 4 on had to
go back through the Build tab with the game file. Now:

- Settings says when the switches no longer match how the game was built
  ("Built without FSR 4", "Built with FSR 4", "Built without DLSS"), with
  Rebuild beside it; the whole sentence and the time it takes are under the
  pointer. Settings scrolls, as Releases does, when that makes it taller.
- The version in the folder, pressed in Releases, offers the same rebuild.

Either asks first, then builds the folder again from the game files it keeps
with that version's own make_game.py --update and the settings as they are
now: FSR 4 on adds it, off takes it out (--no-fsr), DLSS on adds it. Its bar
says "Rebuilding...", and a stop says "Rebuild stopped". The Linux launcher
does the same for DLSS (it has no FSR 4).

Also fixed on the way: in the launcher, Settings showed its switches off
until the Build tab had been opened, and changing one before that saved the
whole settings file from that screen, losing the game file chosen last time
and the other switches. The saved choices are read when the launcher opens.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A builder-settings.json saved before the license was asked has Fsr on and
no FsrLicense: the switch showed on, and no build or update used it (each
needs the license). It shows off now, as builds take it; switched on, the
license is asked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntime

v2.1.0's make_game.py takes --fsr but not --fetch-fsr-runtime (it came with
NotRexed#1866, after that release was cut). The launcher asked for all three words, so
with v2.1.0's tools it left FSR 4 out without a word: a rebuild for FSR 4 had
none, and Settings still said "Built without FSR 4". A first build with FSR 4
on was worse: the builder's own tools were given the words they do not know,
and stopped at once.

Now --fsr goes to any make_game.py that takes it, and the fetch words only to
one that knows them. For one that does not, the launcher fetches AMD's runtime
itself (FfxRuntime.cs: the same pinned release, files and SHA-256s as
scripts/ffx_sdk.py, each file checked) into the builder's ffx-sdk folder and
puts it in the game folder: before an update or rebuild, and at the end of a
first build. Settings counts FSR 4 as in the game only with AMD's runtime
beside it.

tests/test_ffx_runtime_pins.py keeps FfxRuntime.cs and ffx_sdk.py the same.
Checked with AMD's real files: a rebuild through a stand-in for v2.1.0's tools
passed --fsr alone, and the three files reached the game folder, checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
NotRexed pushed a commit that referenced this pull request Nov 12, 2026
…s runners (#2543)

**What this is for:** on GitHub's runners the updater digest check must
keep building for the builder's `net8.0`. Since #2294 it falls back to
the SDK's own framework when the .NET 8 reference pack is missing. That
is right on a PC with only a later SDK and wrong in CI, where it would
hide a runner image without the pack, or an `Updater.cs` that no longer
builds for .NET 8. This is the follow-up @Joey-Magnus suggested in his
review of #2294. **What is measured:** on Linux with only the .NET 10
SDK (10.0.113): run as a PC the check passes through the fallback (20
cases); with `GITHUB_ACTIONS=true` it fails with dotnet's NU1100 output;
main's file passes in that same situation. **Not measured:** a runner
that really lacks the pack. This pull request's own jobs have .NET 8
installed, so they only show that the normal path is unchanged.

## Problem

From the review of #2294: "on CI the fallback would hide exactly the
regression the builder cares about. If a runner image ever drops the
.NET 8 pack, or `Updater.cs` starts using an API newer than .NET 8, the
check would quietly build for net10.0 and pass, while the real builder
no longer compiles. CI only prints one line in that case."

## What changed

- `tests/test_updater_asset_digest.py`: when the `net8.0` restore fails
with NU1100 and `GITHUB_ACTIONS` is `true`, the check exits with a
message and dotnet's output instead of switching the framework.
`GITHUB_ACTIONS == "true"` is the test `tests/run_all.py` already uses
to tell a runner from a PC. Everywhere else the fallback is unchanged.

## How to try it

```
python -I -B tests/test_updater_asset_digest.py                       # a PC with only a later SDK: falls back, 20 passed
GITHUB_ACTIONS=true python -I -B tests/test_updater_asset_digest.py   # the same PC as a runner: fails with NU1100
```

## How it was tested

Arch Linux, .NET SDK 10.0.113 only (no .NET 8 reference pack), head
`2294f29d` on main `fa2b25b2`.

| File | Environment | Result |
|---|---|---|
| this branch | `GITHUB_ACTIONS` unset | the fallback's line, then
`Updater digest cases: 20 passed, 0 failed.`, exit 0 |
| this branch | `GITHUB_ACTIONS=true` | `on a GitHub runner the updater
must build for the builder's net8.0, and its reference pack is missing:
no fallback to a later framework here`, then dotnet's NU1100 lines, exit
1 |
| main's file | `GITHUB_ACTIONS=true` | falls back, 20 passed, exit 0
(what this change prevents) |

## Not tested

- A real runner without .NET 8, and Windows on my side (no Windows
machine).
- `--strict` on a PC is unchanged: the fallback still applies there. The
review named `--strict` as an alternative; `run_all.py` does not pass it
on to a check, and `GITHUB_ACTIONS` is what it already reads.

## Risks

- A self-hosted runner (or a local runner tool) that sets
`GITHUB_ACTIONS=true` without .NET 8 now fails this check instead of
passing for a later framework. That is the intent.

## Overlaps

- #2373 touches the same file and merges cleanly with this change (`git
merge-tree`).

Assisted-by: Claude Fable 5.2
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>

Co-authored-by: Claude Fable 5.2 <noreply@anthropic.com>
@hckrman101

Copy link
Copy Markdown
Collaborator

This is starting to look good. Few notes:

  • We should put the update button above the play button
  • Hookshot logo and buttons could be centered a bit more

@Joey-Magnus Joey-Magnus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My last points are handled: BuilderHome.PassTo on the hidden --engine setup, the portable home in FreeAfterBuild (and the init_home ordering bug you caught), and the link_tree no-write rule written down. It conflicts with main again in builder/HookshotBuilder/MainWindow.xaml.cs (#2528, #2598), and only the title check has run on 5ccc2f1.

The new FfxRuntime.cs is sound: a pinned commit, .part first, each SHA-256 checked, and tests/test_ffx_runtime_pins.py keeps it in step with scripts/ffx_sdk.py. One small thing: Launcher.cs calls FfxRuntime.Ensure(Storage.FfxSdk) without a cancellation token, so Cancel waits out the 120 s timeout.

On the PR head, test_linux_launcher_lists, test_linux_launcher_work, test_linux_update_cancel, test_builder_update_back and test_ffx_runtime_pins pass (the C# and mingw checks need Windows).

At +7.4k lines it keeps gaining features (rebuild, FSR runtime fetch). The scope (installing any older release, Nightly not selectable) still needs NotRexed's decision; moving the rebuild and FSR work to a follow-up would help it land. hckrman101's layout notes are open too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants