`gbrgames draft -name <game> -image <image> [-steam-app ID] [-first-boot report.json]` has a model write games/<id>/game.yaml and test.yaml, then checks them like any template and sends problems back for up to three fix rounds. Ollama (default qwen3.8:27b, OLLAMA_URL) or Gemini (GEMINI_API_KEY). The model gets AUTHORING.md (new: the house rules, from how nodes really run servers and today's bugs: x-generate passwords, no published RCON ports, containerUser, heap below the limit, env var naming, ready lines), the inspect-image facts, the first-boot report and the two smallest templates as examples. With Ollama the answer is forced into the template schema, tightened for drafts so ports, install config, settings and their descriptions can't be left out or null. On top of validation and the image checks, drafts must: - be complete: player ports, >=3 settings each described, real memory, a readyPattern or settleSec - name settings so they reach the container as variables the image reads (ServerPassword -> SERVER_PASSWORD when the image reads SERVER_PASS) - not leave files first boot saw outside /server (lost on every start) A first boot showing the image dies within seconds on permission errors (it needs root) stops the draft before any model call. A missing readyPattern is filled from first boot's verified suggestion. Valheim, which we have no template for: lloesche/valheim-server was rejected as root-only; via SteamCMD (app 896660) qwen3.8:27b on a local 3090 Ti wrote a passing template in two rounds (~1 min). It is about 70% there: it chose "--save-dir /server", which Valheim doesn't have (-savedir), and missed -crossplay and 2457/udp. The save check is a text check; booting the draft itself is the next step. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
5.8 KiB
5.8 KiB
Writing a game template
House rules for games/<id>/game.yaml and test.yaml. They apply to people and to the AI builder alike;
the builder reads this file as its instructions. Most rules exist because breaking them broke a real server.
gbrgames validate and gbrgames inspect-image -game <id> enforce what they can.
How a server actually runs
Know this before writing anything:
- The node-agent creates one Docker volume per server and mounts it at both
/serverand/data. Game files, saves and config live there. Anything written elsewhere is lost when the container is recreated, which happens on every start. - The container runs as
1000:1000unless the template setscontainerUser. Never as root. - Every key in
defaultConfig(merged with the owner's changes) reaches the container as an environment variable, named by the agent's rule: insert_before an upper-case run that follows a lower-case letter, then upper-case everything.ServerName→SERVER_NAME,MaxPlayers→MAX_PLAYERS,RCONPort→RCONPORT(no underscore: no lower-case letter beforeP),bIsPvP→B_IS_PV_P. - If
configFilePathis set, the same settings are also written to that file inconfigFileFormatbefore each start. defaultStartupCommand, when set, runs asbash -c "… cd /server && <command>"; otherwise the image's own entrypoint runs.installMethod: STEAMCMDinstallsinstallConfig.appIdinto/serverwith anonymous SteamCMD before the server starts.- Nodes reach a server's RCON, REST and query ports over the Docker network, never through published ports.
Rules
Passwords and secrets
- Admin, RCON and REST passwords:
defaultConfigvalue"", and theconfigSchemaproperty gets"x-generate": {"type": "password"}. The panel then gives every server its own password. Never put a placeholder or default password in a template. Shared defaults are readable by anyone from the public game list. - Join passwords stay
"", with"x-savedValue": {"key": "GAME_JOIN_PASSWORD", "label": "Game join password", "secret": true}. - No real credentials, tokens or IDs anywhere in this repo. A value a server can't start without (a
player ID, a license token) goes in
test.yamlundersecrets, mapped to a CI secret name.
Ports
defaultPortslists only what players connect to (game and query ports). RCON, REST and admin ports are never published.- Use
"7777/udp"or"8211:8211/udp". Every port needs its protocol.
The container user
- Run
gbrgames inspect-image -game <id>. If the image is built around its own user (it declaresUSER, or locks a home folder to a uid other than 1000), setcontainerUserto thatuid:gid. Example: ARK's image needs25000:25000.
The startup command
- Read settings under the names the agent gives them (see above).
${RCON_PORT}when the setting isRCONPortsilently ignores the owner's value; it arrives asRCONPORT. - Use
${VAR:-default}for anything that may be unset. - If the image installs the game itself on start, make sure a failed install is retried rather than starting a server that isn't there (see ARK's template).
Memory
requiredMemoryMbis the container's hard limit. A JVM or engine heap must stay about 1 GB below it: Minecraft'sMEMORYis3Gfor4096MB. A heap equal to the limit is killed on start.
Settings (defaultConfig and configSchema)
- Take keys and default values from what the server really writes: run
gbrgames first-boot -game <id>and read the settings in its report. - Every key in
defaultConfighas aconfigSchemaproperty, and the other way round. - Every property has a
title, a shortdescriptionfor a player, atype, adefaultequal to thedefaultConfigvalue, and acategory: one ofSecurity & Access,Gameplay & Rules,World & Environment,System & General. Mark the few that matter when deploying with"showOnDeploy": true. - An EULA or other agreement is a boolean with
"x-consent": {"label": "...", "url": "..."}and defaultfalse. Nevertrue; the owner accepts it themselves. - Keys may only use letters, digits,
_,-and.. - Placeholders the panel fills in:
{SERVER_NAME},{PUBLIC_IP},{PUBLIC_HOST},{PUBLIC_PORT_<port>},{saved:KEY}. Server names default toGBRHosting - {SERVER_NAME}.
Being ready
readyPattern: a regex that matches the line the server prints once players can join. Take it from a first boot (its report suggests one), and put that real line intest.yamlasreadyExample; validation checks the pattern matches it.failPatterns: lines that mean the server will never come up (e.g. an unaccepted EULA).- No reliable ready line: leave
readyPatternout, and the boot test checks the server stays up (settleSecintest.yaml, default 120 s).
test.yaml
config: only what the server can't start without in CI (accepting the EULA, for example).secrets: required values that can't be public, mapped to CI secret names.timeoutSeccovers install plus start; SteamCMD games need 2400 or more.
Commands and players (agnosticCommandConfig)
- Kick, ban, save and broadcast go through RCON or REST using the server's own password key
(
passwordKey/rconPasswordKeypointing at thex-generatesetting). If a game has neither, mark the action"type": "UNSUPPORTED"; never invent one. playerJoinRegex/playerLeaveRegexcapture the player name from real log lines.
Before opening a PR
go run ./cmd/gbrgames validate
go run ./cmd/gbrgames inspect-image -game <id>
go run ./cmd/gbrgames first-boot -game <id> # on a machine with Docker and the gbr-boottest network
CI then boot-tests the changed game with the node-agent's real deploy code before anything reaches the panel.