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)
| Field | Type |
|---|---|
| Signature | (self: Storage, key: string) → any? |
| Returns | any? — previously stored value, or nil when the key has never been set (or was deleted) |
| Async | no |
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)
| Field | Type |
|---|---|
| Signature | (self: Storage, key: string, value: any) → () |
| Returns | nothing |
| Common errors | raises when value isn't JSON-serializable (e.g. function values) |
| Async | no |
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)
| Field | Type |
|---|---|
| Signature | (self: Storage, key: string) → boolean |
| Returns | boolean — true if the key existed (and the delete actually flushed); false when the key was already absent |
| Async | no |
if storage:delete("counter") then
print("counter cleared")
end
storage:keys()
| Field | Type |
|---|---|
| Signature | (self: Storage) → {string} |
| Returns | {string} — 1-indexed array of every key in this namespace |
| Async | no |
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()
| Field | Type |
|---|---|
| Signature | (self: Storage) → () |
| Returns | nothing |
| Async | no |
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