forked from metin-server/m2dev-client
Compare commits
6 Commits
claude/upd
...
claude/mak
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5d046c91a | ||
|
|
c2804fe1a6 | ||
| 1dba3e3c91 | |||
| 9c95590099 | |||
|
|
dd0643137f | ||
|
|
0aa8361f09 |
91
docs/linux-wine.md
Normal file
91
docs/linux-wine.md
Normal 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.
|
||||||
@@ -163,14 +163,23 @@ Files under `.updates/` are created by the launcher. The user shouldn't touch th
|
|||||||
|
|
||||||
1. On a trusted machine (not random laptop), with the private signing key present:
|
1. On a trusted machine (not random laptop), with the private signing key present:
|
||||||
```bash
|
```bash
|
||||||
./scripts/make-release.sh --version 2026.04.14-1 --source /path/to/fresh/client
|
./scripts/make-release.sh --source /path/to/fresh/client --version 2026.04.14-1 \
|
||||||
|
--previous 2026.04.13-3 --notes notes.md --dry-run
|
||||||
```
|
```
|
||||||
2. The script walks the client directory, computes sha256 for each file, writes a `manifest.json`, signs it, and produces a release directory `release/2026.04.14-1/` containing the manifest, its signature, and only the new blobs (ones not already present on the server).
|
[`scripts/make-release.sh`](../scripts/make-release.sh) is the single entry
|
||||||
|
point for the v1 manual flow. It drives `make-manifest.py` + `sign-manifest.py`,
|
||||||
|
builds the content-addressed blob tree under `files/<hash[0:2]>/<hash>` with
|
||||||
|
hardlink-based deduplication, archives the signed manifest into
|
||||||
|
`manifests/<version>.json`, and — unless `--dry-run` is passed — rsyncs the
|
||||||
|
blob tree first and the `manifest.json` + `manifest.json.sig` pair last so the
|
||||||
|
release becomes visible atomically. Flags: `--key` (default
|
||||||
|
`~/.config/metin/launcher-signing-key`, must be mode 600), `--out` (default
|
||||||
|
`/tmp/release-<version>`), `--force` to overwrite a non-empty out dir, `--yes`
|
||||||
|
to skip the interactive rsync confirmation, `--rsync-target <user@host:/path>`
|
||||||
|
to override the upload destination.
|
||||||
|
2. The script walks the client directory, computes sha256 for each file, writes a `manifest.json`, signs it, and produces a release directory containing the manifest, its signature, and the deduplicated blob tree.
|
||||||
3. Human review: diff the new manifest against the previous one, sanity-check size and file count.
|
3. Human review: diff the new manifest against the previous one, sanity-check size and file count.
|
||||||
4. `rsync` the release directory to the VPS:
|
4. Re-run without `--dry-run` (same args) to rsync to the VPS. The script prints the target and waits for confirmation unless `--yes` is passed.
|
||||||
```bash
|
|
||||||
rsync -av release/2026.04.14-1/ mt2.jakubkadlec.dev@mt2.jakubkadlec.dev:/var/www/updates.jakubkadlec.dev/
|
|
||||||
```
|
|
||||||
5. Verify from a second machine: `curl` the manifest, check signature, check a random blob.
|
5. Verify from a second machine: `curl` the manifest, check signature, check a random blob.
|
||||||
6. Tag the release in git.
|
6. Tag the release in git.
|
||||||
|
|
||||||
|
|||||||
169
scripts/make-release.sh
Executable file
169
scripts/make-release.sh
Executable file
@@ -0,0 +1,169 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# make-release.sh — assemble, sign, and (optionally) publish a client release.
|
||||||
|
#
|
||||||
|
# Drives scripts/make-manifest.py + scripts/sign-manifest.py, then builds the
|
||||||
|
# content-addressed blob tree the launcher pulls from. See docs/update-manager.md
|
||||||
|
# for the end-to-end design; this script is the v1 manual publishing flow.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# scripts/make-release.sh --source <client-dir> --version <version> \
|
||||||
|
# [--previous <version>] [--notes <file>] \
|
||||||
|
# [--key <path>] [--out <path>] [--force] \
|
||||||
|
# [--dry-run] [--yes] [--rsync-target <user@host:/path>]
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# -------- arg parsing --------
|
||||||
|
SOURCE=""
|
||||||
|
VERSION=""
|
||||||
|
PREVIOUS=""
|
||||||
|
NOTES_FILE=""
|
||||||
|
KEY="${HOME}/.config/metin/launcher-signing-key"
|
||||||
|
OUT=""
|
||||||
|
FORCE=0
|
||||||
|
DRY_RUN=0
|
||||||
|
YES=0
|
||||||
|
RSYNC_TARGET="mt2.jakubkadlec.dev@mt2.jakubkadlec.dev:/var/www/updates.jakubkadlec.dev/"
|
||||||
|
|
||||||
|
die() { echo "error: $*" >&2; exit 1; }
|
||||||
|
say() { echo "[make-release] $*"; }
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--source) SOURCE="$2"; shift 2 ;;
|
||||||
|
--version) VERSION="$2"; shift 2 ;;
|
||||||
|
--previous) PREVIOUS="$2"; shift 2 ;;
|
||||||
|
--notes) NOTES_FILE="$2"; shift 2 ;;
|
||||||
|
--key) KEY="$2"; shift 2 ;;
|
||||||
|
--out) OUT="$2"; shift 2 ;;
|
||||||
|
--force) FORCE=1; shift ;;
|
||||||
|
--dry-run) DRY_RUN=1; shift ;;
|
||||||
|
--yes) YES=1; shift ;;
|
||||||
|
--rsync-target) RSYNC_TARGET="$2"; shift 2 ;;
|
||||||
|
-h|--help) sed -n '1,15p' "$0"; exit 0 ;;
|
||||||
|
*) die "unknown arg: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -n "$SOURCE" ]] || die "--source is required"
|
||||||
|
[[ -n "$VERSION" ]] || die "--version is required"
|
||||||
|
[[ -d "$SOURCE" ]] || die "source dir does not exist: $SOURCE"
|
||||||
|
[[ -f "$SOURCE/Metin2.exe" ]] || die "source does not look like a client dir (missing Metin2.exe): $SOURCE"
|
||||||
|
[[ -f "$KEY" ]] || die "signing key not found: $KEY"
|
||||||
|
|
||||||
|
KEY_MODE=$(stat -c '%a' "$KEY")
|
||||||
|
[[ "$KEY_MODE" == "600" ]] || die "signing key $KEY must be mode 600, got $KEY_MODE"
|
||||||
|
|
||||||
|
[[ -n "$NOTES_FILE" && ! -f "$NOTES_FILE" ]] && die "notes file not found: $NOTES_FILE"
|
||||||
|
|
||||||
|
: "${OUT:=/tmp/release-${VERSION}}"
|
||||||
|
SOURCE=$(cd "$SOURCE" && pwd)
|
||||||
|
OUT_ABS=$(mkdir -p "$OUT" && cd "$OUT" && pwd)
|
||||||
|
|
||||||
|
if [[ -n "$(ls -A "$OUT_ABS" 2>/dev/null)" && "$FORCE" -ne 1 ]]; then
|
||||||
|
die "output directory $OUT_ABS is non-empty (use --force to overwrite)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
|
||||||
|
|
||||||
|
say "source: $SOURCE"
|
||||||
|
say "version: $VERSION"
|
||||||
|
say "out: $OUT_ABS"
|
||||||
|
say "key: $KEY"
|
||||||
|
|
||||||
|
# -------- [1/6] manifest --------
|
||||||
|
say "[1/6] building manifest"
|
||||||
|
mkdir -p "$OUT_ABS/manifests" "$OUT_ABS/files"
|
||||||
|
MANIFEST="$OUT_ABS/manifest.json"
|
||||||
|
|
||||||
|
mk_args=(--source "$SOURCE" --version "$VERSION" --out "$MANIFEST")
|
||||||
|
[[ -n "$PREVIOUS" ]] && mk_args+=(--previous "$PREVIOUS")
|
||||||
|
if [[ -n "$NOTES_FILE" ]]; then
|
||||||
|
notes_text=$(cat "$NOTES_FILE")
|
||||||
|
mk_args+=(--notes "$notes_text")
|
||||||
|
fi
|
||||||
|
python3 "$SCRIPT_DIR/make-manifest.py" "${mk_args[@]}"
|
||||||
|
|
||||||
|
# -------- [2/6] sign --------
|
||||||
|
say "[2/6] signing manifest"
|
||||||
|
python3 "$SCRIPT_DIR/sign-manifest.py" --manifest "$MANIFEST" --key "$KEY"
|
||||||
|
SIG="$MANIFEST.sig"
|
||||||
|
[[ -f "$SIG" ]] || die "signature not produced"
|
||||||
|
sig_len=$(stat -c '%s' "$SIG")
|
||||||
|
[[ "$sig_len" == "64" ]] || die "signature is $sig_len bytes, expected 64"
|
||||||
|
|
||||||
|
# -------- [3/6] archive historical manifest --------
|
||||||
|
say "[3/6] archiving historical manifest -> manifests/${VERSION}.json"
|
||||||
|
cp -f "$MANIFEST" "$OUT_ABS/manifests/${VERSION}.json"
|
||||||
|
cp -f "$SIG" "$OUT_ABS/manifests/${VERSION}.json.sig"
|
||||||
|
|
||||||
|
# -------- [4/6] blob tree --------
|
||||||
|
say "[4/6] building content-addressed blob tree"
|
||||||
|
|
||||||
|
# Extract (path, sha256) pairs for launcher + every file entry.
|
||||||
|
mapfile -t PAIRS < <(jq -r '
|
||||||
|
([.launcher] + .files)
|
||||||
|
| .[]
|
||||||
|
| "\(.sha256)\t\(.path)"
|
||||||
|
' "$MANIFEST")
|
||||||
|
|
||||||
|
total_entries=${#PAIRS[@]}
|
||||||
|
unique_count=0
|
||||||
|
dedup_count=0
|
||||||
|
bytes_written=0
|
||||||
|
|
||||||
|
declare -A SEEN
|
||||||
|
for pair in "${PAIRS[@]}"; do
|
||||||
|
hash="${pair%%$'\t'*}"
|
||||||
|
rel="${pair#*$'\t'}"
|
||||||
|
src="$SOURCE/$rel"
|
||||||
|
[[ -f "$src" ]] || die "file in manifest missing from source: $rel"
|
||||||
|
if [[ -n "${SEEN[$hash]:-}" ]]; then
|
||||||
|
dedup_count=$((dedup_count + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
SEEN[$hash]=1
|
||||||
|
unique_count=$((unique_count + 1))
|
||||||
|
prefix="${hash:0:2}"
|
||||||
|
dst_dir="$OUT_ABS/files/$prefix"
|
||||||
|
dst="$dst_dir/$hash"
|
||||||
|
mkdir -p "$dst_dir"
|
||||||
|
if [[ -f "$dst" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
# Try hardlink first, fall back to copy across filesystems.
|
||||||
|
if ! cp -l "$src" "$dst" 2>/dev/null; then
|
||||||
|
cp "$src" "$dst"
|
||||||
|
fi
|
||||||
|
sz=$(stat -c '%s' "$dst")
|
||||||
|
bytes_written=$((bytes_written + sz))
|
||||||
|
done
|
||||||
|
|
||||||
|
say " entries: $total_entries unique blobs: $unique_count deduped: $dedup_count"
|
||||||
|
say " bytes written: $bytes_written"
|
||||||
|
|
||||||
|
# -------- [5/6] layout summary --------
|
||||||
|
say "[5/6] final layout:"
|
||||||
|
(cd "$OUT_ABS" && find . -maxdepth 2 -mindepth 1 -printf ' %p\n' | sort | head -40)
|
||||||
|
|
||||||
|
# -------- [6/6] rsync --------
|
||||||
|
if [[ "$DRY_RUN" -eq 1 ]]; then
|
||||||
|
say "[6/6] --dry-run set, skipping rsync. target would be: $RSYNC_TARGET"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
say "[6/6] rsync target: $RSYNC_TARGET"
|
||||||
|
if [[ "$YES" -ne 1 ]]; then
|
||||||
|
read -r -p "continue? [y/N] " ans
|
||||||
|
[[ "$ans" == "y" || "$ans" == "Y" ]] || die "aborted by user"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Stage 1: everything except manifest.json(.sig) — blobs and historical archive.
|
||||||
|
rsync -av --delay-updates --checksum --omit-dir-times --no-perms \
|
||||||
|
--exclude 'manifest.json' --exclude 'manifest.json.sig' \
|
||||||
|
"$OUT_ABS"/ "$RSYNC_TARGET"
|
||||||
|
|
||||||
|
# Stage 2: manifest + signature, so the new release becomes visible last.
|
||||||
|
rsync -av --checksum --omit-dir-times --no-perms \
|
||||||
|
"$MANIFEST" "$SIG" "$RSYNC_TARGET"
|
||||||
|
|
||||||
|
say "done."
|
||||||
97
scripts/setup-wine-prefix.sh
Executable file
97
scripts/setup-wine-prefix.sh
Executable 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"
|
||||||
Reference in New Issue
Block a user