panel v0.9.1 — open-source game server manager

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 23:37:25 -07:00
commit 582b5a6b08
2161 changed files with 300950 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
# panel-native Conan Exiles runtime image.
#
# Conan Exiles Dedicated Server is a Windows-only UE4 build. We bring 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 install
# /game/ConanSandbox/Binaries/Win64/ConanSandboxServer-Win64-Shipping.exe — server binary
# /game/ConanSandbox/Saved/Config/WindowsServer/*.ini — config files
# /game/ConanSandbox/Mods/ — workshop paks + modlist.txt
# /game-saves — saves, configs, logs
# /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 the 64-bit stack (dedicated binary is
# x86_64). cabextract + winbind satisfy Wine's helpers; procps for signal
# handling and pgrep/pkill debugging.
#
# The libfontconfig1 ... libasound2 block is the UE5 dedicated-server runtime
# dep set for the native Linux Conan Enhanced binary at
# /game/ConanSandbox/Binaries/Linux/ConanSandboxServer-Linux-Shipping. Funcom's
# UE5 ELF dynamically links against these even in headless server mode (UE5
# pulls in font/X/SDL/audio plumbing during engine init). Kept alongside Wine
# so a single image can host either edition — Wine still drives the legacy
# UE4 (conan-exiles-legacy) Windows-only path.
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 \
libfontconfig1 \
libfreetype6 \
libxcursor1 \
libxinerama1 \
libxi6 \
libxrandr2 \
libxxf86vm1 \
libgl1 \
libvulkan1 \
libsdl2-2.0-0 \
libpulse0 \
libasound2 \
&& 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/*
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
# Conan Exiles default ports (see module.yaml comment for reference).
EXPOSE 7777/tcp 7777/udp 7778/udp 27015/udp
EXPOSE 25575/tcp
ENTRYPOINT ["/usr/bin/tini", "--", "/entrypoint.sh"]
+55
View File
@@ -0,0 +1,55 @@
# Conan Exiles — panel-native module
Panel-native Conan Exiles dedicated server. **Enhanced (UE5)** runs as a native Linux binary; **Legacy (UE4, `conan-exiles-legacy` beta)** still runs under Wine + Xvfb. Both editions share one debian:12-slim image — the entrypoint picks launch mode from the `EDITION` config_value.
- **Image:** `panel-conan-exiles:latest` — built from `./Dockerfile`.
- **Game files:** downloaded by the panel's SteamCMD updater sidecar (app 443030) into the `panel-<id>-game` named volume. Edition matrix: **Enhanced = native Linux depot** (443032, ~4.27 GiB; `platform: linux` in `module.yaml`). **Legacy = Windows-only depot** under Wine (`platform: windows`, `beta: conan-exiles-legacy`).
- **Saves:** `panel-<id>-saves` at `/game-saves`; entrypoint moves `ConanSandbox/Saved/` from the game install into the save volume on first boot and symlinks back, so SteamCMD validate can't clobber world data.
- **Config:** Enhanced uses `ConanSandbox/Saved/Config/LinuxServer/`; Legacy uses `ConanSandbox/Saved/Config/WindowsServer/`. On the first native boot after Wine, the entrypoint auto-copies `ServerSettings.ini` / `Engine.ini` / `Game.ini` from `WindowsServer/` to `LinuxServer/` so operator-tuned values survive the cut-over. Edit via Files tab.
- **Mods:** drop Workshop pak files + `modlist.txt` into `ConanSandbox/Mods/` via the Files tab.
- **RCON:** real Source-RCON on `25575/tcp` (internal). Password `panel_rcon` (default) — editable in `module.yaml` `env.RCON_PASSWORD`. Tracker dials on start; our agent redials on EOF/broken-pipe automatically.
## Ports
| Port | Proto | Purpose |
|------|-------|---------|
| 7777 | TCP+UDP | Game traffic + client mod downloads |
| 7778 | UDP | Pinger (server-browser probe; `game + 1`) |
| 27015 | UDP | Steam query |
| 25575 | TCP | RCON (internal) |
## RCON commands wired
- `list``listplayers`
- `broadcast {msg}``broadcast {msg}`
- `kick {player}``kickplayer {player}`
- `ban {player}``banplayer {player}`
- `save``saveworld`
- `shutdown``shutdown`
## Log events captured
- **Join:** `LogNet: Join succeeded: <name>#<discriminator>`
- **Leave:** `LogNet: Player disconnected: <name>#<discriminator>`
- **Chat:** `ChatWindow: Character <name> said: <message>`
## Environment vars
| Var | Default | Notes |
|-----|---------|-------|
| `SERVER_NAME` | `panel Conan Exiles` | Displayed in server browser |
| `MAX_PLAYERS` | `40` | Hard cap (AMP also defaults 40) |
| `RCON_PASSWORD` | `panel_rcon` | Source RCON auth |
| `SERVER_MAP` | `/Game/Maps/ConanSandbox/ConanSandbox` | Exiled Lands. For Isle of Siptah: `/Game/DLC_EXT/DLC_Siptah/Maps/DLC_Isle_of_Siptah` |
| `STEAM_APP_ID` | `440900` | Steamworks client init context — required for Steam master-server listing. Exported as both `STEAM_APP_ID` and `SteamAppId` for cross-build compat. |
## Known gotchas
- Edition split: Enhanced (UE5) ships a real Linux depot since 2026-05-05 and runs native (no Wine). Legacy (UE4, `conan-exiles-legacy` beta) is Windows-only and stays on Wine.
- Wine under Xvfb (Legacy only) adds ~30 s to first boot (prefix init) and ~15 s to subsequent boots (X server + Wine cold start). Native Enhanced has no Wine overhead.
- Workshop mod downloads happen to `ConanSandbox/Mods/` — both the pak files AND `modlist.txt` need to be there for mods to load.
- Ready signal: `LogGameState: Match State Changed from WaitingToStart to InProgress`. Expect 13 min between docker-start and this line while UE4 loads the world.
## Reference
AMP template (CubeCoders): https://github.com/CubeCoders/AMPTemplates — `conan-exiles.kvp`, `conan-exilesports.json`, `conan-exilesupdates.json`, `conan-exilesconfig.json`, `conan-exilesmetaconfig.json`.
+399
View File
@@ -0,0 +1,399 @@
#!/bin/bash
# panel-native Conan Exiles entrypoint.
#
# Contract:
# /game — SteamCMD-installed Windows build of Conan Exiles Dedicated
# Server (populated by the panel's SteamCMD updater sidecar).
# If missing, exit with EX_CONFIG and a helpful message.
# /game-saves — ConanSandbox/Saved/ tree (world data) + Config/ INIs.
#
# Stage 1 runs as root: chown volumes (SteamCMD wrote as root), nuke stale
# X lock, symlink /Logs + ConanSandbox/Saved into the save volume, then drop
# to the `panel` user via setpriv. Wine refuses to run as root.
#
# Stage 2 (panel user): stamp config_values env vars into [ServerSettings]
# of ServerSettings.ini, then exec the dedicated server under Wine + Xvfb.
set -euo pipefail
log() { printf '[panel-conan] %s\n' "$*"; }
# ----- stage 1: root-only bootstrap -----
if [[ "$(id -u)" = "0" ]]; then
chown -R panel:panel /game /game-saves /home/panel 2>/dev/null || true
# UE4/UE5 under Wine writes to Z:\Logs (= /Logs). Pre-create as symlink
# into save volume so logs survive container recreate + show up in Files
# tab.
mkdir -p /game-saves/ConanLogs
chown -R panel:panel /game-saves/ConanLogs
if [[ ! -e /Logs || -L /Logs ]]; then
ln -sfn /game-saves/ConanLogs /Logs
fi
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 \
STEAM_APP_ID="${STEAM_APP_ID:-440900}" \
SteamAppId="${STEAM_APP_ID:-440900}" \
SERVER_NAME="${SERVER_NAME:-panel Conan Exiles}" \
SERVER_PASSWORD="${SERVER_PASSWORD:-}" \
ADMIN_PASSWORD="${ADMIN_PASSWORD:-}" \
RCON_PASSWORD="${RCON_PASSWORD:-panel_rcon}" \
MAX_PLAYERS="${MAX_PLAYERS:-40}" \
SERVER_REGION="${SERVER_REGION:-1}" \
SERVER_COMMUNITY="${SERVER_COMMUNITY:-0}" \
MAX_NUDITY="${MAX_NUDITY:-0}" \
SERVER_MAP="${SERVER_MAP:-/Game/Maps/ConanSandbox/ConanSandbox}" \
CUSTOM_MAP="${CUSTOM_MAP:-}" \
ENABLE_BATTLEYE="${ENABLE_BATTLEYE:-False}" \
ENABLE_VAC="${ENABLE_VAC:-True}" \
PVP_ENABLED="${PVP_ENABLED:-}" \
AVATARS_ENABLED="${AVATARS_ENABLED:-}" \
CONTAINERS_IGNORE_OWNERSHIP="${CONTAINERS_IGNORE_OWNERSHIP:-}" \
CAN_DAMAGE_PLAYER_OWNED_STRUCTURES="${CAN_DAMAGE_PLAYER_OWNED_STRUCTURES:-}" \
DYNAMIC_BUILDING_DAMAGE="${DYNAMIC_BUILDING_DAMAGE:-}" \
FRIENDLY_FIRE_DAMAGE="${FRIENDLY_FIRE_DAMAGE:-}" \
PLAYER_XP_RATE="${PLAYER_XP_RATE:-}" \
PLAYER_XP_KILL="${PLAYER_XP_KILL:-}" \
PLAYER_XP_HARVEST="${PLAYER_XP_HARVEST:-}" \
PLAYER_XP_CRAFT="${PLAYER_XP_CRAFT:-}" \
PLAYER_XP_TIME="${PLAYER_XP_TIME:-}" \
PLAYER_DAMAGE_MULTIPLIER="${PLAYER_DAMAGE_MULTIPLIER:-}" \
PLAYER_DAMAGE_TAKEN="${PLAYER_DAMAGE_TAKEN:-}" \
PLAYER_HEALTH_REGEN="${PLAYER_HEALTH_REGEN:-}" \
PLAYER_STAMINA_COST="${PLAYER_STAMINA_COST:-}" \
PLAYER_SPRINT_SPEED="${PLAYER_SPRINT_SPEED:-}" \
PLAYER_MOVEMENT_SPEED="${PLAYER_MOVEMENT_SPEED:-}" \
PLAYER_ENCUMBRANCE="${PLAYER_ENCUMBRANCE:-}" \
NPC_DAMAGE_MULTIPLIER="${NPC_DAMAGE_MULTIPLIER:-}" \
NPC_DAMAGE_TAKEN="${NPC_DAMAGE_TAKEN:-}" \
NPC_HEALTH_MULTIPLIER="${NPC_HEALTH_MULTIPLIER:-}" \
NPC_RESPAWN_MULTIPLIER="${NPC_RESPAWN_MULTIPLIER:-}" \
NPC_MAX_SPAWN_CAP="${NPC_MAX_SPAWN_CAP:-}" \
STRUCTURE_DAMAGE_MULTIPLIER="${STRUCTURE_DAMAGE_MULTIPLIER:-}" \
STRUCTURE_HEALTH_MULTIPLIER="${STRUCTURE_HEALTH_MULTIPLIER:-}" \
LANDCLAIM_RADIUS_MULTIPLIER="${LANDCLAIM_RADIUS_MULTIPLIER:-}" \
AVATAR_DOME_DURATION="${AVATAR_DOME_DURATION:-}" \
AVATAR_DOME_DAMAGE="${AVATAR_DOME_DAMAGE:-}" \
GAME_PORT="${GAME_PORT:-7777}" \
PINGER_PORT="${PINGER_PORT:-7778}" \
QUERY_PORT="${QUERY_PORT:-27015}" \
RCON_PORT="${RCON_PORT:-25575}" \
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
/entrypoint.sh "$@"
fi
GAME_DIR=/game
SAVE_DIR=/game-saves
# --- edition-aware launch mode ---
# Enhanced (UE5) is the native-Linux depot — direct exec, no Wine.
# Legacy (UE4) is Windows-only — Wine + Xvfb.
# Drives binary path, fallback find pattern, config subdir, prefix bootstrap,
# SIGTERM trap, and the launch line further below.
case "${EDITION:-enhanced}" in
legacy)
LAUNCH_MODE=wine
CANDIDATES=("${GAME_DIR}/ConanSandbox/Binaries/Win64/ConanSandboxServer-Win64-Shipping.exe")
CONFIG_SUBDIR=WindowsServer
;;
*)
LAUNCH_MODE=native
CANDIDATES=("${GAME_DIR}/ConanSandbox/Binaries/Linux/ConanSandboxServer-Linux-Shipping")
CONFIG_SUBDIR=LinuxServer
;;
esac
BIN_BASENAME=$(basename "${CANDIDATES[0]}")
log "edition: ${EDITION:-enhanced} launch mode: ${LAUNCH_MODE} config subdir: ${CONFIG_SUBDIR}"
# --- locate the server binary ---
# Enhanced UE5 Linux depot ships the binary at
# /game/ConanSandbox/Binaries/Linux/ConanSandboxServer-Linux-Shipping (ELF).
# Legacy UE4 Windows depot ships
# /game/ConanSandbox/Binaries/Win64/ConanSandboxServer-Win64-Shipping.exe (PE).
# Mod loading happens via ServerSettings.ini's ServerModList= key (populated
# by the panel's Mods tab) AND ConanSandbox/Mods/modlist.txt — Conan reads both.
DEDI_EXE=""
for c in "${CANDIDATES[@]}"; do
if [[ -f "$c" ]]; then
DEDI_EXE="$c"
break
fi
done
if [[ -z "${DEDI_EXE}" ]]; then
found=$(find "${GAME_DIR}" -maxdepth 6 -iname "${BIN_BASENAME}" -type f 2>/dev/null | head -1 || true)
if [[ -n "${found}" ]]; then
DEDI_EXE="${found}"
fi
fi
if [[ -z "${DEDI_EXE}" ]]; then
log "ERROR: ${BIN_BASENAME} not found under ${GAME_DIR}."
log " Run 'Update' on this server in the panel — that launches a"
log " SteamCMD sidecar which installs Conan Exiles into this volume"
log " for the selected edition (${EDITION:-enhanced})."
exit 78 # EX_CONFIG
fi
log "found server binary: ${DEDI_EXE}"
# --- persist ConanSandbox/Saved into /game-saves ---
# UE4/UE5 writes world saves + config INIs under <GameRoot>/ConanSandbox/Saved/.
# Move to save volume on first boot + symlink back, so SteamCMD validate
# can't wipe world data. Idempotent on subsequent boots.
mkdir -p "${SAVE_DIR}/ConanSandbox"
if [[ -d "${GAME_DIR}/ConanSandbox/Saved" && ! -L "${GAME_DIR}/ConanSandbox/Saved" ]]; then
if [[ ! -d "${SAVE_DIR}/ConanSandbox/Saved" ]]; then
mv "${GAME_DIR}/ConanSandbox/Saved" "${SAVE_DIR}/ConanSandbox/Saved"
else
rm -rf "${GAME_DIR}/ConanSandbox/Saved"
fi
fi
mkdir -p "${SAVE_DIR}/ConanSandbox/Saved/Config/${CONFIG_SUBDIR}"
ln -sfn "${SAVE_DIR}/ConanSandbox/Saved" "${GAME_DIR}/ConanSandbox/Saved"
# First-time native boot after Wine: if WindowsServer/ has config and
# LinuxServer/ does not yet, copy ServerSettings.ini + Engine.ini + Game.ini
# across so operator-tuned values survive the Wine -> native cut-over. Wine
# path is untouched; both dirs coexist.
if [[ "${LAUNCH_MODE}" = native ]]; then
WIN_CFG_DIR="${SAVE_DIR}/ConanSandbox/Saved/Config/WindowsServer"
LINUX_CFG_DIR="${SAVE_DIR}/ConanSandbox/Saved/Config/LinuxServer"
mkdir -p "${LINUX_CFG_DIR}"
for f in ServerSettings.ini Engine.ini Game.ini; do
if [[ -f "${WIN_CFG_DIR}/${f}" && ! -f "${LINUX_CFG_DIR}/${f}" ]]; then
log "migrating ${f} from WindowsServer/ to LinuxServer/ (first native boot)"
cp -a "${WIN_CFG_DIR}/${f}" "${LINUX_CFG_DIR}/${f}"
fi
done
fi
# OSS / Engine.ini: intentionally left alone.
#
# Empirically (see reference_conan_oss_steam_impossible.md): Conan Exiles
# Enhanced (UE5) native Linux dedi cannot be coerced into OSS=Steam without
# breaking the socket bind. AMP's Conan01 instance on figaro runs the same
# build with OSS=Null, no Engine.ini, no steam_appid.txt, and no
# ~/.steam/sdk64/steamclient.so symlink — and we now do the same. Engine
# default is Null, which binds IpNetDriver cleanly. Direct-IP join works;
# Steam server-browser listing is not achievable on this build.
#
# SteamAppId env var is already exported in stage 1 — that's enough for the
# Steamworks Game Server API to identify itself without a steam_appid.txt
# file on disk.
# --- stamp config_values env into ServerSettings.ini ---
# Each non-empty env value is written under [ServerSettings] (replace if the
# key already exists, append if not). Empty values are SKIPPED so operator
# manual edits to ServerSettings.ini via the Files tab survive boot. The
# panel UI's Config tab → Save → recreate-with-env path is the canonical
# way to mutate INI values; direct file edits are for fields the panel
# doesn't expose.
INI="${SAVE_DIR}/ConanSandbox/Saved/Config/${CONFIG_SUBDIR}/ServerSettings.ini"
if [[ ! -f "${INI}" ]]; then
log "ServerSettings.ini missing — initializing with [ServerSettings] header"
printf '[ServerSettings]\n' > "${INI}"
fi
if ! grep -qE '^\[ServerSettings\]' "${INI}"; then
log "ServerSettings.ini lacked [ServerSettings] header — prepending"
printf '[ServerSettings]\n' > "${INI}.new"
cat "${INI}" >> "${INI}.new"
mv "${INI}.new" "${INI}"
fi
# set_ini_kv KEY VALUE: write/replace KEY=VALUE under [ServerSettings].
# Skip on empty value. Idempotent. Tolerates `&` and `\` in values via the
# safe_value escape (sed RHS replacement metachars).
set_ini_kv() {
local key="$1" value="$2"
if [[ -z "${value}" ]]; then return 0; fi
local safe_value
safe_value=$(printf '%s' "${value}" | sed -e 's/[\\&|]/\\&/g')
if grep -qE "^${key}=" "${INI}"; then
sed -i "s|^${key}=.*|${key}=${safe_value}|" "${INI}"
else
sed -i "/^\[ServerSettings\]/a ${key}=${safe_value}" "${INI}"
fi
}
# Resolve the {{CUSTOM_MAP}} sentinel: if Map is set to "Custom" via the UI
# the panel passes SERVER_MAP="{{CUSTOM_MAP}}" or "{{CustomMap}}" — swap in
# the operator's CUSTOM_MAP value (or fall back to Exiled Lands if blank).
case "${SERVER_MAP}" in
"{{CUSTOM_MAP}}"|"{{CustomMap}}")
if [[ -n "${CUSTOM_MAP}" ]]; then
log "Map set to Custom; resolving to ${CUSTOM_MAP}"
SERVER_MAP="${CUSTOM_MAP}"
else
log "WARNING: Map set to Custom but CUSTOM_MAP is empty; falling back to Exiled Lands"
SERVER_MAP="/Game/Maps/ConanSandbox/ConanSandbox"
fi
;;
esac
log "stamping operator config_values into ${INI}"
# Identity
set_ini_kv "ServerName" "${SERVER_NAME}"
set_ini_kv "ServerPassword" "${SERVER_PASSWORD}"
set_ini_kv "AdminPassword" "${ADMIN_PASSWORD}"
set_ini_kv "MaxPlayers" "${MAX_PLAYERS}"
set_ini_kv "serverRegion" "${SERVER_REGION}"
set_ini_kv "ServerCommunity" "${SERVER_COMMUNITY}"
set_ini_kv "MaxNudity" "${MAX_NUDITY}"
# Anti-cheat
set_ini_kv "IsBattlEyeEnabled" "${ENABLE_BATTLEYE}"
set_ini_kv "IsVACEnabled" "${ENABLE_VAC}"
# PvP / world
set_ini_kv "PVPEnabled" "${PVP_ENABLED}"
set_ini_kv "AvatarsEnabled" "${AVATARS_ENABLED}"
set_ini_kv "ContainersIgnoreOwnership" "${CONTAINERS_IGNORE_OWNERSHIP}"
set_ini_kv "CanDamagePlayerOwnedStructures" "${CAN_DAMAGE_PLAYER_OWNED_STRUCTURES}"
set_ini_kv "DynamicBuildingDamage" "${DYNAMIC_BUILDING_DAMAGE}"
set_ini_kv "FriendlyFireDamageMultiplier" "${FRIENDLY_FIRE_DAMAGE}"
# XP rates
set_ini_kv "PlayerXPRateMultiplier" "${PLAYER_XP_RATE}"
set_ini_kv "PlayerXPKillMultiplier" "${PLAYER_XP_KILL}"
set_ini_kv "PlayerXPHarvestMultiplier" "${PLAYER_XP_HARVEST}"
set_ini_kv "PlayerXPCraftMultiplier" "${PLAYER_XP_CRAFT}"
set_ini_kv "PlayerXPTimeMultiplier" "${PLAYER_XP_TIME}"
# Player tuning
set_ini_kv "PlayerDamageMultiplier" "${PLAYER_DAMAGE_MULTIPLIER}"
set_ini_kv "PlayerDamageTakenMultiplier" "${PLAYER_DAMAGE_TAKEN}"
set_ini_kv "PlayerHealthRegenSpeedScale" "${PLAYER_HEALTH_REGEN}"
set_ini_kv "PlayerStaminaCostMultiplier" "${PLAYER_STAMINA_COST}"
set_ini_kv "PlayerSprintSpeedScale" "${PLAYER_SPRINT_SPEED}"
set_ini_kv "PlayerMovementSpeedScale" "${PLAYER_MOVEMENT_SPEED}"
set_ini_kv "PlayerEncumbranceMultiplier" "${PLAYER_ENCUMBRANCE}"
# NPC tuning
set_ini_kv "NPCDamageMultiplier" "${NPC_DAMAGE_MULTIPLIER}"
set_ini_kv "NPCDamageTakenMultiplier" "${NPC_DAMAGE_TAKEN}"
set_ini_kv "NPCHealthMultiplier" "${NPC_HEALTH_MULTIPLIER}"
set_ini_kv "NPCRespawnMultiplier" "${NPC_RESPAWN_MULTIPLIER}"
set_ini_kv "NPCMaxSpawnCapMultiplier" "${NPC_MAX_SPAWN_CAP}"
# Structures / land claim
set_ini_kv "StructureDamageMultiplier" "${STRUCTURE_DAMAGE_MULTIPLIER}"
set_ini_kv "StructureHealthMultiplier" "${STRUCTURE_HEALTH_MULTIPLIER}"
set_ini_kv "LandClaimRadiusMultiplier" "${LANDCLAIM_RADIUS_MULTIPLIER}"
set_ini_kv "AvatarDomeDurationMultiplier" "${AVATAR_DOME_DURATION}"
set_ini_kv "AvatarDomeDamageMultiplier" "${AVATAR_DOME_DAMAGE}"
# --- Steam OSS fix — INCOMPLETE, currently disabled ---
# Live experiment on conan-1 (2026-05-10) showed:
# 1. Adding DefaultPlatformService=Steam to Engine.ini alone is a no-op:
# Steam SDK reports "[STEAM] disabled" because the Windows binary can't
# locate steamclient64.dll (Funcom's dedi-server depot ships these at
# /game root, not under ConanSandbox/Binaries/Win64/).
# 2. Copying steamclient*.dll + tier0_s*.dll + vstdlib_s*.dll +
# steamwebrtc*.dll + a steam_appid.txt (440900) into the binary dir
# makes the SDK initialize successfully:
# LogOnline: STEAM: [AppId: 440900] Game Server API initialized 1
# LogOnline: OSS: Created online subsystem instance for: STEAM
# 3. BUT once Steam OSS comes up under Wine, the engine hangs the
# GameThread within ~80s, never binds the game port (8010 UDP),
# and LogServerStats reports "NetDriver is null". Pre-fix logs had
# none of these symptoms (server bound + responded to A2S fine, just
# couldn't accept real-client joins past encryption handshake).
# Net effect: enabling Steam OSS via Engine.ini + DLL drops trades one
# failure mode (handshake reject) for a worse one (no NetDriver at all).
# Leaving the original Engine.ini untouched until we find the correct
# NetDriverDefinitions / SteamSockets-disable knob that lets Steam OSS
# coexist with a working IpNetDriver under Wine.
#
# See session log 2026-05-10 for full investigation.
# --- Wine prefix bootstrap (first boot only, legacy/Wine path only). xvfb-run
# gives Wine a guaranteed-fresh DISPLAY without colliding with host Xvfb
# on :99. Native (enhanced) path skips this entirely. ---
if [[ "${LAUNCH_MODE}" = wine ]]; then
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
fi
# --- go ---
# CLI-arg fallbacks: env values default to empty strings in module.yaml so
# the entrypoint's INI-stamper leaves operator INI edits alone. But Conan's
# launch CLI requires real values for ServerName, MaxPlayers, ports, and
# RconPassword. Resolve those here so empty config_values still produce a
# launchable server. These fallbacks should match the previous module.yaml
# defaults so behaviour is unchanged for fresh installs.
SERVER_NAME_LAUNCH="${SERVER_NAME:-panel Conan Exiles}"
MAX_PLAYERS_LAUNCH="${MAX_PLAYERS:-40}"
RCON_PASSWORD_LAUNCH="${RCON_PASSWORD:-panel_rcon}"
BIN_DIR="$(dirname "${DEDI_EXE}")"
cd "${BIN_DIR}"
log "starting Conan Exiles dedicated server"
log " game dir: ${GAME_DIR}"
log " save dir: ${SAVE_DIR}"
log " binary: ${DEDI_EXE}"
log " bin dir: ${BIN_DIR}"
log " map: ${SERVER_MAP}"
log " max: ${MAX_PLAYERS_LAUNCH}"
log " game port: ${GAME_PORT}/tcp+udp"
log " query: ${QUERY_PORT}/udp"
log " rcon: ${RCON_PORT}/tcp"
if [[ "${LAUNCH_MODE}" = wine ]]; then
trap 'log "SIGTERM received; stopping server"; wineserver -k || true; exit 0' TERM INT
else
trap 'log "SIGTERM received; stopping server"; kill -TERM "${SERVER_PID:-0}" 2>/dev/null || true; wait "${SERVER_PID:-0}" 2>/dev/null || true; exit 0' TERM INT
fi
# Launch flags. Conan reads most settings from ServerSettings.ini (which we
# just stamped) — CLI args set the boot-time essentials. Pinger port is
# `Port + 1` per AMP convention; setting only -Port is enough — Conan
# derives pinger from it automatically.
#
# Native (enhanced) — match Funcom's launcher convention: `ConanSandbox`
# first positional, then the map URL. No -log/-nosound/-SteamServerName
# (those are Wine-era flags). ServerName lives in [ServerSettings]
# ServerName= which our INI stamper already handles.
if [[ "${LAUNCH_MODE}" = native ]]; then
chmod +x "${DEDI_EXE}" || true
exec stdbuf -oL -eL "${DEDI_EXE}" \
ConanSandbox \
"${SERVER_MAP}" \
-stdout \
-FullStdOutLogOutput \
"-ServerName=${SERVER_NAME_LAUNCH}" \
"-MaxPlayers=${MAX_PLAYERS_LAUNCH}" \
"-Port=${GAME_PORT}" \
"-QueryPort=${QUERY_PORT}" \
-RconEnabled=1 \
"-RconPort=${RCON_PORT}" \
"-RconPassword=${RCON_PASSWORD_LAUNCH}"
fi
# Wine (legacy) — xvfb-run -a auto-allocates a free X display (host has its
# own Xvfb on :99 / :100 / :101 — see panel/memory/gotchas.md "network_mode:
# host Xvfb collision"). `ConanSandbox` first positional matches Funcom's
# launcher convention; was missing on the old Wine launch line.
exec stdbuf -oL -eL xvfb-run -a --server-args="-screen 0 1024x768x24" \
wine "${DEDI_EXE}" \
ConanSandbox \
"${SERVER_MAP}" \
-log \
"-SteamServerName=${SERVER_NAME_LAUNCH}" \
"-ServerName=${SERVER_NAME_LAUNCH}" \
"-MaxPlayers=${MAX_PLAYERS_LAUNCH}" \
"-Port=${GAME_PORT}" \
"-QueryPort=${QUERY_PORT}" \
-RconEnabled=1 \
"-RconPort=${RCON_PORT}" \
"-RconPassword=${RCON_PASSWORD_LAUNCH}" \
-nosound \
-stdout \
-FullStdOutLogOutput
+573
View File
@@ -0,0 +1,573 @@
# Conan Exiles dedicated server module.
#
# Conan Exiles Dedicated Server (Steam app 443030) — Funcom rolled out the
# UE5 "Enhanced" remaster as the default branch on 2026-05-05. The UE4 build
# remains available indefinitely as Steam beta branch `conan-exiles-legacy`
# (description: "UE4 version of Conan Exiles Dedicated Server"). Both ride
# the same appid; only the branch differs. Default install pulls Enhanced.
#
# Hosting matrix as of 2026-05-10:
# Enhanced (UE5, public branch) — NATIVE LINUX. Funcom shipped a real Linux
# dedicated-server depot (443032, ~4.27 GiB) alongside the 2026-05-05
# Enhanced launch. Binary at
# ConanSandbox/Binaries/Linux/ConanSandboxServer-Linux-Shipping. We run
# it directly — no Wine, no Xvfb. Config dir is Config/LinuxServer/.
# Legacy (UE4, conan-exiles-legacy beta) — STILL WINDOWS-ONLY. No Linux
# depot exists for this branch; we keep the Wine + Xvfb path for it.
# Binary at ConanSandbox/Binaries/Win64/ConanSandboxServer-Win64-Shipping.exe,
# config dir Config/WindowsServer/. Same pattern as Empyrion / V Rising /
# Sons of the Forest / Windrose.
# The container ships with both runtimes (Wine + UE5 Linux deps) so a single
# image can host either edition; entrypoint picks the launch mode from the
# EDITION config_value.
#
# Memory footprint: Enhanced UE5 runs ~2-4x heavier than UE4 Legacy on
# populated worlds. Defaults bumped accordingly (see `resources:` below).
#
# RCON is real Source-RCON on 25575/tcp — AMP's template, community
# servers, and the rcon plugin that ships with Conan all speak it. We
# surface it via our source_rcon adapter; the tracker's redial-on-EOF
# logic covers any idle disconnects (same fix landed for Palworld).
#
# Reference: https://github.com/CubeCoders/AMPTemplates (conan-exiles.kvp,
# conan-exilesports.json, conan-exilesupdates.json, conan-exilesconfig.json)
# Reference: https://low.ms/knowledgebase/conan-exiles-enhanced-update
id: conan-exiles
name: "Conan Exiles"
version: 0.3.0
authors:
- panel contributors
supported_modes:
- docker
runtime:
docker:
# Built locally from ./Dockerfile. Rebuild with:
# docker build -t panel-conan-exiles:latest modules/conan-exiles
image: panel-conan-exiles: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: "ServerSettings.ini, Game.ini, ConanSandbox/Saved/ world data"
- name: "Game Files"
path: /game
hint: "ConanSandbox/Binaries/Linux/ for Enhanced (UE5 native), ConanSandbox/Binaries/Win64/ for Legacy (UE4 under Wine). Engine/, ConanSandbox/Mods/ (Workshop pak drops). Workshop downloads land at /game/steamapps/workshop/content/440900/<id>/ via the shared volume mount."
# Pre-declare every env key that any config_value below maps to. The
# agent's resolve.go ONLY propagates config_values whose key is
# already present in `docker.env` — keys not listed here get silently
# filtered out. Defaults below are starting values for fresh instances;
# operator edits in the panel's Config tab override per-instance.
# Empty-string defaults mean "leave the INI alone" (entrypoint stamps
# only non-empty values, so unset fields fall back to game defaults
# or operator's manual ServerSettings.ini edits).
env:
# Steamworks client runtime init context — the dedicated-server app
# (443030) reports itself to Steam as Conan Exiles (440900). AMP
# sets this too; the server refuses to join the Steam master-server
# listing without it.
STEAM_APP_ID: "440900"
# EDITION — see config_values block below. Default `enhanced` (UE5).
# Pre-declared here so resolve.go propagates the operator's choice
# into the container env (the agent's docker.env filter only forwards
# config_values whose key already appears in this map). The entrypoint
# logs it so operators can grep boot output to confirm what's running.
EDITION: "enhanced"
# --- Identity ---
# All identity fields default to EMPTY. The entrypoint only stamps
# non-empty values into [ServerSettings] of ServerSettings.ini, so a
# recreate with empty env preserves whatever the operator set
# previously via the Files tab. CLI-launch args have hardcoded
# fallbacks in entrypoint.sh (e.g. ServerName falls back to
# "panel Conan Exiles" for the Steam server-name flag only when
# SERVER_NAME is genuinely unset).
SERVER_NAME: ""
SERVER_PASSWORD: ""
ADMIN_PASSWORD: ""
RCON_PASSWORD: ""
MAX_PLAYERS: ""
SERVER_REGION: ""
MAX_NUDITY: ""
SERVER_COMMUNITY: ""
# --- Map ---
# /Game/Maps/ConanSandbox/ConanSandbox = Exiled Lands (default)
# /Game/DLC_EXT/DLC_Siptah/Maps/DLC_Isle_of_Siptah = Isle of Siptah
# SERVER_MAP keeps a default because Conan REQUIRES a map URL on the
# CLI; empty would mean "use last used", which is fine but surprising
# for new instances.
SERVER_MAP: "/Game/Maps/ConanSandbox/ConanSandbox"
CUSTOM_MAP: ""
# --- Anti-cheat ---
# Empty means "leave INI default" (entrypoint skips stamping). Conan's
# game default is BattlEye=False, VAC=True — same as ours used to be.
ENABLE_BATTLEYE: ""
ENABLE_VAC: ""
# --- Persistence / online ---
PVP_ENABLED: ""
AVATARS_ENABLED: ""
CONTAINERS_IGNORE_OWNERSHIP: ""
CAN_DAMAGE_PLAYER_OWNED_STRUCTURES: ""
DYNAMIC_BUILDING_DAMAGE: ""
# --- Gameplay multipliers (empty = use game default) ---
PLAYER_XP_RATE: ""
PLAYER_XP_KILL: ""
PLAYER_XP_HARVEST: ""
PLAYER_XP_CRAFT: ""
PLAYER_XP_TIME: ""
PLAYER_DAMAGE_MULTIPLIER: ""
PLAYER_DAMAGE_TAKEN: ""
PLAYER_HEALTH_REGEN: ""
PLAYER_STAMINA_COST: ""
PLAYER_SPRINT_SPEED: ""
PLAYER_MOVEMENT_SPEED: ""
PLAYER_ENCUMBRANCE: ""
NPC_DAMAGE_MULTIPLIER: ""
NPC_DAMAGE_TAKEN: ""
NPC_HEALTH_MULTIPLIER: ""
NPC_RESPAWN_MULTIPLIER: ""
NPC_MAX_SPAWN_CAP: ""
STRUCTURE_DAMAGE_MULTIPLIER: ""
STRUCTURE_HEALTH_MULTIPLIER: ""
LANDCLAIM_RADIUS_MULTIPLIER: ""
AVATAR_DOME_DURATION: ""
AVATAR_DOME_DAMAGE: ""
FRIENDLY_FIRE_DAMAGE: ""
# --- Pre-declared so allocator-derived ports survive resolve.go's filter. ---
GAME_PORT: "7777"
PINGER_PORT: "7778"
QUERY_PORT: "27015"
RCON_PORT: "25575"
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" }
# Shared Steam-Workshop content volume — exact same pattern as DayZ
# (modules/dayz/module.yaml mounts panel-dayz-workshop at
# /game/steamapps/workshop). Volume's root contains a flat
# `content/<appid>/<id>/` tree (no extra prefix); mounting at
# /game/steamapps/workshop makes it appear as
# /game/steamapps/workshop/content/440900/<id>/<file>.pak
# — under the /game declared root, so FsList/FsSymlink work without
# extra browseable_roots. The SteamCMD sidecar uses a different deep
# path on its end (/conan-workshop/steamapps/workshop) so its
# +force_install_dir matches; see conanmods.go's
# runSteamCMDConanWorkshopDownloadOnceStreaming for the args. Both
# the sidecar and the game container are talking to the same volume,
# they just label the mount point differently.
- { type: volume, name: "panel-conan-workshop", container: "/game/steamapps/workshop" }
# Conan Exiles ports (matches AMP template):
# 7777/both (tcp+udp) — main game traffic + client mod downloads
# 7778/udp — pinger (game+1 offset; server browser probe)
# 27015/udp — Steam query
# 25575/tcp — RCON (internal)
ports:
# Conan binds the game socket on UDP `-Port=N` and the pinger
# (server-browser probe) on UDP N+1 — internally derived, no override
# flag exists. The panel allocator scans the agent window per-port; with
# no prior holes the first three rows land on consecutive numbers
# N, N+1, N+2 — so PINGER_PORT == GAME_PORT+1 and Conan's auto-derived
# pinger lands on the row we forward.
#
# `game` MUST be UDP — Conan's game socket is UDP-only. (AMP's template
# historically listed it as tcp+udp because of optional in-game mod
# downloads, but the dedicated-server depot never opens a TCP listener
# on -Port; the opnfwd orchestrator forwards by declared proto, so a
# tcp row here ends up forwarding a dead TCP port and leaves the real
# UDP game socket unforwarded — players can't join, server doesn't
# register on the master server.)
- { name: game, proto: udp, default: 7777, required: true, env: GAME_PORT }
- { name: pinger, proto: udp, default: 7778, required: true, env: PINGER_PORT }
- { name: query, proto: udp, default: 27015, required: true, env: QUERY_PORT }
- { name: rcon, proto: tcp, default: 25575, internal: true, env: RCON_PORT }
resources:
# Bumped for Enhanced UE5 (was 6144 / 12288 for UE4). Funcom + community
# benchmarks have populated Enhanced worlds at 2-4x the UE4 footprint —
# 8 GB minimum keeps a small server alive, 24 GB recommended for a
# populated long-running build-heavy server.
min_ram_mb: 8192
recommended_ram_mb: 24576
# Per-instance generated RCON secret — overrides env RCON_PASSWORD at
# create so every instance gets a unique password (see pkg/module
# GenerateSecrets). Operators can still override via the RCON Password
# config field.
secrets:
- name: RCON_PASSWORD
description: "Conan Exiles Source-RCON password — generated per instance"
generated: true
rcon:
adapter: source_rcon
host_port: rcon
auth: source_rcon_login
password_secret: RCON_PASSWORD
# Legacy fallback only (pre-generated-secret instances).
password_literal: "panel_rcon"
commands:
# Conan's RCON commands. `listplayers` is the roster query; `broadcast`
# sends an in-game message; `kick`/`ban` take a player # identifier.
list: "listplayers"
broadcast: "broadcast {msg}"
kick: "kickplayer {player}"
ban: "banplayer {player}"
save: "saveworld"
shutdown: "shutdown"
state_sources:
- type: rcon
command: "listplayers"
every: 60s
# Log-line event patterns. AMP's template is the source of these regexes —
# tested against real Conan Exiles boot output. Go regex = RE2; convert
# `(?<name>...)` → `(?P<name>...)`.
events:
join:
pattern: 'LogNet: Join succeeded: (?P<name>.+?#\d+)'
kind: join
leave:
pattern: 'LogNet: Player disconnected: (?P<name>.+?#\d+)'
kind: leave
chat:
pattern: 'ChatWindow: Character (?P<name>.+?) said: (?P<msg>.+)'
kind: chat
# Two update branches on the same appid. Operator picks via the Update
# button in the panel UI — `enhanced` is the default (UE5, public branch);
# `legacy` pins to UE4 via the `conan-exiles-legacy` Steam beta branch
# (Funcom's announced indefinite UE4 archive).
update_providers:
# `id` is also the user-facing label in the UI (no separate label field on
# this struct yet; see pkg/module/manifest.go UpdateProvider). Choose
# short ids that read well in the Update dropdown.
- id: enhanced
kind: steamcmd
app_id: "443030"
platform: linux # Enhanced (UE5) has a real Linux depot (443032, ~4.27 GiB) since 2026-05-05 — run native, no Wine.
install_path: /game
# Conan's `app_update 443030 validate` fails on empty volumes with
# "Missing configuration" (4/4 retries). Plain `app_update 443030`
# succeeds. Skip validate so first install just works; operators can
# trigger a manual Update later to re-validate existing files.
skip_validate: true
- id: legacy
kind: steamcmd
app_id: "443030"
beta: conan-exiles-legacy # confirmed branch name via app_info_print 2026-05-07
platform: windows # Legacy (UE4) has NO Linux depot — this is the only remaining Wine path in the module.
install_path: /game
skip_validate: true
# UI-driven configuration. Each `key` here maps 1:1 to an env key declared
# above; the entrypoint reads non-empty env values and stamps them into
# [ServerSettings] in ServerSettings.ini at every boot (idempotent — same
# value re-stamped is a no-op). Empty strings mean "leave the INI alone";
# operator edits via the Files tab to ServerSettings.ini are preserved for
# any field whose corresponding config_value is left blank.
#
# The most-tuned fields are at the top (no advanced flag); niche / tuning
# multipliers are tagged `advanced: true` so the basic Config tab stays
# scannable on a phone. AMP's official template surfaces ~11 fields; we
# expose a superset (~30) covering identity, anti-cheat, PvP, and the
# common gameplay knobs — beyond that, edit ServerSettings.ini directly.
config_values:
# ===== Edition (UE4 vs UE5) =====
# Pick at create time. Drives:
# 1. Which Steam branch downloads on first install (auto-update picks
# the matching update_provider via dispatch.go's edition-aware
# auto-update logic — `enhanced` → public branch, `legacy` →
# `conan-exiles-legacy` Steam beta).
# 2. Which Workshop mods the panel's Mods tab surfaces — Steam tags
# every published item as either "Enhanced" or "Legacy"; the panel's
# workshop search adds `requiredtags[]=Enhanced|Legacy` per the
# instance's edition. UE5 servers don't see UE4-only mods (they
# load silently as compatible-format files but don't actually run
# because the .pak ABI changed).
# Operators changing edition after creation: change this value, then
# click Update (which now uses the matching branch). Existing world
# data is preserved.
- key: EDITION
label: "Edition"
description: "Conan Exiles version. Enhanced (UE5, 2026-05+) is Funcom's current default. Legacy (UE4) is the indefinitely-archived prior build — pick this only if your players need UE4 mods that haven't been ported yet. Switching after creation requires clicking Update; saves are preserved across editions."
default: "enhanced"
options:
- { value: "enhanced", label: "Enhanced (UE5, default)" }
- { value: "legacy", label: "Legacy (UE4 — Steam beta branch)" }
# ===== Identity =====
- key: SERVER_NAME
label: "Server Name"
description: "Name shown in the server browser and steam community list."
default: "panel Conan Exiles"
- key: SERVER_PASSWORD
label: "Server Password"
description: "Password required to join the server. Leave blank for an open server."
default: ""
- key: ADMIN_PASSWORD
label: "Admin Password"
description: "Lets the operator open the in-game admin panel via 'Make Me Admin' on a logged-in client. Stamped into [ServerSettings] AdminPassword= on every restart. Leave blank to leave the existing INI value alone."
default: ""
- key: RCON_PASSWORD
label: "RCON Password"
description: "Source-RCON password used by the panel for player listing, broadcast, kick, ban, and saveworld. Avoid spaces or quotes. Leave blank to use the per-instance generated secret (recommended)."
default: ""
- key: MAX_PLAYERS
label: "Max Players"
description: "Player slot limit. Conan's official cap is 70; communities run 40-60 for most build densities."
default: "40"
- key: SERVER_REGION
label: "Server Region"
description: "Region tag used for server-browser filtering. Picks up the client's regional preferences."
default: "1"
options:
- { value: "0", label: "Europe" }
- { value: "1", label: "North America" }
- { value: "2", label: "Asia" }
- { value: "3", label: "Australia" }
- { value: "4", label: "South America" }
- { value: "5", label: "Japan" }
# ===== Map =====
- key: SERVER_MAP
label: "Map"
description: "Map loaded at server start. For a custom map, choose 'Custom' and fill in the path below."
default: "/Game/Maps/ConanSandbox/ConanSandbox"
options:
- { value: "/Game/Maps/ConanSandbox/ConanSandbox", label: "Exiled Lands (default)" }
- { value: "/Game/DLC_EXT/DLC_Siptah/Maps/DLC_Isle_of_Siptah", label: "Isle of Siptah" }
- { value: "{{CUSTOM_MAP}}", label: "Custom (use Custom Map below)" }
- key: CUSTOM_MAP
label: "Custom Map Path"
description: "Path to a custom map's UE asset. Only used when Map is set to 'Custom'. Example: /Game/Mods/Savage_Wilds/Savage_Wilds"
default: ""
# ===== Anti-cheat =====
- key: ENABLE_BATTLEYE
label: "Enable BattlEye"
description: "BattlEye anti-cheat. Off by default — works under Wine but adds a startup handshake that occasionally races on cold boots."
default: "False"
options:
- { value: "True", label: "On" }
- { value: "False", label: "Off" }
- key: ENABLE_VAC
label: "Enable Valve Anti-Cheat"
description: "Steam VAC. Generally safe to leave on for public servers."
default: "True"
options:
- { value: "True", label: "On" }
- { value: "False", label: "Off" }
# ===== PvP / world =====
- key: PVP_ENABLED
label: "PvP Enabled"
description: "Master toggle for player-vs-player damage. Time-window restrictions are configured in ServerSettings.ini directly (PVPRestrict* keys); this just flips the global switch."
default: ""
options:
- { value: "", label: "(leave INI default)" }
- { value: "True", label: "On" }
- { value: "False", label: "Off" }
- key: AVATARS_ENABLED
label: "Avatars Enabled"
description: "Allow players to summon god avatars. Off in many private cluster setups to prevent griefing."
default: ""
options:
- { value: "", label: "(leave INI default)" }
- { value: "True", label: "On" }
- { value: "False", label: "Off" }
- key: CONTAINERS_IGNORE_OWNERSHIP
label: "Containers Ignore Ownership"
description: "If true, players can open any container regardless of owner. Useful for co-op-friendly servers; disable for strict PvP."
default: ""
options:
- { value: "", label: "(leave INI default)" }
- { value: "True", label: "On" }
- { value: "False", label: "Off" }
- key: CAN_DAMAGE_PLAYER_OWNED_STRUCTURES
label: "Allow Building Damage"
description: "Master toggle for player-owned structure damage. Combine with the dynamic decay setting for a softer PvP feel."
default: ""
options:
- { value: "", label: "(leave INI default)" }
- { value: "True", label: "On" }
- { value: "False", label: "Off" }
- key: DYNAMIC_BUILDING_DAMAGE
label: "Dynamic Building Damage"
description: "Decays vacant structures over time. Helps clean up abandoned bases on long-running servers."
default: ""
options:
- { value: "", label: "(leave INI default)" }
- { value: "True", label: "On" }
- { value: "False", label: "Off" }
- key: FRIENDLY_FIRE_DAMAGE
label: "Friendly Fire Damage Multiplier"
description: "0.01.0+ multiplier on damage between clan members. Default 0.25."
default: ""
# ===== Niche knobs (advanced) =====
- key: MAX_NUDITY
label: "Max Nudity"
description: "0=none, 1=partial, 2=full. Affects the default character render plus what skins players can wear."
default: "0"
advanced: true
options:
- { value: "0", label: "None" }
- { value: "1", label: "Partial" }
- { value: "2", label: "Full" }
- key: SERVER_COMMUNITY
label: "Server Community"
description: "0=Purist, 1=Relaxed, 2=Hard, 3=Hardcore, 4=Roleplaying, 5=Experimental. Just a tag in the server browser."
default: "0"
advanced: true
options:
- { value: "0", label: "Purist" }
- { value: "1", label: "Relaxed" }
- { value: "2", label: "Hard" }
- { value: "3", label: "Hardcore" }
- { value: "4", label: "Roleplaying" }
- { value: "5", label: "Experimental" }
# ===== XP rates (advanced) =====
- key: PLAYER_XP_RATE
label: "XP — Overall Rate Multiplier"
description: "Overall multiplier applied on top of every XP source. Default 1.0."
default: ""
advanced: true
- key: PLAYER_XP_KILL
label: "XP — Kill Multiplier"
description: "XP multiplier per kill. Default 1.0."
default: ""
advanced: true
- key: PLAYER_XP_HARVEST
label: "XP — Harvest Multiplier"
description: "XP multiplier per harvest tick. Default 1.0."
default: ""
advanced: true
- key: PLAYER_XP_CRAFT
label: "XP — Craft Multiplier"
description: "XP multiplier per crafted item. Default 1.0."
default: ""
advanced: true
- key: PLAYER_XP_TIME
label: "XP — Time Multiplier"
description: "Idle / time-based XP. Default 1.0."
default: ""
advanced: true
# ===== Player tuning (advanced) =====
- key: PLAYER_DAMAGE_MULTIPLIER
label: "Player Damage Output"
description: "Damage dealt by players. Default 1.0."
default: ""
advanced: true
- key: PLAYER_DAMAGE_TAKEN
label: "Player Damage Taken"
description: "Damage received by players. Lower = tougher players."
default: ""
advanced: true
- key: PLAYER_HEALTH_REGEN
label: "Player Health Regen Speed"
description: "Default 1.0."
default: ""
advanced: true
- key: PLAYER_STAMINA_COST
label: "Player Stamina Cost"
description: "Multiplier on stamina drained by sprint/jump/swim. Lower = forgiving."
default: ""
advanced: true
- key: PLAYER_SPRINT_SPEED
label: "Player Sprint Speed Scale"
description: "Default 1.0. Bumping past 1.5 makes ranged combat awkward."
default: ""
advanced: true
- key: PLAYER_MOVEMENT_SPEED
label: "Player Movement Speed Scale"
description: "Default 1.0."
default: ""
advanced: true
- key: PLAYER_ENCUMBRANCE
label: "Player Encumbrance Multiplier"
description: "Carry weight cap multiplier. Default 1.0."
default: ""
advanced: true
# ===== NPC tuning (advanced) =====
- key: NPC_DAMAGE_MULTIPLIER
label: "NPC Damage Output"
description: "Damage dealt by hostile NPCs. Default 1.0."
default: ""
advanced: true
- key: NPC_DAMAGE_TAKEN
label: "NPC Damage Taken"
description: "Damage NPCs receive. Lower = bullet-sponge enemies."
default: ""
advanced: true
- key: NPC_HEALTH_MULTIPLIER
label: "NPC Health Multiplier"
description: "Base HP scaling. Default 1.0."
default: ""
advanced: true
- key: NPC_RESPAWN_MULTIPLIER
label: "NPC Respawn Multiplier"
description: "How fast world spawns refill. >1.0 = faster respawn."
default: ""
advanced: true
- key: NPC_MAX_SPAWN_CAP
label: "NPC Max Spawn Cap Multiplier"
description: "Cap on simultaneous active world NPCs. Lower = less server CPU under load."
default: ""
advanced: true
# ===== Structures / land claim (advanced) =====
- key: STRUCTURE_DAMAGE_MULTIPLIER
label: "Structure Damage Multiplier"
description: "Damage dealt to player-built structures. Default 1.0."
default: ""
advanced: true
- key: STRUCTURE_HEALTH_MULTIPLIER
label: "Structure Health Multiplier"
description: "Base HP of placed pieces. >1.0 = sturdier bases."
default: ""
advanced: true
- key: LANDCLAIM_RADIUS_MULTIPLIER
label: "Landclaim Radius Multiplier"
description: "How big the no-build bubble around each placeable is. <1.0 lets settlements pack tighter."
default: ""
advanced: true
- key: AVATAR_DOME_DURATION
label: "Avatar Dome Duration Multiplier"
description: "How long avatar shields hold. Default 1.0."
default: ""
advanced: true
- key: AVATAR_DOME_DAMAGE
label: "Avatar Dome Damage Multiplier"
description: "Damage vs. avatar shields. Default 1.0."
default: ""
advanced: true
# --- 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: '/LogGameState: Match State Changed from WaitingToStart to InProgress/i'
appearance:
emoji: "⚔️"
grad: "linear-gradient(135deg,#5a1a00,#c2410c)"
art: "/game-art/conan-exiles.jpg"
steam: "440900"