NarraLeaf

saves

List and read save slots, and — with the heavier capability — overwrite one or load it.

app.game.saves reaches the player's save slots. It is split across two capabilities because listing saves and destroying a playthrough are not the same request:

{
  "contributes": {
    "runtimeCapabilities": ["saves.read", "saves.write"]
  }
}

Methods

MethodSignatureNeeds
listIds() => Promise<string[]>saves.read
readMetadata(id: string) => Promise<RuntimePluginSaveMetadata | null>saves.read
write(id: string, metadata?: unknown) => Promise<void>saves.write
load(id: string) => Promise<void>saves.write
type RuntimePluginSaveMetadata = {
    id: string;
    updatedAt?: number;
    metadata?: unknown;
};

write and load are absent from the object without saves.write, so guard them: app.game.saves?.write?.(...).

Quick save, in full

const SLOT = "yourname.quick-save.slot";

// write
await app.game.saves?.write?.(SLOT, { at: "chapter-2" });

// read back
const meta = await app.game.saves?.readMetadata(SLOT);

// load — replaces the running playthrough
await app.game.saves?.load?.(SLOT);

Pick a slot id namespaced with your plugin id so it never collides with a slot the player named. metadata round-trips through JSON — pass plain data only.

write overwrites the slot and load abandons the current playthrough. The install prompt tells the author exactly that, in those words. Declare saves.write only if overwriting a slot is the point of your plugin — a quick-save plugin, a chapter-select — and never as a convenience on top of saves.read.

What is not here

There is no deleteSave, no screenshot capture, and no way to enumerate another plugin's slots beyond the ids listIds returns. A plugin can see that slots exist and what metadata was stored with them; the contents of a playthrough are not readable through this API.

On this page