Persist player data
Save and load player progress correctly with Cloud Save — including the read-modify-write pattern for concurrent edits.
Uses Cloud Save (durable) and, for hot counters, Memory Store (volatile).
Load on join, save on change
import { HelixInstance } from '@helix/server-sdk';
type Progress = { level: number; coins: number };
const DEFAULT: Progress = { level: 1, coins: 0 };
export default class MyWorld extends HelixInstance {
async onPlayerJoin(player) {
const p = (await Helix.cloudSave.get<Progress>(key(player.id))) ?? DEFAULT;
this.setState(player.id, p);
}
async onPlayerLeave(player) {
await Helix.cloudSave.set(key(player.id), this.getState(player.id));
}
}
const key = (id: string) => `progress:${id}`;Persist value from the server only
Never let a client write its own coins. Loading and saving value-bearing state lives in
server-authoritative code. Clients send intent (events); the server updates and persists.
Concurrent edits: read–modify–write
Two instances of your world can run at once. For a value many players or sessions can change, don't blind-write — read, modify, and write inside a server-guarded step:
async function addCoins(playerId: string, delta: number) {
const cur = (await Helix.cloudSave.get<Progress>(key(playerId))) ?? DEFAULT;
const next = { ...cur, coins: cur.coins + delta };
await Helix.cloudSave.set(key(playerId), next);
return next.coins;
}For hot counters (e.g. "players in this zone right now") that don't need durability, use Memory
Store's atomic increment and only flush a summary to Cloud Save occasionally:
const inZone = await Helix.memoryStore.increment(`zone:${zoneId}`, 1, 60);Choosing the store
| Data | Store | Why |
|---|---|---|
| Progress, settings, owned state | Cloud Save | Must survive sessions |
| Live leaderboard / matchmaking | Memory Store | Fast, shared, disposable |
| Hot counters | Memory Store increment | Atomic, cheap |
Next
Sell an item for LIX
Add a server-authoritative purchase to your world.