Files
panel/modules/conan-exiles/module.yaml
T
2026-07-15 01:06:16 -07:00

574 lines
24 KiB
YAML
Raw 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.
# 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"