docs: add Linux Wine runtime guide and setup script #1

Merged
jakub merged 2 commits from jann/m2dev-client:claude/linux-wine-runtime into main 2026-04-14 10:30:08 +02:00
2 changed files with 188 additions and 0 deletions

91
docs/linux-wine.md Normal file
View File

@@ -0,0 +1,91 @@
# Running the client on Linux with Wine
This is an interim path for playing and testing on Linux while a native Linux port is a longer-term goal. Wine runs the unmodified Windows build of `Metin2.exe` / `Metin2_Debug.exe` directly. Verified to reach the character selection screen on Fedora 41 with Wine 10 Staging; other modern distros should work the same.
Use this when you want to:
- Smoke-test the Windows binary without rebooting into Windows
- Develop server-side with a live client connected from the same machine
- Run a dev loop without owning a Windows install
## Requirements
- A recent Wine (10.x Staging tested, 9.x stable should work). Older than 8 may be rough on D3D9.
- `winetricks` for installing MSVC runtime, D3DX9 helper DLLs, core fonts, and Tahoma
- A copy of the client deploy folder (the one containing `Metin2.exe`, `Metin2_Debug.exe`, `assets/`, `pack/`, `bgm/`, `config/`, `log/`). The whole folder is ~4.3 GB.
- ~7 GB free disk for the writable client copy plus the Wine prefix
On Fedora:
```bash
sudo dnf install -y wine winetricks
```
On Debian/Ubuntu (use the WineHQ repo for a modern version):
```bash
sudo apt install -y wine winetricks
```
## One-shot setup
The easiest way is the helper script in this repo:
```bash
./scripts/setup-wine-prefix.sh /path/to/windows/client ~/metin-wine
```
This will:
1. Copy the client folder to `~/metin-wine/client` (needs to be on a writable filesystem, so an NTFS read-only mount won't do).
2. Create a fresh Wine prefix at `~/metin-wine/prefix`.
3. Install `vcrun2022`, `d3dx9`, `corefonts`, and `tahoma` via winetricks.
4. Print the launch command.
See the script itself for exact steps if you prefer to run them manually.
## Why Tahoma is required
The client hard-codes Tahoma as its UI font. On Windows this is invisible because Tahoma ships with the OS; on a fresh Wine prefix it's missing, and the result is that the login screen renders layouts and backgrounds correctly but **all text is invisible**. You can reach the server picker and character selection, you just can't read anything. Installing Tahoma via `winetricks tahoma` fixes it in one shot.
If the login screen looks right but has no readable text, this is what you're seeing.
## Launching
After setup, the launch command is just:
```bash
cd ~/metin-wine/client
WINEPREFIX=~/metin-wine/prefix wine Metin2.exe
```
Use `Metin2_Debug.exe` instead of `Metin2.exe` if you want more verbose client-side logging via `OutputDebugString`. Wine will echo those to stderr when `WINEDEBUG` includes `+seh` or you pass `+outputdebugstring`. For normal play use `-all,+err`.
## Logs and debug output
Useful `WINEDEBUG` settings:
- `WINEDEBUG=-all,+err` — quiet, only real errors. Use this for normal play.
- `WINEDEBUG=-all,+loaddll,+module,+err` — shows which DLLs Wine loads, handy when the client crashes early with a missing DLL.
- `WINEDEBUG=-all,+err,+seh` — captures the client's own `OutputDebugString` calls via SEH, which is how metin2's internal logging surfaces. Very noisy but useful when diagnosing client-side issues ("CResource::Load file not exist X", "CPythonNonPlayer::LoadNonPlayerData", etc.).
Redirect to a file and grep the signal out of the noise:
```bash
WINEDEBUG=-all,+err,+seh wine Metin2_Debug.exe >wine-run.log 2>&1
grep -E 'OutputDebugString[AW] "' wine-run.log | sed 's/.*OutputDebugString[AW] //' | sort -u
```
The client also writes its own logs to `log/` inside the client folder. Those are plain text and more readable than the Wine SEH traces.
## Known quirks
- **Wayland:** works via XWayland, no special config. If the window opens minimized or off-screen, `Alt+Tab` to find it.
- **Read-only NTFS mount:** don't try to launch from a read-only mount of your Windows partition. The client creates and writes `log/`, `config/`, and cache files; on a read-only FS the launch will be confusing. Always copy to a writable location first. `setup-wine-prefix.sh` does this for you.
- **DXVK render state warnings:** lines like `D3D9DeviceEx::SetRenderState: Unhandled render state 163` in the log are harmless. DXVK doesn't implement every legacy D3D9 render state, but the ones metin2 cares about all work.
- **SEH dispatch spam:** `dispatch_exception code=4001000a` / `4001000c` are how Windows signals `OutputDebugStringW` / `OutputDebugStringA`. They're soft exceptions, not errors. They only show up if you enable `+seh` in `WINEDEBUG`.
- **First launch is slower:** DXVK compiles its shader pipelines on first run and writes a state cache. Subsequent launches are noticeably faster.
## When to stop using Wine
This guide is for the interim. The longer-term plan is a native Linux build of the client with a free-software replacement for Granny2 animation runtime. Until that lands, Wine is the way.

97
scripts/setup-wine-prefix.sh Executable file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env bash
# Set up a Wine prefix for running the Metin2 client on Linux.
# Idempotent: re-running on an existing prefix skips steps that are already done.
#
# Usage:
# ./scripts/setup-wine-prefix.sh <source-client-dir> <target-dir>
#
# Example:
# ./scripts/setup-wine-prefix.sh /mnt/windows_c/Users/me/metin/client ~/metin-wine
#
# Result layout:
# <target-dir>/client/ — writable copy of the client deploy folder
# <target-dir>/prefix/ — Wine prefix with required runtime deps installed
set -euo pipefail
if [[ $# -lt 2 ]]; then
echo "usage: $0 <source-client-dir> <target-dir>" >&2
echo " source-client-dir: path containing Metin2.exe, assets/, pack/, etc." >&2
echo " target-dir: directory to create (holds client/ and prefix/)" >&2
exit 2
fi
SRC=$1
DEST=$2
if [[ ! -f "$SRC/Metin2.exe" && ! -f "$SRC/Metin2_Debug.exe" ]]; then
echo "error: $SRC does not look like a client folder (no Metin2.exe or Metin2_Debug.exe)" >&2
exit 1
fi
for tool in wine winetricks; do
if ! command -v "$tool" >/dev/null 2>&1; then
echo "error: $tool not found in PATH. Install it via your package manager." >&2
exit 1
fi
done
CLIENT_DIR=$DEST/client
PREFIX_DIR=$DEST/prefix
mkdir -p "$DEST"
if [[ -d "$CLIENT_DIR" && -f "$CLIENT_DIR/Metin2.exe" ]] || [[ -d "$CLIENT_DIR" && -f "$CLIENT_DIR/Metin2_Debug.exe" ]]; then
echo "[1/3] client already present at $CLIENT_DIR, skipping copy"
else
echo "[1/3] copying client from $SRC to $CLIENT_DIR (this can take a minute)"
cp -a "$SRC" "$CLIENT_DIR"
fi
export WINEPREFIX=$PREFIX_DIR
export WINEARCH=win64
if [[ -f "$PREFIX_DIR/system.reg" ]]; then
echo "[2/3] wine prefix already exists at $PREFIX_DIR, skipping wineboot"
else
echo "[2/3] creating wine prefix at $PREFIX_DIR"
mkdir -p "$PREFIX_DIR"
wineboot --init >/dev/null 2>&1 || true
fi
# vcrun2022 — MSVC 2015-2022 runtime, required because the client is an MSVC build
# d3dx9 — D3DX9 helper DLLs (Wine implements d3d9 but not the d3dx9 helpers)
# corefonts — Arial/Courier/Times/etc., needed by some UI elements
# tahoma — the client hard-codes Tahoma as the UI font; without it, all text renders invisibly
VERBS=(vcrun2022 d3dx9 corefonts tahoma)
TO_INSTALL=()
for v in "${VERBS[@]}"; do
case $v in
vcrun2022)
if [[ -f "$PREFIX_DIR/drive_c/windows/system32/msvcp140.dll" ]]; then continue; fi ;;
d3dx9)
if [[ -f "$PREFIX_DIR/drive_c/windows/system32/d3dx9_43.dll" ]]; then continue; fi ;;
corefonts)
if [[ -f "$PREFIX_DIR/drive_c/windows/Fonts/arial.ttf" ]]; then continue; fi ;;
tahoma)
if [[ -f "$PREFIX_DIR/drive_c/windows/Fonts/tahoma.ttf" ]]; then continue; fi ;;
esac
TO_INSTALL+=("$v")
done
if [[ ${#TO_INSTALL[@]} -eq 0 ]]; then
echo "[3/3] all winetricks verbs already installed"
else
echo "[3/3] installing winetricks verbs: ${TO_INSTALL[*]}"
winetricks -q "${TO_INSTALL[@]}"
fi
echo
echo "done. to launch:"
echo
echo " cd $CLIENT_DIR"
echo " WINEPREFIX=$PREFIX_DIR wine Metin2.exe"
echo
echo "or with verbose client logging:"
echo
echo " WINEPREFIX=$PREFIX_DIR WINEDEBUG=-all,+err,+seh wine Metin2_Debug.exe"