From 87ffc4d696d012febbce822e63c34e7a2e53e71a Mon Sep 17 00:00:00 2001 From: valgulnecron <39313199+ValgulNecron@users.noreply.github.com> Date: Tue, 14 Jul 2026 23:55:44 +0200 Subject: [PATCH] feat(dayz): adopt the battleye RCon protocol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gameplane's agent now speaks BattlEye RCon, so DayZ goes from having no console at all to a working console, a players list and moderation. ServerZ only writes a beserver_x64.cfg when one of BE_IP/BE_PORT/ BE_PASSWORD is set, so all three are: the operator injects the generated password into BE_PASSWORD (written out as RConPassword), and BE_IP pins the listener to loopback — the agent sidecar shares the pod's network namespace and reaches it there, while an RCon port that isn't on a public interface can't be brute-forced from the internet. players parses BattlEye's table, skipping its header and the '(N players in total)' footer. kick/ban are deliberately left unset: BattlEye moderates by player number rather than name, so wiring them to the name the list returns would silently target the wrong player. No lifecycle.stop — DayZ persists continuously rather than on a save command, so a SIGTERM isn't the data-loss risk it is for Minecraft, and shipping a stop sequence that doesn't reliably work would be a lie. Signed-off-by: valgulnecron <39313199+ValgulNecron@users.noreply.github.com> --- dayz/module.yaml | 2 +- dayz/template.yaml | 52 ++++++++++++++++++++++++++++++++++++++-------- validate.py | 2 +- 3 files changed, 45 insertions(+), 11 deletions(-) diff --git a/dayz/module.yaml b/dayz/module.yaml index 92f3fb1..90df79b 100644 --- a/dayz/module.yaml +++ b/dayz/module.yaml @@ -1,7 +1,7 @@ apiVersion: gameplane.local/module/v1 name: dayz displayName: DayZ -version: 1.0.1 +version: 1.1.0 game: dayz categories: [Survival, Shooter, PvP, Horror] summary: DayZ dedicated server (GodBleak/ServerZ). No RCON is reachable (proprietary BattlEye RCon, not Source RCON) — manage via SIGTERM-safe restarts and config fields. diff --git a/dayz/template.yaml b/dayz/template.yaml index eb8f5d9..6616e43 100644 --- a/dayz/template.yaml +++ b/dayz/template.yaml @@ -6,14 +6,21 @@ # Hub/GHCR mirror). The previous draft's image (jammsen/dayz-dedicated- # server) does not exist on any registry at all — this replaces it. # -# DayZ speaks BattlEye RCon, a proprietary protocol distinct from both -# Source RCON and telnet — the agent has no client for it, so -# rcon.protocol is "none". The game binary itself has no interactive -# admin console either (admin is BattlEye RCon or an external tool only), -# so consoleMode is also left unset ("no live console" in the UI) rather -# than "pty", which would attach to a process that reads no meaningful -# input. With no RCON and no working console, there is no capabilities -# block, no lifecycle.stop, and no capabilities.mods — see README for why. +# DayZ speaks BattlEye RCon, a proprietary UDP protocol distinct from both +# Source RCON and telnet. The agent now implements it +# (rcon.protocol: battleye), so the console, players list and moderation +# work. The game binary has no interactive stdin console at all — admin is +# BattlEye RCon or nothing — so consoleMode is "rcon", not "pty". +# +# ServerZ only writes a beserver_x64.cfg at all if one of BE_IP/BE_PORT/ +# BE_PASSWORD is set, so all three are set below: the operator injects the +# generated password into BE_PASSWORD, and BE_IP pins the listener to +# loopback, which is where the agent sidecar dials it from inside the pod. +# +# No lifecycle.stop: BattlEye RCon has a "#shutdown" command, but DayZ's +# is unreliable across builds and the server persists continuously rather +# than on a save command, so a SIGTERM is not the data-loss risk it is for +# Minecraft or Valheim. Left out rather than shipping a stop that lies. # # Cluster-scoped; apply once per cluster: # kubectl apply -f modules/dayz/template.yaml @@ -45,6 +52,15 @@ spec: value: "2302" - name: STEAM_QUERY_PORT value: "27015" + # ServerZ generates beserver_x64.cfg only when one of BE_* is set. + # BE_PASSWORD is injected by the operator (see rcon.passwordEnv). + - name: BE_PORT + value: "2305" + # Bind RCon to loopback: the agent sidecar shares the pod's network + # namespace, so it reaches 127.0.0.1:2305 — and an RCon port that is + # not on a public interface cannot be brute-forced from the internet. + - name: BE_IP + value: "127.0.0.1" ports: - name: game @@ -75,7 +91,25 @@ spec: memory: 12Gi rcon: - protocol: none + protocol: battleye + port: 2305 + # ServerZ writes this into beserver_x64.cfg as RConPassword. + passwordEnv: BE_PASSWORD + + consoleMode: rcon + + capabilities: + players: + # BattlEye's "players" reply is a table: + # [#] [IP Address]:[Port] [Ping] [GUID] [Name] + # 0 10.0.0.5:2304 32 abc123…(OK) Survivor + # followed by a "(N players in total)" footer, which this regex skips. + list: + command: "players" + entryRegex: '^\s*\d+\s+\S+:\d+\s+\d+\s+\S+\s+(?P.+?)\s*$' + # BattlEye moderation is by player number, not name — the dashboard + # passes what the list returned, so kick/ban are left unset rather + # than shipping commands that would silently target the wrong player. # No console/probes block: the game's only ports are UDP (no TCP surface # to probe), and there is no HTTP endpoint either. Kubernetes falls back diff --git a/validate.py b/validate.py index 3ba1e88..8f93c87 100644 --- a/validate.py +++ b/validate.py @@ -124,7 +124,7 @@ # the GameTemplate CRD's rcon.protocol enum and agent/internal/rcon/. A protocol # listed here but not implemented lets a module ship a console that never # connects, so this list is deliberately conservative. -RCON_PROTOCOLS = ("source", "telnet", "websocket", "none") +RCON_PROTOCOLS = ("source", "telnet", "websocket", "battleye", "none")