Storage
Persist player data scoped to game ID and authenticated user. Supports cloud and local modes.
Storage Modes
| Mode | Cross-device | Auth required | Offline |
|---|---|---|---|
cloud (default) | Yes | JWT + game-id | No |
local | No | No | Yes |
Set default mode via HyveSdkProvider config or at runtime:
// Via provider
<HyveSdkProvider config={{ storageMode: 'local' }}>
// At runtime
hyve.configureStorage('cloud');
hyve.getStorageMode(); // 'cloud' | 'local'
CRUD
// Save — uses default storage mode
await hyve.saveGameData('high_score', 1500);
// Save to specific mode
await hyve.saveGameData('settings', { volume: 0.8 }, undefined, undefined, 'local');
// Get — returns null if key never saved
const data = await hyve.getGameData('high_score');
// { key, value, created_at, updated_at } | null
// Delete — returns true if key existed
await hyve.deleteGameData('high_score');
Atomic Operations
Read-modify-write server-side in a single call. Avoids race conditions.
saveGameData(key, value, operation?, path?, storage?): Promise<SaveGameDataResponse>
// operation: 'set' | 'add' | 'subtract' | 'multiply' | 'divide' | 'modulo' | 'min' | 'max' | 'append'
// path: dot-notation up to 5 levels deep (e.g. 'stats.score')
await hyve.saveGameData('score', 100, 'add'); // top-level add
await hyve.saveGameData('stats', 100, 'add', 'score'); // nested: stats.score += 100
await hyve.saveGameData('stats', 9999, 'max', 'high_score'); // keep higher value
await hyve.saveGameData('achievements', 'badge_gold', 'append'); // push to array
const res = await hyve.saveGameData('score', 500, 'add');
console.log(res.result); // new value after operation
| Operation | Behavior |
|---|---|
set | Full replace (default) |
add | Numeric addition |
subtract | Numeric subtraction |
multiply | Numeric multiplication |
divide | Numeric division (float result) |
modulo | Numeric remainder |
min | Keep the smaller value |
max | Keep the larger value |
append | Append to a JSON array |
Batch Operations
// Save multiple
await hyve.batchSaveGameData([
{ key: 'level', value: 5 },
{ key: 'score', value: 1000, operation: 'add' },
{ key: 'achievements', value: 'badge_1', operation: 'append' },
]);
// Per-item results when any item uses an operation
const res = await hyve.batchSaveGameData([...]);
for (const item of res.results ?? []) {
if (item.success) console.log(item.key, '->', item.result);
else console.error(item.key, item.error);
}
// Get multiple
const items = await hyve.getMultipleGameData(['score', 'level']);
// Delete multiple — returns count deleted
const count = await hyve.deleteMultipleGameData(['score', 'level']);
Leaderboards
Rank players by a numeric value stored in persistent game data. Game ID is resolved automatically.
// 1. Store the player's score
await hyve.saveGameData('high_score', 4200);
// 2. Fetch ranked list
const board = await hyve.getGameDataLeaderboard({ key: 'high_score' });
for (const entry of board.entries) {
console.log(`#${entry.rank} ${entry.username}: ${entry.score}`);
}
// Caller's own rank is always included
if (board.user_position) {
console.log('Your rank:', board.user_position.rank);
}
If the stored value is an object, use score_path to point at the numeric field:
await hyve.saveGameData('stats', { level: 12, score: 9800 });
const board = await hyve.getGameDataLeaderboard({
key: 'stats',
score_path: 'score', // dot-notation
sort: 'desc', // 'asc' | 'desc' (default: 'desc')
limit: 20, // 1–100 (default: 10)
});
// Pagination
if (board.has_more && board.next_cursor) {
const next = await hyve.getGameDataLeaderboard({ key: 'stats', cursor: board.next_cursor });
}
Types
type GameDataValue = string | number | boolean | null | GameDataValue[] | { [key: string]: GameDataValue };
type GameDataOperation = 'set' | 'add' | 'subtract' | 'multiply' | 'divide' | 'modulo' | 'min' | 'max' | 'append';
interface GameDataItem {
key: string;
value: GameDataValue;
created_at: string; // ISO 8601
updated_at: string; // ISO 8601
}
interface GameDataBatchItem {
key: string;
value: GameDataValue;
operation?: GameDataOperation;
path?: string;
}
interface SaveGameDataResponse {
success: boolean;
message: string;
result?: GameDataValue;
}
interface BatchSaveGameDataResponse {
success: boolean;
message: string;
results?: { key: string; success: boolean; result?: GameDataValue; error?: string }[];
}
interface GetGameDataLeaderboardParams {
key: string;
score_path?: string;
sort?: 'asc' | 'desc';
limit?: number;
cursor?: string;
}
interface GameDataLeaderboardEntry {
rank: number;
username: string;
avatar_url?: string | null;
hyve_user_id: string;
score: number;
}
interface GameDataLeaderboardResponse {
entries: GameDataLeaderboardEntry[];
next_cursor?: string | null;
has_more: boolean;
user_position?: {
rank: number;
score: number;
entry: GameDataLeaderboardEntry;
} | null;
}
Limits
| Limit | Value |
|---|---|
| Max key length | 255 characters |
| Max value size | 1 MB |
| Max batch size | 100 items |
| Key format | Case-sensitive |