HELIX 3 Docs
Guides

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

src/server.ts
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

DataStoreWhy
Progress, settings, owned stateCloud SaveMust survive sessions
Live leaderboard / matchmakingMemory StoreFast, shared, disposable
Hot countersMemory Store incrementAtomic, cheap

Next

Sell an item for LIX

Add a server-authoritative purchase to your world.

On this page