DataStore
A named store of saved values, read and written by key.
- Get it:
DataStoreService.getDataStorereturns one - Available on: Game server only.
Every method is asynchronous and can fail (a network problem, a limit, too many requests): call
them inside task.spawn or an async handler, and catch errors. Keys are 1–50 characters; use
player.userId for per-player data. Values are JSON: numbers, strings, booleans, null, arrays and
plain objects, up to 64 KB serialized. To save a Vector3 or Color3, store toArray() or
toHex().
Methods
Section titled “Methods”get<T extends DataValue = DataValue>(key: string): Promise<T | undefined>The value stored under key, or undefined if there’s none.
set(key: string, value: DataValue): Promise<void>Stores value under key, replacing what was there.
update
Section titled “update”update<T extends DataValue = DataValue>(key: string, fn: (current: T | undefined) => T | undefined): Promise<T | undefined>Read-modify-write without lost updates across servers. Return undefined to leave it as is.
fn receives the current value (or undefined) and returns the new one; return undefined to
leave it as it is. If another server writes the key in between, fn runs again with the new
value, so keep it free of side effects. After 5 conflicting tries the promise rejects. Resolves
with the stored value.
increment
Section titled “increment”increment(key: string, delta?: number): Promise<number>Adds delta (default 1) to the number stored under key and resolves with the result; a missing
key counts as 0. Safe when several servers increment at once. Rejects if the stored value isn’t a
number.
remove
Section titled “remove”remove(key: string): Promise<void>Deletes key.
Examples
Section titled “Examples”Load on join, save on leave
Section titled “Load on join, save on leave”import { DataStoreService, NumberValue, Player, Players, createLeaderstats, task, warn,} from '@rally/sdk';
type Progress = { v: 1; level: number; gold: number };
const progress = DataStoreService.getDataStore('Progress');const loaded = new Map<Player, { level: NumberValue; gold: NumberValue }>();
Players.playerAdded.connect((player) => { const stats = createLeaderstats(player, { Level: 1, Gold: 0 }); task.spawn(async () => { try { const saved = await progress.get<Progress>(player.userId); if (saved) { stats.Level.value = saved.level; stats.Gold.value = saved.gold; } // Only save players whose data loaded, so a failed load never overwrites saved data. loaded.set(player, { level: stats.Level, gold: stats.Gold }); } catch (e) { warn(`couldn't load ${player.displayName}`, e); } });});
Players.playerRemoving.connect(async (player) => { const stats = loaded.get(player); if (!stats) return; loaded.delete(player); try { await progress.set(player.userId, { v: 1, level: stats.level.value, gold: stats.gold.value }); } catch (e) { warn(`couldn't save ${player.displayName}`, e); }});Counters and read-modify-write
Section titled “Counters and read-modify-write”import { DataStoreService, Players, task } from '@rally/sdk';
const wins = DataStoreService.getDataStore('Wins');const inventory = DataStoreService.getDataStore('Inventory');
Players.playerAdded.connect((player) => { task.spawn(async () => { await wins.increment(player.userId); await inventory.update<string[]>(player.userId, (items = []) => items.includes('hat') ? undefined : [...items, 'hat'], ); });});