panel v0.9.1 — open-source game server manager
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# panel-native Empyrion runtime image.
|
||||
#
|
||||
# Empyrion's dedicated server is a Windows-only Unity build, so we bring
|
||||
# along just enough Wine + Xvfb to host it headlessly. SteamCMD is NOT
|
||||
# installed here — the panel's generic updater sidecar uses the official
|
||||
# steamcmd image and writes into our /game volume.
|
||||
#
|
||||
# Expected runtime layout inside the container:
|
||||
# /game — SteamCMD-installed Windows build (panel volume)
|
||||
# /game/DedicatedServer/DedicatedServer64/
|
||||
# EmpyrionDedicated.exe — the dedicated server binary
|
||||
# /game-saves — saves, dedicated.yaml, admin files
|
||||
# /entrypoint.sh — boot Xvfb, launch exe under Wine
|
||||
|
||||
FROM debian:12-slim
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive \
|
||||
TZ=Etc/UTC \
|
||||
LANG=en_US.UTF-8 \
|
||||
LC_ALL=en_US.UTF-8 \
|
||||
WINEDEBUG=-all \
|
||||
WINEPREFIX=/home/panel/.wine \
|
||||
DISPLAY=:99
|
||||
|
||||
# Wine pulls in ~500 MB of deps; keep to the 64-bit stack (Empyrion's
|
||||
# dedicated binary is x86_64). winetricks isn't strictly required — the
|
||||
# game's Mono runtime ships with the install — but it's 8 MB and saves
|
||||
# future debugging headaches.
|
||||
RUN dpkg --add-architecture i386 \
|
||||
&& apt-get update \
|
||||
&& apt-get install -y --no-install-recommends \
|
||||
ca-certificates \
|
||||
locales \
|
||||
tini \
|
||||
curl \
|
||||
xvfb \
|
||||
xauth \
|
||||
wine64 \
|
||||
wine \
|
||||
winbind \
|
||||
cabextract \
|
||||
procps \
|
||||
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \
|
||||
&& locale-gen \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Unprivileged user owning /game + /game-saves. The SteamCMD sidecar
|
||||
# writes as root (official image runs as root); we chown once at start
|
||||
# time so the long-running game process stays non-root.
|
||||
RUN groupadd -g 1000 panel \
|
||||
&& useradd -u 1000 -g 1000 -m -s /bin/bash panel \
|
||||
&& mkdir -p /game /game-saves \
|
||||
&& chown -R panel:panel /game /game-saves /home/panel
|
||||
|
||||
COPY entrypoint.sh /entrypoint.sh
|
||||
RUN chmod +x /entrypoint.sh
|
||||
|
||||
# Pre-staged EPM mod (TCP relay for the Eleon Mod API). Entrypoint copies
|
||||
# this into /game/Content/Mods/EPM/ on first boot so every empyrion
|
||||
# instance comes EAH-bridge-ready out of the box. See modules/empyrion/epm-mod/.
|
||||
COPY epm-mod/ /opt/panel-epm/
|
||||
|
||||
# Empyrion default ports (see module.yaml comment for reference).
|
||||
EXPOSE 30000/udp 30001/udp 30002/udp 30003/udp
|
||||
EXPOSE 30004/tcp
|
||||
|
||||
ENTRYPOINT ["/usr/bin/tini", "--", "/entrypoint.sh"]
|
||||
@@ -0,0 +1,35 @@
|
||||
# Empyrion - Galactic Survival
|
||||
|
||||
Panel-native dedicated server module for Empyrion.
|
||||
|
||||
## How it works
|
||||
|
||||
- Image: `panel-empyrion:latest` — built from `./Dockerfile` (Debian 12 + Wine + Xvfb + tini).
|
||||
- Game files: downloaded by the panel's SteamCMD updater sidecar into the `panel-<id>-game` named volume. Because Empyrion's dedicated server ships only as a Windows binary, the updater uses `+@sSteamCmdForcePlatformType windows` (declared in `module.yaml` via `platform: windows`).
|
||||
- Entrypoint: boots Xvfb, initializes a Wine prefix on first run, then runs `EmpyrionDedicated.exe` under `wine64` with `-batchmode -nographics -logFile -` so Unity pipes its log to stdout.
|
||||
|
||||
## First-run flow
|
||||
|
||||
1. Build the image (one-time):
|
||||
```bash
|
||||
docker build -t panel-empyrion:latest modules/empyrion
|
||||
```
|
||||
2. Create an instance in the panel (defaults are fine).
|
||||
3. Click **Update** on the instance — the SteamCMD sidecar downloads ~3.5 GB of Windows build into the `game` volume.
|
||||
4. Click **Start** — the entrypoint seeds `dedicated.yaml` from the shipped default, starts Xvfb, bootstraps Wine, and launches the server.
|
||||
|
||||
First boot is slow (Wine prefix init + Empyrion's own first-run map generation). Subsequent starts are 30–60s.
|
||||
|
||||
## Ports
|
||||
|
||||
| Name | Proto | Default | Notes |
|
||||
|--------|-------|---------|-----------------------------|
|
||||
| game | UDP | 30000 | Srv_Port — main traffic |
|
||||
| query | UDP | 30001 | Steam query |
|
||||
| client | UDP | 30002 | Client connect |
|
||||
| eac | UDP | 30003 | EasyAntiCheat |
|
||||
| csw | TCP | 30004 | CSW API (internal for now) |
|
||||
|
||||
## No RCON (yet)
|
||||
|
||||
Empyrion has no native RCON protocol. The in-game `admin` console commands can only be issued from a connected client. Third-party tools (Empyrion Web Admin / EAH) fill this gap and can be layered on later via plugins — not wired into this module for v1.
|
||||
@@ -0,0 +1,220 @@
|
||||
#!/bin/bash
|
||||
# panel-native Empyrion entrypoint.
|
||||
#
|
||||
# Contract:
|
||||
# /game — SteamCMD-installed Windows build of Empyrion (volume,
|
||||
# populated by the panel's SteamCMD updater sidecar).
|
||||
# If missing, exit with EX_CONFIG and a helpful message.
|
||||
# /game-saves — dedicated.yaml + Saves/ tree (volume).
|
||||
#
|
||||
# Runs as root for its opening stage (fix up ownership after the SteamCMD
|
||||
# sidecar wrote as root, nuke any stale X lock from a previous crash),
|
||||
# then drops to the `panel` user for Wine + Xvfb + the game. Wine refuses
|
||||
# to run as root, so the drop is load-bearing.
|
||||
#
|
||||
# On every start we verify the binary is there, start Xvfb, boot a minimal
|
||||
# Wine prefix if needed, then exec EmpyrionDedicated.exe under Wine with
|
||||
# stdout/stderr line-buffered so docker logs → agent → panel console
|
||||
# delivers live output.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
log() { printf '[panel-empyrion] %s\n' "$*"; }
|
||||
|
||||
# ----- stage 1: root-only bootstrap (runs once per container start) -----
|
||||
if [[ "$(id -u)" = "0" ]]; then
|
||||
# SteamCMD writes as root in its sidecar; chown once so the panel user
|
||||
# (UID 1000) can read/write the game dir without fiddling with perms.
|
||||
chown -R panel:panel /game /game-saves /home/panel 2>/dev/null || true
|
||||
|
||||
# Nuke any stale Xvfb state from a previous boot. The container's /tmp
|
||||
# persists across restarts (same writable layer), so /tmp/.X99-lock plus
|
||||
# /tmp/.X11-unix/X99 left by a SIGKILL'd Xvfb make the next start die
|
||||
# with "_XSERVTransMakeAllCOTSServerListeners: server already running".
|
||||
# Single rm wasn't catching every variant (verified live 2026-04-29);
|
||||
# nuking the whole dir + re-creating with the right mode is bulletproof.
|
||||
pkill -9 Xvfb 2>/dev/null || true
|
||||
rm -rf /tmp/.X11-unix /tmp/.X99-lock 2>/dev/null || true
|
||||
mkdir -p /tmp/.X11-unix && chmod 1777 /tmp/.X11-unix && chown root:root /tmp/.X11-unix 2>/dev/null || true
|
||||
|
||||
# Empyrion writes build-stamped logs to Z:\Logs\<buildnum> (= /Logs/...
|
||||
# because Wine maps Z:\ to /). Without this symlink panel gets an
|
||||
# UnauthorizedAccessException on first boot. Pointing it into the save
|
||||
# volume keeps logs persistent + visible in the Files tab.
|
||||
mkdir -p /game-saves/EmpyrionLogs
|
||||
chown -R panel:panel /game-saves/EmpyrionLogs
|
||||
if [[ ! -e /Logs || -L /Logs ]]; then
|
||||
ln -sfn /game-saves/EmpyrionLogs /Logs
|
||||
fi
|
||||
|
||||
# Re-exec as `panel` user. setpriv comes with util-linux in debian-slim.
|
||||
log "dropping privileges to panel (UID 1000)"
|
||||
exec setpriv --reuid=panel --regid=panel --clear-groups \
|
||||
--inh-caps=-all --bounding-set=-all \
|
||||
env \
|
||||
HOME=/home/panel \
|
||||
WINEPREFIX=/home/panel/.wine \
|
||||
WINEDEBUG=-all \
|
||||
SRV_PORT="${SRV_PORT:-30000}" \
|
||||
EAC_PORT="${EAC_PORT:-30003}" \
|
||||
CSW_PORT="${CSW_PORT:-30004}" \
|
||||
EAH_API_PORT="${EAH_API_PORT:-30007}" \
|
||||
SCENARIO="${SCENARIO:-}" \
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
|
||||
/entrypoint.sh "$@"
|
||||
fi
|
||||
|
||||
GAME_DIR=/game
|
||||
SAVE_DIR=/game-saves
|
||||
DEDI_DIR="${GAME_DIR}/DedicatedServer"
|
||||
DEDI_EXE="${DEDI_DIR}/EmpyrionDedicated.exe"
|
||||
CONFIG="${SAVE_DIR}/dedicated.yaml"
|
||||
# Empyrion ships its default dedicated.yaml at the game root.
|
||||
SHIPPED_CONFIG="${GAME_DIR}/dedicated.yaml"
|
||||
|
||||
# --- sanity: Windows build installed? ---
|
||||
if [[ ! -f "${DEDI_EXE}" ]]; then
|
||||
log "ERROR: ${DEDI_EXE} not found."
|
||||
log " Game files haven't been downloaded yet."
|
||||
log " Run 'Update' on this server in the panel — that launches a"
|
||||
log " SteamCMD sidecar which installs Empyrion (Windows build) into"
|
||||
log " this volume via +@sSteamCmdForcePlatformType windows."
|
||||
exit 78 # EX_CONFIG
|
||||
fi
|
||||
|
||||
# --- dedicated.yaml: copy shipped default on first run ---
|
||||
if [[ ! -s "${CONFIG}" && -s "${SHIPPED_CONFIG}" ]]; then
|
||||
log "no panel config found — seeding ${CONFIG} from ${SHIPPED_CONFIG}"
|
||||
cp -f "${SHIPPED_CONFIG}" "${CONFIG}"
|
||||
fi
|
||||
if [[ ! -s "${CONFIG}" ]]; then
|
||||
log "WARN: no dedicated.yaml found in game install. Empyrion will create"
|
||||
log " one with defaults on first boot. Edit via the Files tab."
|
||||
fi
|
||||
|
||||
# Symlink so the binary finds the config at its expected relative path.
|
||||
# Empyrion reads dedicated.yaml from the game root (same dir as the .exe's
|
||||
# -dedicated argument resolves).
|
||||
ln -sf "${CONFIG}" "${GAME_DIR}/dedicated.yaml"
|
||||
|
||||
# --- patch dedicated.yaml ports from env on every boot ---
|
||||
# Empyrion reads Srv_Port / EAC_Port / Tel_Port from dedicated.yaml. Without
|
||||
# this, multiple instances on the same agent all bind 30000-30004 and only
|
||||
# one is reachable. SRV_PORT comes from the panel allocator via the
|
||||
# manifest's port→env mapping (`env: SRV_PORT` in module.yaml). yaml is
|
||||
# whitespace-sensitive but these keys are a flat top-level "Key: NNN" so
|
||||
# sed is safe.
|
||||
if [[ -s "${CONFIG}" ]]; then
|
||||
log "patching dedicated.yaml (Srv_Port=${SRV_PORT}, EAC_Port=${EAC_PORT})"
|
||||
sed -i -E \
|
||||
-e "s|^([[:space:]]*Srv_Port:[[:space:]]*)[0-9]+|\1${SRV_PORT}|" \
|
||||
-e "s|^([[:space:]]*EAC_Port:[[:space:]]*)[0-9]+|\1${EAC_PORT}|" \
|
||||
"${CONFIG}"
|
||||
fi
|
||||
|
||||
# --- scenario selection ---
|
||||
# SCENARIO env names a folder under /game/Content/Scenarios/. The empyrion-
|
||||
# shipped default is "Default Random". Reforged Eden / Reforged Eden 2 /
|
||||
# Project Eden need to be present in /game/Content/Scenarios/<name>/ first
|
||||
# (drop via the panel's Files tab, or future workshop-subscribe feature).
|
||||
# This block keeps dedicated.yaml's CustomScenario in sync with the env.
|
||||
SCENARIO="${SCENARIO:-}"
|
||||
if [[ -n "${SCENARIO}" && -s "${CONFIG}" ]]; then
|
||||
if [[ ! -d "${GAME_DIR}/Content/Scenarios/${SCENARIO}" ]]; then
|
||||
log "WARN: scenario folder '${SCENARIO}' not found at ${GAME_DIR}/Content/Scenarios/${SCENARIO}"
|
||||
log " The panel UI Files tab can drop scenario contents there. For"
|
||||
log " Reforged Eden 2 (Workshop ID 3041847672), unzip into:"
|
||||
log " /game/Content/Scenarios/Reforged Eden 2/"
|
||||
log " Booting with Default for now."
|
||||
else
|
||||
log "selecting scenario: ${SCENARIO}"
|
||||
if grep -qE '^[[:space:]]*CustomScenario:' "${CONFIG}"; then
|
||||
sed -i -E "s|^([[:space:]]*CustomScenario:[[:space:]]*).*|\1${SCENARIO}|" "${CONFIG}"
|
||||
else
|
||||
# Insert under GameConfig: at the top, ahead of dedicated game block.
|
||||
printf '\nGameConfig:\n CustomScenario: %s\n' "${SCENARIO}" >> "${CONFIG}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- stage EPM mod (TCP relay for Eleon Mod API) on first boot ---
|
||||
# Pre-staged at /opt/panel-epm in the image. Copy into the live game
|
||||
# Content/Mods/EPM/ if it isn't there yet. This makes the panel-empyrion-
|
||||
# bridge sidecar work out-of-the-box: every new empyrion instance comes
|
||||
# with EPM listening on EAH_API_PORT.
|
||||
EPM_SRC=/opt/panel-epm
|
||||
EPM_DST="${GAME_DIR}/Content/Mods/EPM"
|
||||
if [[ -d "${EPM_SRC}" && ! -f "${EPM_DST}/EmpyrionNetworkRelayMod.dll" ]]; then
|
||||
log "staging EPM mod into ${EPM_DST}"
|
||||
mkdir -p "${EPM_DST}"
|
||||
cp -a "${EPM_SRC}/." "${EPM_DST}/"
|
||||
# If a panel-allocated EAH_API_PORT is set, patch EPM's Settings.yaml
|
||||
# so its TCP listener binds the right port for this instance.
|
||||
if [[ -n "${EAH_API_PORT:-}" && -f "${EPM_DST}/Settings.yaml" ]]; then
|
||||
sed -i -E "s|^GameServerApiPort:[[:space:]]*[0-9]+|GameServerApiPort: ${EAH_API_PORT}|" \
|
||||
"${EPM_DST}/Settings.yaml"
|
||||
fi
|
||||
fi
|
||||
# Re-patch port even if EPM was already staged, in case panel reallocated.
|
||||
if [[ -f "${EPM_DST}/Settings.yaml" && -n "${EAH_API_PORT:-}" ]]; then
|
||||
sed -i -E "s|^GameServerApiPort:[[:space:]]*[0-9]+|GameServerApiPort: ${EAH_API_PORT}|" \
|
||||
"${EPM_DST}/Settings.yaml"
|
||||
fi
|
||||
|
||||
# --- prep saves dir (Empyrion writes under /game/Saves by default) ---
|
||||
# Redirect Saves into the save volume via a symlink so steam validate
|
||||
# can't clobber world data.
|
||||
mkdir -p "${SAVE_DIR}/Saves"
|
||||
if [[ -d "${GAME_DIR}/Saves" && ! -L "${GAME_DIR}/Saves" ]]; then
|
||||
# Steam shipped an empty Saves dir; replace with symlink.
|
||||
rm -rf "${GAME_DIR}/Saves"
|
||||
fi
|
||||
ln -sfn "${SAVE_DIR}/Saves" "${GAME_DIR}/Saves"
|
||||
|
||||
# --- Wine + Xvfb via xvfb-run (auto-allocates a free display) ---
|
||||
#
|
||||
# We previously hand-started Xvfb on :99. That broke once the host had
|
||||
# its OWN Xvfb on :99 — the empyrion container uses network_mode: host,
|
||||
# which shares the network namespace, and X server abstract Unix sockets
|
||||
# (`@/tmp/.X11-unix/X99`) are namespaced by network ns. Result: container
|
||||
# Xvfb fails with "_XSERVTransMakeAllCOTSServerListeners: server already
|
||||
# running" and Wine then dies "Authorization required, but no auth
|
||||
# protocol specified" → Unity exits "Failed to create batch mode window:
|
||||
# Success." (verified 2026-04-29).
|
||||
#
|
||||
# `xvfb-run -a` finds a free display number, starts Xvfb with proper
|
||||
# xauth set up, exports DISPLAY to its child, and cleans up on exit.
|
||||
# Bulletproof against host's existing Xvfb instances.
|
||||
|
||||
# --- Wine prefix bootstrap (first boot only). xvfb-run handles the X
|
||||
# server for the actual run; for prefix init we don't strictly need
|
||||
# X but wineboot complains less when one is available. ---
|
||||
if [[ ! -d "${WINEPREFIX}" || ! -f "${WINEPREFIX}/system.reg" ]]; then
|
||||
log "initializing Wine prefix at ${WINEPREFIX}"
|
||||
xvfb-run -a wineboot --init 2>&1 | sed 's/^/ /' || true
|
||||
wineserver -w || true
|
||||
fi
|
||||
|
||||
# --- go ---
|
||||
cd "${GAME_DIR}"
|
||||
log "starting Empyrion Dedicated Server"
|
||||
log " game dir: ${GAME_DIR}"
|
||||
log " save dir: ${SAVE_DIR}"
|
||||
log " config: ${CONFIG}"
|
||||
log " binary: ${DEDI_EXE}"
|
||||
|
||||
# Shutdown handler: forward SIGTERM (xvfb-run forwards to wine; wine's
|
||||
# Unity hook persists state on quit).
|
||||
trap 'log "SIGTERM received; stopping server"; wineserver -k 2>/dev/null || true; exit 0' TERM INT
|
||||
|
||||
# -batchmode / -nographics make Unity skip the renderer; -logFile - pipes
|
||||
# the Unity log to our stdout so the panel console sees everything.
|
||||
# `xvfb-run -a` picks a free display, sets up xauth, and exports DISPLAY.
|
||||
# `--server-args="-screen 0 1024x768x24"` matches our previous Xvfb args.
|
||||
exec stdbuf -oL -eL xvfb-run -a --server-args="-screen 0 1024x768x24" \
|
||||
wine "${DEDI_EXE}" \
|
||||
-batchmode \
|
||||
-nographics \
|
||||
-logFile - \
|
||||
-dedicated dedicated.yaml \
|
||||
-startDedicated
|
||||
@@ -0,0 +1,8 @@
|
||||
# Empyrion mod manifest. Tells Empyrion to load this mod into the dedicated
|
||||
# server process (Game_Start hook), where it opens a TCP listener on the
|
||||
# port configured in Settings.yaml — 7028 in our case (panel-allocated).
|
||||
Name: EPM
|
||||
Description: Empyrion Network Relay - exposes Mod API over TCP for panel/EAH/etc
|
||||
Author: W3DG (open-source via MichaelGoulding/sample-empyrion-mod)
|
||||
Version: 1.0
|
||||
ModTargets: Dedi
|
||||
@@ -0,0 +1,43 @@
|
||||
# EPM mod artifacts
|
||||
|
||||
Drop-in `Content/Mods/EPM/` for Empyrion. Loaded by the dedicated server's
|
||||
mod loader; opens a TCP listener on `Settings.yaml.GameServerApiPort` (we
|
||||
ship `0.0.0.0:7028`, panel-allocated to whatever `EAH_API_PORT` env says).
|
||||
|
||||
## Files
|
||||
|
||||
- `EmpyrionNetworkRelayMod.dll` — the mod (`ModTargets: Dedi`). Compiled
|
||||
from `EmpyrionNetworkConnectedMods/sample-empyrion-mod/Empyrion Mod/`
|
||||
with one tweak: `Configuration.cs` was rewritten to manually parse
|
||||
`Settings.yaml` (key: value lines) instead of using `YamlDotNet`. Reason:
|
||||
current Empyrion 1.16.x ships `YamlAssembly.dll` rather than the old
|
||||
`Assembly-CSharp-firstpass.dll` that bundled `YamlDotNet`, so the
|
||||
upstream binary fails with `TypeLoadException` at `Game_Start`.
|
||||
- `EPMConnector.dll` — wire protocol library (TCP framing + protobuf-net
|
||||
message dispatch). Linked by both EPM and any C# client.
|
||||
- `EmpyrionNetworkRelayMod_Info.yaml` — manifest. Per-DLL naming is
|
||||
required because there are two DLLs in the folder; with a single DLL,
|
||||
`Info.yaml` works, but with multiple Empyrion's loader needs each main
|
||||
DLL to have a sibling `<DllName>_Info.yaml`.
|
||||
- `Settings.yaml` — host+port for the TCP listener. `0.0.0.0` is fine
|
||||
inside the container (host networking limits exposure to the container
|
||||
port; opnfwd handles WAN forwarding).
|
||||
|
||||
## Verified end-to-end on 2026-04-30
|
||||
|
||||
1. EPM loads under Wine: `Loaded Mod 'EmpyrionNetworkRelayMod': "EPM"`.
|
||||
2. TCP listener opens: `Now listening on port 7028`.
|
||||
3. C# probe (`/eah_probe/probe/Probe.cs`) connects via `EPMConnector.Client`
|
||||
and gets typed protobuf-net responses for `Request_Get_Factions`,
|
||||
`Request_Player_List`, `Request_Playfield_List`. Default Human
|
||||
faction came back deserialized into `Eleon.Modding.FactionInfoList`.
|
||||
|
||||
## How to rebuild
|
||||
|
||||
1. Open `C:\Users\dbledeez\AppData\Local\Temp\eah_probe\EmpyrionNetworkConnectedMods\sample-empyrion-mod\Empyrion Mod\` (or re-clone from `MichaelGoulding/EmpyrionNetworkConnectedMods`).
|
||||
2. Replace `Configuration.cs` with the YAML-free version (see
|
||||
`feedback_empyrion_yaml_dotnet_drop.md` for the snippet).
|
||||
3. `cd` to that folder and `csc.exe @build.rsp`. Output goes to
|
||||
`bin\Release\EmpyrionNetworkRelayMod.dll`.
|
||||
4. Copy `EmpyrionNetworkRelayMod.dll` + `EPMConnector.dll` + the two yamls
|
||||
into this directory; commit.
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
GameServerIp: "0.0.0.0"
|
||||
GameServerApiPort: 7028
|
||||
@@ -0,0 +1,152 @@
|
||||
# Empyrion — Galactic Survival dedicated server module.
|
||||
#
|
||||
# Same panel-native pattern as 7dtd: a minimal image we own
|
||||
# (panel-empyrion:latest, Debian 12 + Wine + tini), with game files
|
||||
# populated by the generic SteamCMD updater sidecar into a named volume.
|
||||
#
|
||||
# Empyrion's dedicated server is shipped as a Windows binary only, so the
|
||||
# updater is told to pretend SteamCMD is running on Windows
|
||||
# (`platform: windows` → +@sSteamCmdForcePlatformType windows), and the
|
||||
# entrypoint runs the exe under Wine under Xvfb (Unity server still touches
|
||||
# X even in -batchmode).
|
||||
#
|
||||
# First-run flow:
|
||||
# - Create instance → container spec + empty volumes
|
||||
# - Click "Update" → SteamCMD downloads ~3.5 GB Windows build
|
||||
# - Click "Start" → entrypoint boots Wine + EmpyrionDedicated.exe
|
||||
#
|
||||
# Notes for operators:
|
||||
# - The game server ports must be UDP; Empyrion uses a handful of them.
|
||||
# - Main config file is dedicated.yaml, lives under /game-saves after
|
||||
# first boot (entrypoint copies the shipped default on first run).
|
||||
# - No in-game RCON: Empyrion's admin console is in-process only. The
|
||||
# "Empyrion Web Admin" (EWA) third-party tool can be layered on top
|
||||
# later via a plugin; not wired for v1.
|
||||
|
||||
id: empyrion
|
||||
name: "Empyrion - Galactic Survival"
|
||||
version: 0.1.0
|
||||
authors:
|
||||
- panel contributors
|
||||
|
||||
supported_modes:
|
||||
- docker
|
||||
|
||||
runtime:
|
||||
docker:
|
||||
# Built locally from ./Dockerfile. Rebuild with:
|
||||
# docker build -t panel-empyrion:latest modules/empyrion
|
||||
image: panel-empyrion:latest
|
||||
# Host networking -- container shares the host net namespace, so
|
||||
# any port the game/mod binds is directly on the LAN. AMP-like
|
||||
# model. Multi-instance safety comes from env-driven per-instance
|
||||
# ports (panel allocator + env: mapping on each ports entry).
|
||||
network_mode: host
|
||||
browseable_root: /game-saves
|
||||
browseable_roots:
|
||||
- name: "Saves & Configs"
|
||||
path: /game-saves
|
||||
hint: "Saves/, dedicated.yaml, admin files"
|
||||
- name: "Game Files"
|
||||
path: /game
|
||||
hint: "Binaries, Content/, Mods/ — drop mod folders here"
|
||||
# Pre-declare the port env keys so the agent's resolve filter lets the
|
||||
# panel-allocated values flow through to the container env (resolve.go
|
||||
# only overrides keys already present in docker.env). Defaults match
|
||||
# the manifest port defaults below; agent overrides at create time.
|
||||
env:
|
||||
SRV_PORT: "30000"
|
||||
QUERY_PORT: "30001"
|
||||
CLIENT_PORT: "30002"
|
||||
EAC_PORT: "30003"
|
||||
CSW_PORT: "30004"
|
||||
EAH_API_PORT: "30007" # Empyrion's Mod API TCP port (used by EAH/EPM and our future bridge)
|
||||
# SCENARIO is declared empty here so the agent's resolver passes the
|
||||
# config_values.SCENARIO override into the container env. Without
|
||||
# this pre-declaration, resolve.go would silently drop the value
|
||||
# (it only overrides keys already in docker.env), the entrypoint
|
||||
# would see SCENARIO="" and skip the dedicated.yaml patch, and
|
||||
# Empyrion would fall back to "Default Multiplayer" no matter what
|
||||
# the operator picked in the dropdown.
|
||||
SCENARIO: ""
|
||||
volumes:
|
||||
- { type: volume, name: "panel-$INSTANCE_ID-saves", container: "/game-saves" }
|
||||
- { type: volume, name: "panel-$INSTANCE_ID-game", container: "/game" }
|
||||
- { target: "$DATA_PATH/logs", container: "/game-saves/logs" }
|
||||
|
||||
# Empyrion port reference:
|
||||
# Srv_Port (UDP) — main game traffic default 30000
|
||||
# Srv_Port+1 (UDP) — Steam query default 30001
|
||||
# Srv_Port+2 (UDP) — client connect default 30002
|
||||
# EAC_Port (UDP) — EasyAntiCheat default 30003
|
||||
# CSWApi_Port (TCP) — CSW API (remote admin) default 30004 (internal)
|
||||
ports:
|
||||
- { name: game, proto: udp, default: 30000, required: true, env: SRV_PORT }
|
||||
- { name: query, proto: udp, default: 30001, required: true, env: QUERY_PORT } # Empyrion derives query from Srv_Port + 1; we still allocate it for tracking
|
||||
- { name: client, proto: udp, default: 30002, required: true, env: CLIENT_PORT } # Srv_Port + 2 by convention
|
||||
- { name: eac, proto: udp, default: 30003, required: true, env: EAC_PORT }
|
||||
- { name: csw, proto: tcp, default: 30004, internal: true, env: CSW_PORT }
|
||||
# Empyrion's native Mod API TCP port. EPM mod listens on this; EAH GUI
|
||||
# and our future panel-empyrion-bridge will connect here. Public so
|
||||
# remote EAH GUIs work; auth model not yet validated, see
|
||||
# panel/memory/research_eah_integration.md before exposing externally.
|
||||
- { name: eah-api, proto: tcp, default: 30007, required: true, env: EAH_API_PORT }
|
||||
|
||||
resources:
|
||||
min_ram_mb: 4096
|
||||
recommended_ram_mb: 8192
|
||||
|
||||
# Operator-tunable knobs. Panel renders these as a config form; the entrypoint
|
||||
# reads them from env on every boot.
|
||||
config_values:
|
||||
- key: SCENARIO
|
||||
label: "Scenario"
|
||||
description: "Folder name under Content/Scenarios/. Leave blank for game default. Reforged Eden 2 needs the workshop content (ID 3041847672) dropped into Content/Scenarios/Reforged Eden 2/ first — use the Files tab."
|
||||
default: ""
|
||||
options:
|
||||
- { value: "", label: "Default Random (vanilla)" }
|
||||
- { value: "Reforged Eden 2", label: "Reforged Eden 2 (workshop)" }
|
||||
- { value: "Reforged Eden", label: "Reforged Eden (legacy)" }
|
||||
- { value: "Default Akua", label: "Default Akua (vanilla)" }
|
||||
- { value: "Default Multiplayer Survival", label: "Default Multiplayer Survival" }
|
||||
- key: EAH_API_PORT
|
||||
label: "EAH/Mod API port"
|
||||
description: "TCP port the EPM mod listens on for the panel-empyrion-bridge sidecar. Auto-allocated by panel; rarely needs manual override."
|
||||
default: ""
|
||||
advanced: true
|
||||
# Log-derived player events. Regexes converted from CubeCoders'
|
||||
# AMPTemplates (empyrion-galactic-survival.kvp) — .NET (?<n>) syntax -> Go (?P<n>).
|
||||
# Sourced-from-template; not yet exercised against a live client.
|
||||
events:
|
||||
join:
|
||||
pattern: '-LOG-.*Player (?P<id>.+?)/''(?P<name>[^'']+)'' login ok'
|
||||
kind: join
|
||||
leave:
|
||||
pattern: '-LOG-.*, (?P<id>.+?)/=/''(?P<name>[^'']+)'' disconnected$'
|
||||
kind: leave
|
||||
|
||||
|
||||
update_providers:
|
||||
- id: stable
|
||||
kind: steamcmd
|
||||
app_id: "530870"
|
||||
platform: windows # force Windows depot on Linux hosts — server is Windows-only
|
||||
install_path: /game
|
||||
- id: experimental
|
||||
kind: steamcmd
|
||||
app_id: "530870"
|
||||
beta: experimental
|
||||
platform: windows
|
||||
install_path: /game
|
||||
|
||||
# --- Manifest-driven UI metadata (WI-07) --------------------------
|
||||
# Consumed by the CONTROLLER/dashboard only (the controller loads its
|
||||
# own ./modules copy at startup); the agent ignores these at runtime.
|
||||
# ready_pattern is a JavaScript regex (slash-delimited when it needs
|
||||
# flags) moved verbatim from the dashboard's READY_PATTERNS map.
|
||||
ready_pattern: '/Server started listening on port|Dedi is ready|Ready for connections/i'
|
||||
appearance:
|
||||
emoji: "🚀"
|
||||
grad: "linear-gradient(135deg,#0b2545,#5c7cfa)"
|
||||
art: "/game-art/empyrion.jpg"
|
||||
steam: "383120"
|
||||
Reference in New Issue
Block a user