feat: route double-clicked patch files to the right game client#49
Merged
Conversation
316172e to
74f03a4
Compare
Every installer file association and URL protocol points at MultiWorldGG.exe "%1", but the argument parser had no positional, so any double-clicked patch file died with SystemExit(2) before logging existed. MultiWorld.py now accepts an optional positional (patch file, tool file, or archipelago:// URL): - Patch containers are identified by their root archipelago.json (the Patch.py pre-load pattern, no world imports), resolved to a module via GameIndex (custom_worlds included via the launch-time scan), and routed through discover_and_launch_module once the launcher frontend is up -- beta clients take over the launcher UI rather than running standalone. - Files claimed by a builtin tool component (.archipelago/.mwgg/.zip multidata -> Host) launch that component directly. - Launch URLs no longer crash; they open the launcher. Utils plumbing forwards the patch file to whichever client resolves: as the positional CLI arg for entry-point/component launch functions (via the new _client_launch_argv), as diff_file= for the SNI registry path, and as the patch positional for the BizHawk registry fallback, none of which previously received a file. Also fixes two latent bugs this work exposed: - run_client(*args) packed the Namespace into a tuple, so the --game/--server-address branch always died on AttributeError and fell through to the plain GUI; it now routes through the same UI-ready deferred launch as patch files. - The cached-stub launch helpers in LauncherComponents imported the nonexistent Launcher module; get_exe/launch_exe are now implemented beta-natively and the callable-stub relaunch (a Launcher-subprocess mechanism upstream) degrades to the loaded-component fallback.
81fbc9a to
4ed0111
Compare
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.
Problem
Every installer file association (
.aplttp,.apsm,.apz5,.apemerald, ...) and URL protocol (archipelago://,mwgg://,multiworldgg://) points atMultiWorldGG.exe "%1", but the argument parser defined only flags — a double-clicked patch file was an unrecognized argument and died withSystemExit(2)before logging even started. The upstream Archipelago "double-click and play" flow was broken beta-wide, for every game.Approach
Beta clients are takeover-only: per-world clients swap themselves into the running launcher frontend (
CommonContext._takeover_existing_ui); the SNI client, text client, andlaunch_textclientall refuse to run standalone. So unlike upstream (which launches the matched component as a subprocess pre-GUI), routing here is GUI-first:MultiWorld.pyaccepts an optional positional (make_arg_parser()), resolves it before the GUI with no world imports: patch containers are identified by their rootarchipelago.json(the same pre-load pattern asPatch.py) and mapped to a module slug viaGameIndex(custom_worlds included via the launch-time scan)._route_module_when_ui_readytask waits for the frontend, then routes throughUtils.discover_and_launch_module— the exact code path the GUI's launch button uses, so UI takeover, on-demand world install, and the SNI/BizHawk registry fallbacks behave identically to a button click. Screen-flip hooks (client_console_init/console_init/change_screen) are feature-detected, so the TUI skips them gracefully.patch_filekwarg is forwarded to whichever client resolves: positional CLI arg for entry-point/component launch functions (new pure helper_client_launch_argv),diff_file=for the SNI registry path, patch positional for the BizHawk registry fallback — none of which previously received a file..archipelago/.mwgg/.zipmultidata → Host) launch that component directly (frozen:MultiWorldGGServer.exe). Launch URLs no longer crash; they open the launcher. Unroutable files log a warning and fall back to the launcher, matching upstream.Latent bugs fixed along the way
run_client(*args)packed the Namespace into a tuple, so the--game/--server-addressbranch (used by the GUI's restart-after-update flow) always died on a swallowedAttributeErrorand silently fell through to the plain GUI. It now routes through the same UI-ready deferred launch as patch files, which also makes it work cold-start.worlds/LauncherComponents.pydidfrom Launcher import get_exe, launch— a module that doesn't exist in beta (ImportError if ever exercised).get_exe/launch_exeare now implemented beta-natively, and the cached-callable relaunch (an upstream Launcher-subprocess mechanism) degrades to the loaded-component fallback. Launcher-cache machinery otherwise untouched.Testing
test/general/test_patch_routing.py: parser positional (file paths, URLs, flag combos),read_patch_game_name(root manifest vs multidata/apworld/missing),_client_launch_argvtranslation,LauncherComponents.identify/get_exerouting.🤖 Generated with Claude Code