feat!: balances and items replace the wallet in the player profile
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
# @rudder/sdk
|
# @rudder/sdk
|
||||||
|
|
||||||
Rudder client SDK for web games: device and social auth, player state
|
Rudder client SDK for web games: device and social auth, player state
|
||||||
(profile, wallets, counters), catalog currencies, player storage, and calls to
|
(profile, balances, items), catalog currencies, player storage, and calls to
|
||||||
installed modules through generated typed clients.
|
installed modules through generated typed clients.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
@@ -34,8 +34,8 @@ const client = createClient({
|
|||||||
await client.auth.loginWithDevice();
|
await client.auth.loginWithDevice();
|
||||||
|
|
||||||
const profile = client.player.value;
|
const profile = client.player.value;
|
||||||
const gold = profile?.wallets?.find((w) => w.currency === 'gold')?.balance;
|
const gold = profile?.balances?.find((b) => b.currency === 'gold')?.balance;
|
||||||
const wins = profile?.counters?.wins ?? 0;
|
const swords = profile?.items?.find((i) => i.item === 'sword')?.quantity ?? 0;
|
||||||
|
|
||||||
await client.storage.save('progress', JSON.stringify({ level: 7 }));
|
await client.storage.save('progress', JSON.stringify({ level: 7 }));
|
||||||
|
|
||||||
@@ -90,13 +90,13 @@ can also call directly:
|
|||||||
| Area | Access |
|
| Area | Access |
|
||||||
|---|---|
|
|---|---|
|
||||||
| Auth | `client.auth.loginWithDevice()`, `loginWithGoogle({ idToken })`, `loginWithApple({ idToken })`, `loginWithCustom({ customData })`, `linkWithGoogle(idToken)`, `linkWithApple(idToken)`, `linkWithCustom({ customData })`, `unlinkIdentity(provider)`, `logout()`, `isAuthenticated`, `onAuthStateChange(cb)` |
|
| Auth | `client.auth.loginWithDevice()`, `loginWithGoogle({ idToken })`, `loginWithApple({ idToken })`, `loginWithCustom({ customData })`, `linkWithGoogle(idToken)`, `linkWithApple(idToken)`, `linkWithCustom({ customData })`, `unlinkIdentity(provider)`, `logout()`, `isAuthenticated`, `onAuthStateChange(cb)` |
|
||||||
| Player | `client.player` state: `player`, `identities`, `wallets`, `counters` |
|
| Player | `client.player` state: `player`, `identities`, `balances`, `items` |
|
||||||
| Catalog currencies | `client.catalog` state (`Map<slug, CatalogCurrency>`) |
|
| Catalog currencies | `client.catalog` state (`Map<slug, CatalogCurrency>`) |
|
||||||
| Storage | `client.storage` state + `.save(type, data)` / `.delete(type)` |
|
| Storage | `client.storage` state + `.save(type, data)` / `.delete(type)` |
|
||||||
| Modules | `client.modules.call<T>(module, fn, args?, { moduleVersion? })`, usually through a generated client |
|
| Modules | `client.modules.call<T>(module, fn, args?, { moduleVersion? })`, usually through a generated client |
|
||||||
|
|
||||||
`counters` holds the values of the project's client-visible number counters
|
`balances` carries one entry per released currency and `items` one per
|
||||||
by slug; currency balances are in `wallets`.
|
inventory stack.
|
||||||
|
|
||||||
## Error handling
|
## Error handling
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ function useSynced<T>(state: { onChange(cb: () => void): () => void; value: T |
|
|||||||
|
|
||||||
function Wallet() {
|
function Wallet() {
|
||||||
const profile = useSynced(client.player);
|
const profile = useSynced(client.player);
|
||||||
return <div>{profile?.wallets?.map((w) => `${w.currency}: ${w.balance}`).join(', ')}</div>;
|
return <div>{profile?.balances?.map((b) => `${b.currency}: ${b.balance}`).join(', ')}</div>;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: rudder-web-sdk
|
name: rudder-web-sdk
|
||||||
description: Use when working with the Rudder TypeScript/JavaScript client SDK (@rudder/sdk) for web games. Covers createClient, device/google/apple/custom auth, identity linking, state objects (player with wallets and counters, catalog, storage), module calls through clients generated by `rudder client generate`, idempotent retries, and error handling. Load this whenever code imports from @rudder/sdk or a generated rudder.modules.ts.
|
description: Use when working with the Rudder TypeScript/JavaScript client SDK (@rudder/sdk) for web games. Covers createClient, device/google/apple/custom auth, identity linking, state objects (player with balances and items, catalog, storage), module calls through clients generated by `rudder client generate`, idempotent retries, and error handling. Load this whenever code imports from @rudder/sdk or a generated rudder.modules.ts.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Rudder Web SDK (@rudder/sdk)
|
# Rudder Web SDK (@rudder/sdk)
|
||||||
@@ -75,7 +75,7 @@ refresh clears tokens and emits `signed-out`. Details:
|
|||||||
| Area | Access | Kind |
|
| Area | Access | Kind |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| Auth | `client.auth` | service |
|
| Auth | `client.auth` | service |
|
||||||
| Player | `client.player` (`player`, `identities`, `wallets`, `counters`) | `SyncedState<PlayerProfile>` |
|
| Player | `client.player` (`player`, `identities`, `balances`, `items`) | `SyncedState<PlayerProfile>` |
|
||||||
| Catalog currencies | `client.catalog` | `SyncedState<Map<string, CatalogCurrency>>` |
|
| Catalog currencies | `client.catalog` | `SyncedState<Map<string, CatalogCurrency>>` |
|
||||||
| Player storage | `client.storage` + `.save(type, data)` / `.delete(type)` | `SyncedState<GetStorageResponse>` + mutations |
|
| Player storage | `client.storage` + `.save(type, data)` / `.delete(type)` | `SyncedState<GetStorageResponse>` + mutations |
|
||||||
| Modules | `client.modules.call<T>(module, fn, args?, { moduleVersion? })` | resolves to `result` as `T` |
|
| Modules | `client.modules.call<T>(module, fn, args?, { moduleVersion? })` | resolves to `result` as `T` |
|
||||||
@@ -116,7 +116,7 @@ and commit it when installed modules change. Details:
|
|||||||
revision poll (`GET /sdk/v1/sync`, keys `profile`, `catalog`, `storage`) runs
|
revision poll (`GET /sdk/v1/sync`, keys `profile`, `catalog`, `storage`) runs
|
||||||
every 30 s (±20% jitter), pauses while the tab is hidden, and reloads the
|
every 30 s (±20% jitter), pauses while the tab is hidden, and reloads the
|
||||||
states whose revision grew. Storage mutations invalidate immediately. Call
|
states whose revision grew. Storage mutations invalidate immediately. Call
|
||||||
`player.reload()` after a module call that changes wallets or counters when
|
`player.reload()` after a module call that changes balances or items when
|
||||||
you need fresh values right away.
|
you need fresh values right away.
|
||||||
|
|
||||||
## Errors
|
## Errors
|
||||||
@@ -137,6 +137,6 @@ timeout a 504, both without details.
|
|||||||
## Reference files
|
## Reference files
|
||||||
|
|
||||||
- [reference/auth.md](reference/auth.md): login flows, options, token store, session lifecycle
|
- [reference/auth.md](reference/auth.md): login flows, options, token store, session lifecycle
|
||||||
- [reference/player.md](reference/player.md): profile, wallets, counters
|
- [reference/player.md](reference/player.md): profile, balances, items
|
||||||
- [reference/storage.md](reference/storage.md): player storage, limits
|
- [reference/storage.md](reference/storage.md): player storage, limits
|
||||||
- [reference/modules.md](reference/modules.md): generated clients, `modules.call`, idempotency, retries, errors
|
- [reference/modules.md](reference/modules.md): generated clients, `modules.call`, idempotency, retries, errors
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
# Player — `client.player`
|
# Player — `client.player`
|
||||||
|
|
||||||
`PlayerDomain` (source: `src/domains/PlayerDomain.ts`) — the observable player
|
`PlayerDomain` (source: `src/domains/PlayerDomain.ts`) — the observable player
|
||||||
profile: player, linked identities, currency wallets and
|
profile: player, linked identities, currency balances and inventory stacks.
|
||||||
number counters. Synced under revision key `profile`.
|
Synced under revision key `profile`.
|
||||||
|
|
||||||
There is no separate wallet service in this SDK. Wallet balances are read from
|
There is no separate ledger service in this SDK. Balances and items are read
|
||||||
the profile and are credited/debited server-side (admin wallet adjust, module
|
from the profile and are written server-side (admin adjust, module functions).
|
||||||
functions).
|
|
||||||
|
|
||||||
## Surface
|
## Surface
|
||||||
|
|
||||||
@@ -24,10 +23,10 @@ await client.player.reload(); // force refetch
|
|||||||
|
|
||||||
```ts
|
```ts
|
||||||
interface PlayerProfile {
|
interface PlayerProfile {
|
||||||
counters?: { [slug: string]: number }; // client-visible number counters
|
balances?: Balance[];
|
||||||
identities?: PlayerProfileIdentity[];
|
identities?: PlayerProfileIdentity[];
|
||||||
|
items?: InventoryStack[];
|
||||||
player?: Player;
|
player?: Player;
|
||||||
wallets?: Wallet[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PlayerProfileIdentity {
|
interface PlayerProfileIdentity {
|
||||||
@@ -45,20 +44,25 @@ interface Player {
|
|||||||
region?: string;
|
region?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Wallet {
|
interface Balance {
|
||||||
balance?: number;
|
balance?: number;
|
||||||
currency?: string; // currency code configured in the dashboard
|
currency?: string; // currency slug configured in the dashboard
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InventoryStack {
|
||||||
|
item?: string; // item slug
|
||||||
|
quantity?: number;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Behavior notes
|
## Behavior notes
|
||||||
|
|
||||||
- `counters` holds the values of `number` counters whose definition is
|
- `balances` carries one entry per currency in the latest release, zero-filled
|
||||||
`client_visible`; hidden counters are left out. Currency counters are in
|
for a player who never received it; `items` carries one entry per non-empty
|
||||||
`wallets`, not in `counters`. Counter changes bump the `profile` revision.
|
inventory stack. Both bump the `profile` revision when they change.
|
||||||
- Warmed automatically at login (with catalog).
|
- Warmed automatically at login (with catalog).
|
||||||
- Kept fresh by the revision poll; call `reload()` after a module function
|
- Kept fresh by the revision poll; call `reload()` after a module function
|
||||||
that changes wallets or counters.
|
that changes balances or items.
|
||||||
- All fields are optional on the wire (`?`); code defensively.
|
- All fields are optional on the wire (`?`); code defensively.
|
||||||
- React: `useSyncExternalStore` maps directly onto `onChange` (see the SDK
|
- React: `useSyncExternalStore` maps directly onto `onChange` (see the SDK
|
||||||
README "React recipe").
|
README "React recipe").
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { PlayerProfile } from '../generated/player.js';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* PlayerDomain — the observable player profile (player, identities,
|
* PlayerDomain — the observable player profile (player, identities,
|
||||||
* wallets, counters).
|
* balances, items).
|
||||||
* Synced under revision key `profile`.
|
* Synced under revision key `profile`.
|
||||||
*/
|
*/
|
||||||
export class PlayerDomain extends SyncedState<PlayerProfile> {
|
export class PlayerDomain extends SyncedState<PlayerProfile> {
|
||||||
|
|||||||
+12
-7
@@ -1,5 +1,15 @@
|
|||||||
// Code generated by apigen. DO NOT EDIT.
|
// Code generated by apigen. DO NOT EDIT.
|
||||||
|
|
||||||
|
export interface Balance {
|
||||||
|
"balance"?: number;
|
||||||
|
"currency"?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface InventoryStack {
|
||||||
|
"item"?: string;
|
||||||
|
"quantity"?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Player {
|
export interface Player {
|
||||||
"avatarUrl"?: string;
|
"avatarUrl"?: string;
|
||||||
"createdAt"?: string;
|
"createdAt"?: string;
|
||||||
@@ -11,10 +21,10 @@ export interface Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface PlayerProfile {
|
export interface PlayerProfile {
|
||||||
"counters"?: { [key: string]: number };
|
"balances"?: Balance[];
|
||||||
"identities"?: PlayerProfileIdentity[];
|
"identities"?: PlayerProfileIdentity[];
|
||||||
|
"items"?: InventoryStack[];
|
||||||
"player"?: Player;
|
"player"?: Player;
|
||||||
"wallets"?: Wallet[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PlayerProfileIdentity {
|
export interface PlayerProfileIdentity {
|
||||||
@@ -23,8 +33,3 @@ export interface PlayerProfileIdentity {
|
|||||||
"subject": string;
|
"subject": string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Wallet {
|
|
||||||
"balance"?: number;
|
|
||||||
"currency"?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -46,7 +46,7 @@ export type { StorageDomain } from './domains/StorageDomain.js';
|
|||||||
|
|
||||||
// Generated wire types — the shapes returned by the observable state and
|
// Generated wire types — the shapes returned by the observable state and
|
||||||
// handles, so consumers can name them instead of re-declaring mirrors.
|
// handles, so consumers can name them instead of re-declaring mirrors.
|
||||||
export type { Player, PlayerProfile, PlayerProfileIdentity, Wallet } from './generated/player.js';
|
export type { Balance, InventoryStack, Player, PlayerProfile, PlayerProfileIdentity } from './generated/player.js';
|
||||||
export type { CatalogCurrency } from './generated/catalog.js';
|
export type { CatalogCurrency } from './generated/catalog.js';
|
||||||
export type { StorageItem, GetStorageResponse } from './generated/storage.js';
|
export type { StorageItem, GetStorageResponse } from './generated/storage.js';
|
||||||
export type { ModuleErrorResponse } from './generated/modules.js';
|
export type { ModuleErrorResponse } from './generated/modules.js';
|
||||||
|
|||||||
Reference in New Issue
Block a user