Jan Nedbal e28b243100 launcher: scaffold avalonia main window, view models, and settings dialog
Adds the Avalonia App, MainWindow (900x560 fixed, dark-red banner, left
status/progress column, right news panel) and a modal SettingsDialog with
language + dev-mode override. MainWindowViewModel binds to the orchestrator
through IProgress, throttles UI updates to ~10 Hz via a DispatcherTimer
flush, drives state-specific status text and Play-button gating, fetches
previous-manifest notes best-effort, and forces a red status banner with
Play disabled on a SignatureFailed result.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:15:44 +02:00
2026-04-14 11:02:30 +02:00

metin-launcher

Self-updating launcher for the Metin2 client.

This is the single entry point the player runs. It fetches a signed manifest from updates.jakubkadlec.dev, verifies an Ed25519 signature against a compiled-in public key, reconciles the local client against the manifest by sha256, downloads anything that differs from a content-addressed blob store (with HTTP Range resume), applies updates atomically, hands launcher self-update off to Velopack, and then starts Metin2.exe. If the update server is unreachable the launcher falls back to launching whatever the player already has.

Architecture and failure modes live in the client repo under docs/update-manager.md and docs/update-manifest.md. This project implements those specs in C# .NET 8; it does not own the spec.

Build

dotnet build Launcher.sln -c Release
dotnet test

Tests are xUnit and run on Linux. A local HttpListener stands in for the blob server in BlobDownloaderTests.

Publish (Windows single-file)

dotnet publish src/Metin2Launcher/Metin2Launcher.csproj \
    -c Release -r win-x64 --self-contained \
    -p:PublishSingleFile=true

The CI hasn't been wired yet; the MVP only requires the build to succeed.

Run

Drop Metin2Launcher.exe next to Metin2.exe in the client install directory and run it. On first launch it will:

  1. fetch https://updates.jakubkadlec.dev/manifest.json + .sig
  2. verify the signature against the compiled-in public key
  3. hash every file listed in the manifest, download anything that doesn't match into .updates/staging/, then atomically move each one into place
  4. check for a Velopack launcher update against the feed at https://updates.jakubkadlec.dev/launcher (skipped cleanly on non-installed dev builds)
  5. start Metin2.exe

A log is written to both stdout and <client>/.updates/launcher.log. On a plain dotnet run (no client root yet), the log falls back to %LOCALAPPDATA%/Metin2Launcher/launcher.log on Windows or $XDG_DATA_HOME/Metin2Launcher/launcher.log on Linux.

Project layout

src/Metin2Launcher/
  Program.cs               orchestration + Velopack bootstrap + game launch
  Config/LauncherConfig.cs hardcoded URLs, timeouts, public key
  Manifest/                DTOs, HTTP loader, Ed25519 verifier
  Transfer/                streaming sha256, Range-resume blob downloader
  Apply/                   atomic staging -> final move
  GameLaunch/              CreateProcess wrapper
  Logging/                 hand-rolled file + stdout logger
tests/Metin2Launcher.Tests/

Dependencies

  • NSec.Cryptography — libsodium-backed Ed25519 (.NET 8 BCL does not have it).
  • Velopack — launcher self-update. Only used for the launcher binary itself; game assets (~4 GB) are handled by our own patcher code.
  • xunit / xunit.runner.visualstudio — tests.

Signing key

LauncherConfig.PublicKeyHex is the Ed25519 public key that every manifest must verify against:

1d2b63751ea0e0354d28e7eb4ec175919a01518b0bcf5878f0a3aa8e7c6ce2bc

The matching private key lives only on the release machine at ~/.config/metin/launcher-signing-key with chmod 600, is never committed to any repo, and is used by scripts/sign-manifest.py in the m2dev-client repo to sign manifest.json before upload. Rotation is done by shipping a new launcher binary that embeds the new public key; the rotation flow is documented in m2dev-client/docs/update-manager.md.

Description
Self-updating launcher for the Metin2 client (dev repo)
Readme MIT 92 MiB
Languages
C# 96.4%
Shell 3.6%