feat(builder): the launcher, one window to play, build and update (Windows and Linux) #2373
Open
madebyjordan wants to merge 17 commits from
madebyjordan/builder/launcher into main
pull from: madebyjordan/builder/launcher
merge into: MagicBeanDev:main
MagicBeanDev:main
MagicBeanDev:github/pr/2893
MagicBeanDev:github/pr/2890
MagicBeanDev:github/pr/2887
MagicBeanDev:github/pr/2878
MagicBeanDev:github/pr/2875
MagicBeanDev:github/pr/2874
MagicBeanDev:github/pr/2873
MagicBeanDev:github/pr/2871
MagicBeanDev:github/pr/2756
MagicBeanDev:github/pr/2897
MagicBeanDev:github/pr/2896
MagicBeanDev:github/pr/2718
MagicBeanDev:github/pr/2842
MagicBeanDev:github/pr/2691
MagicBeanDev:github/pr/2895
MagicBeanDev:github/pr/2743
MagicBeanDev:github/pr/2765
MagicBeanDev:github/pr/2708
MagicBeanDev:github/pr/2732
MagicBeanDev:github/pr/2755
MagicBeanDev:github/pr/2817
MagicBeanDev:github/pr/2876
MagicBeanDev:release-please--branches--main
MagicBeanDev:release-please--branches--main--release-notes
MagicBeanDev:github/pr/2778
MagicBeanDev:github/pr/2888
MagicBeanDev:github/pr/2866
MagicBeanDev:github/pr/2877
MagicBeanDev:github/pr/2800
MagicBeanDev:github/pr/2829
MagicBeanDev:github/pr/2850
MagicBeanDev:github/pr/2889
MagicBeanDev:github/pr/2882
MagicBeanDev:github/pr/2860
MagicBeanDev:github/pr/2849
MagicBeanDev:github/pr/2886
MagicBeanDev:github/pr/2794
MagicBeanDev:github/pr/2840
MagicBeanDev:github/pr/2830
MagicBeanDev:github/pr/2834
MagicBeanDev:github/pr/2719
MagicBeanDev:github/pr/2742
MagicBeanDev:github/pr/2741
MagicBeanDev:github/pr/2820
MagicBeanDev:github/pr/2771
MagicBeanDev:github/pr/2859
MagicBeanDev:github/pr/2858
MagicBeanDev:github/pr/2823
MagicBeanDev:github/pr/2757
MagicBeanDev:github/pr/2769
MagicBeanDev:github/pr/2844
MagicBeanDev:github/pr/2843
MagicBeanDev:github/pr/2673
MagicBeanDev:github/pr/2855
MagicBeanDev:github/pr/2739
MagicBeanDev:github/pr/2807
MagicBeanDev:github/pr/2851
MagicBeanDev:github/pr/2789
MagicBeanDev:github/pr/2814
MagicBeanDev:github/pr/2818
MagicBeanDev:github/pr/2774
MagicBeanDev:github/pr/2772
MagicBeanDev:github/pr/2750
MagicBeanDev:github/pr/2786
MagicBeanDev:github/pr/2785
MagicBeanDev:github/pr/2782
MagicBeanDev:github/pr/2779
MagicBeanDev:github/pr/2775
MagicBeanDev:github/pr/2706
MagicBeanDev:github/pr/2766
MagicBeanDev:github/pr/2747
MagicBeanDev:github/pr/2759
MagicBeanDev:github/pr/2745
MagicBeanDev:github/pr/2740
MagicBeanDev:github/pr/2733
MagicBeanDev:github/pr/2725
MagicBeanDev:github/pr/2727
MagicBeanDev:github/pr/2728
MagicBeanDev:github/pr/2729
MagicBeanDev:github/pr/2721
MagicBeanDev:github/pr/2722
MagicBeanDev:github/pr/2715
MagicBeanDev:github/pr/2714
MagicBeanDev:github/pr/2707
MagicBeanDev:github/pr/2703
MagicBeanDev:github/pr/2705
MagicBeanDev:github/pr/2697
MagicBeanDev:github/pr/2679
MagicBeanDev:github/pr/2676
MagicBeanDev:github/pr/2669
MagicBeanDev:docs/what-hookshot-connects-to
MagicBeanDev:fix/hdr-encode-test
MagicBeanDev:fix/main-tests
MagicBeanDev:fix/play-cache-files
MagicBeanDev:perf/game-barrier-order
MagicBeanDev:feat/heap-lazy
MagicBeanDev:feat/texture-detail
MagicBeanDev:perf/astc-opaque-bc1
MagicBeanDev:fix/fs-dir-lock
MagicBeanDev:fix/framegen-native-kept
MagicBeanDev:fix/update-no-downgrade
MagicBeanDev:port/k10-gcn-f16-mirrors
MagicBeanDev:fix/framegen-mult-cap
MagicBeanDev:chore/release-as-2.1.1
MagicBeanDev:perf/pcache-load-switch
MagicBeanDev:fix/prepare-progress-log
MagicBeanDev:fix/update-closes-stale-game
MagicBeanDev:fix/hdr-scaled-sampled
MagicBeanDev:perf/first-start-preset
MagicBeanDev:perf/zero-upload
MagicBeanDev:fix/vcore-escape-2s
MagicBeanDev:test/fake-gh-atomic
MagicBeanDev:perf/batch-idle-flush
MagicBeanDev:test/finish-stub-a
MagicBeanDev:chore/record-2.1.0
MagicBeanDev:fix/main-red-tests
MagicBeanDev:fix/test-display-startup-driver-cache
MagicBeanDev:feat/mesh-shader-mode
MagicBeanDev:fix/menu-probes-at-startup
MagicBeanDev:fix/fps-counter-classic
MagicBeanDev:fix/test-log-batch-flake
MagicBeanDev:fix/narrow-screen-hud
MagicBeanDev:ci/prereleases
MagicBeanDev:release/v2.0.15
MagicBeanDev:hotfix/v2.0.15
MagicBeanDev:fix/present-ahead-replay
MagicBeanDev:release/v2.0.13
MagicBeanDev:fix/bracket-paths-2013
MagicBeanDev:fix/hdr-overlays-2013
MagicBeanDev:fix/fps-counter-scale-2013
MagicBeanDev:fix/stick-seen-at-rest
MagicBeanDev:hotfix/menu-fits-screen
MagicBeanDev:fix/f1-always-opens-menu
MagicBeanDev:fix/ini-keep-lines
MagicBeanDev:claude/cranky-pike-7cf247
MagicBeanDev:perf/texture-answer-flat
MagicBeanDev:fix/stamp-test-modules
MagicBeanDev:fix/appimage-fonts
MagicBeanDev:fix/display-awake
MagicBeanDev:release/v2.0.12
MagicBeanDev:fix/discord-windows
MagicBeanDev:rc/v2.0.12-tests
MagicBeanDev:fix/dlss-keep-feature
MagicBeanDev:rc/v2.0.12
MagicBeanDev:button-icons
No reviewers
Labels
Clear labels
accessibility
Barrier affecting people with disabilities
autorelease: pending
autorelease: tagged
bug
Something isn't working
dependencies
Pull requests that update a dependency file
documentation
Improvements or additions to documentation
duplicate
This issue or pull request already exists
enhancement
New feature or request
github_actions
Pull requests that update GitHub Actions code
good first issue
Good for newcomers
help wanted
Extra attention is needed
invalid
This doesn't seem right
question
Further information is requested
testing wanted
wontfix
This will not be worked on
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
Milestone
Clear milestone
No items
No milestone
Assignees
Clear assignees
No assignees
2 participants
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2373
Loading…
Reference in a new issue
No description provided.
Delete branch "madebyjordan/builder/launcher"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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.
What changed
hookshot.exein 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.setup.c --engine, no window) and rebuilds the game with that release's ownmake_game.py.rc.10afterrc.9).RX_VK_PCACHE_LOAD=0, one line of the game folder'shookshot_env.txt, so it holds however the game is started. The cache file is kept, and a note gives its size.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:
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.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.csandLaunchWork.cs: the pure rules (which versions show, what the bar and the words say). The Linux side mirrors them inlaunch_list.py/launch_work.py, and both have tests.MainWindow.xaml: the screen itself.setup.c: the--enginemode (g_engine).Already settled in review (no need to raise again):
launcher.jpg, the Linuxpicture.png,make_picture.py). The only new asset is the Marcellus font, with its OFL licence.list_name_ok;--taglimited to[0-9A-Za-z._-]; never over the version incurrent.txt; staged in<tag>.part. The127.0.0.1exception applies only to a list that itself came from localhost (the rig).BuilderHome.PassTo.Storage.BaseisBuilderHome.Base, so the clean-up after a build stays in the portable home.work()path, sobuilder_readyruns before@@installed.link_tree.builder_workers.BackFromBuild, returns to the launcher, which offers the update again.Checks to run:
dotnet run -c Releaseintests/builder_launcher: list, work and settings rules.python -I -B tests/run_all.py builder launcher portable setup update.tests/test_linux_launcher_{lists,work,art}.pyandtests/test_linux_update*.py.tests/builder_launcher/rig/run.ps1. It uses fake releases from a local server, a scratch install (HOOKSHOT_SETUP_BASE) and a stand-inmake_game.py. The real install is untouched.HOOKSHOT_UI_SHOT=<file.png>draws the window to a PNG and exits.HOOKSHOT_UI_PREVIEWpicks the state:tab=build|releases|settings,older,pick=…,go, orfound,build/found,stopped/update,menufor the build pages.HOOKSHOT_LATEST_TAG=v2.2.0,v2.1.0fakes GitHub's answer. The Linux launcher takesHOOKSHOT_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:--update;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.pyhit-tests the Show details / Cancel row: the words take clicks, and the blank beside them does not.main:test_portable_setup_transaction.pyalso 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.pychecks the launcher's way back.test_linux_update_cancel.pycompiles the Linux builder'smessage_inbesidepump.main: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 ofmainon the same machine.test_linux_update*,test_release_scriptsand the launcher's screens, in each state.Open for @NotRexed
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
Linux
Linux draws the same screens, with Inter in place of Segoe UI.
🤖 Generated with Claude Code
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 editinghookshot_env.txt, which most players won't do.It fits what's already here.
StartGameinMainWindow.xaml.csalready starts the game with a variable of its own (si.Environment["HOOKSHOT_NO_UPDATE_CHECK"] = "1"). The Linux launcher'splay()passes its own environment too. So it would be:BuilderSettings(for examplebool SkipPipelineCacheFile), off by default as in #2206.si.Environment["RX_VK_PCACHE_LOAD"] = "0"when the toggle is on, and the same line in the Linuxplay()'s environment.Two things to decide:
bin\hookshot.exedirectly, 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 anRX_VK_PCACHE_LOAD=0line in the game folder'shookshot_env.txt, which the game reads at every start (it takesRX_names). The Play variable is simpler and matches whatStartGamealready does.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.pyhere, althoughhookshot-folderis 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.@nullifyshot Thanks, added in
96eaeefd: Lower memory use in Settings, off by default. It writesRX_VK_PCACHE_LOAD=0as one line of the game folder'shookshot_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.@Arkex-ai Thanks for checking. This PR doesn't touch
hookshot-folder, and its only change tolinux_update.pyis aValueErrorguard ingame_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 ordersrc.N). Its--update <folder>start with no--tag(the game's own update offer) now asks before an older target too:a14058e1. Forchoose_mode, fixing it inlinux_update.pyas you suggest, compared byrelease_key, sounds right, and I'll rebase on whichever lands first.Going to do very deep testing on this, plus will have a few design feedback. Few to start:
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:
--enginemode, 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.@hckrman101 The two cases are in (
4ee39e21), on Windows and Linux: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.jpgis a picture of the game (the XAML calls it that: "the picture of the game, dim behind everything"), andbuilder/linux/launcher/picture.pngis the same image dimmed bymake_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 everyHookshotBuilder.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), keepingmake_picture.pyonly if there's an image that can ship.Updater / setup security looks right to me:
setup.c --enginekeeps the existing checks: parts must be https with a 64-hex sha256 that's verified before unpacking, list names go throughlist_name_ok, the--tagis 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>.partand is renamed when complete, and it refuses to install over the version incurrent.txt.http://127.0.0.1:exception inpart_url_okonly applies whenHOOKSHOT_SETUP_LISTitself points at localhost, so a list downloaded from GitHub can't use it. Fine for the rig.link_treehard-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.linux_update.installpath (https only, redirects too, sha256 per part) and checksmanifest.tag == tagbeforeexecve.ReleaseListdrops tags that aren't versions, and the tag goes into the API URL withUri.EscapeDataStringand to the setup throughArgumentList. No quoting problems that I could see.Process launching: Windows
StartGameis unchanged apart from the error box (working directory is the folder,HOOKSHOT_NO_UPDATE_CHECK=1). Linux Play still goes throughhookshot_builder.play(), so the socketpair stderr from #2279 is kept and the game still writeshookshot_log.txt. The Lower memory switch only editsRX_VK_PCACHE_LOADlines inhookshot_env.txt, through a.newfile 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.Freeablenow 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,FreeAfterBuilddeletes that version's builder again (it isn'tcurrent.txt), so switching back and forth downloads it again each time. That's probably intended, just confirming.Things for @NotRexed (product, not code):
docs/versioning.mdstill 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.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--enginesetup andFreeAfterBuilduse 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_artdeclares numpy). The two red jobs aren't from this PR: Windows part 2/2 failstest_native_astc_texture_count.py(ascanfdeprecation in its fixture under clang-Werror), andappimagetimed out installing system packages. Re-run the AppImage job once the runner's network is back, sinceappimage.shchanged here.Small things inline.
@ -153,6 +154,12 @@ jobs:run: |Nit: unlike
builder_workersjust above, this has nomatrix.shard == '1/2'condition, so it restores and builds on both Windows shards. Same tests, twice the cost.@ -24,11 +24,11 @@<DebugType>none</DebugType>This embeds the game picture in every
HookshotBuilder.exewe publish. See the review body: it needs @NotRexed's OK, or an image we own, before it ships.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.Heads-up: #2367 replaces this line with
BuilderHome.Base, and its setup setsHOOKSHOT_SETUP_BASEfor portable homes. Whichever lands second should check that the launcher's--enginesetup (which inherits the environment) andFreeAfterBuildend up in the portable home.picture.pngis made fromlauncher.jpg, so it has the same problem, andappimage.shcopiesbuilder/linuxinto every AppImage. If the picture is replaced, regenerate this (or drop it and draw the page colour) in the same change.Thanks @hckrman101, that's a thorough first pass. Fixed in
d1a41557:launcher.jpg, the Linuxpicture.pngandmake_picture.pyare 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.builder_workers.docs/versioning.mdnow 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
--enginesetup 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.shchanged here?Looking forward to the hands-on testing.
#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.dllfrom the pinned official release and ran the managed CLI successfully.For your
--engineroute, 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.Thanks for the heads-up @Arkex-ai. The
--engineroute goes through the samework()path, so it getsbuilder_readybefore@@installedwith 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
runpath, 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.jsonnames from the exe's own name rather thanHookshotBuilder, so the check holds for a renamed builder too. Noted on Avalonia / single-file: that contract will change with the standalone launcher anyway.It now conflicts with main in
MainWindow.xaml.cs,builder/linux/hookshot_builder.py,builder/linux/linux_update.pyandtests/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:
--enginestill requires https parts with sha256, the localhost exception only applies when the list itself came from localhost, andRX_VK_PCACHE_LOADis rewritten through.newplus rename.Minor:
link_treehard-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.PassToon the hidden--enginesetup and the portable home inFreeAfterBuild. The scope (Releases tab installing any older release, Nightly no longer selectable) is NotRexed's call.Thanks @Joey-Magnus. #2367 was already in, so I merged
main(55dd6414) and followed your order:--enginesetup now getsBuilderHome.PassTo, andStorage.BaseisBuilderHome.Base, soFreeAfterBuildcleans up in the portable home. The merge also caught a real problem: engine mode ran beforeinit_home, so every engine install would have stopped on "home unavailable". It now opens the home first.test_portable_setup_transaction.pynow also runs that path and checks the files are links, in both homes.link_tree: I checked what writes into the shared parts, and nothing writes in place. llvm-mingw, Python, emsdk (its clang only, neveremccand its cache), cpm, third_party and wgpu-native are only read. Python's bytecode caches are written to a new file and renamed over, which replaces that version's link alone. That contract is now written besidelink_tree.test_builder_update_back.pynow checks the launcher's flow.message_in.Agreed that the scope (installing older releases, Nightly not selectable) is @NotRexed's call.
This is starting to look good. Few notes:
My last points are handled:
BuilderHome.PassToon the hidden--enginesetup, the portable home inFreeAfterBuild(and theinit_homeordering bug you caught), and thelink_treeno-write rule written down. It conflicts with main again inbuilder/HookshotBuilder/MainWindow.xaml.cs(#2528, #2598), and only the title check has run on5ccc2f16.The new
FfxRuntime.csis sound: a pinned commit,.partfirst, each SHA-256 checked, andtests/test_ffx_runtime_pins.pykeeps it in step withscripts/ffx_sdk.py. One small thing:Launcher.cscallsFfxRuntime.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_backandtest_ffx_runtime_pinspass (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.