# Runbook: native Windows launch test **Audience:** anyone on the team with a Windows 10/11 box and Visual Studio Build Tools 2022 installed. Primary target is Jakub or kolega 4. **Goal:** end-to-end verification that `Metin2Launcher.exe` (from `jann/metin-launcher`) and `Metin2.exe` (from `metin-server/m2dev-client-src`) actually work on native Windows. So far the client is only confirmed working under Linux+Wine (`m2dev-client/docs/linux-wine.md`). A Windows native run closes that gap before anyone else wastes time tracking Wine-specific artefacts. This runbook is Windows-first by design and aligns with this repo's `AGENTS.md` (Windows is the canonical target for `m2dev-client-src`). Nothing here modifies the C++ source — you only build and run. **Time budget:** ~2 h from a clean Windows box to "character walking on the first map". --- ## 0. Scope and non-goals Covered: - Installing the toolchain - Cloning both source repos + the runtime client assets - Building the C++ client (`Metin2.exe`) with MSVC - Building the launcher (`Metin2Launcher.exe`) with `dotnet publish` - First launch, login, character create, entering game - What to capture when anything breaks - Common Windows-specific pitfalls and their fixes - How to roll back to a clean client if you break the install - Where to file findings Explicitly NOT covered: - Any source-level debugging. If something crashes in C++ land, capture the logs + dump and file an issue; don't patch. - Running the server. The live VPS `mt2.jakubkadlec.dev` is what you'll be connecting to. Do not try to start a local server as part of this test. - Packaging, signing or distribution. That's a separate flow. --- ## 1. Prerequisites Install these on the Windows box **in this order**. Skip items you already have. | Tool | Why | Installer | | --------------------------------- | ------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | | Visual Studio Build Tools 2022 | MSVC `cl.exe`, Windows SDK, CMake integration | — during install tick **Desktop development with C++** (brings MSVC v143, Windows 11 SDK, CMake). | | Git for Windows | `git`, `bash`, line-ending handling | | | .NET 8 SDK (x64) | `dotnet publish` for the launcher | | | PowerShell 7.4+ | better terminal, required for copy-paste blocks below | | | (optional) Windows Terminal | Multi-tab terminal, much nicer than conhost | Microsoft Store | **Do not install a separate CMake** unless you already use one; the Build Tools ship a recent CMake and the `Developer PowerShell for VS 2022` shortcut puts it on `PATH`. ### Verify the toolchain Open **Developer PowerShell for VS 2022** (Start menu → "Developer PowerShell") and run: ```powershell cl # expected: Microsoft (R) C/C++ Optimizing Compiler Version 19.4x cmake --version # expected: cmake version 3.28+ git --version dotnet --list-sdks # must show an 8.0.x entry ``` **If any of these fail:** the rest of the runbook won't work. Re-run the Build Tools installer and tick **Desktop development with C++**. Close and reopen Developer PowerShell afterwards. --- ## 2. Choose a working directory Pick a path that is **NOT under OneDrive, Documents, Desktop, or `C:\Program Files`**. OneDrive sync mangles pack files and locks them mid-build; `Program Files` needs admin writes and triggers UAC prompts per-file. Recommended: ```powershell $root = "C:\dev\metin" New-Item -ItemType Directory -Force -Path $root | Out-Null Set-Location $root ``` From here on, all path snippets assume `C:\dev\metin` as the root. --- ## 3. Clone the three repos All three are on `gitea.jakubkadlec.dev`. You need an SSH key on that server (same one you use for the rest of the project). If you've never cloned from Gitea before, use HTTPS + a Gitea personal access token instead — both work. ```powershell Set-Location C:\dev\metin # 1) C++ client source (this repo) git clone ssh://git@gitea.jakubkadlec.dev:2222/metin-server/m2dev-client-src.git # 2) C# launcher git clone ssh://git@gitea.jakubkadlec.dev:2222/jann/metin-launcher.git # 3) client runtime assets (packs, bgm, config, log dir, Metin2.exe prebuilt) # Skip this if you already have a populated client\ folder from an older # Windows install. git clone ssh://git@gitea.jakubkadlec.dev:2222/metin-server/m2dev-client.git client ``` `m2dev-client` is ~4 GB because it contains the packed assets (`pack\*.pck`, `bgm\`, `assets\`). Expect the clone to take several minutes. Git LFS is not used — everything is vanilla git, large files included. Expected layout after this step: ``` C:\dev\metin\ ├── m2dev-client-src\ ← C++ source, CMakeLists.txt at root ├── metin-launcher\ ← C# source, Launcher.sln at root └── client\ ← runtime: assets\, pack\, bgm\, config\, log\, Metin2.exe (prebuilt) ``` **If `client\Metin2.exe` already works for you** you can skip the C++ build in step 4 and only replace `Metin2.exe` if step 4 produces a newer one. --- ## 4. Build the C++ client (`Metin2.exe`) From **Developer PowerShell for VS 2022** (important — regular PowerShell won't see `cl.exe`): ```powershell Set-Location C:\dev\metin\m2dev-client-src New-Item -ItemType Directory -Force -Path build | Out-Null Set-Location build cmake .. -G "Visual Studio 17 2022" -A Win32 2>&1 | Tee-Object ..\cmake-configure.log cmake --build . --config RelWithDebInfo 2>&1 | Tee-Object ..\cmake-build.log ``` Notes: - `-A Win32` is intentional. The client is 32-bit because of FMOD, Granny 2, the embedded Python, and decades of assumptions in EterLib. A 64-bit build will not produce a runnable exe today. - Use `RelWithDebInfo` so we get a `.pdb` next to the exe for crash dumps. - If the build fails, the full log is in `cmake-build.log` next to the build dir. **Attach that file verbatim** when filing the issue — do not paraphrase. **Expected output:** ``` C:\dev\metin\m2dev-client-src\build\...\RelWithDebInfo\Metin2.exe C:\dev\metin\m2dev-client-src\build\...\RelWithDebInfo\Metin2.pdb ``` Copy the exe into the runtime client directory, **overwriting** the prebuilt one (keep a backup): ```powershell $client = "C:\dev\metin\client" Copy-Item $client\Metin2.exe $client\Metin2.exe.backup -ErrorAction SilentlyContinue $built = Get-ChildItem -Recurse -Filter Metin2.exe C:\dev\metin\m2dev-client-src\build | Where-Object { $_.FullName -like "*RelWithDebInfo*" } | Select-Object -First 1 Copy-Item $built.FullName $client\Metin2.exe -Force Copy-Item ($built.FullName -replace '\.exe$','.pdb') $client\ -Force ``` **If it fails:** - *Missing `cl.exe` / `link.exe`*: you're in the wrong shell. Use the Developer PowerShell shortcut. - *"Cannot open include file: 'd3d9.h'"*: Windows SDK is missing. Re-run Build Tools installer and tick the Windows 11 SDK. - *"LNK2019 unresolved external"* against FMOD / Granny / Python: the build tree is picking up the wrong vendored lib. Check `extern\` and `vendor\` subdirs and make sure your checkout is clean (`git status` should be empty). - *Build succeeds but no `Metin2.exe`*: check `cmake-build.log` for the actual target name — upstream may have it under a slightly different casing. --- ## 5. Build the launcher (`Metin2Launcher.exe`) From a **regular** PowerShell 7 window (Developer PowerShell also works, but `dotnet` doesn't need the MSVC env): ```powershell Set-Location C:\dev\metin\metin-launcher dotnet publish src\Metin2Launcher\Metin2Launcher.csproj ` -c Release ` -r win-x64 ` --self-contained ` -p:PublishSingleFile=true ` -p:IncludeNativeLibrariesForSelfExtract=true ` 2>&1 | Tee-Object publish.log ``` **Expected output:** ``` C:\dev\metin\metin-launcher\src\Metin2Launcher\bin\Release\net8.0\win-x64\publish\Metin2Launcher.exe ``` The single-file exe is ~80–120 MB because Avalonia + .NET runtime are bundled. That's normal. Copy it next to `Metin2.exe`: ```powershell Copy-Item ` C:\dev\metin\metin-launcher\src\Metin2Launcher\bin\Release\net8.0\win-x64\publish\Metin2Launcher.exe ` C:\dev\metin\client\Metin2Launcher.exe -Force ``` **If it fails:** - *"SDK 8.0.x not found"*: install .NET 8 SDK, then reopen the shell. - *Avalonia / Velopack restore errors*: check internet connectivity, the build pulls packages from nuget.org. Corporate proxies will need `NUGET_HTTP_PROXY` or equivalent. - *`dotnet test` fails* (if you chose to run it): the tests run on Linux in CI. On Windows they should still pass; if they don't, capture `publish.log` plus the failing test output and file an issue. --- ## 6. First run — launcher GUI ```powershell Set-Location C:\dev\metin\client .\Metin2Launcher.exe ``` **Expected behaviour:** 1. Avalonia window appears, 900×560 fixed size, with a banner, a status / progress panel on the left, and a news panel on the right. 2. Launcher attempts to fetch `https://updates.jakubkadlec.dev/manifest.json`. DNS for that host is currently broken on purpose; the launcher should time out and fall back to **"Server unreachable — using local client"**. 3. The **Play** button becomes enabled. 4. `C:\dev\metin\client\.updates\launcher.log` is created and starts getting lines written to it. **If the launcher window never shows up:** - Run from a terminal and watch stdout — Velopack bootstrapping errors land on stderr only. Redirect with `.\Metin2Launcher.exe 2>&1 | Tee-Object launcher-stdout.log`. - SmartScreen may have quarantined the exe because it's unsigned. Windows Defender → **Protection history** → Allow. Or right-click the exe → Properties → **Unblock**. - If you see `This app can't run on your PC`, you built for the wrong arch. The launcher is `win-x64` but must be run on 64-bit Windows (it is; Win32 apps work fine on x64). If you copied a `Metin2.exe` build instead, you'll see this; re-check step 5. **If the status panel stays at "Checking for updates..." forever:** - Expected to time out within ~15 s and flip to "Server unreachable". If it never does, the launcher is stuck in a DNS retry loop. Close it, grab `.updates\launcher.log`, and file the log — this is a launcher bug, not a client bug. --- ## 7. Play — reach the game 1. Click **Play**. `Metin2.exe` should start in a separate process within a couple of seconds. The launcher window should remain visible (or minimise, depending on the configured behaviour). 2. **Language bar** appears along the top of the Metin2 window. 3. The **Metin2 logo** screen animates. 4. A **server picker** shows one row: `01. Metin2` / `CH1 NORM`. - Select it and click OK. 5. Login screen: - Register a new account (the server allows on-the-fly registration — use a throwaway ID and password). Or use an existing account if you have one. - Proceed to character selection. 6. Create a fresh character of any class, any empire. Confirm. 7. You should land on the first map (Yongbi / Seoungmahn depending on empire). Move with `WASD`, open the menu with `ESC`, try the chat by pressing `Enter` and typing a line. **Acceptance bar for this runbook:** you reached the first map, you can move, chat works. Anything beyond that is a nice-to-have. **If `Metin2.exe` crashes on click-Play:** - Look in `C:\dev\metin\client\log\syserr.txt`. That file is how the client's legacy error reporter logs crashes. The last dozen lines are usually enough to classify the failure. - If Windows shows a `.exe has stopped working` dialog, check **Event Viewer → Windows Logs → Application** for the Faulting Module entry. - If it silently exits, the problem is usually missing DirectX runtime (d3dx9). Install the legacy DirectX End-User Runtimes (June 2010) from Microsoft. **If login fails with "Connection closed":** - This is the DNS breakage again. The launcher's fallback path does not help `Metin2.exe`, which reads its own `serverinfo` file. Check `C:\dev\metin\client\serverinfo.py` or whatever config points at the live server's IP. The audit branch has the current public IP of `mt2.jakubkadlec.dev`. --- ## 8. What to capture and send back Regardless of whether it worked or not, collect and attach these to a Gitea issue in `metin-server/m2dev-client-src` with label `windows-test`: | File | Why | | ------------------------------------------------- | ---------------------------------------------------- | | `client\.updates\launcher.log` | Launcher side — manifest fetch, update decisions, Play-button flow. | | `client\log\syserr.txt` | C++ client crashes and asserts. | | `client\log\packetdump.txt` (if present) | Last packets before disconnect/crash; rare but invaluable for login breaks. | | `m2dev-client-src\cmake-configure.log` | Build: CMake configuration output. | | `m2dev-client-src\cmake-build.log` | Build: MSVC compile output. **Verbatim**, not paraphrased. | | `metin-launcher\publish.log` | Launcher publish output. | | Screenshots | Any visual glitch, stuck screen, or error dialog. | | Windows version (`winver`) + MSVC version (`cl`) | Paste at top of issue. | | GPU + driver version | `dxdiag` → Save All Information → attach `DxDiag.txt`. | Zip the whole set: ```powershell $stamp = Get-Date -Format "yyyyMMdd-HHmmss" Compress-Archive -Path ` C:\dev\metin\client\.updates\launcher.log, ` C:\dev\metin\client\log\syserr.txt, ` C:\dev\metin\client\log\packetdump.txt, ` C:\dev\metin\m2dev-client-src\cmake-configure.log, ` C:\dev\metin\m2dev-client-src\cmake-build.log, ` C:\dev\metin\metin-launcher\publish.log ` -DestinationPath "$env:USERPROFILE\Desktop\windows-test-$stamp.zip" ` -ErrorAction SilentlyContinue ``` Missing files are fine — the `-ErrorAction SilentlyContinue` keeps the command going if one of them doesn't exist (e.g. `packetdump.txt` on a successful run). --- ## 9. Windows-specific gotchas Distilled from `m2dev-client/docs/linux-wine.md` (Wine oddities that *also* apply on Windows in different forms) and from general Windows experience with legacy 32-bit game clients: - **Tahoma font.** On fresh Wine prefixes you have to install it; on Windows it's always present, so this *should* be a non-issue. If text is missing on the login screen, check `fonts\Tahoma.ttf` is still in `C:\Windows\Fonts` and hasn't been replaced by a custom font manager. - **SmartScreen / Defender quarantine.** `Metin2Launcher.exe` and `Metin2.exe` are unsigned. On first run Windows will warn and may move them to quarantine. Either sign them (out of scope for this runbook), or Allow + Unblock per-file in Properties. - **UAC + `C:\Program Files`.** Don't install there. The client writes to `log\`, `config\`, `.updates\`, and into its own install dir; UAC will silently redirect writes to `%LOCALAPPDATA%\VirtualStore` and you'll chase phantom bugs. - **OneDrive / Documents / Desktop.** Same reason. OneDrive can lock files during sync, which corrupts pack reads. Use `C:\dev\metin`. - **Antivirus with heuristic scanning.** The 32-bit client, the embedded Python, the unsigned exe, plus FMOD unpacking into temp, together look like a toy virus to aggressive AVs. If the process is killed mid-load check the AV quarantine first. - **DirectX End-User Runtimes.** Modern Windows still doesn't install the d3dx9 helper DLLs by default. If the launcher works but `Metin2.exe` silently exits, install the DirectX End-User Runtimes (June 2010) package from Microsoft. - **Display scaling.** The client is DPI-unaware. On a 4K laptop with 150% scaling the window can render as a tiny 900×600 rectangle. Right- click `Metin2.exe` → Properties → Compatibility → **Change high DPI settings** → tick "Override high DPI scaling behavior" = System. - **Windows Firewall prompt on first connect.** The first time `Metin2.exe` tries to reach port 11000, Defender Firewall will ask. Allow on private networks at minimum. - **32-bit vs 64-bit confusion.** The client is strictly 32-bit. If a Python extension or native DLL is built x64 you'll get a load error on startup. Check the build arch on every custom DLL. - **Non-English locale.** The client's Python side assumes certain code pages. If you run on a system with a non-ASCII username (`C:\Users\ Jánek`), watch for path encoding issues in `launcher.log`. Use a plain-ASCII user or set `C:\dev\metin` from the root of the drive. --- ## 10. Rollback / reset to a known state If you've made a mess and want to start over without re-cloning 4 GB of assets: ```powershell Set-Location C:\dev\metin\client # Restore original Metin2.exe if you kept the backup if (Test-Path Metin2.exe.backup) { Copy-Item Metin2.exe.backup Metin2.exe -Force } # Wipe launcher + updater state Remove-Item -Recurse -Force .\.updates -ErrorAction SilentlyContinue Remove-Item -Recurse -Force .\log -ErrorAction SilentlyContinue New-Item -ItemType Directory -Path .\log | Out-Null # Remove the launcher exe; it'll be re-copied from the publish dir next time Remove-Item .\Metin2Launcher.exe -ErrorAction SilentlyContinue ``` The runtime asset tree (`pack\`, `assets\`, `bgm\`) is read-only during a normal run, so you almost never have to re-pull it. If you do need to reset everything to HEAD: ```powershell Set-Location C:\dev\metin\client git reset --hard origin/main git clean -fdx ``` **Do NOT** `git clean -fdx` inside `m2dev-client-src\` unless you're ready to re-download vendored third-party trees that may not be on disk elsewhere. --- ## 11. Where to report findings File a new issue in `metin-server/m2dev-client-src` on `gitea.jakubkadlec.dev`: - Title: `windows-test: `, e.g. `windows-test: launcher OK, Metin2.exe crashes in EterLib on first map`. - Label: `windows-test`. Create the label if it doesn't exist. - Body: a short timeline of what you did, which step in this runbook failed (e.g. "Step 7, after clicking OK in server picker"), and the zipfile from step 8 attached. - Assign Jan (`jann`) for triage. For launcher-only issues, cross-link to `jann/metin-launcher` with the same label. If everything worked end-to-end, still file an issue titled `windows-test: full run PASS ` with the zip attached — we want a reproducible baseline of a good Windows run on record. --- ## Appendix A — the absolute-minimum fast path For when you just want to verify "does it start at all" and don't care about building from source: 1. Clone `m2dev-client` → `C:\dev\metin\client`. 2. Download the latest CI artifact of `Metin2Launcher.exe` from `jann/metin-launcher` releases (once CI is wired; until then, build it yourself per step 5). 3. Drop the launcher next to `client\Metin2.exe`. 4. Run `Metin2Launcher.exe`. Click Play. Done. No MSVC needed. If this path fails but the full build path in steps 4–7 works, the bug is in the prebuilt binary, not in the source tree.