Skip to main content

Storage

Persistent key/value store, backed by a JSON file at <config>/Seraph/storage.json. Values are JSON-serialisable Lua (string, number, boolean, nested table) — round-tripped via serde so a stored table comes back with the same shape.

Two globals share the same backing store but use different namespaces:

  • storage — namespaced to the parent client's bot id, so each bot's keys are isolated. Falls back to __noparent__ for the global executor.
  • globalStorage — shared namespace __shared__, every bot reads/writes the same bucket. Use for cross-bot coordination ("world_busy" flag, round-robin assignment, etc).

Both expose the same five methods.

Methods

storage:get(key)

FieldType
Signature(self: Storage, key: string) → any?
Returnsany? — previously stored value, or nil when the key has never been set (or was deleted)
Asyncno

Numbers come back as numbers, tables as tables — same shape that was stored.

local v = storage:get("counter")
print(v) -- 1, or nil if never set

storage:set(key, value)

FieldType
Signature(self: Storage, key: string, value: any) → ()
Returnsnothing
Common errorsraises when value isn't JSON-serializable (e.g. function values)
Asyncno

Triggers an immediate storage.json flush so a crash mid-script won't lose the write.

storage:set("counter", (storage:get("counter") or 0) + 1)

storage:delete(key)

FieldType
Signature(self: Storage, key: string) → boolean
Returnsbooleantrue if the key existed (and the delete actually flushed); false when the key was already absent
Asyncno
if storage:delete("counter") then
print("counter cleared")
end

storage:keys()

FieldType
Signature(self: Storage) → {string}
Returns{string} — 1-indexed array of every key in this namespace
Asyncno

Order is HashMap-iteration order — don't rely on it being stable.

for _, k in ipairs(storage:keys()) do
print(k, "=", storage:get(k))
end

storage:clear()

FieldType
Signature(self: Storage) → ()
Returnsnothing
Asyncno

Wipes every key in this namespace. Idempotent.

storage:clear()

Per-bot counter

storage:set("counter", (storage:get("counter") or 0) + 1)
print(storage:get("counter"))

Cross-bot lock

First bot to claim "MYWORLD" wins, the rest skip:

if not globalStorage:get("world_busy") then
globalStorage:set("world_busy", getClient().id)
client:warp("MYWORLD")
-- ... do the work
globalStorage:delete("world_busy")
end