@table-games-engine/persistence
v0.1.3
Published
Snapshot, event and replay persistence (in-memory and SQLite) for table-games-engine matches.
Maintainers
Readme
@table-games-engine/persistence
Persistence for table-games-engine matches: event and snapshot storage (in-memory or SQLite), snapshot policies, state checksums, replay, and ownership leases.
Most game authors don't install this directly: it comes with @table-games-engine/game-kit and is used by @table-games-engine/nakama-adapter. Install it to keep matches alive across restarts on your own host.
Install
npm install @table-games-engine/persistenceSqliteAdapter uses better-sqlite3 (a native module, installed as a dependency).
What is in it
InMemoryAdapter- no I/O, for tests and ephemeral runs.SqliteAdapter(path, opts?)- SQLite viabetter-sqlite3; takes a file path or':memory:'; WAL mode, migrations applied on open.createPersistedEngine(args)- wraps a coreEngineso every transition appends events and snapshots per policy.checksumState(state)- deterministic checksum of aMatchState(FNV-1a 32-bit over canonical JSON).replayMatch(adapter, matchId, toVersion?)andloadEventsBetween(adapter, matchId, from, to).- Snapshot policies:
everyNTransitions,everyTTicks,always,never,combine.
Quick start
import { SqliteAdapter, createPersistedEngine, everyNTransitions, replayMatch, checksumState } from '@table-games-engine/persistence';
const adapter = new SqliteAdapter('./match.db');
const engine = await createPersistedEngine({
manifest, initialState, handlers,
adapter,
matchId: 'match-42',
policy: everyNTransitions(50), // default
ownerNode: 'node-A', // optional ownership lease
leaseSeconds: 60, // optional, default 60
});
const r = await engine.applyAction(myInput); // same shape as the core Engine, but async
if (r.ok) await engine.resolveTopFrame();
await engine.forceSnapshot(); // e.g. on match end
const recovered = await replayMatch(adapter, 'match-42');
checksumState(recovered!) === checksumState(engine.getState());
await engine.close(); // releases the lease and closes the DB handleSnapshot policies
| Policy | Fires |
|---|---|
| everyNTransitions(n) | after every n transitions |
| everyTTicks(t) | after every t logical ticks |
| always() | every transition (best for async games and deterministic tests) |
| never() | never |
| combine(...policies) | when any sub-policy fires |
Replay
replayMatch returns the latest snapshot at or before toVersion (or the latest overall), or null if nothing was stored. It does not re-apply events, so to recover an exact version use a policy that snapshots there (for example always()). Use loadEventsBetween for the audit log.
Failure behaviour
Adapter errors reject the returned promise and do not roll back in-memory state. If you need all-or-nothing, close the engine and recover from the last good snapshot. With ownerNode set, construction throws if another node holds an unexpired lease.
Licence
FSL-1.1-ALv2 (Functional Source License, converts to Apache-2.0 after two years). See LICENSE.md in the package.
