Files
gbr-games/schema/game.schema.json
T
gregandClaude Opus 5.5 2ad445ffdd
Validate game templates / validate (push) Successful in 9m32s
Validate game templates / boot-test (push) Failing after 11m8s
Validate game templates / publish (push) Skipped
fix(ark): run as the image's own user; honour custom RCON ports
ARK's image is built around user gameserver (uid 25000) and locks
/home/gameserver to it, so under the node's default 1000:1000 the
startup script got "Permission denied" and the server restart-looped,
on real nodes as well as in boot tests. Set containerUser 25000:25000
(needs node-agent and panel support for containerUser).

The startup command read ${RCON_PORT}, but the node passes the RCONPort
setting as RCONPORT, so custom RCON ports were ignored for 27020.

Boot-tested in the Linux agent image: up 30 minutes with no restart.
The test now waits 10 minutes, covering the first ~13 GB download.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-24 22:17:03 -04:00

94 lines
4.6 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://www.gitea.gbreed.com/gbreed-public/gbr-games/raw/branch/main/schema/game.schema.json",
"title": "GBR Hosting game template",
"description": "One game.yaml per game. Field names match the control panel's Game entity. Business settings (enabled, paid/private hosting, cover art, pricing tiers) are managed in the panel, not here.",
"type": "object",
"additionalProperties": false,
"required": ["id", "name", "defaultDockerImage", "installMethod"],
"properties": {
"id": {
"description": "Stable identifier. Must match the folder name. Never rename: servers reference it.",
"type": "string",
"pattern": "^[a-z0-9]+(-[a-z0-9]+)*$",
"maxLength": 64
},
"name": { "description": "Display name shown to users.", "type": "string", "minLength": 1 },
"defaultDockerImage": { "description": "Image the game server runs in.", "type": "string", "minLength": 1 },
"containerUser": {
"description": "uid:gid the server runs as, for images built around their own user (ARK's needs 25000:25000). Defaults to 1000:1000; root is never allowed.",
"type": ["string", "null"],
"pattern": "^[1-9][0-9]{0,8}:[1-9][0-9]{0,8}$"
},
"defaultStartupCommand": { "description": "Command run inside the container. May use config placeholders.", "type": ["string", "null"] },
"requiredMemoryMb": { "type": ["integer", "null"], "minimum": 128 },
"requiredDiskMb": { "type": ["integer", "null"], "minimum": 128 },
"defaultPorts": {
"description": "Ports to expose, e.g. \"25565/tcp\", \"7777/udp\" or \"8211:8211/udp\".",
"type": ["array", "null"],
"items": { "type": "string", "pattern": "^([0-9]{1,5}:)?[0-9]{1,5}(-[0-9]{1,5})?(/(tcp|udp))?$" }
},
"installMethod": { "enum": ["STEAMCMD", "BASH_SCRIPT", "NONE"] },
"installConfig": {
"description": "STEAMCMD needs appId; BASH_SCRIPT needs script.",
"type": ["object", "null"]
},
"defaultConfig": {
"description": "Default values for every server. Users override these per server. Keys may only contain letters, digits, '_', '-' and '.' (the panel drops anything else).",
"type": ["object", "null"],
"propertyNames": { "pattern": "^[a-zA-Z0-9_.-]+$", "maxLength": 128 }
},
"configSchema": {
"description": "JSON Schema for the settings users can edit. A property with \"x-consent\" (e.g. an EULA) must be explicitly accepted on deploy.",
"type": ["object", "null"]
},
"configFilePath": { "description": "Where defaultConfig + user overrides are written, relative to the server root.", "type": ["string", "null"] },
"configFileFormat": {
"description": "INI writes key=value lines; STANDARD uses configFormatOptions (header, separator, quoting) for formats like Unreal's OptionSettings=(...).",
"enum": ["INI", "STANDARD", "JSON", "ENV", null]
},
"configFormatOptions": { "type": ["object", "null"] },
"agnosticCommandConfig": { "description": "How the panel sends console commands (save, broadcast, kick...).", "type": ["object", "null"] },
"clusterConfig": { "type": ["object", "null"] },
"logFilePath": { "type": ["string", "null"] },
"readyPattern": {
"description": "Regex matched against the server's console output and log file once it can accept players. Without one, boot tests only check the server stays up.",
"type": ["string", "null"]
},
"failPatterns": {
"description": "Regexes that mean the server will not come up (e.g. an unaccepted EULA). Boot tests fail as soon as one matches.",
"type": ["array", "null"],
"items": { "type": "string" }
},
"backupPaths": { "type": ["array", "null"], "items": { "type": "string" } },
"maxBackupSizeMb": { "type": ["integer", "null"], "minimum": 1 },
"publicIpConfigKey": { "description": "Config key that receives the server's public address.", "type": ["string", "null"] },
"maxPlayersConfigKey": { "description": "Config key locked to the pricing tier's player count.", "type": ["string", "null"] },
"mapConfigKey": { "type": ["string", "null"] },
"aiCoverPrompt": { "type": ["string", "null"] }
},
"allOf": [
{
"if": { "properties": { "installMethod": { "const": "STEAMCMD" } } },
"then": {
"required": ["installConfig"],
"properties": { "installConfig": { "type": "object", "required": ["appId"] } }
}
},
{
"if": { "properties": { "installMethod": { "const": "BASH_SCRIPT" } } },
"then": {
"required": ["installConfig"],
"properties": { "installConfig": { "type": "object", "required": ["script"] } }
}
}
]
}