Typed state10 / 12

Saving the high score

Save player data separately from game configuration.

TypedStateStore provides state reads and writes through a single decoder.

Save the best result of a game. This code runs after starting the engine with system.persistence.namespace: 'sdk-game':

import {
  PersistentStorageAdapter, StorageNamespace, TypedStateStore,
} from '@gamedevland/engine/configs';
import { SchemaDecoder } from '@gamedevland/engine/validation';

const store = new TypedStateStore({
  adapter: new PersistentStorageAdapter(),
  namespace: new StorageNamespace('sdk-game'),
  key: 'progress-v1',
  decoder: SchemaDecoder.object({
    bestScore: SchemaDecoder.number({ integer: true, min: 0 }),
  }),
  defaults: () => ({ bestScore: 0 }),
});

const progress = store.load();
store.save({ bestScore: Math.max(progress.bestScore, 100) });

Here, we save 100 points. During play, the progress service supplies the result.

Configuration fileboard.json → ConfigStore → board dimensions
Player stateTypedStateStore → adapter → high score
The sdk-game:progress-v1 key separates project data and the structure version.

load() returns defaults if the record is missing, the JSON is corrupted or the schema does not match. save() validates the value first; invalid data should not silently enter storage. clear() removes the record.

PersistentStorageAdapter uses the persistence coordinator prepared by the engine through system.persistence. LocalStorageAdapter and IndexedDbStorageAdapter are also available. Multiple storage backends do not add a synchronization server to the application.

In Block Puzzle, the global progress service uses a repository. The scene service passes the game result to it. This lets the storage method change without affecting score calculation or display.

Changing the format requires an explicit choice: migrate the data or use a new key. The v1 suffix does not perform a migration by itself.

Sources ↗

Block Puzzle · BlockPuzzlePlayerProgressService.ts↗The game's progress service.