Skip to main content

The player's hub

One hub per player, keyed by their player id: what they own and what they have done. Their inventory (stacks in slots, and mail for what did not fit) and soft currencies; crafting from the game's recipes; loot tables with pity; idle production that catches up on the time away; experience and levels; achievements; and quests, first-session tutorials among them. It is not a ledger.

Only the player reads their inventory, wallet and progress, with calls the hub answers for them alone, and reads them again when the hub's changed message names them. The profile (level, achievements, quests done) is public. What a player did reaches the hub as counters (crafted, collected, won): from the hub that decided it (record, grant), or, for the counters the config lets a client report (a tutorial's steps), from the player's own client (step).

Starter: crate player, node type player, keyed by the player's id. Modules: kit.player and its views kit.inventory, kit.wallet, kit.progression, kit.achievements, kit.quests, kit.crafting, kit.loot, kit.production in CrowdyJS and CrowdyPy; player() and the same views in CrowdyCPP. The kit needs the player's id: pass playerId (player_id) to the kit, or let it ask who the player is.

Endpoints​

EndpointWhoWhat
me, inventory, wallet, progressthe player (or a developer)What only they read: forbidden for anyone else
profile, rulesanyoneThe public profile; the game's items, recipes, loot tables, quests and achievements
craft { recipe, times }the playerTakes the inputs and gives the outputs, together or not at all: insufficient, full, forbidden (level), not_found
open { table }the playerPays the table's cost and rolls once, with pity: insufficient, full, empty
collectthe playerMoves what the producers made into the inventory, as far as it fits
accept { quest }, claim { quest }the playerconflict (taken, done, or another first), forbidden (level); not_ready before it is complete
step { counter }the playerA counter the config lets a client report: forbidden for any other
discard { item, count }, take_mailthe playerinsufficient; moves the mail in as far as it fits
grant { ref, bundle, xp, counters }, record { ref, counter, n }the app's hubs, and developersRewards and counts, from the hub that decided them; each ref is answered once
deposit { ref, bundle }the app's hubs, and developersGives a bundle; items that do not fit go to the mail
hold, settle, release, exchange { ref, .. }the app's hubsThe steps of a transfer a market drives; each ref is answered once

Topics​

player: snapshots of the public profile, event messages (type: level, achievement), and changed messages naming the player (what: inventory, wallet, progress).

Config​

A type's seed (seed_b64 in the manifest, the same for every key) is the starter's JSON config; what it leaves out keeps its default. slots (30), the items catalog (each id, max_stack, tradable), currencies, what a player starts with, the levels curve (base, growth, max), achievements and quests (with tutorial: true for those taken on as the player starts), the client_counters a client may step, recipes, loot tables (each with its cost, entries and pity_within), production (producers and capacity), and a secret mixed into the loot seed (never answered by rules).

CrowdyJS​

const kit = game.kit(appId, { playerId });
kit.inventory.items.subscribe((inv) => renderBag(inv));
kit.player.onEvent((e) => e.type === 'level' && celebrate(e.level));
await kit.player.watch(); // the profile, and inventory, wallet and progress
await kit.quests.step('tutorial:inventory');
const { made } = await kit.crafting.craft('plank', 2);
const { roll } = await kit.loot.open('chest');
await kit.production.collect();

CrowdyCPP​

crowdy::kit::GameKitOptions options;
options.runtime.playerId = playerId;
auto kit = crowdy::kit::makeKit(game, appId, nullptr, options);
kit->player().watch([&](crowdy::kit::KitReply) { renderBag(kit->inventory().items().get()); });
kit->crafting().craft("plank", 2, [](crowdy::kit::KitReply r) {
if (r.code() == "insufficient") showMissing();
});
game.poll(); // replies, topic messages and mirror changes arrive here

CrowdyPy​

kit = game.kit(app_id, player_id=player_id)
await kit.player.watch()
await kit.quests.step("tutorial:inventory")
crafted = await kit.crafting.craft("plank", 2)
print(kit.wallet.balance("gold"), kit.inventory.count("plank"))