Saves

Usage guide of save.persistence: slots, quick save and autosave, the Load / Save screen, storage, the save.write and save.load hooks, modding, engines.

Source: module-kits:kits/save_persistence/usage/en.mdView Markdown source

Also read offline — CLI: grip-forge docs save-persistenceMCP tool: gripforge_docs { "topic": "save-persistence" }

Save slots (save.persistence) #

Save and load the whole game into manual slots, a quick save and an autosave, with a Load / Save screen. Readable offline: gripforge docs save-persistence, or the MCP tool gripforge_docs (topic save-persistence).

What it is for #

Every slot holds a whole gripforge.save/1 save of the game: the game state, the entities, the mission run and the state of every kit, with its date, play time, location, picture and game version. The model is the same on the web, Godot, Unity and Unreal.

It is installed by default in every new game, with ui.menu, input.remap and settings.graphics. It requires runtime.core (the host's own save) and game.engine. Configure it freely; remove it only when asked.

Turn it on #

gripforge_gamekit_install   { project_id: "gkp_…", id: "save.persistence" }
gripforge_gamekit_configure { project_id: "gkp_…", id: "save.persistence",
                              config: { "slots": 5, "autosaveSeconds": 120, "gameVersion": "0.3.0" } }

Only the keys that differ from the defaults need to be written. The full reference is gripforge kit api save.persistence.

Settings and defaults #

KeyDefaultMeaning
slots3manual slots (1 to 20)
quicksavetruea quick-save slot, saved with F5 and loaded with F9 (input actions quicksave and quickload, rebindable with input.remap)
autosaveSeconds60interval autosave in seconds of play; 0 means none
autosaveOninterval, mission, checkpoint, zone, scenewhat autosaves
autosaveTopics[]other bus events that autosave (a boss defeated, a level reached)
autosaveMinGap5seconds of play between two event autosaves
storagelocallocal or cloud
compatibilitylenientlenient loads saves of another kit set or older versions; strict refuses them
gameVersion1.0.0version written in each save and shown on the Load screen
locationVaremptyvariable holding the location shown on a save
thumbnail, thumbnailWidthtrue, 192a picture of the game with each save (16:9, 32 to 512 px)

Slots #

Manual slots are slot1 to slot<slots>; quick is the quick save and auto the autosave. The service accepts 2, "2" or slot2. Each save keeps the date (savedAt), play time, location (setLocation(name), else locationVar, else the title of the current scene, else the last zone entered), the picture, the game version and a counter seq: the most recent save has the highest, and that is what Continue loads.

  • save(slot?, label?) (default auto) and load(slot) run at the end of the current tick when asked during one (a trigger, a mission event, the quick-save key), at once otherwise.
  • A load is refused (save.failed) for an empty or unreadable slot or a save of another project. A save made with another kit set loads with migrated: true unless compatibility is strict.
  • A save taken while a menu pauses the game is stored as playing, so a load never leaves the game paused without a menu.

Autosave #

Into auto, on the triggers of autosaveOn: interval (every autosaveSeconds), mission (mission.completed), checkpoint (checkpoint.reached, course.checkpoint), zone (zone.entered), scene (after a scene change, never on the start scene nor on a scene rebuilt by a load), plus any topic of autosaveTopics. Event autosaves are at most one per autosaveMinGap. autosave(false) pauses autosave, autosaveNow(reason) forces one.

Load / Save screen #

The kit is the options tab saves of ui.menu: one row per slot with picture, date, play time, location and version, and the actions Load, Save (manual slots) and Delete. Overwriting and deleting ask for confirmation. ui.menu opens it from its buttons load (title and pause) and save (pause); nothing can be saved on the title screen. A load from the screen closes the menu, and Continue on the title loads latest(). setOption("mode", "load" | "save" | "manage") picks the actions shown.

Service, events, variables #

Service save.persistence: slotIds(), slots() and info(slot) ({ slot, label, tick, elapsed, storage, kind, savedAt, playtime, location, thumbnail, gameVersion, seq, format }), latest(), has(slot), save(slot?, label?), load(slot), remove(slot), quicksave(), quickload(), autosave(on?), autosaveNow(reason?), storage(), exportSlot(slot), importSlot(slot, json), setLocation(name), location(), setClock(ms, offsetMinutes) (a fixed clock for replays and tests), setThumbnailSource(fn) (web), and the options tab (optionsTab(), optionFields(lang), setOption(id, value)).

Events: save.saved { slot, label, tick, auto, kind, reason }, save.loaded { slot, tick, migrated }, save.removed { slot }, save.failed { slot, op, reason }. Variables: save.last, save.autosave. Script action: save_game { slot?, label? } (default slot auto), for example { "save_game": "slot1" }.

A second capability, save.replays, is Rust only: validated immutable archives and separate viewing and preferences through request(command).

Storage and the picture #

local is the host's local store: the browser's local storage in a page (memory in headless runs), files under user://gripforge_saves on Godot, Application.persistentDataPath on Unity, save game slots on Unreal, or a store the application passes. cloud is the store the application passes as cloud; without one the kit warns once and saves locally (storage() says which is used). Keys are gripforge.save/<projectId>/<slot>. Records are gripforge.slot/2; older gripforge.slot/1 records are read as they are and become slot/2 at the next save in that slot.

The picture is a capture of the renderer: the web play page installs the source, Godot reads the viewport, Unity renders the main camera, Unreal a scene capture. It is taken when a menu opens (the frame under it) and used by the saves made from that menu. Headless runs keep no picture.

Hooks and modding #

  • save.write { slot, label, kind, auto, reason }: asked before every save (reason: manual, quick, menu, action, or for an autosave interval, mission, checkpoint, zone, scene, event). A tap may change label. A cancel writes nothing, emits save.failed and makes save(), quicksave() and autosaveNow() answer false; a refused interval autosave is asked again one interval later.
  • save.load { slot, label, kind, tick, migrated }: asked for a load that passed the kit's own checks. A cancel loads nothing and the Load screen stays open.
{ "id": "no_save_in_fight", "hook": "save.write", "if": [{ "var": "boss.active", "op": "==", "value": true }], "cancel": true, "reason": "in_fight" }

remove(slot) and importSlot are not intercepted (the player's own management of the slots). A mod may override slots, quicksave, autosaveSeconds, autosaveOn, autosaveTopics and autosaveMinGap. Not moddable: storage, compatibility, gameVersion, locationVar, thumbnail, thumbnailWidth. The hooks run on the web and Rust.

Engines #

EngineWhere the saves go
Web and native playerlocal storage of the page, or a store the application passes
Godotfiles under user://gripforge_saves, picture from the viewport
UnityApplication.persistentDataPath, picture from the main camera
Unrealsave game slots, picture from a scene capture of the player camera

When it does not do what you expect #

SymptomCause and fix
Continue does nothingno save exists yet; Continue loads latest()
A load is refusedempty slot, another project, or compatibility: "strict" with another kit set; read save.failed.reason
Autosave is rareautosaveMinGap holds event autosaves; raise autosaveSeconds or list more autosaveOn triggers
No picture on a saveheadless run, or thumbnail is false
Saving is refused in a fighta save.write tap cancels it
Cloud saves stay localthe application passed no cloud store; storage() shows local