release: separate Metin2Launcher.exe (updater) from Metin2.exe (game client) per manifest spec #12
Reference in New Issue
Block a user
Delete Branch "%!s()"
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?
The release pipeline this week conflated two distinct entities that the manifest spec keeps separate:
Metin2Launcher.exe— the self-updating updater (what thelaunchertop-level field of the manifest is meant to describe, privileged, replaced via rename-before-replace, never infiles)Metin2.exe— the game client (a regular content file that belongs infileslike any other blob)My release wrapper was passing
--launcher Metin2.exetomake-manifest.py. That did what it was asked: putMetin2.exein the top-levellauncherfield. The orchestrator then never downloaded it (it only iteratesfiles), so I patched around that by also injectingMetin2.exeback intofilespost-hoc and re-signing. Result:Metin2.exeended up in both places with a double role, and the release tree had no realMetin2Launcher.exeat all.This is architecturally wrong. Jakub flagged it in the PR #1 review of jann/metin-launcher. See spec:
docs/update-manifest.md.What a correct m2pack release tree should look like
Metin2Launcher.exe— the .NET Avalonia updater binary, in the tree at root, referenced from the top-levellauncherfield of the manifest, not infilesMetin2.exe— the game client, in the tree at root, listed infileslike any other file*.dll,*.pyd,python314.zip,python314._pth— infilespack/*.m2p— infilesbgm/*,mark/*,config/*— infilesruntime-key.json— infiles(theM2PackFormat.OnAppliedhook in the launcher loads it after apply)What's needed
Release tree builder must ship
Metin2Launcher.exealongsideMetin2.exe. Velopack produces it; it is the binary that self-updates and spawns the game. Today the launcher is built separately (jann/metin-launcherbin dir) and is not part of the release tree.make-release.sh/build-release-tree.sh(see #11) should copyMetin2Launcher.exefrom the launcher build output into the release tree root beforemake-manifest.pyruns.make-manifest.py --launchershould then point atMetin2Launcher.exe, notMetin2.exe. No post-process patch needed.Orchestrator prune must preserve
manifest.launcher.path— already fixed in jann/metin-launcher PR #1 round 2.Orchestrator should probably also apply the launcher file using rename-before-replace semantics per spec, so the updater can update itself without a second Velopack feed. Right now the launcher assumes Velopack for self-update (
VelopackFeedUrl) and the manifestlauncherfield is effectively unused. Pick one path and document it. This is a bigger design decision and doesn't block the immediate fix.Related
Keep-partially checklist for
release-v2/client/packProduced from a full
m2pack diffsweep of every.m2pin the release tree against its source asset dir (m2dev-client/assets/<Name>/) using the canonical master key from.secrets/m2pack-secure/2026-04-14/. Reports under/home/mt2.jakubkadlec.dev/work/release-v2/diff-reports/<Name>.jsonon the VPS.Aggregate result: 91/91 packs diffed without tool error. 90/91 are byte-identical to source. 1 pack has content divergence.
Category A — KEEP AS-IS (89 packs, clean diff)
All 49 of my mass-migrated
.m2pfiles (the.pck-only groups I rebuilt this morning) diff clean against their source asset dirs — zeroadded, zeroremoved, zerochanged, every file path in the archive matches the source with byte-identical content. Same for 40 of Jakub's 42 pre-existing.m2pfiles. These are good.indoordeviltower1,indoormonkeydungeon1..3,indoorspiderdungeon1,metin2_patch_5th_armor,metin2_patch_6th_armor,metin2_patch_bleudmetin,metin2_patch_cash_costume,metin2_patch_christmas_dungeon,metin2_patch_dawndungeon,metin2_patch_event,metin2_patch_event_dungeon,metin2_patch_fishing2,metin2_patch_flame,metin2_patch_flame_dungeon,metin2_patch_flower_hunting,metin2_patch_guild_dungeon,metin2_patch_halloween,metin2_patch_halloween2,metin2_patch_honor_dungeon,metin2_patch_ice,metin2_patch_kingdomquest,metin2_patch_melhountain,metin2_patch_mining,metin2_patch_newyear,metin2_patch_pet1..2,metin2_patch_porcelain,metin2_patch_ramadan_costume,metin2_patch_season1..2,metin2_patch_snow,metin2_patch_snow_dungeon,metin2_patch_snowyquest,metin2_patch_spiderdungeon,metin2_patch_thursday_dungeon,metin2_patch_tree,metin2_patch_vegasnight,metin2_patch_winter,metin2_patch_woods,outdoora1..4,outdoorb2,outdoorc2,outdoorguild1..3,outdoormilgyo1,outdoort1..4,outdoortrent,outdoortrent02,outdoorwedding,property2,season1..2,terrain2,tree2,uiloading,uiscript,zone2effect,etc(uppercase),item,icon,locale,monster,npc,outdoor,outdoorA1..3,outdoorB1/B3,outdoorC1/C3,outdoorsnow1,patch1..2,pc,pc2,property,root,season3_eu,sound,sound_m,terrain,textureset,tree,zoneKeep decision: ship as-is. Strong evidence they're not lossy relative to source.
Residual gap: diff doesn't cover runtime behavior. In-game walkthrough still needed before production.
Category B — REBUILD REQUIRED (1 pack)
sound2.m2p: diff shows 2 removed + 1 changed paths.sound/monster2/outlaw/fall.wav,sound/pc2/assassin/bow/attack1.wavsound/pc2/assassin/dualhand_sword/combo_07.mss5e79fc27 Fix missing dualhand combo 7 sound referenceupdated the asset source butsound2.m2pwas built before that fix. Stale artifact.m2pack build --input assets/sound2 --output pack/sound2.m2p --key master.key --sign-secret-key signing.key --key-id 1. Unblocks once done; full.m2pcontent would be current.Category C — UNKNOWN / NEEDS HUMAN CALL (0 packs)
No packs in this category after diff. Nothing else flagged.
Category D — DROP (dropped during cleanup, informational)
19 case-collision duplicates were dropped earlier tonight (lowercase-first stems whose uppercase-first sibling already existed in Jakub's batch):
effect,etc,monster,npc,outdoor,outdoora1..3,outdoorb1/b3,outdoorc1/c3,outdoorsnow1,pc,property,sound,terrain,tree,zone. Their uppercase-first counterparts (in Category A) are what survives.Gates still required before production release
These are the remaining steps from
m2pack-secure/docs/migration.mdthat I skipped and which this diff does not cover:validate_runtime_gate.pyneeds theassets/source tree, not the release artifact, so it has to run against the m2dev-client canonical source against a live client boot. Not run tonight.strict-known-issuesgate againstm2pack-secure/known_issues/runtime_known_issues.json— tells you if any baseline regressions have landed. Needs the source tree integration.Recommendation for tonight: Keep Category A, rebuild sound2 (Category B), then do not promote to root until at least the runtime walkthrough passes. Root stays on
2026.04.15-cleanas safe fallback.