Wallet & Inventory
Rudder tracks two kinds of player-owned goods:
- Wallet — part of the kernel. One integer balance per currency; currencies are
currency-kind counters. - Inventory — provided by the first-party
inventorymodule. One row per player and item in the player resourceinventory(itemSlug,amount); items come from theitemsmodule.
Both are server-authoritative. Game clients only read them; every change happens in a module function or an admin operation inside a transaction, so a hacked client cannot mint currency or items.
Set up currencies and items
Section titled “Set up currencies and items”In the dashboard at https://app.rudder.build/, open your project:
- Counters — create a
currencycounter for each currency (e.g.coins,gems) and ship it in a release. Wallets list only currencies from the environment’s latest release. - Modules — install
inventory(it bringsitems), then create items in Items. Item rows are live as soon as you save them.
Read wallet balances
Section titled “Read wallet balances”Balances are part of the player profile. Every released currency-kind counter appears in the response — a player who never received that currency gets a virtual 0 balance, so you don’t need to special-case missing wallets.
// client.player is a state object, loaded at login and kept fresh by syncconst profile = await client.player.load();
for (const wallet of profile.wallets ?? []) { console.log(wallet.currency, wallet.balance);}
// Or subscribe to updates (revision sync or reload() refresh it)client.player.onChange(({ value }) => { const coins = value?.wallets?.find((w) => w.currency === 'coins'); renderBalance(coins?.balance ?? 0);});var profile = await client.Player.LoadAsync();
foreach (var wallet in profile.Wallets ?? new()){ Console.WriteLine($"{wallet.Currency}: {wallet.Balance}");}
client.Player.Changed += p => RenderWallets(p.Wallets);Number-kind counters never appear in the wallet; visible ones are in the profile’s counters map. See Counters.
Read the inventory
Section titled “Read the inventory”Inventory rows are module data in the player resource inventory. Game clients read them with the inventory.list client function through a generated module client: await modules(client).inventory.list({}) returns {items: [{itemSlug, amount, item}]}, where item carries the display data from items (properties is JSON text) and is null for an unknown slug. Rewards returned by quests.claim and offers returned by store.list already carry item slugs and amounts.
How players receive currency and items
Section titled “How players receive currency and items”There is no client-side grant call. Modules pay out through the reward module, which applies currencies, items and counters in one call:
- Store —
store.buydebits the offer’s price and grants its reward in one transaction. - Quests —
quests.claimgrants a completed quest’s reward. - Battle pass —
battlepass.claimgrants a tier’s reward. - Leaderboards — the scheduled reset grants rank rewards.
Your own modules can call reward.grant (declare reward in deps) or use the kernel wallet directly. See Functions runtime.
Adjust as an admin
Section titled “Adjust as an admin”Open Players → player detail in the dashboard. The wallet tab shows balances and transaction history; Adjust applies a signed delta with a mandatory reason. The inventory module adds its rows and Grant item / Remove item actions to the same page.
From a backend, call the /game/v1 HTTP API with the environment’s admin key (X-API-Key): POST /game/v1/players/wallet/adjust (a batch with an optional idempotency key) and GET /game/v1/players/{playerId}/wallet/history for the wallet, /game/v1/resources/{slug}/rows for module data, and POST /game/v1/modules/{module}/{fn} for admin functions such as inventory.adminGrant.
Edge cases and limits
Section titled “Edge cases and limits”- Balances never go negative. A debit that would drop a balance below zero fails with
insufficient_fundsand changes nothing. - Crediting auto-creates the wallet — the first grant of a currency works without any setup call.
- Every wallet change is audited. The transaction history (amount, balance before/after, reason, source) is visible in the dashboard player view and through
GET /game/v1/players/{playerId}/wallet/history. - Currencies come from releases. A player’s wallet list is filled with a zero balance for every currency in the latest release.
- Client state is a cache. The SDKs refresh the profile through revision sync; call
client.player.reload()/client.Player.ReloadAsync()after a module call that changes balances. The server is the source of truth.