commit 822b44e2e5e62938069e13f26711874460e7e4f2 Author: Greg Date: Sat Oct 24 16:54:21 2026 -0400 feat: game templates, schemas and validator Public game templates for GBR Hosting, one games//game.yaml per game, imported from the panel. A Go tool validates them against JSON Schemas and checks for credentials, private addresses and consents that default to accepted; CI runs it on every push. Admin/RCON passwords are no longer shared defaults: they are blank with x-generate so the panel creates one per server, and Palworld/ARK no longer publish their RCON/REST ports. Co-Authored-By: Claude Opus 5.5 diff --git a/.gitea/workflows/validate.yml b/.gitea/workflows/validate.yml new file mode 100644 index 0000000..ecc4314 --- /dev/null +++ b/.gitea/workflows/validate.yml @@ -0,0 +1,23 @@ +name: Validate game templates + +# Push-only on purpose: this repo is public, and a pull_request trigger would run a fork's +# version of this workflow on our self-hosted runner. PRs from branches in this repo are +# still checked through their push. +on: + push: + workflow_dispatch: + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Validate + run: go run ./cmd/gbrgames validate diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c7a0bfc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Raw panel exports contain disabled games and panel-only settings; only game folders are published. +*.export.json +export.json + +/gbrgames +/gbrgames.exe diff --git a/README.md b/README.md new file mode 100644 index 0000000..f68b618 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# GBR Hosting game templates + +Every game you can host on [GBR Hosting](https://gbrhosting.com) is defined by a template in this repo. +They're public so you can see exactly what your server runs: the Docker image, the startup command, +the install script, and every default setting. + +## Layout + +``` +games//game.yaml the template +games//test.yaml values CI uses to boot-test it +schema/ JSON Schemas for both files +cmd/gbrgames/ validator and importer +``` + +## Reading a template + +| Field | What it means | +| --- | --- | +| `defaultDockerImage` | The container image your server runs in. | +| `defaultStartupCommand` | The command started inside that container. | +| `installMethod` / `installConfig` | How game files are installed: `STEAMCMD` (anonymous SteamCMD download of `appId`), `BASH_SCRIPT` (the `script` shown in full), or `NONE` (the image already contains the game). | +| `defaultPorts` | Ports opened for players. | +| `defaultConfig` | Starting value for every setting. | +| `configSchema` | Which settings you can edit in the panel, with their types and limits. | +| `configFilePath` / `configFileFormat` | Where settings are written in your server's files, and in what format (`INI`, `JSON` or `ENV`). | +| `backupPaths` | What's included in backups. | + +### Your settings vs. the defaults + +When you create a server, it starts with `defaultConfig`. Anything you change in the panel overrides +that one setting for your server only; everything else keeps following the template. A few settings +are fixed by your plan. For example, the key named in `maxPlayersConfigKey` always matches your plan's +player count. + +### Values filled in for you + +Some defaults are placeholders the panel replaces when your server starts: + +| Placeholder | Becomes | +| --- | --- | +| `{SERVER_NAME}` | Your server's name. | +| `{PUBLIC_IP}`, `{PUBLIC_HOST}`, `{PUBLIC_PORT_n}` | The address players connect to. | +| `{saved:KEY}` | A value you stored under Saved Values (e.g. a password or token). | + +### Generated passwords + +Admin and RCON passwords are never shared between servers. A setting marked `x-generate` is left blank +in the template, and if you leave it blank when you deploy, your server gets its own easy password like +`otter-maple-42`. You can see it and change it any time in your server's settings. Values under +`replace` are old shared defaults: a server still using one gets a fresh password the next time it starts. + +### Agreements + +A setting marked `x-consent` in `configSchema` (a game's EULA, for example) is never accepted for you. +You tick it yourself when you deploy. + +## Changes + +Templates are maintained by GBR Hosting. Every change is a commit here, so the history shows what +changed for each game and when. Want a game added or something fixed? Open an issue. + +## Tooling + +```bash +go run ./cmd/gbrgames validate # what CI runs on every push +go run ./cmd/gbrgames import admin-games.export.json # pull templates from GET /api/v1/admin/games +``` + +Besides the schemas, `validate` checks that each folder name matches its `id`, that referenced config +keys exist, that consents don't default to accepted, and that no credentials or private network +addresses end up in a template. diff --git a/cmd/gbrgames/import.go b/cmd/gbrgames/import.go new file mode 100644 index 0000000..d6cd789 --- /dev/null +++ b/cmd/gbrgames/import.go @@ -0,0 +1,162 @@ +package main + +import ( + "encoding/json" + "errors" + "flag" + "fmt" + "math" + "os" + "path/filepath" + "strings" + + "gopkg.in/yaml.v3" +) + +// Game fields in the order they are written to game.yaml. Panel-owned business settings +// (enabled, allowPaidHosting, allowPrivateHosting, imageUrl) are deliberately left out. +var gameFields = []string{ + "id", "name", + "defaultDockerImage", "defaultStartupCommand", "requiredMemoryMb", "requiredDiskMb", "defaultPorts", + "installMethod", "installConfig", + "defaultConfig", "configSchema", "configFilePath", "configFileFormat", "configFormatOptions", + "agnosticCommandConfig", "clusterConfig", + "logFilePath", "backupPaths", "maxBackupSizeMb", + "publicIpConfigKey", "maxPlayersConfigKey", "mapConfigKey", + "aiCoverPrompt", +} + +const gameHeader = "# yaml-language-server: $schema=../../schema/game.schema.json\n" +const testHeader = "# yaml-language-server: $schema=../../schema/test.schema.json\n" + + "# Values CI uses to boot-test this game. Never put real credentials here: map them under 'secrets'.\n" + +func runImport(args []string) error { + fs := flag.NewFlagSet("import", flag.ExitOnError) + gamesDir := fs.String("games", "games", "directory to write one folder per game into") + all := fs.Bool("all", false, "also import games that are disabled in the panel") + fs.Parse(args) + if fs.NArg() != 1 { + return errors.New("import needs the path to a GET /api/v1/admin/games export") + } + + raw, err := os.ReadFile(fs.Arg(0)) + if err != nil { + return err + } + var games []map[string]any + if err := json.Unmarshal(raw, &games); err != nil { + return fmt.Errorf("export is not a JSON array of games: %w", err) + } + + for _, g := range games { + id, _ := g["id"].(string) + if id == "" { + return errors.New("export contains a game without an id") + } + if g["enabled"] == false && !*all { + fmt.Printf("skip %s (disabled in panel; use -all to include)\n", id) + continue + } + dir := filepath.Join(*gamesDir, id) + if err := os.MkdirAll(dir, 0o755); err != nil { + return err + } + if err := writeGame(filepath.Join(dir, "game.yaml"), g); err != nil { + return fmt.Errorf("%s: %w", id, err) + } + testPath := filepath.Join(dir, "test.yaml") + if _, err := os.Stat(testPath); errors.Is(err, os.ErrNotExist) { + if err := writeTest(testPath, g); err != nil { + return fmt.Errorf("%s: %w", id, err) + } + } + fmt.Printf("wrote %s\n", dir) + } + return nil +} + +func writeGame(path string, g map[string]any) error { + doc := &yaml.Node{Kind: yaml.MappingNode} + for _, f := range gameFields { + v, ok := g[f] + if !ok || isEmpty(v) { + continue + } + var val yaml.Node + if err := val.Encode(normalize(v)); err != nil { + return err + } + doc.Content = append(doc.Content, &yaml.Node{Kind: yaml.ScalarNode, Value: f}, &val) + } + return writeYAML(path, gameHeader, doc) +} + +// writeTest seeds a boot-test fixture: consents (EULAs) accepted, a timeout sized to the install method. +func writeTest(path string, g map[string]any) error { + cfg := map[string]any{} + for key, p := range schemaProperties(g) { + if prop, ok := p.(map[string]any); ok { + if c, ok := prop["x-consent"]; ok && c != false { + cfg[key] = true + } + } + } + timeout := 900 + if g["installMethod"] == "STEAMCMD" { + timeout = 2400 + } + test := map[string]any{"timeoutSec": timeout} + if len(cfg) > 0 { + test["config"] = cfg + } + var doc yaml.Node + if err := doc.Encode(test); err != nil { + return err + } + return writeYAML(path, testHeader, &doc) +} + +func writeYAML(path, header string, doc *yaml.Node) error { + var b strings.Builder + b.WriteString(header) + enc := yaml.NewEncoder(&b) + enc.SetIndent(2) + if err := enc.Encode(doc); err != nil { + return err + } + enc.Close() + return os.WriteFile(path, []byte(b.String()), 0o644) +} + +// normalize turns whole-number floats from encoding/json back into ints so YAML shows 25565, not 25565.0. +func normalize(v any) any { + switch x := v.(type) { + case float64: + if x == math.Trunc(x) && math.Abs(x) < 1<<53 { + return int64(x) + } + case map[string]any: + for k, e := range x { + x[k] = normalize(e) + } + case []any: + for i, e := range x { + x[i] = normalize(e) + } + } + return v +} + +func isEmpty(v any) bool { + switch x := v.(type) { + case nil: + return true + case string: + return x == "" + case map[string]any: + return len(x) == 0 + case []any: + return len(x) == 0 + } + return false +} diff --git a/cmd/gbrgames/main.go b/cmd/gbrgames/main.go new file mode 100644 index 0000000..41c23b9 --- /dev/null +++ b/cmd/gbrgames/main.go @@ -0,0 +1,34 @@ +// Command gbrgames validates the game templates in this repo and imports them from a panel export. +// +// go run ./cmd/gbrgames validate # check every games//game.yaml (+ test.yaml) +// go run ./cmd/gbrgames import export.json # write games// from GET /api/v1/admin/games +package main + +import ( + "fmt" + "os" +) + +func main() { + if len(os.Args) < 2 { + usage() + } + var err error + switch os.Args[1] { + case "validate": + err = runValidate(os.Args[2:]) + case "import": + err = runImport(os.Args[2:]) + default: + usage() + } + if err != nil { + fmt.Fprintln(os.Stderr, "error:", err) + os.Exit(1) + } +} + +func usage() { + fmt.Fprintln(os.Stderr, "usage: gbrgames validate [-games DIR] | import [-games DIR] [-all] EXPORT.json") + os.Exit(2) +} diff --git a/cmd/gbrgames/validate.go b/cmd/gbrgames/validate.go new file mode 100644 index 0000000..4720e11 --- /dev/null +++ b/cmd/gbrgames/validate.go @@ -0,0 +1,255 @@ +package main + +import ( + "bytes" + "encoding/json" + "errors" + "flag" + "fmt" + "os" + "path/filepath" + "regexp" + "sort" + "strings" + + "github.com/santhosh-tekuri/jsonschema/v6" + "gopkg.in/yaml.v3" +) + +var ( + // Config keys whose default value must never be a real credential. + sensitiveKey = regexp.MustCompile(`(?i)(pass(word)?|secret|token|api[_-]?key|rcon)`) + // A value made only of panel placeholders, e.g. "{saved:RCON_PASSWORD}". + placeholderOnly = regexp.MustCompile(`^(\{[A-Za-z0-9_:.-]+\})+$`) + // RFC 1918 addresses: home-lab and VPC details have no place in a public repo. + privateIP = regexp.MustCompile(`\b(10\.\d{1,3}\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3}|172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3})\b`) +) + +func runValidate(args []string) error { + fs := flag.NewFlagSet("validate", flag.ExitOnError) + gamesDir := fs.String("games", "games", "directory holding one folder per game") + schemaDir := fs.String("schemas", "schema", "directory holding game.schema.json and test.schema.json") + fs.Parse(args) + + gameSchema, err := compileSchema(filepath.Join(*schemaDir, "game.schema.json")) + if err != nil { + return err + } + testSchema, err := compileSchema(filepath.Join(*schemaDir, "test.schema.json")) + if err != nil { + return err + } + + entries, err := os.ReadDir(*gamesDir) + if err != nil { + return err + } + failed, checked := 0, 0 + for _, e := range entries { + if !e.IsDir() { + continue + } + checked++ + problems := validateGame(filepath.Join(*gamesDir, e.Name()), e.Name(), gameSchema, testSchema) + if len(problems) == 0 { + fmt.Printf("ok %s\n", e.Name()) + continue + } + failed++ + fmt.Printf("FAIL %s\n", e.Name()) + for _, p := range problems { + fmt.Printf(" - %s\n", p) + } + } + if checked == 0 { + return fmt.Errorf("no games found in %s", *gamesDir) + } + if failed > 0 { + return fmt.Errorf("%d of %d games failed validation", failed, checked) + } + fmt.Printf("\nall %d games valid\n", checked) + return nil +} + +func validateGame(dir, folder string, gameSchema, testSchema *jsonschema.Schema) []string { + var problems []string + add := func(format string, a ...any) { problems = append(problems, fmt.Sprintf(format, a...)) } + + raw, err := os.ReadFile(filepath.Join(dir, "game.yaml")) + if err != nil { + return []string{"missing game.yaml"} + } + game, err := loadYAML(raw) + if err != nil { + return []string{"game.yaml: " + err.Error()} + } + if err := gameSchema.Validate(game); err != nil { + add("game.yaml does not match schema:\n%s", indent(schemaErrors(err))) + return problems // later checks assume the basic shape is right + } + g := game.(map[string]any) + + if g["id"] != folder { + add("id %q must match folder name %q", g["id"], folder) + } + scanPrivateIPs(raw, "game.yaml", add) + + defaults, _ := g["defaultConfig"].(map[string]any) + props := schemaProperties(g) + known := map[string]bool{} + for k := range defaults { + known[k] = true + } + for k := range props { + known[k] = true + } + + // publicIpConfigKey and maxPlayersConfigKey are injected by the panel, so only the map key must already exist. + if key, ok := g["mapConfigKey"].(string); ok && key != "" && !known[key] { + add("mapConfigKey %q is not in defaultConfig or configSchema", key) + } + + for _, key := range sortedKeys(defaults) { + if sensitiveKey.MatchString(key) && isCredential(defaults[key]) { + add("defaultConfig.%s looks like a credential; leave it empty or use a placeholder like {saved:%s}", key, key) + } + } + for _, key := range sortedKeys(props) { + prop, _ := props[key].(map[string]any) + if sensitiveKey.MatchString(key) && isCredential(prop["default"]) { + add("configSchema.properties.%s.default looks like a credential; leave it empty", key) + } + } + + for _, key := range sortedKeys(props) { + prop, _ := props[key].(map[string]any) + if c, ok := prop["x-consent"]; !ok || c == false { + continue + } + if truthy(prop["default"]) || truthy(defaults[key]) { + add("consent %q must not default to true: users have to accept it themselves", key) + } + } + + testRaw, err := os.ReadFile(filepath.Join(dir, "test.yaml")) + if errors.Is(err, os.ErrNotExist) { + return problems + } + if err != nil { + add("test.yaml: %v", err) + return problems + } + test, err := loadYAML(testRaw) + if err != nil { + add("test.yaml: %v", err) + return problems + } + if err := testSchema.Validate(test); err != nil { + add("test.yaml does not match schema:\n%s", indent(schemaErrors(err))) + return problems + } + scanPrivateIPs(testRaw, "test.yaml", add) + t := test.(map[string]any) + cfg, _ := t["config"].(map[string]any) + secrets, _ := t["secrets"].(map[string]any) + for _, key := range sortedKeys(cfg) { + if !known[key] { + add("test.yaml config.%s is not a key of this game", key) + } + if sensitiveKey.MatchString(key) { + add("test.yaml config.%s looks like a credential; map it under 'secrets' to a Gitea secret instead", key) + } + } + for _, key := range sortedKeys(secrets) { + if !known[key] { + add("test.yaml secrets.%s is not a key of this game", key) + } + } + return problems +} + +func compileSchema(path string) (*jsonschema.Schema, error) { + raw, err := os.ReadFile(path) + if err != nil { + return nil, err + } + doc, err := jsonschema.UnmarshalJSON(bytes.NewReader(raw)) + if err != nil { + return nil, fmt.Errorf("%s: %w", path, err) + } + id, _ := doc.(map[string]any)["$id"].(string) + if id == "" { + return nil, fmt.Errorf("%s: missing $id", path) + } + c := jsonschema.NewCompiler() + if err := c.AddResource(id, doc); err != nil { + return nil, err + } + return c.Compile(id) +} + +// loadYAML decodes YAML into the JSON data model the schema validator expects. +func loadYAML(raw []byte) (any, error) { + var v any + if err := yaml.Unmarshal(raw, &v); err != nil { + return nil, err + } + if v == nil { + return nil, errors.New("file is empty") + } + j, err := json.Marshal(v) + if err != nil { + return nil, err + } + return jsonschema.UnmarshalJSON(bytes.NewReader(j)) +} + +func schemaProperties(g map[string]any) map[string]any { + s, _ := g["configSchema"].(map[string]any) + p, _ := s["properties"].(map[string]any) + return p +} + +func scanPrivateIPs(raw []byte, file string, add func(string, ...any)) { + for i, line := range strings.Split(string(raw), "\n") { + if ip := privateIP.FindString(line); ip != "" { + add("%s line %d contains private address %s", file, i+1, ip) + } + } +} + +func schemaErrors(err error) string { + var ve *jsonschema.ValidationError + if errors.As(err, &ve) { + return fmt.Sprintf("%v", ve) + } + return err.Error() +} + +func indent(s string) string { + return " " + strings.ReplaceAll(strings.TrimSpace(s), "\n", "\n ") +} + +func isCredential(v any) bool { + s, ok := v.(string) + return ok && s != "" && !placeholderOnly.MatchString(s) +} + +func truthy(v any) bool { + switch x := v.(type) { + case bool: + return x + case string: + return strings.EqualFold(strings.TrimSpace(x), "true") + } + return false +} + +func sortedKeys(m map[string]any) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} diff --git a/games/ark-survival-ascended-crossplay/game.yaml b/games/ark-survival-ascended-crossplay/game.yaml new file mode 100644 index 0000000..0b44b81 --- /dev/null +++ b/games/ark-survival-ascended-crossplay/game.yaml @@ -0,0 +1,332 @@ +# yaml-language-server: $schema=../../schema/game.schema.json +id: ark-survival-ascended-crossplay +name: 'ARK: Survival Ascended (Crossplay Dedicated Server)' +defaultDockerImage: mschnitzer/asa-linux-server:latest +defaultStartupCommand: bash -c 'mkdir -p /server /data/Steam /data/steamcmd /data/cluster-shared && rm -rf /home/gameserver/server-files /home/gameserver/Steam /home/gameserver/steamcmd /home/gameserver/cluster-shared && ln -sfn /server /home/gameserver/server-files && ln -sfn /data/Steam /home/gameserver/Steam && ln -sfn /data/steamcmd /home/gameserver/steamcmd && ln -sfn /data/cluster-shared /home/gameserver/cluster-shared && export ASA_START_PARAMS="${ASA_START_PARAMS:-${SERVER_MAP:-TheIsland_WP}?listen?Port=${PORT:-7777}?RCONPort=${RCON_PORT:-27020}?RCONEnabled=True -WinLiveMaxPlayers=${MAX_PLAYERS:-70} -ServerPlatform=ALL}" && exec /usr/bin/start_server' +requiredMemoryMb: 16384 +requiredDiskMb: 32768 +defaultPorts: + - 7777/udp +installMethod: NONE +defaultConfig: + AdminLogging: false + AllowFlyerCarryPvE: false + AllowHitMarkers: true + AllowThirdPersonPlayer: true + AlwaysAllowStructurePickup: true + AutoSavePeriodMinutes: 15 + DayCycleSpeedScale: 1 + DayTimeSpeedScale: 1 + DifficultyOffset: 1 + DisableDinoDecayPvE: false + DisableStructureDecayPvE: false + EnablePVPGamma: false + MaxPlayers: 70 + NightTimeSpeedScale: 1 + OverrideOfficialDifficulty: 5 + Port: 7777 + PreventDiseases: false + PreventDownloadDinos: false + PreventDownloadItems: false + PreventDownloadSurvivors: false + PreventUploadDinos: false + PreventUploadItems: false + PreventUploadSurvivors: false + RCONEnabled: true + RCONPort: 27020 + ServerAdminPassword: "" + ServerCrosshair: true + ServerHardcore: false + ServerMap: TheIsland_WP + ServerPVE: false + ServerPassword: "" + ShowFloatingDamageText: false + ShowMapPlayerLocation: true + bDisableStructurePlacementCollision: false +configSchema: + $schema: http://json-schema.org/draft-07/schema# + properties: + AdminLogging: + category: Security & Access + default: false + description: Broadcasts in-game administrative commands in chat to all connected players. + title: Admin Command Logging + type: boolean + AllowFlyerCarryPvE: + category: Gameplay & Rules + default: false + description: Allows airborne tamed creatures to pick up and carry wild or non-allied dinosaurs in PvE. + title: Allow Flyer Carry in PvE + type: boolean + AllowHitMarkers: + category: Gameplay & Rules + default: true + description: Shows floating visual indicators when striking an enemy or creature. + title: Allow Hit Markers + type: boolean + AllowThirdPersonPlayer: + category: Gameplay & Rules + default: true + description: Permits players to switch between first-person and third-person camera views. + title: Allow Third Person + type: boolean + AlwaysAllowStructurePickup: + category: Gameplay & Rules + default: true + description: Allows players to pick up placed structures indefinitely without demolition restrictions. + title: Always Allow Structure Pickup + type: boolean + AutoSavePeriodMinutes: + category: World & Environment + default: 15 + description: Frequency in minutes at which the server automatically saves world and player state. + title: Auto-Save Interval (Minutes) + type: number + DayCycleSpeedScale: + category: World & Environment + default: 1 + description: Global multiplier adjusting the overall speed of the 24-hour day/night cycle. + title: Day Cycle Speed Scale + type: number + DayTimeSpeedScale: + category: World & Environment + default: 1 + description: Multiplier governing the passage speed of daytime hours relative to standard speed. + title: Day Time Speed Scale + type: number + DifficultyOffset: + category: Gameplay & Rules + default: 1 + description: Base difficulty multiplier governing wild creature level distributions (0.0 to 1.0). + title: Difficulty Offset + type: number + DisableDinoDecayPvE: + category: Gameplay & Rules + default: false + description: Disables unclaiming timers and decay for inactive tamed creatures in PvE mode. + title: Disable Dino Decay in PvE + type: boolean + DisableStructureDecayPvE: + category: Gameplay & Rules + default: false + description: Disables the automatic structure decay and demolition timer on abandoned PvE bases. + title: Disable Structure Decay in PvE + type: boolean + EnablePVPGamma: + category: Gameplay & Rules + default: false + description: Allows players to use client gamma brightness adjustments during PvP sessions. + title: Enable PvP Gamma + type: boolean + MaxPlayers: + category: System & General + default: 70 + description: Maximum number of simultaneous players allowed on the server. + title: Max Players + type: integer + NightTimeSpeedScale: + category: World & Environment + default: 1 + description: Multiplier governing the passage speed of nighttime hours relative to standard speed. + title: Night Time Speed Scale + type: number + OverrideOfficialDifficulty: + category: Gameplay & Rules + default: 5 + description: Sets the maximum wild dino level ceiling (e.g. 5.0 allows wild creatures up to level 150). + title: Override Official Difficulty + type: number + Port: + category: System & General + default: 7777 + description: Primary UDP port used by game clients to connect to the dedicated server. + title: Game Port + type: integer + PreventDiseases: + category: Gameplay & Rules + default: false + description: Disables all permanent sicknesses and diseases from afflicting players. + title: Prevent Diseases + type: boolean + PreventDownloadDinos: + category: Security & Access + default: false + description: Blocks transferring tamed creatures into the server from external cluster servers. + title: Prevent Download Dinos + type: boolean + PreventDownloadItems: + category: Security & Access + default: false + description: Blocks downloading items or inventory into the server from external sources or cluster. + title: Prevent Download Items + type: boolean + PreventDownloadSurvivors: + category: Security & Access + default: false + description: Blocks transferring survivor characters into the server from other cluster servers. + title: Prevent Download Survivors + type: boolean + PreventUploadDinos: + category: Security & Access + default: false + description: Disallows survivors from uploading tamed creatures to cluster cloud storage. + title: Prevent Upload Dinos + type: boolean + PreventUploadItems: + category: Security & Access + default: false + description: Disallows survivors from uploading items and equipment to cluster cloud storage. + title: Prevent Upload Items + type: boolean + PreventUploadSurvivors: + category: Security & Access + default: false + description: Disallows survivors from uploading their character data to cluster cloud storage. + title: Prevent Upload Survivors + type: boolean + RCONEnabled: + category: Security & Access + default: true + description: Enables remote console (RCON) access for external server administration tools. + title: Enable RCON + type: boolean + RCONPort: + category: System & General + default: 27020 + description: TCP port for remote console management and server administration. + title: RCON Port + type: integer + ServerAdminPassword: + category: Security & Access + default: "" + description: Password for admin commands in-game and for GBR actions over RCON (kick, ban, save, broadcast). Leave blank and GBR generates an easy one for your server. + showOnDeploy: true + title: Admin Password + type: string + x-savedValue: + key: GAME_ADMIN_PASSWORD + label: Game admin password + secret: true + x-generate: + type: password + replace: + - AdminPassword123 + ServerCrosshair: + category: Gameplay & Rules + default: true + description: Enables an on-screen targeting crosshair for all players. + title: Enable Crosshair + type: boolean + ServerHardcore: + category: Security & Access + default: false + description: When enabled, player death permanently deletes the character and resets progression. + title: Hardcore Mode + type: boolean + ServerMap: + category: World & Environment + default: TheIsland_WP + description: Select the world map loaded by the dedicated server. + enum: + - TheIsland_WP + - ScorchedEarth_WP + - TheCenter_WP + - Aberration_WP + - Extinction_WP + showOnDeploy: true + title: Active World Map + type: string + ServerPVE: + category: Gameplay & Rules + default: false + description: Disables player-versus-player combat and structure damage, enabling cooperative PvE mode. + title: PvE Mode + type: boolean + ServerPassword: + category: Security & Access + default: "" + description: Password required for players to join the server. Leave blank to keep the server public. + showOnDeploy: true + title: Server Password + type: string + x-savedValue: + key: GAME_JOIN_PASSWORD + label: Game join password + secret: true + ShowFloatingDamageText: + category: Gameplay & Rules + default: false + description: Displays floating damage numbers overhead when inflicting damage on targets. + title: Show Floating Damage Text + type: boolean + ShowMapPlayerLocation: + category: Gameplay & Rules + default: true + description: Displays the player's current position pin on the in-game map. + title: Show Player On Map + type: boolean + bDisableStructurePlacementCollision: + category: Gameplay & Rules + default: false + description: Removes terrain and building clipping restrictions, allowing structures to clip into world geometry. + title: Disable Structure Placement Collision + type: boolean + required: [] + type: object +configFilePath: ShooterGame/Saved/Config/WindowsServer/GameUserSettings.ini +configFileFormat: STANDARD +configFormatOptions: + assignment: = + falseValue: "False" + floatKeys: [] + footer: null + header: '[ServerSettings]' + quoteStrings: false + separator: "" + trueValue: "True" + unquotedKeys: [] + uppercaseKeys: false +agnosticCommandConfig: + actions: + Ban: + command: BanPlayer {target} + passwordKey: ServerAdminPassword + portKey: RCONPort + type: RCON + Broadcast: + command: Broadcast {message} + passwordKey: ServerAdminPassword + portKey: RCONPort + type: RCON + Kick: + command: KickPlayer {target} + passwordKey: ServerAdminPassword + portKey: RCONPort + type: RCON + Save: + command: SaveWorld + passwordKey: ServerAdminPassword + portKey: RCONPort + type: RCON + pingPort: null + pingPortKey: null + playerJoinRegex: (?:^.*?:\s+)?(.+?)\s+(?:has\s+)?joined this [Aa][Rr][Kk]!? + playerLeaveRegex: (?:^.*?:\s+)?(.+?)\s+(?:has\s+)?left this [Aa][Rr][Kk]!? + playersCommand: ListPlayers + playersFormat: REGEX + playersRegex: ^\d+\.\s+([^,]+) + queryPortKey: null + queryType: RCON + rconPasswordKey: ServerAdminPassword + rconPortKey: RCONPort + restEndpoint: null + restPasswordKey: null + restPortKey: null + restUser: null +logFilePath: ShooterGame/Saved/Logs/ShooterGame.log +backupPaths: + - ShooterGame/Saved +maxBackupSizeMb: 4096 +maxPlayersConfigKey: MaxPlayers +mapConfigKey: ServerMap +aiCoverPrompt: A cinematic, photorealistic vista of a primeval tropical island built in Unreal Engine 5, featuring lush ancient rainforests, colossal waterfalls pouring into azure lagoons, and futuristic floating obelisks beaming energy into the sky. A massive Tyrannosaurus Rex roars on a cliffside overlooking prehistoric valleys while Pteranodons glide across a vivid sunset horizon. diff --git a/games/ark-survival-ascended-crossplay/test.yaml b/games/ark-survival-ascended-crossplay/test.yaml new file mode 100644 index 0000000..1b49f91 --- /dev/null +++ b/games/ark-survival-ascended-crossplay/test.yaml @@ -0,0 +1,3 @@ +# yaml-language-server: $schema=../../schema/test.schema.json +# Values CI uses to boot-test this game. Never put real credentials here: map them under 'secrets'. +timeoutSec: 900 diff --git a/games/minecraft-crossplay/game.yaml b/games/minecraft-crossplay/game.yaml new file mode 100644 index 0000000..aeb3995 --- /dev/null +++ b/games/minecraft-crossplay/game.yaml @@ -0,0 +1,387 @@ +# yaml-language-server: $schema=../../schema/game.schema.json +id: minecraft-crossplay +name: Minecraft (Crossplay Java & Bedrock) +defaultDockerImage: itzg/minecraft-server:latest +requiredMemoryMb: 4096 +requiredDiskMb: 8192 +defaultPorts: + - 25565/tcp + - 19132/udp + - 19132/tcp +installMethod: NONE +installConfig: + configPatches: + - enabledWhen: '{config:XBOX_BROADCAST_ENABLED}' + format: YAML + path: plugins/Geyser-Spigot/extensions/mcxboxbroadcast/config.yml + set: + session.remote-address: '{config:XBOX_BROADCAST_REMOTE_ADDRESS}' + session.remote-port: '{config:XBOX_BROADCAST_REMOTE_PORT}' + files: + - enabledWhen: '{config:XBOX_BROADCAST_ENABLED}' + path: plugins/Geyser-Spigot/extensions/MCXboxBroadcastExtension.jar + url: '{config:XBOX_BROADCAST_DOWNLOAD_URL}' +defaultConfig: + ALLOW_FLIGHT: true + DIFFICULTY: easy + ENABLE_RCON: true + ENABLE_WHITELIST: false + ENFORCE_WHITELIST: false + EULA: false + HARDCORE: false + JVM_OPTS: -DgeyserTransport=nethernet -DgeyserAdvertiseAddresses={PUBLIC_IP}:{PUBLIC_PORT_19132} + MAX_PLAYERS: 20 + MEMORY: 4G + MODE: survival + MOTD: GBRHosting - {SERVER_NAME} + ONLINE_MODE: true + PAPER_CHANNEL: default + PLUGINS: https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot,https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot,https://api.spiget.org/v2/resources/19254/download + PVP: true + RCON_PASSWORD: "" + RCON_PORT: 25575 + SERVER_PORT: 25565 + TYPE: PAPER + USE_AIKAR_FLAGS: true + VERSION: LATEST + XBOX_BROADCAST_DOWNLOAD_URL: https://github.com/MCXboxBroadcast/Broadcaster/releases/download/159/MCXboxBroadcastExtension.jar + XBOX_BROADCAST_ENABLED: true + XBOX_BROADCAST_REMOTE_ADDRESS: '{PUBLIC_HOST}' + XBOX_BROADCAST_REMOTE_PORT: '{PUBLIC_PORT_19132}' +configSchema: + $schema: http://json-schema.org/draft-07/schema# + properties: + ALLOW_FLIGHT: + category: Gameplay & Rules + default: true + description: Required for Bedrock and Geyser crossplay compatibility to prevent phantom kick events. + title: Allow Flight + type: boolean + DIFFICULTY: + category: Gameplay & Rules + default: easy + description: The world difficulty level. + enum: + - peaceful + - easy + - normal + - hard + title: Difficulty + type: string + ENABLE_RCON: + category: Security & Access + default: true + description: Enables remote console (RCON) access for GBR action buttons and player moderation. + title: Enable RCON + type: boolean + ENABLE_WHITELIST: + category: Security & Access + default: false + description: When enabled, only players listed in the whitelist are permitted to join. + title: Enable Whitelist + type: boolean + ENFORCE_WHITELIST: + category: Security & Access + default: false + description: Enforces whitelist immediately upon server startup and reloading. + title: Enforce Whitelist + type: boolean + EULA: + category: System & General + default: false + description: Minecraft requires every server owner to accept Mojang's End User License Agreement. The server will not start without it. + showOnDeploy: true + title: Minecraft EULA + type: boolean + x-consent: + label: I have read and agree to the Minecraft End User License Agreement (EULA) + linkText: Read the Minecraft EULA + url: https://aka.ms/MinecraftEULA + HARDCORE: + category: Gameplay & Rules + default: false + description: Players are permanently switched to spectator mode upon death. + title: Hardcore Mode + type: boolean + JVM_OPTS: + category: System & General + default: -DgeyserTransport=nethernet -DgeyserAdvertiseAddresses={PUBLIC_IP}:{PUBLIC_PORT_19132} + description: Extra JVM flags. Enables Geyser NetherNet (required for Bedrock 26.60+, Oct 27 2026). {PUBLIC_IP} and {PUBLIC_PORT_19132} are filled in by GBR with the address players reach (relay, dedicated IP or node). + title: JVM Options + type: string + MAX_PLAYERS: + category: System & General + default: 20 + description: The maximum number of concurrent players allowed on the server. + title: Max Players + type: integer + MEMORY: + category: System & General + default: 4G + description: Maximum heap memory allocated to the Java process (e.g. 4G, 6G, 8G). + title: Memory Allocation + type: string + MODE: + category: Gameplay & Rules + default: survival + description: Default game mode for players. + enum: + - survival + - creative + - adventure + - spectator + title: Game Mode + type: string + MOTD: + category: System & General + default: GBRHosting - {SERVER_NAME} + description: Server name shown in the Java server list and to Bedrock/console friends. Defaults to "GBRHosting - "; type anything to change it. + title: Message of the Day + type: string + ONLINE_MODE: + category: Security & Access + default: true + description: Validates Java accounts with Mojang. Floodgate handles Bedrock account authentication automatically. + title: Online Mode + type: boolean + PAPER_CHANNEL: + category: System & General + default: default + description: Keep set to 'default' for rock-solid stability with Geyser crossplay. Use 'experimental' only for test builds. + enum: + - default + - experimental + title: Paper Release Channel + type: string + PLUGINS: + category: System & General + default: https://download.geysermc.org/v2/projects/geyser/versions/latest/builds/latest/downloads/spigot,https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot,https://api.spiget.org/v2/resources/19254/download + description: Download URLs for Geyser, Floodgate, and ViaVersion for permanent crossplay compatibility. + title: Plugins List / URLs + type: string + PVP: + category: Gameplay & Rules + default: true + description: Allows players to attack and deal damage to one another. + title: Enable PvP + type: boolean + RCON_PASSWORD: + category: Security & Access + default: "" + description: Password GBR uses for RCON actions. RCON is only reachable from the host node. Leave blank and GBR generates one for your server. + title: RCON Password + type: string + x-generate: + type: password + replace: + - minecraft + RCON_PORT: + category: Security & Access + default: 25575 + description: TCP port for remote console. Only reachable from the host node (not published publicly); GBR uses it for actions and player moderation. + title: RCON Port + type: integer + SERVER_PORT: + category: System & General + default: 25565 + description: The TCP network port on which Java Edition Minecraft players connect. + title: Java Server Port + type: integer + TYPE: + category: System & General + default: PAPER + description: The Minecraft server distribution to run. PAPER provides high performance and plugin support. + title: Server Software Type + type: string + USE_AIKAR_FLAGS: + category: System & General + default: true + description: Applies tuned JVM garbage collection flags designed to reduce lag spikes on Paper servers. + title: Use Aikar's Flags + type: boolean + VERSION: + category: System & General + default: LATEST + description: The version of Minecraft to run. With ViaVersion installed, newer clients can always connect even if the server is on the stable release. + title: Minecraft Version + type: string + XBOX_BROADCAST_DOWNLOAD_URL: + category: Crossplay & Friends Broadcast + default: https://github.com/MCXboxBroadcast/Broadcaster/releases/download/159/MCXboxBroadcastExtension.jar + description: Pinned extension build. Update this URL to upgrade (or point it at your own mirror). + title: MCXboxBroadcast Extension URL + type: string + XBOX_BROADCAST_ENABLED: + category: Crossplay & Friends Broadcast + default: true + description: Shows this server in Xbox/PlayStation/Switch friends lists via a spare Microsoft account. Unofficial and not affiliated with Microsoft or Mojang; use a spare account. On first start, sign in with the code shown on the server's Overview page. + title: Friends Broadcast (MCXboxBroadcast) + type: boolean + XBOX_BROADCAST_REMOTE_ADDRESS: + category: Crossplay & Friends Broadcast + default: '{PUBLIC_HOST}' + description: Address friends are sent to when they join. {PUBLIC_HOST} is filled in with this server's relay/public address. Never leave this as 'auto' behind the relay (it would expose the node's home IP). + title: Broadcast Join Address + type: string + XBOX_BROADCAST_REMOTE_PORT: + category: Crossplay & Friends Broadcast + default: '{PUBLIC_PORT_19132}' + description: Bedrock port friends are sent to. {PUBLIC_PORT_19132} is filled in with the public Bedrock port. + title: Broadcast Join Port + type: string + required: + - EULA + type: object +configFileFormat: INI +configFormatOptions: + assignment: = + falseValue: "false" + footer: "" + header: "" + quoteStrings: false + separator: "" + trueValue: "true" + unquotedKeys: [] + uppercaseKeys: false +agnosticCommandConfig: + actions: + Ban: + command: ban {target} + passwordKey: RCON_PASSWORD + portKey: RCON_PORT + type: RCON + Broadcast: + command: say {message} + passwordKey: RCON_PASSWORD + portKey: RCON_PORT + type: RCON + Kick: + command: kick {target} + passwordKey: RCON_PASSWORD + portKey: RCON_PORT + type: RCON + RefreshBroadcastSignIn: + command: mcxboxbroadcast restart + type: STDIN + Save: + command: save-all + passwordKey: RCON_PASSWORD + portKey: RCON_PORT + type: RCON + connectedAccountsConfig: + authType: MINECRAFT_BEDROCK + authUrl: https://www.microsoft.com/link + buttonLabel: Connect Account + clientId: "0000000048183522" + defaultPort: 19132 + description: Hook spare Microsoft/Xbox accounts to keep your server visible in friends' tabs on Xbox, PlayStation, and Nintendo Switch 24/7. + deviceCodeUrl: https://login.live.com/oauth20_connect.srf + dynamicVariables: + - refreshMinutes: 360 + select: + releaseType: release + type: HTTP_JSON + url: https://raw.githubusercontent.com/PrismarineJS/minecraft-data/master/data/bedrock/common/protocolVersions.json + vars: + BEDROCK_PROTOCOL: version + BEDROCK_VERSION: minecraftVersion + enabled: false + gameVersion: '{BEDROCK_VERSION}' + maxAccounts: 5 + mpsdProperties: + BroadcastSetting: 3 + CrossPlayDisabled: false + Joinability: joinable_by_friends + LanGame: false + MaxMemberCount: '{config:MAX_PLAYERS}' + MemberCount: 1 + OnlineCrossPlatformGame: true + SupportedConnections: + - ConnectionType: 6 + HostIpAddress: '{SERVER_HOST}' + HostPort: '{SERVER_PORT}' + RakNetGUID: "" + TitleId: 0 + TransportLayer: 2 + hostName: '{SERVER_HOST}' + isEditorWorld: false + isHardcore: '{config:HARDCORE}' + levelId: level + ownerId: '{XUID}' + protocol: '{BEDROCK_PROTOCOL}' + rakNetGUID: '{RAKNET_GUID}' + version: '{BEDROCK_VERSION}' + worldName: '{config:MOTD}' + worldType: Survival + provider: MICROSOFT_XBOX + providerLabel: Xbox / Microsoft Gamertag + rtaConnection: true + scope: service::user.auth.xboxlive.com::MBI_SSL + serviceConfigId: 4fc10100-5f7a-4470-899b-280835760c07 + sessionTemplateName: MinecraftLobby + title: Crossplay Friends Broadcast + titleId: "1739947436" + tokenUrl: https://login.live.com/oauth20_token.srf + connectionGuides: + - actionText: Link Account + actionUrl: https://www.microsoft.com/link + badge: Crossplay + id: console + steps: + - Add the broadcast account (shown on the server's Overview page after sign-in) as a friend on your console or in the Xbox app. + - Open Minecraft and go to Play → Friends. + - Under 'Joinable Friends', select the server to join. + title: Console (Xbox, PS, Switch) + - actionText: 1-Click Connect + actionUrl: minecraft://?addExternalServer={SERVER_NAME}|{CONNECTION_HOST}:{BEDROCK_PORT} + id: bedrock-direct + steps: + - Open Minecraft (Bedrock Edition) -> Play -> Servers -> Add Server. + - 'Server Name: {SERVER_NAME}' + - 'Server Address: {CONNECTION_HOST}' + - 'Port: {BEDROCK_PORT}' + title: Bedrock (Mobile, Win10) + - id: java-direct + steps: + - Open Minecraft (Java Edition) -> Multiplayer -> Add Server. + - 'Server Address: {CONNECTION_HOST}:{PORT}' + title: Java Edition (PC, Mac) + consoleSignIn: + codePattern: open the page\s+(https://\S+)\s+and enter the code\s+([A-Z0-9-]+) + codeTtlMinutes: 5 + description: Shows this server in Xbox, PlayStation and Switch friends lists using a spare Microsoft account. Unofficial - not affiliated with Microsoft or Mojang. + enabledConfigKey: XBOX_BROADCAST_ENABLED + failurePattern: Failed to get/refresh auth token + refreshAction: RefreshBroadcastSignIn + signInHint: Sign in with a SPARE Microsoft account, never your main one. Enter the code within 5 minutes. + signedInHint: Add this account as a friend on your console or in the Xbox app, then join from Play → Friends. + successPattern: Successfully authenticated as (.+?) \((\d+)\) + title: Crossplay Friends Broadcast + deviceCodePattern: open the page\s+(https://\S+)\s+and enter the code\s+([A-Z0-9-]+) + deviceCodeTitle: Friends Broadcast sign-in required + joinButtonLabel: How to Join + joinModalTitle: How to Join This Server + pingPort: 25565 + pingPortKey: SERVER_PORT + playerJoinRegex: :\s+([a-zA-Z0-9_.*]+) joined the game + playerLeaveRegex: :\s+([a-zA-Z0-9_.*]+) left the game + playersCommand: null + playersFormat: null + playersRegex: null + queryPortKey: null + queryType: MINECRAFT_PING + rconPasswordKey: RCON_PASSWORD + rconPortKey: RCON_PORT + restEndpoint: null + restPasswordKey: null + restPortKey: null + restUser: null +backupPaths: + - world + - world_nether + - world_the_end + - plugins + - config + - server.properties +maxBackupSizeMb: 8192 +maxPlayersConfigKey: MAX_PLAYERS diff --git a/games/minecraft-crossplay/test.yaml b/games/minecraft-crossplay/test.yaml new file mode 100644 index 0000000..430f73c --- /dev/null +++ b/games/minecraft-crossplay/test.yaml @@ -0,0 +1,5 @@ +# yaml-language-server: $schema=../../schema/test.schema.json +# Values CI uses to boot-test this game. Never put real credentials here: map them under 'secrets'. +config: + EULA: true +timeoutSec: 900 diff --git a/games/palworld/game.yaml b/games/palworld/game.yaml new file mode 100644 index 0000000..8187765 --- /dev/null +++ b/games/palworld/game.yaml @@ -0,0 +1,562 @@ +# yaml-language-server: $schema=../../schema/game.schema.json +id: palworld +name: Palworld +defaultDockerImage: cm2network/steamcmd +defaultStartupCommand: ./PalServer.sh -useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS -publiclobby +requiredMemoryMb: 8000 +requiredDiskMb: 20000 +defaultPorts: + - 8211:8211/udp +installMethod: STEAMCMD +installConfig: + appId: "2394010" +defaultConfig: + AdminPassword: "" + AllowConnectPlatform: Xbox,Steam + AutoResetGuildTimeNoOnlinePlayers: "2160.000000" + BanListURL: https://api.palworldgame.com/api/banlist.txt + BaseCampMaxNum: 128 + BaseCampMaxNumInGuild: 10 + BaseCampWorkerMaxNum: 30 + BuildObjectDamageRate: "1.000000" + BuildObjectDeteriorationDamageRate: "1.000000" + CollectionDropRate: "1.000000" + CollectionObjectHpRate: "1.000000" + CollectionObjectRespawnSpeedRate: "1.000000" + CoopPlayerMaxNum: 4 + CrossplayPlatforms: Xbox,Steam + DayTimeSpeedRate: "1.000000" + DeathPenalty: All + Difficulty: None + DropItemAliveMaxHours: "1.000000" + DropItemMaxNum: 3000 + DropItemMaxNum_UNKO: 100 + EnemyDropItemRate: "1.000000" + ExpRate: "1.000000" + GuildPlayerMaxNum: 20 + LogFormatType: Text + NightTimeSpeedRate: "1.000000" + PalAutoHPRegeneRate: "1.000000" + PalAutoHpRegeneRateInSleep: "1.000000" + PalCaptureRate: "1.000000" + PalDamageRateAttack: "1.000000" + PalDamageRateDefense: "1.000000" + PalEggDefaultHatchingTime: "3.000000" + PalSpawnNumRate: "1.000000" + PalStaminaDecreaceRate: "1.000000" + PalStomachDecreaceRate: "1.000000" + PlayerAutoHPRegeneRate: "1.000000" + PlayerAutoHpRegeneRateInSleep: "1.000000" + PlayerDamageRateAttack: "1.000000" + PlayerDamageRateDefense: "1.000000" + PlayerStaminaDecreaceRate: "1.000000" + PlayerStomachDecreaceRate: "1.000000" + PublicIP: "" + PublicPort: 8211 + RCONEnabled: true + RCONPort: 25575 + RESTAPIEnabled: true + RESTAPIPort: 8212 + Region: "" + ServerDescription: Powered by GBR Hosting + ServerName: GBRHosting - {SERVER_NAME} + ServerPassword: "" + ServerPlayerMaxNum: 32 + SupplyDropSpan: 180 + WorkSpeedRate: "1.000000" + bActiveUNKO: false + bAllowGlobalPalboxExport: true + bAllowGlobalPalboxImport: true + bAutoResetGuildNoOnlinePlayers: false + bCanPickupOtherGuildDeathPenaltyDrop: false + bEnableAimAssistKeyboard: false + bEnableAimAssistPad: true + bEnableDefenseOtherGuildPlayer: false + bEnableFastTravel: true + bEnableFriendlyFire: false + bEnableInvaderEnemy: true + bEnableNonLoginPenalty: false + bEnablePlayerToPlayerDamage: false + bExistPlayerAfterLogout: false + bIsMultiplay: true + bIsPvP: false + bIsStartLocationSelectByMap: true + bIsUseBackupSaveData: true + bShowPlayerList: true + bUseAuth: true +configSchema: + properties: + AdminPassword: + default: "" + description: Password for in-game admin (/AdminPassword) and for GBR actions (kick, ban, save, metrics via the REST API). Leave blank and GBR generates an easy one for your server. + showOnDeploy: true + title: Admin Password + type: string + x-savedValue: + key: GAME_ADMIN_PASSWORD + label: Game admin password + secret: true + x-generate: + type: password + replace: + - Admin6! + AllowConnectPlatform: + default: Xbox,Steam + description: Platforms permitted to connect to this server. + enum: + - Steam + - Xbox + - None + - Xbox,Steam + title: Allowed Platforms + type: string + AutoResetGuildTimeNoOnlinePlayers: + default: "2160.000000" + description: Hours of inactivity before an empty guild is deleted (2160.0 = 90 days). + title: Guild Inactivity Disband Hours + type: string + BaseCampMaxNum: + default: 128 + description: Total base camps permitted across the entire server. + title: Max Base Camps Worldwide + type: integer + BaseCampMaxNumInGuild: + default: 10 + description: Maximum base camps a single guild is allowed to construct. + title: Max Bases Per Guild + type: integer + BaseCampWorkerMaxNum: + default: 30 + description: Maximum number of active Pals assigned to work in each base camp. + title: Max Working Pals Per Base + type: integer + BuildObjectDamageRate: + default: "1.000000" + description: Multiplier for damage taken by built structures. + title: Structure Damage Multiplier + type: string + BuildObjectDeteriorationDamageRate: + default: "1.000000" + description: Decay damage applied to structures outside base boundaries (0.0 to disable decay). + title: Structure Decay Rate + type: string + CollectionDropRate: + default: "1.000000" + description: Multiplier for materials gathered from trees, rocks, and ore nodes. + title: Resource Gathering Yield + type: string + CollectionObjectHpRate: + default: "1.000000" + description: Health multiplier of gatherable trees and rocks. + title: Resource Node Durability + type: string + CollectionObjectRespawnSpeedRate: + default: "1.000000" + description: Speed at which mined trees and ore deposits reappear. + title: Resource Respawn Speed + type: string + CrossplayPlatforms: + default: Xbox,Steam + description: Platforms enabled for crossplay connectivity. + enum: + - Steam + - Xbox + - None + - Xbox,Steam + showOnDeploy: true + title: Crossplay Platforms + type: string + DayTimeSpeedRate: + default: "1.000000" + description: Multiplier for daylight progression speed. + title: Daytime Speed Multiplier + type: string + DeathPenalty: + default: All + description: 'Items dropped upon death: None, Item (materials only), ItemAndEquipment, or All.' + enum: + - None + - Item + - ItemAndEquipment + - All + title: Death Penalty + type: string + Difficulty: + default: None + description: Base difficulty preset (None recommended when using custom multipliers). + enum: + - None + - Easy + - Normal + - Hard + title: Difficulty Preset + type: string + DropItemAliveMaxHours: + default: "1.000000" + description: Hours before dropped items on the ground disappear. + title: Dropped Item Despawn Time (Hours) + type: string + EnemyDropItemRate: + default: "1.000000" + description: Quantity multiplier for items dropped by defeated Pals and bosses. + title: Enemy Item Drop Rate + type: string + ExpRate: + default: "1.000000" + description: Multiplier for player and Pal XP gained from all sources. + title: Experience Multiplier + type: string + GuildPlayerMaxNum: + default: 20 + description: Maximum player members per guild. + title: Max Players Per Guild + type: integer + NightTimeSpeedRate: + default: "1.000000" + description: Multiplier for nighttime progression speed. + title: Nighttime Speed Multiplier + type: string + PalCaptureRate: + default: "1.000000" + description: Multiplier for successful Pal capture chances with Pal Spheres. + title: Pal Capture Rate Multiplier + type: string + PalDamageRateAttack: + default: "1.000000" + description: Multiplier for damage dealt by Pals. + title: Pal Attack Multiplier + type: string + PalDamageRateDefense: + default: "1.000000" + description: Multiplier for damage taken by Pals. + title: Pal Defense Multiplier + type: string + PalEggDefaultHatchingTime: + default: "3.000000" + description: Time in hours for huge eggs to incubate (e.g. 72.0 default, 1.0 to 3.0 for fast hatching). + title: Egg Hatching Time (Hours) + type: string + PalSpawnNumRate: + default: "1.000000" + description: Multiplier for the number of wild Pals that spawn in the world. + title: Pal Spawn Density + type: string + PalStaminaDecreaceRate: + default: "1.000000" + description: Rate at which Pal stamina drains while working or riding. + title: Pal Stamina Drain + type: string + PalStomachDecreaceRate: + default: "1.000000" + description: Rate at which Pal hunger drains. + title: Pal Hunger Rate + type: string + PlayerDamageRateAttack: + default: "1.000000" + description: Multiplier for damage dealt by players. + title: Player Attack Multiplier + type: string + PlayerDamageRateDefense: + default: "1.000000" + description: Multiplier for damage taken by players (lower = tankier). + title: Player Defense Multiplier + type: string + PlayerStaminaDecreaceRate: + default: "1.000000" + description: Rate at which player stamina depletes (lower = more stamina). + title: Player Stamina Drain + type: string + PlayerStomachDecreaceRate: + default: "1.000000" + description: Rate at which player hunger depletes (lower = less hungry). + title: Player Hunger Rate + type: string + PublicPort: + default: 8211 + description: UDP game port clients connect to. + title: Public Game Port + type: integer + RCONEnabled: + default: true + description: Enables Remote Console administration port. + title: RCON Enabled + type: boolean + RCONPort: + default: 25575 + description: Port used for RCON connections. + title: RCON Port + type: integer + RESTAPIEnabled: + default: true + description: Enables Palworld's built-in HTTP REST API for dashboards and metrics. + title: REST API Enabled + type: boolean + RESTAPIPort: + default: 8212 + description: Port used for HTTP REST API access. + title: REST API Port + type: integer + ServerDescription: + default: Powered by GBR Hosting + description: Description displayed to players when viewing server details. + title: Server Description + type: string + ServerName: + default: GBRHosting - {SERVER_NAME} + description: Name shown in the in-game server browser. Defaults to "GBRHosting - ". + showOnDeploy: true + title: Server Name + type: string + ServerPassword: + default: "" + description: Password required for players to connect. + showOnDeploy: true + title: Server Password + type: string + x-savedValue: + key: GAME_JOIN_PASSWORD + label: Game join password + secret: true + ServerPlayerMaxNum: + default: 32 + description: Maximum concurrent player connections. + title: Max Players + type: integer + SupplyDropSpan: + default: 180 + description: Interval in minutes between meteorite and supply drop events (default 180). + title: Supply Drop Frequency (Minutes) + type: integer + WorkSpeedRate: + default: "1.000000" + description: Speed at which Pals craft, build, and farm inside bases. + title: Base Work Speed Multiplier + type: string + bAllowGlobalPalboxExport: + default: true + description: Allows players to export Pals to other worlds. + title: Allow Palbox Export + type: boolean + bAllowGlobalPalboxImport: + default: true + description: Allows players to import Pals from other worlds. + title: Allow Palbox Import + type: boolean + bAutoResetGuildNoOnlinePlayers: + default: false + description: Disbands guilds when all members remain offline past timeout. + title: Auto-Disband Inactive Guilds + type: boolean + bCanPickupOtherGuildDeathPenaltyDrop: + default: false + description: Allows players from other guilds to pick up items dropped on death. + title: Loot Other Guild Death Drops + type: boolean + bEnableFastTravel: + default: true + description: Allows fast traveling between unlocked Great Eagle statues. + title: Enable Fast Travel + type: boolean + bEnableFriendlyFire: + default: false + description: Allows players in the same guild to damage each other. + title: Enable Friendly Fire + type: boolean + bEnableInvaderEnemy: + default: true + description: Enables enemy NPCs and wild Pal raids attacking player bases. + title: Enable Raids + type: boolean + bIsPvP: + default: false + description: Enables combat and damage between opposing players. + title: Enable PvP + type: boolean + bIsStartLocationSelectByMap: + default: true + description: Allows players to choose their respawn location from the map. + title: Select Respawn Location By Map + type: boolean + bShowPlayerList: + default: true + description: Displays connected players in the server player list. + title: Show Player List + type: boolean + bUseAuth: + default: true + description: Enforces valid Steam account authentication upon connection. + title: Require Steam Authentication + type: boolean + type: object +configFilePath: Pal/Saved/Config/LinuxServer/PalWorldSettings.ini +configFileFormat: STANDARD +configFormatOptions: + assignment: = + falseValue: "False" + footer: ) + header: |- + [/Script/Pal.PalGameWorldSettings] + OptionSettings=( + quoteStrings: true + separator: ',' + trueValue: "True" + unquotedKeys: + - Difficulty + - RandomizerType + - DeathPenalty + - CrossplayPlatforms + - AllowConnectPlatform + - LogFormatType + - DenyTechnologyList + - DayTimeSpeedRate + - NightTimeSpeedRate + - ExpRate + - PalCaptureRate + - PalSpawnNumRate + - PalDamageRateAttack + - PalDamageRateDefense + - PlayerDamageRateAttack + - PlayerDamageRateDefense + - PlayerStomachDecreaceRate + - PlayerStaminaDecreaceRate + - PlayerAutoHPRegeneRate + - PlayerAutoHpRegeneRateInSleep + - PalStomachDecreaceRate + - PalStaminaDecreaceRate + - PalAutoHPRegeneRate + - PalAutoHpRegeneRateInSleep + - BuildObjectHpRate + - BuildObjectDamageRate + - BuildObjectDeteriorationDamageRate + - CollectionDropRate + - CollectionObjectHpRate + - CollectionObjectRespawnSpeedRate + - EnemyDropItemRate + - DropItemAliveMaxHours + - AutoResetGuildTimeNoOnlinePlayers + - PalEggDefaultHatchingTime + - WorkSpeedRate + - AutoSaveSpan + - ItemWeightRate + - ServerReplicatePawnCullDistance + - EquipmentDurabilityDamageRate + - ItemContainerForceMarkDirtyInterval + - PlayerDataPalStorageUpdateCheckTickInterval + - ItemCorruptionMultiplier + - MonsterFarmActionSpeedRate + - AutoTransferMasterCheckIntervalSeconds + - BlockRespawnTime + - RespawnPenaltyDurationThreshold + - RespawnPenaltyTimeScale + - VoiceChatMaxVolumeDistance + - VoiceChatZeroVolumeDistance + uppercaseKeys: false +agnosticCommandConfig: + actions: + Ban: + body: + message: '{reason}' + userid: '{target}' + endpoint: /v1/api/ban + passwordKey: AdminPassword + portKey: RESTAPIPort + type: REST_POST + user: admin + Broadcast: + body: + message: '{message}' + endpoint: /v1/api/announce + passwordKey: AdminPassword + portKey: RESTAPIPort + type: REST_POST + user: admin + Kick: + body: + message: '{reason}' + userid: '{target}' + endpoint: /v1/api/kick + passwordKey: AdminPassword + portKey: RESTAPIPort + type: REST_POST + user: admin + Save: + body: {} + endpoint: /v1/api/save + passwordKey: AdminPassword + portKey: RESTAPIPort + type: REST_POST + user: admin + Shutdown: + body: + message: '{message}' + waittime: 60 + endpoint: /v1/api/shutdown + passwordKey: AdminPassword + portKey: RESTAPIPort + type: REST_POST + user: admin + chatRegex: \[CHAT\] <(.*?)> (.*) + commandMap: + Ban: BanPlayer {target} + Broadcast: Broadcast {message} + Kick: KickPlayer {target} + Save: Save + Shutdown: DoExit + metricsConfig: + endpoint: /v1/api/metrics + fields: + - decimals: 1 + key: serverfps + label: Server Tickrate + suffix: ' FPS' + - decimals: 1 + key: serverframetime + label: Frame Time + suffix: ' ms' + - key: currentplayernum + label: Active Players + - key: maxplayernum + label: Max Capacity + - key: days + label: World Day + - key: basecampnum + label: Base Camps + - format: duration + key: uptime + label: Uptime + passwordKey: AdminPassword + portKey: RESTAPIPort + type: REST_API + user: admin + playerConfig: + actions: + - destructive: false + name: Kick + title: Kick Player + - destructive: true + name: Ban + title: Ban Player + fields: + - key: level + prefix: 'Lvl ' + - decimals: 0 + key: ping + suffix: ms + - decimals: 0 + key: location_x + label: X + - decimals: 0 + key: location_y + label: "Y" + queryType: REST_API + restEndpoint: /v1/api/players + restPasswordKey: AdminPassword + restPortKey: RESTAPIPort + restUser: admin +backupPaths: + - Pal/Saved/SaveGames/* + - Pal/Saved/Config/LinuxServer/* + - Pal/Saved/Config/WindowsServer/* +maxBackupSizeMb: 5000 +publicIpConfigKey: PublicIP +maxPlayersConfigKey: ServerPlayerMaxNum diff --git a/games/palworld/test.yaml b/games/palworld/test.yaml new file mode 100644 index 0000000..d8b0933 --- /dev/null +++ b/games/palworld/test.yaml @@ -0,0 +1,3 @@ +# yaml-language-server: $schema=../../schema/test.schema.json +# Values CI uses to boot-test this game. Never put real credentials here: map them under 'secrets'. +timeoutSec: 2400 diff --git a/games/runescape-dragonwilds-native-linux/game.yaml b/games/runescape-dragonwilds-native-linux/game.yaml new file mode 100644 index 0000000..ee11c7b --- /dev/null +++ b/games/runescape-dragonwilds-native-linux/game.yaml @@ -0,0 +1,117 @@ +# yaml-language-server: $schema=../../schema/game.schema.json +id: runescape-dragonwilds-native-linux +name: 'RuneScape: Dragonwilds (Native Linux Server)' +defaultDockerImage: cm2network/steamcmd:root +defaultStartupCommand: ./RSDragonwildsServer.sh -log -Port=7777 +requiredMemoryMb: 10240 +requiredDiskMb: 20480 +defaultPorts: + - 7777/udp + - 8888/udp +installMethod: STEAMCMD +installConfig: + appId: "4019830" +defaultConfig: + AdminPassword: "" + DefaultWorldName: '{SERVER_NAME}' + OwnerId: "" + PlatformPolicy: Crossplay + ServerName: GBRHosting - {SERVER_NAME} + WorldPassword: "" +configSchema: + $schema: http://json-schema.org/draft-07/schema# + properties: + AdminPassword: + category: Security & Access + default: "" + description: Password required to access the Server Management screen in-game. + showOnDeploy: true + title: Admin Password + type: string + x-savedValue: + key: GAME_ADMIN_PASSWORD + label: Game admin password + secret: true + DefaultWorldName: + category: World & Environment + default: '{SERVER_NAME}' + description: Players find your server in Dragonwilds under Worlds → Public by searching this name. Defaults to your server's name in GBR; type any short name to change it. + showOnDeploy: true + title: World Name (what players search for) + type: string + OwnerId: + category: Security & Access + default: "" + description: 'Your RuneScape: DragonWilds Player ID (bottom of in-game Settings). Required for server to boot.' + showOnDeploy: true + title: Owner Player ID + type: string + x-savedValue: + key: DRAGONWILDS_PLAYER_ID + label: Dragonwilds Player ID + PlatformPolicy: + category: Security & Access + default: Crossplay + description: Controls platform authorization and crossplay access policies. + enum: + - Crossplay + - PC + - PlayStation + - Xbox + - Nintendo + title: Platform Policy + type: string + ServerName: + category: System & General + default: GBRHosting - {SERVER_NAME} + description: Display name of the dedicated server. Defaults to "GBRHosting - ". + showOnDeploy: true + title: Server Name + type: string + WorldPassword: + category: Security & Access + default: "" + description: Optional password for players to join. Leave empty for public access. + showOnDeploy: true + title: World Password + type: string + x-savedValue: + key: GAME_JOIN_PASSWORD + label: Game join password + secret: true + required: + - OwnerId + - AdminPassword + type: object +configFilePath: RSDragonwilds/Saved/Config/LinuxServer/DedicatedServer.ini +configFileFormat: STANDARD +configFormatOptions: + assignment: = + falseValue: "False" + footer: "" + header: | + [/Script/Dominion.DedicatedServerSettings] + quoteStrings: false + separator: "" + trueValue: "True" + uppercaseKeys: false +agnosticCommandConfig: + actions: + Ban: + type: UNSUPPORTED + Broadcast: + type: UNSUPPORTED + Kick: + type: UNSUPPORTED + Save: + type: UNSUPPORTED + playerJoinRegex: 'LogNet: Join succeeded: ([^\r\n]+)' + playerLeaveRegex: 'LogNet: UNetConnection::.*Player(?: name)?:\s*([^,\r\n]+)' + queryType: LOG_ONLY +logFilePath: RSDragonwilds/Saved/Logs/RSDragonwilds.log +backupPaths: + - RSDragonwilds/Saved +maxBackupSizeMb: 5000 +publicIpConfigKey: MULTIHOME +maxPlayersConfigKey: MaxPlayers +aiCoverPrompt: 'A cinematic wide shot of Ashenfall in the fantasy world of Gielinor from RuneScape: Dragonwilds. Ancient stone spires and forgotten fortresses rise amidst untamed wilderness. Colossal ancient dragons circle through stormy twilight skies illuminated by magical embers, while adventurers gather around a runic encampment wielding enchanted sorcery and survival gear.' diff --git a/games/runescape-dragonwilds-native-linux/test.yaml b/games/runescape-dragonwilds-native-linux/test.yaml new file mode 100644 index 0000000..d8b0933 --- /dev/null +++ b/games/runescape-dragonwilds-native-linux/test.yaml @@ -0,0 +1,3 @@ +# yaml-language-server: $schema=../../schema/test.schema.json +# Values CI uses to boot-test this game. Never put real credentials here: map them under 'secrets'. +timeoutSec: 2400 diff --git a/games/satisfactory-vanilla-stable/game.yaml b/games/satisfactory-vanilla-stable/game.yaml new file mode 100644 index 0000000..03e88f4 --- /dev/null +++ b/games/satisfactory-vanilla-stable/game.yaml @@ -0,0 +1,190 @@ +# yaml-language-server: $schema=../../schema/game.schema.json +id: satisfactory-vanilla-stable +name: Satisfactory (Vanilla Stable) +defaultDockerImage: wolveix/satisfactory-server:latest +requiredMemoryMb: 8192 +requiredDiskMb: 16384 +defaultPorts: + - 7777/tcp + - 7777/udp + - 8888/tcp +installMethod: NONE +defaultConfig: + AUTOSAVENUM: 5 + DEBUG: false + DISABLESEASONALEVENTS: false + LOG: false + MAXOBJECTS: 2162688 + MAXPLAYERS: 4 + MAXTICKRATE: 30 + MULTIHOME: '::' + PGID: 1000 + PUID: 1000 + SERVERGAMEPORT: 7777 + SERVERMESSAGINGPORT: 8888 + SERVERSTREAMING: true + SKIPUPDATE: false + STEAMBETA: false + STEAMBETAID: "" + STEAMBETAKEY: "" + TIMEOUT: 30 + VMOVERRIDE: false +configSchema: + $schema: http://json-schema.org/draft-07/schema# + properties: + AUTOSAVENUM: + category: World & Environment + default: 5 + description: The number of rotating autosave files to keep in the save folder. + title: Autosave Rotation Count + type: integer + DEBUG: + category: System & General + default: false + description: Prints diagnostic environment and system details on startup, then stops. + title: Debug Mode + type: boolean + DISABLESEASONALEVENTS: + category: Gameplay & Rules + default: false + description: Disables seasonal in-game events such as FICSMAS holiday events. + title: Disable Seasonal Events + type: boolean + LOG: + category: System & General + default: false + description: Disables automatic pruning of previous Satisfactory server log files on container startup. + title: Retain Old Logs + type: boolean + MAXOBJECTS: + category: World & Environment + default: 2162688 + description: Unreal Engine garbage collector UObject limit to support large late-game factories. + title: Max Objects Limit + type: integer + MAXPLAYERS: + category: System & General + default: 4 + description: Sets the maximum number of concurrent players allowed on the server. + title: Max Players + type: integer + MAXTICKRATE: + category: System & General + default: 30 + description: Sets the maximum simulation tick rate for the server in Hz. + title: Max Tick Rate + type: integer + MULTIHOME: + category: System & General + default: '::' + description: 'Network interface binding IP address (defaults to :: for dual-stack IPv4/IPv6).' + title: Multihome Address + type: string + PGID: + category: System & General + default: 1000 + description: Sets the Linux group ID under which the dedicated server process runs. + title: Process Group ID + type: integer + PUID: + category: System & General + default: 1000 + description: Sets the Linux user ID under which the dedicated server process runs. + title: Process User ID + type: integer + SERVERGAMEPORT: + category: System & General + default: 7777 + description: Primary game port for UDP and TCP network communication. + title: Server Game Port + type: integer + SERVERMESSAGINGPORT: + category: System & General + default: 8888 + description: Port used for reliable server messaging and beacon network traffic. + title: Server Messaging Port + type: integer + SERVERSTREAMING: + category: System & General + default: true + description: Toggles whether the game server utilizes Unreal Engine World Partition asset streaming. + title: Server Streaming + type: boolean + SKIPUPDATE: + category: System & General + default: false + description: Skips automatic SteamCMD game file update checks on container startup. + title: Skip Update + type: boolean + STEAMBETA: + category: System & General + default: false + description: When enabled, updates and runs the Experimental branch instead of the stable release. + title: Steam Beta Branch + type: boolean + STEAMBETAID: + category: System & General + default: "" + description: Specific beta branch identifier to download via SteamCMD. + title: Steam Beta Branch Name + type: string + STEAMBETAKEY: + category: Security & Access + default: "" + description: Password required to access a private Steam beta branch. + title: Steam Beta Password + type: string + TIMEOUT: + category: System & General + default: 30 + description: Client network connection timeout threshold in seconds. + title: Connection Timeout + type: integer + VMOVERRIDE: + category: System & General + default: false + description: Skips CPU model checking on hypervisors to prevent crash warnings on KVM/QEMU hosts. + title: VM CPU Check Override + type: boolean + required: [] + type: object +configFileFormat: INI +configFormatOptions: + assignment: = + falseValue: "false" + footer: "" + header: "" + quoteStrings: false + separator: "" + trueValue: "true" + unquotedKeys: [] + uppercaseKeys: false +agnosticCommandConfig: + actions: + Ban: + type: UNSUPPORTED + Broadcast: + type: UNSUPPORTED + Kick: + type: UNSUPPORTED + Save: + type: UNSUPPORTED + pingPort: null + pingPortKey: null + playerJoinRegex: '.*Join succeeded: (\S+).*' + playerLeaveRegex: .*UNetConnection::Close:.*?\[PlayerName:\s*([^\]]+)\].* + playersCommand: null + playersFormat: null + playersRegex: null + queryPortKey: null + queryType: LOG_ONLY + rconPasswordKey: null + rconPortKey: null + restEndpoint: null + restPasswordKey: null + restPortKey: null + restUser: null +backupPaths: + - saved +maxBackupSizeMb: 1024 +maxPlayersConfigKey: MAXPLAYERS diff --git a/games/satisfactory-vanilla-stable/test.yaml b/games/satisfactory-vanilla-stable/test.yaml new file mode 100644 index 0000000..1b49f91 --- /dev/null +++ b/games/satisfactory-vanilla-stable/test.yaml @@ -0,0 +1,3 @@ +# yaml-language-server: $schema=../../schema/test.schema.json +# Values CI uses to boot-test this game. Never put real credentials here: map them under 'secrets'. +timeoutSec: 900 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0e387ae --- /dev/null +++ b/go.mod @@ -0,0 +1,10 @@ +module www.gitea.gbreed.com/gbreed-public/gbr-games + +go 1.24 + +require ( + github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 + gopkg.in/yaml.v3 v3.0.1 +) + +require golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..01768f4 --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 h1:1EYB5IzjZawrrnELUi78f9fPu57HuXjmddZPjrls/28= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.3/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/schema/game.schema.json b/schema/game.schema.json new file mode 100644 index 0000000..fd09fef --- /dev/null +++ b/schema/game.schema.json @@ -0,0 +1,79 @@ +{ + "$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 }, + "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"] }, + "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"] } } + } + } + ] +} diff --git a/schema/test.schema.json b/schema/test.schema.json new file mode 100644 index 0000000..631fea0 --- /dev/null +++ b/schema/test.schema.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://www.gitea.gbreed.com/gbreed-public/gbr-games/raw/branch/main/schema/test.schema.json", + "title": "GBR Hosting game boot-test fixture", + "description": "Values CI uses when boot-testing a template. Never put real credentials here: map them to a Gitea Actions secret under 'secrets'.", + "type": "object", + "additionalProperties": false, + "properties": { + "config": { + "description": "Config values for the test server, layered on top of defaultConfig (e.g. EULA: true).", + "type": "object", + "propertyNames": { "pattern": "^[a-zA-Z0-9_.-]+$" } + }, + "secrets": { + "description": "Config key -> name of a Gitea Actions secret. If a secret is missing, the boot test is skipped, not failed.", + "type": "object", + "propertyNames": { "pattern": "^[a-zA-Z0-9_.-]+$" }, + "additionalProperties": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" } + }, + "timeoutSec": { + "description": "How long to wait for the server to become ready, including install.", + "type": "integer", + "minimum": 60, + "maximum": 7200 + } + } +}