Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ jobs:
- name: Install PyYAML
run: pip install --disable-pip-version-check --user pyyaml

- name: Check flush-only backup capabilities
run: python3 test-backup-capabilities.py

- name: Run module preflight validator
run: python3 validate.py

Expand Down
19 changes: 18 additions & 1 deletion .schema/gametemplate.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,13 @@
"maxItems": 32,
"type": "array"
},
"backupFlush": {
"description": "BackupFlush requests an acknowledged checkpoint without disabling writes.",
"enum": [
"minecraft-save-all"
],
"type": "string"
},
"lifecycle": {
"description": "Lifecycle configures graceful lifecycle transitions the agent drives\nover RCON \u2014 currently the in-game stop sequence the operator runs\nbefore scaling the server down, so the world is saved and shut down\ncleanly instead of relying on a container SIGTERM. Requires\nrcon.protocol != none.",
"properties": {
Expand Down Expand Up @@ -836,7 +843,13 @@
"type": "object"
}
},
"type": "object"
"type": "object",
"x-kubernetes-validations": [
{
"message": "choose a flush-only checkpoint or a write-freeze declaration",
"rule": "!(has(self.backupFlush) && has(self.quiesce))"
}
]
},
"categories": {
"description": "Categories group this game in the dashboard's catalog and Create\nServer picker (e.g. [\"Survival\", \"Sandbox\"]). A game may belong to\nseveral at once \u2014 Minecraft is reasonably Sandbox, Survival and\nCreative. The dashboard builds its category filter from the distinct\nvalues present across installed templates, so a module introduces a\nnew category simply by naming it here \u2014 no frontend change. Empty\nfalls back to a heuristic on the game slug, and finally to \"Other\".\ndocs/module-authoring.md publishes the canonical vocabulary the\nofficial modules use.",
Expand Down Expand Up @@ -2174,6 +2187,10 @@
{
"message": "capabilities.mods.loaders requires a non-empty spec.versions catalog (a mod loader is selected by versions[].loader)",
"rule": "!has(self.capabilities) || !has(self.capabilities.mods) || !has(self.capabilities.mods.loaders) || size(self.capabilities.mods.loaders) == 0 || (has(self.versions) && size(self.versions) > 0)"
},
{
"message": "backupFlush requires the Minecraft Java adapter",
"rule": "!has(self.capabilities) || !has(self.capabilities.backupFlush) || self.game == 'minecraft-java'"
}
]
},
Expand Down
7 changes: 7 additions & 0 deletions ark-survival-ascended/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ actually holds saves and `GameUserSettings.ini`. Default size is 30 GiB;
ASA's base install alone is roughly 11 GiB, so heavy mod/save usage may
need more.

## Backups

**Save world** runs `SaveWorld`. ARK can change world and player files again
while a live backup is copying them. For a stopped copy, shut down the server,
wait for it to stop, then request a backup with quiesce enabled. Keep the
server stopped until the backup completes.

## Known limitations

- `SaveWorld`/`DoExit` and `ServerChat` are corroborated by community
Expand Down
2 changes: 1 addition & 1 deletion ark-survival-ascended/module.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: gameplane.local/module/v1
name: ark-survival-ascended
displayName: "ARK: Survival Ascended"
version: 1.0.8
version: 1.0.9
game: ark-survival-ascended
categories: [Survival, PvP, Co-op, Modded]
summary: "ARK: Survival Ascended dedicated server (mschnitzer/asa-linux-server) with RCON console/moderation. CurseForge mods install via the game's own -mods= launch parameter."
Expand Down
5 changes: 1 addition & 4 deletions ark-survival-ascended/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ metadata:
spec:
displayName: "ARK: Survival Ascended"
game: ark-survival-ascended
version: 1.0.8
version: 1.0.9
categories: [Survival, PvP, Co-op, Modded]
accentColor: "#00b2e2"
description: |
Expand Down Expand Up @@ -157,9 +157,6 @@ spec:
# sufficient.
lifecycle:
stop: ["SaveWorld", "DoExit"]
quiesce:
quiesce: ["SaveWorld"]
unquiesce: ["SaveWorld"]
actions:
- id: broadcast
displayName: Broadcast
Expand Down
11 changes: 6 additions & 5 deletions project-zomboid/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,12 @@ Default size is 15 GiB — heavy Workshop mod usage may need more.

## Backups

`AUTOSAVE_INTERVAL` controls the game's own periodic save; the **Save
world** action forces one immediately and is also what backup quiesce
runs before a snapshot. The image's own `BACKUPS_PERIOD`/`BACKUPS_COUNT`
env vars are not wired here — use the Gameplane Backup CRD as the
authoritative snapshot path instead.
`AUTOSAVE_INTERVAL` controls periodic saves. **Save world** forces an
immediate save. Project Zomboid can write map chunks and player data during
a live backup. For a stopped copy, shut down the server, wait for it to stop,
then request a backup with quiesce enabled. Keep the server stopped until the
backup completes. Use the Gameplane Backup CRD to request the copy. The
image also supports `BACKUPS_PERIOD` and `BACKUPS_COUNT` outside this template.

## Sample

Expand Down
2 changes: 1 addition & 1 deletion project-zomboid/module.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
apiVersion: gameplane.local/module/v1
name: project-zomboid
displayName: Project Zomboid
version: 1.0.5
version: 1.0.6
game: project-zomboid
categories: [Survival, Horror, Co-op]
summary: Project Zomboid dedicated server (sknnr/zomboid-dedicated-server) with RCON console/moderation and Steam Workshop mods via the game's own MOD_IDS/MOD_NAMES sync.
Expand Down
8 changes: 1 addition & 7 deletions project-zomboid/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ metadata:
spec:
displayName: Project Zomboid
game: project-zomboid
version: 1.0.5
version: 1.0.6
categories: [Survival, Horror, Co-op]
accentColor: "#336600"
description: |
Expand Down Expand Up @@ -132,12 +132,6 @@ spec:
# first is the commonly recommended safe sequence.
lifecycle:
stop: ["save", "quit"]
# `save` is a real, idempotent force-save command; there's no PZ
# equivalent of Minecraft's save-off/save-on write-pause pair, so
# quiesce and unquiesce both just force a flush around the snapshot.
quiesce:
quiesce: ["save"]
unquiesce: ["save"]
actions:
- id: broadcast
displayName: Broadcast
Expand Down
25 changes: 25 additions & 0 deletions test-backup-capabilities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Check the backup and manual save declarations for flush-only engines."""

from pathlib import Path
import unittest

import yaml


class BackupCapabilitiesTest(unittest.TestCase):
def test_flush_only_games_offer_manual_save_and_clean_stop(self):
for game, save, stop in (
("ark-survival-ascended", "SaveWorld", "DoExit"),
("project-zomboid", "save", "quit"),
):
with self.subTest(game=game):
template = Path(__file__).parent / game / "template.yaml"
caps = yaml.safe_load(template.read_text(encoding="utf-8"))["spec"]["capabilities"]
self.assertNotIn("quiesce", caps)
self.assertEqual(caps["lifecycle"]["stop"], [save, stop])
actions = {action["id"]: action["command"] for action in caps["actions"]}
self.assertEqual(actions["save-world"], save)


if __name__ == "__main__":
unittest.main()
Loading