init
This commit is contained in:
339
SETUP.md
Normal file
339
SETUP.md
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
# 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: 15–25 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: …
|
||||||
|
```
|
||||||
Reference in New Issue
Block a user