Game services

The things a game keeps for its players: realms, inventories, currencies, items, cards and decks, XP, skill trees, unlocks and stats. You define them in the console; your game server grants them; your players' clients read them, buy with currency, build decks and unlock skill nodes. Five services cover them, each switched on for the project on its own:

Service What it covers
Realms Seasons, or any span of time that starts every player with a fresh inventory
Inventory Inventories, currencies and their ledger, items, and buying items with currency
Cards Card types and templates, the cards a player owns, decks, buying cards
Progression XP, skill trees and unlocks
Stats Counters per player and per environment

Like everything else, all of it is per environment: your test environment has its own definitions and its own players' things. Definitions are config, so you can promote them from test to production.

Who does what

Two rules keep a game fair when players can edit their own clients:

  • Only your server grants. Crediting and debiting currency, granting and taking away items and cards, adding XP, recording unlocks and moving stats need your server key. A player's token gets forbidden from every one of those routes, whatever it sends.
  • Prices are always the definitions'. A client names what it buys, never what it costs. The price is read from the definition when the purchase is made.

With a player's token, :playerId in a path is me, and reaches only that player's things. With your server key it is the player's id, and a player of another environment is not-found.

A player's client, with its token Your server, with its server key
Reads realms, inventories, balances and the ledger Everything a client does, for any of its players
Reads items, cards, decks, XP, trees and unlocks Credits, debits, consumes and refunds currency
Reads its stats and the environment's Grants and takes away items and cards
Buys items and cards with currency Adds XP, records and removes unlocks
Uses and opens its items Moves player and environment stats
Builds and edits its own decks Reads the definitions: currencies, items, cards, trees
Unlocks skill nodes it can pay for Unlocks nodes priced in a consumed currency (see below)

Realms

A realm is a span of time whose things are its own: a new realm starts every player with an empty inventory, and what they held in the last one stays there. Every environment has realm 0, permanent, so a game without seasons never thinks about realms: everything happens in realm 0.

On the Game data pages, Realms lists them. Each has a number, a key, a name, a start and optionally an end, and two switches: switched on (playable) and listed (shown). The current realm is the one that is switched on, listed, started and not ended, with the highest number: that is where anything held per realm goes unless a request names another. GET /realms/current answers it (not-found while no realm is open), and GET /realms every open one, newest first.

Inventories

A player has one account inventory for what outlives realms, and one inventory per realm they have played. Inventories are made the first time anything is put in them. A path names one as account, current (the current realm's) or its id, for example GET /players/me/inventories/account/balances. A request that puts something somewhere takes inventoryId, or realmId (null for the account), and otherwise uses the place the definition says: the account for what is held on the account, the current realm for what is held per realm.

Currencies

A currency is held on the account or per realm, and is one of two kinds:

  • Balance: earned and spent, never below zero, with an optional most a balance may hold.
  • Consumed: only what was spent is stored. What the player is allowed in all is your game's rule (their level, say), which your server sends as allowance when it spends. A spend succeeds only if what was spent plus this spend is within the allowance. Giving points back (a skill tree reset) is a refund.

Every movement is written with a line in the player's ledger, in the same transaction: the change, the balance after it, a reason (your machine key, such as match.settlement) and a reference (a match, an order). A wrong movement is put right by another, never by editing the ledger. GET /players/:playerId/ledger pages it newest first: pass the answer's next as cursor for the page after, as with every list.

Your server moves currency with POST /players/:playerId/currencies/:currencyKey/credit (and debit, consume, refund). Each needs an Idempotency-Key header naming that one movement, the same on every retry: a retry answers the first movement's receipt with replayed: true and moves nothing, so a server that timed out can always ask again. Derive the key from what the movement is for (match-8812:reward), never from a clock. The same key for a different movement is refused with idempotency-conflict.

Items

An item type is owned counted (a player holds a number of them) or one by one (each item its own, with its own data), held on the account or per realm, and may have a most one inventory holds. A type can be switched off, need an unlock before it can be used (requiresUnlock), be usable, and be for sale at a price list.

Your server grants with POST /players/:playerId/items/grant, and takes away an item with POST /players/:playerId/items/:itemId/consume, or some of a counted type with POST /players/:playerId/item-stacks/consume. Each needs an Idempotency-Key header, as currency does, naming that one grant or take-back: a retry answers the first answer and gives or takes nothing more. The same holds for counted cards (/card-stacks/grant, /card-stacks/consume) and XP (POST /players/:playerId/xp). A grantKey also makes a grant of one item happen once, whatever the header: granting again under the same key answers the first item with alreadyGranted: true. Derive either from what the grant is for (starter:sword, crate-8812:slot-2), never from a clock or a random number.

A player uses an item with POST /players/me/items/:itemId/use, or one of a counted type with POST /players/me/item-stacks/use, which consumes it. An item whose category is container is opened with POST /players/me/items/:itemId/open, once: a second open is refused with already-consumed. What a container holds is your game's, so opening only consumes it; your server then grants its contents under keys derived from the item's id (crate-8812:slot-2), so a retry never gives twice.

Prices and buying

An item type, a card type or a skill node carries a price list: an amount in each of one or more currencies, and a rule saying whether every price is paid (all) or any one of them (any; name it with payWith, or the first the player can pay is paid). A purchase charges the prices and grants what was bought in one transaction, so a player never pays without receiving.

POST /players/me/purchases/items and POST /players/me/purchases/cards take an optional Idempotency-Key: a retried purchase answers the first one's receipts with replayed: true and grants nothing again. A price in a consumed currency needs the allowance, which only your server sends, so a client cannot buy with one.

Cards and decks

Cards are their own model beside items. A card type may be for sale, be a starter (one your game gives every new player), and limit how many copies one deck holds. A player owns cards one by one (each with its own template and data) or by count (a number of a type), or both. A card template is a frame or style; owning one is the unlock card-template:<key>, which your server records.

A card has no place of its own, so granting, buying or building a deck names the inventory, the current realm's when nothing is named. A deck belongs to one inventory, and each line is an owned card or a card type with a count. A player's client makes and edits its own decks (POST /players/me/decks, and PUT /players/me/decks/:deckId/cards/:cardId or /types/:cardTypeKey); up to 50 decks a player and 200 lines a deck. GET /players/me/decks/:deckId/validation checks the general rules (the cards are the player's, each type within its limit, and a size cap if you pass maxSize) and lists every problem; what else makes a deck playable is your game's to check on your server.

Progression

  • XP is a number per player per track (account unless you name one). Your server adds it; what it means (a level, a rank) is your game's.
  • Skill trees are nodes, each priced, each unlocked once every node it depends on is. POST /players/me/skill-nodes/:nodeKey/unlock pays the price and records the unlock node:<key> together; unlocking a node already unlocked charges nothing. A node priced in a consumed currency is unlocked by your server, which sends the allowance. GET /players/me/skill-trees answers each tree with each node's price and whether it is unlocked or unlockable; a hidden tree is left out for a player's token.
  • Unlocks are keys a player holds: skill nodes, card templates, or your own (feature:crafting). A feature gated by an unlock asks one question: GET /players/me/unlocks?prefix=feature:.

Stats

64-bit counters for a player (/players/:playerId/stats) or the whole environment (/stats), in one realm or, without a realmId, across every realm. Your server moves one with POST .../stats/:key/increment and { "by": 1 }, which adds atomically, so increments made at the same moment all count. Values are answered as strings, since a 64-bit number does not fit a JSON number.

Your own data

A studio can keep its own fields on item types, card types and card templates (up to 4 KB each) and on an item or a card a player owns (up to 1 KB), as a JSON object called data. The platform keeps it and never reads it. Sizes are measured written compactly; a request over the limit is refused with invalid-request, naming body.data.

Deleting a player

Deleting a player (DELETE /players/:playerId) deletes their game state with them: inventories, balances and their ledger, items, cards, decks, XP, unlocks and stats. The environment's own stats stay.

Selling currency

With the Inventory service on, a Commerce product whose grant is a currency this environment defines credits the player's balance directly when it is bought, and a refund takes back what the player still holds of it. You do not need to collect those grants.

In the console

  • Game data, in each environment's sidebar: Realms, Currencies, Item types, Card types, Card templates and Skill trees, each added, edited and deleted in place. A key cannot be changed once made: what players hold names its definition by key. Changing definitions needs the Config permission.
  • Players: a player's page shows their inventories with balances, items and cards, their ledger, decks, XP, unlocks and stats, for anybody who may view players. Somebody with Manage players can adjust a balance: the change is a ledger line with the reason staff.adjustment and the note they give, which is required. Nothing is ever overwritten.

Refusals

A refusal is the platform's, as everywhere: { "error", "message", "details"? }, where details carries the figures behind it (what is held, what was asked, the limit). Besides the codes every route can answer (see the API reference):

error Status When
not-found 404 No such definition, item, card, deck or inventory, or not this player's
invalid-request 400 A request that cannot be meant, such as a consumed currency spent without allowance; or data too big
insufficient-balance 409 Not enough of a currency, or of a counted item or card
allowance-exceeded 409 A consumed currency's spend would pass the allowance
balance-cap 409 A credit would pass the most the currency allows
idempotency-conflict 409 The Idempotency-Key already names a different movement, grant or purchase
disabled 409 The definition is switched off
not-purchasable 409 Not for sale
owned-limit 409 The inventory holds as many as allowed, or the player as many decks
locked 409 It needs an unlock, or skill nodes, the player does not have
not-usable 409 It cannot be used, or does not open
already-consumed 409 Already used, opened or taken away
no-current-realm 409 Something held per realm, and no realm is open

Every route, with its fields, is in the API reference, under Game services.