Files
windrose-server/SETUP.md
Marek Lenczewski 51b6309584 init
2026-04-17 17:33:23 +02:00

340 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Windrose Dedicated Server auf Hetzner CPX22 — Runbook
Anleitung für einen Agent zum Aufsetzen eines privaten Windrose-Dedicated-Servers
(max. 4 Spieler, Invite-Code-only) auf einem frischen Hetzner CPX22 mit Ubuntu 24.04.
## Eckdaten
- **Hardware:** CPX22 = 2 vCPU AMD EPYC, **4 GB RAM**, 80 GB NVMe SSD.
- **RAM-Risiko:** Windrose verlangt offiziell min. 8 GB für 2 Spieler. Wir kompensieren mit
8 GB Swap und cappen `MaxPlayerCount=4`. Wenn Performance schlecht ist → Hetzner-Console
→ Rescale auf CPX31 (8 GB RAM, ~€13/Monat), kein Datenverlust.
- **Server ist Windows-only.** Lauf über Wine + Xvfb (headless).
- **Setup-Variante:** Bare-Metal mit systemd (geringerer RAM-Footprint als Docker).
- **Connectivity:** Spieler joinen per Invite-Code. UDP 7777/7778 müssen aber trotzdem
reachable sein (NAT-Punch-Through über die öffentliche IP).
- **SteamCMD App-ID:** `4129620` (Windrose Dedicated Server, anonymous).
## Regeln für den ausführenden Agent
- Schritte **2, 3, 6 als root**, Schritte **4, 5, 7 als User `windrose`** (`sudo -iu windrose`).
- **Idempotent** arbeiten: vor jedem destruktiven Schritt prüfen, ob der Zielzustand schon
existiert (Swapfile, WineHQ-Repo, systemd-Unit, …) und ggf. überspringen.
- **Nicht-interaktiv**: bei `apt install` immer `-y`. Für `steamcmd` vorher die Lizenz
per debconf akzeptieren (siehe Schritt 2.6).
- Nach Schritt 5.2 den **InviteCode** aus `ServerDescription.json` extrahieren und im
finalen Report zurückmelden — den braucht der User zum Joinen.
- **Verifikation (Schritt 8) zwingend** ausführen und Ergebnisse berichten.
- **Nicht** ohne Rückfrage den Server neu starten, Snapshots löschen oder die
Hetzner-Cloud-Firewall ändern. Bei Fehlschlag: Log + Hypothese melden, nicht stumm
reparieren.
- Erwartete Dauer: 1525 Min. (SteamCMD-Download ~3 GB + winetricks vcrun2022).
---
## Schritt 1 — Hetzner-Console (manuell durch den User)
Vor dem Server-Setup in der Hetzner Cloud Console:
1. **Snapshot** des frischen CPX22 anlegen (Rollback-Punkt).
2. **Cloud-Firewall** anhängen mit Inbound-Regeln:
- SSH: TCP 22 (am besten nur eigene IP)
- Windrose Game: **UDP 7777** (any source)
- Windrose Query: **UDP 7778** (any source)
3. Outbound: alles erlauben (default). Wichtig für STUN/TURN nach
`*.windrose.support:3478` (UDP/TCP).
---
## Schritt 2 — System-Vorbereitung (als root)
```bash
# 2.1 Basis-Update
apt update && apt -y full-upgrade
# 2.2 Server-User
id windrose >/dev/null 2>&1 || adduser --disabled-password --gecos "" windrose
usermod -aG sudo windrose
# 2.3 Swap (8 GB)
if ! swapon --show | grep -q '/swapfile'; then
fallocate -l 8G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstab
fi
echo 'vm.swappiness=10' > /etc/sysctl.d/99-swappiness.conf
sysctl --system
# 2.4 Multiarch + Repos
dpkg --add-architecture i386
add-apt-repository -y multiverse
add-apt-repository -y universe
# 2.5 WineHQ-Repo (stable, aktueller als Distro-Wine)
mkdir -pm755 /etc/apt/keyrings
wget -O /etc/apt/keyrings/winehq-archive.key https://dl.winehq.org/wine-builds/winehq.key
wget -NP /etc/apt/sources.list.d/ \
https://dl.winehq.org/wine-builds/ubuntu/dists/noble/winehq-noble.sources
apt update
# 2.6 SteamCMD-Lizenz vorab akzeptieren (sonst hängt apt interaktiv)
echo steam steam/question select "I AGREE" | debconf-set-selections
echo steam steam/license note '' | debconf-set-selections
# 2.7 Pakete
DEBIAN_FRONTEND=noninteractive apt install -y --install-recommends winehq-stable
DEBIAN_FRONTEND=noninteractive apt install -y \
software-properties-common lib32gcc-s1 steamcmd \
xvfb cabextract winbind ufw fail2ban unzip jq wget
# 2.8 winetricks
if [ ! -x /usr/local/bin/winetricks ]; then
wget -O /usr/local/bin/winetricks \
https://raw.githubusercontent.com/Winetricks/winetricks/master/src/winetricks
chmod +x /usr/local/bin/winetricks
fi
```
---
## Schritt 3 — Lokale Firewall + SSH-Härtung (als root)
```bash
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 7777/udp
ufw allow 7778/udp
ufw --force enable
systemctl enable --now fail2ban
```
In `/etc/ssh/sshd_config` setzen (nur wenn SSH-Key bereits funktioniert):
```
PasswordAuthentication no
PermitRootLogin prohibit-password
```
Dann `systemctl reload ssh`.
---
## Schritt 4 — Server-Installation (als User `windrose`)
```bash
sudo -iu windrose bash <<'EOF'
mkdir -p ~/steam ~/log ~/backup
cd ~/steam
# 4.1 SteamCMD: Server-Files (forciert Windows-Platform)
steamcmd \
+@sSteamCmdForcePlatformType windows \
+force_install_dir /home/windrose/steam/windrose \
+login anonymous \
+app_update 4129620 validate \
+quit
# 4.2 Wine-Prefix initialisieren
export WINEPREFIX=/home/windrose/steam/windrose/pfx
export WINEARCH=win64
xvfb-run -a wineboot --init
xvfb-run -a winetricks -q win10 vcrun2022
EOF
```
> `vcrun2022` verhindert die häufigsten "missing MSVC runtime"-Crashes.
---
## Schritt 5 — Server-Config
### 5.1 Start-Script `/home/windrose/steam/start_windrose.sh`
```bash
#!/usr/bin/env bash
set -e
export WINEPREFIX=/home/windrose/steam/windrose/pfx
export WINEARCH=win64
export WINEDEBUG=-all
export DISPLAY=:99
if ! pgrep -f "Xvfb :99" >/dev/null; then
Xvfb :99 -screen 0 1024x768x16 &
sleep 2
fi
cd /home/windrose/steam/windrose
exec wine64 WindroseServer.exe
```
`chmod +x /home/windrose/steam/start_windrose.sh`
### 5.2 Erst-Start (Config-Generierung)
Als `windrose`:
```bash
~/steam/start_windrose.sh &
SERVER_PID=$!
sleep 60 # warten bis Erst-Init durch ist
kill $SERVER_PID 2>/dev/null || true
sleep 5
```
Damit werden `ServerDescription.json` und der erste Welt-Ordner unter
`R5/Saved/SaveProfiles/Default/RocksDB/<version>/Worlds/<world_id>/` erzeugt.
### 5.3 ServerDescription patchen
Datei finden + patchen:
```bash
CFG=$(find /home/windrose/steam/windrose -maxdepth 4 -name ServerDescription.json | head -n1)
# InviteCode für Report extrahieren
INVITE=$(jq -r '.ServerDescription_Persistent.InviteCode' "$CFG")
# ServerName + MaxPlayer setzen, kein Passwort
jq '.ServerDescription_Persistent.ServerName = "Marhas Windrose"
| .ServerDescription_Persistent.MaxPlayerCount = 4
| .ServerDescription_Persistent.IsPasswordProtected = false
| .ServerDescription_Persistent.Password = ""' \
"$CFG" > "$CFG.tmp" && mv "$CFG.tmp" "$CFG"
echo "INVITE_CODE=$INVITE" # !!! Dem User zurückmelden !!!
```
---
## Schritt 6 — systemd Service (als root)
`/etc/systemd/system/windrose.service`:
```ini
[Unit]
Description=Windrose Dedicated Server
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=windrose
Group=windrose
WorkingDirectory=/home/windrose/steam
ExecStart=/home/windrose/steam/start_windrose.sh
Restart=always
RestartSec=10
StandardOutput=append:/home/windrose/log/windrose.log
StandardError=append:/home/windrose/log/windrose.log
MemoryHigh=3G
MemoryMax=3500M
[Install]
WantedBy=multi-user.target
```
Aktivieren:
```bash
systemctl daemon-reload
systemctl enable --now windrose
systemctl status windrose --no-pager
```
---
## Schritt 7 — Update- & Backup-Helfer (als User `windrose`)
### 7.1 Update-Script `/home/windrose/steam/update_windrose.sh`
```bash
#!/usr/bin/env bash
set -e
sudo systemctl stop windrose
steamcmd \
+@sSteamCmdForcePlatformType windows \
+force_install_dir /home/windrose/steam/windrose \
+login anonymous \
+app_update 4129620 validate \
+quit
sudo systemctl start windrose
```
### 7.2 Backup-Script `/home/windrose/steam/backup_windrose.sh`
```bash
#!/usr/bin/env bash
set -e
TS=$(date +%Y%m%d-%H%M%S)
SRC=/home/windrose/steam/windrose/R5/Saved
DST=/home/windrose/backup/windrose-${TS}.tar.gz
tar -czf "$DST" -C "$(dirname "$SRC")" "$(basename "$SRC")"
ls -1t /home/windrose/backup/windrose-*.tar.gz | tail -n +8 | xargs -r rm
```
Beide ausführbar machen: `chmod +x ~/steam/{update,backup}_windrose.sh`
### 7.3 Cron (täglich 04:00) als `windrose`
```
0 4 * * * /home/windrose/steam/backup_windrose.sh >> /home/windrose/log/backup.log 2>&1
```
---
## Schritt 8 — Verifikation (zwingend)
```bash
systemctl status windrose --no-pager
ss -ulnp | grep -E '7777|7778' || echo "WARN: Ports nicht gebunden"
free -h
tail -n 50 /home/windrose/log/windrose.log
vmstat 5 3 # Swap-Aktivität: si/so sollten ~0 sein im Idle
```
Erwartet:
- Service `active (running)`
- UDP 7777 + 7778 gebunden
- Idle-RAM <2 GB belegt, Swap fast leer
- Log zeigt "Server ready" o.ä., keine Wine-Stacktraces
Im Spiel-Client: "Join via Invite Code" → den im Report ausgegebenen Code eingeben.
---
## Eskalation bei Problemen
- **OOM / extreme Lags:** Hetzner-Console → Server stoppen → "Rescale" auf CPX31 → starten. ~3 Min., kein Datenverlust.
- **Wine-Crashes beim Start:** `journalctl -u windrose -n 200` nach `wine: Unhandled` durchsuchen, ggf. `winetricks -q dotnet48` nachschieben oder `winehq-staging` statt `winehq-stable` testen.
- **Clients finden Server nicht:** Hetzner-Cloud-Firewall checken (UDP 7777/7778 wirklich offen?), Outbound zu `*.windrose.support:3478` nicht geblockt.
---
## Kritische Dateien
| Pfad | Zweck |
|---|---|
| `/home/windrose/steam/windrose/WindroseServer.exe` | Server-Binary |
| `/home/windrose/steam/windrose/ServerDescription.json` | ServerName, InviteCode, MaxPlayer |
| `…/R5/Saved/SaveProfiles/Default/RocksDB/<ver>/Worlds/<id>/WorldDescription.json` | Welt-Konfig |
| `/home/windrose/steam/windrose/pfx` | Wine-Prefix |
| `/home/windrose/steam/start_windrose.sh` | Launcher (Xvfb + wine) |
| `/etc/systemd/system/windrose.service` | systemd-Unit |
| `/home/windrose/log/windrose.log` | Server-Log |
| `/home/windrose/backup/` | tägliche Save-Backups |
---
## Final-Report-Template (vom Agent zurück an User)
```
Status: success | failed
Invite-Code: <CODE>
Server-Name: Marhas Windrose
Max-Spieler: 4
Public-IPv4: <hetzner-ip>
Service-Status: <output von systemctl status windrose>
RAM-Idle: <free -h>
Swap-Aktivität: <vmstat>
Letzte Log-Zeilen: <tail -n 20 windrose.log>
Auffälligkeiten / Warnings: …
```