launcher: wire m2pack key delivery, wine prefix, platform filter, and prune #1
Reference in New Issue
Block a user
Delete Branch "avalonia-gui"
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?
What changed since round 1
Rebased on top of
main(which now has the full m2pack infra:Runtime/key model,Formats/dispatch,EnvVarKeyDelivery, tests). The previous round duplicated that infrastructure by hand; this round drops those duplicated bits and keeps only the four surgical fixes that are still needed.Commits
orchestration: target windows platform + prune stale files— addresses both the platform filter bug (droppingplatform=windowsfiles on Linux host) and the missing prune. Prune keep set now includesmanifest.launcher.pathper Jakub's review — a correctly-authored manifest with a top-levellauncherentry will not have it deleted.game: auto-pick metin wine prefix when WINEPREFIX is unset— surgical addition toBuildStartInfoLinux branch, falls back to~/metin/wine-metinonly if env not set and dir exists. No-op on deployments withoutsetup-wine-prefix.shrun.gui: thread m2pack runtime key from orchestrator result into play command— GUI pathPlaynow passes_lastRuntimeKeytoGameProcess.Launch, matching the headless path inProgram.cs. Caches the key on the view model whenStartUpdateAsynccompletes.gitignore: exclude runtime client data dropped into source tree— unchanged from round 1.Dropped from round 1
M2PACK_MASTER_KEY_HEXenv var wiring inGameProcess— superseded byRuntime/EnvVarKeyDeliveryand the existingGameProcess.BuildStartInfo(..., RuntimeKey?)parameter in main.runtime-key.jsonparsing inGameProcess— superseded byM2PackFormat.OnApplied+RuntimeKey.Loadin main.Test plan
dotnet build -c Releasegreendotnet test -c Release— 92/92 passOutstanding from round 1 review
Addressed here:
PruneStaleFilesrespects top-levellauncherentryTracked elsewhere (not in this PR):
release-v2/client/pack— fixed in the release tree rebuild, not a launcher concernmake-manifest.pyissue #9 — needs to be reframed; the upstream script works as spec-intended, the problem is my post-process that was usingMetin2.exeas the manifestlauncherfield (which is reserved forMetin2Launcher.exeper spec). Will open a separate issue correctly framed.Two fixes addressing review feedback on the previous avalonia-gui PR round. 1. Platform filter was using host OS (`IsWindows() ? "windows" : "linux"`), which dropped all `platform=windows` manifest entries whenever the launcher ran on Linux. Since the client is always a Windows PE binary launched through Wine on Linux hosts, the target platform we apply is always `"windows"` regardless of host. Before this, the orchestrator silently skipped Metin2.exe and the python314 / openssl / mingw runtime DLLs on Linux, leaving an unbootable install dir. 2. Added `PruneStaleFiles` that walks clientRoot after every successful update (both the normal apply path and the already-up-to-date short-circuit) and deletes any file not in the manifest. Without it, switching from a dirty release to a leaner one — e.g. dropping legacy .pck after a .m2p migration — left orphaned files on disk and inflated the install dir. The keep set is `manifest.files ∪ {manifest.launcher.path}`. Per the manifest spec in `m2dev-client/docs/update-manifest.md`, the top-level launcher entry is privileged and never listed in files; it must survive prune so a correctly-authored manifest does not delete the updater itself (caught in Jakub's review of the previous round). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>When the launcher is run from its bin/ dir during local dev, CWD becomes the source tree and the orchestrator stages the client release into src/Metin2Launcher/{pack,bgm,mark,config,.updates,...}. None of that should ever land in git. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>e23f4e1e6etoac0034fc51The canonical launcher runtime-key.json shape is { key_id: string, master_key_hex, sign_pubkey_hex } but `m2pack export-runtime-key --format json` emits { version, mapping_name, key_id: int, master_key_hex, sign_public_key_hex } because its JSON is really a dump of the Windows shared-memory struct. Parsing the CLI output with the old strict deserializer throws JsonException on key_id (int != string) and silently drops the public key field (name mismatch), after which Validate() rejects the key as not 64 hex chars and the m2pack release fails to boot with "runtime master key with key_id=1 required for 'pack/root.m2p'". Hit this tonight during the 2026.04.15-m2pack-v2 release and worked around it by hand-writing runtime-key.json. Fix: parse into a JsonElement and extract fields tolerantly — key_id accepts either a JSON string or a JSON number (stringified), and the pubkey field is looked up under both "sign_pubkey_hex" and "sign_public_key_hex". Added a test covering the m2pack CLI shape end to end. Also kept the malformed-input path on JsonSerializer.Deserialize so it still throws JsonException (JsonDocument.Parse throws its internal subtype which breaks Assert.Throws<JsonException>). Tracked separately as metin-server/m2pack-secure#3 — the m2pack side should also align its JSON to the canonical shape; this commit is the client-side belt to the server-side suspenders. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>