117 lines
4.4 KiB
Markdown
117 lines
4.4 KiB
Markdown
# InventoryApi
|
|
|
|
## Purpose
|
|
Owns item-instance state.
|
|
|
|
This service should answer:
|
|
- what item records currently belong to a character, location, or other container owner
|
|
- where each item currently is
|
|
- whether an item move, split, merge, equip, or consume action is valid
|
|
- which specific item instance is being traded, auctioned, or transferred
|
|
|
|
This service should not own:
|
|
- auth/token issuance
|
|
- character identity creation
|
|
- location generation
|
|
- auction bidding logic
|
|
|
|
## Ownership model
|
|
- every inventory record has an `ownerType` and `ownerId`
|
|
- `USER` can read and mutate items owned by their own characters
|
|
- `USER` can read and mutate location/container items only when gameplay rules allow it
|
|
- `SUPER` can read and mutate any inventory item
|
|
- access should be resolved through the current owner, not from the item id alone
|
|
|
|
## Initial design
|
|
Use one MongoDB document per inventory item record.
|
|
|
|
Practical interpretation:
|
|
- non-stackable items: one document per item instance
|
|
- stackable items: one document per stack
|
|
|
|
Reasons:
|
|
- every item or stack gets a stable UUID, which is useful for auctions, trade, mail, and auditing
|
|
- ownership transfer is explicit and cheap: update `ownerType`, `ownerId`, and slot fields
|
|
- future item metadata like durability, rarity rolls, or provenance can live on the item document
|
|
- auction listings can point to specific item ids instead of vague stack descriptions
|
|
|
|
Tradeoffs:
|
|
- inventory reads are queries over many documents instead of one aggregate read
|
|
- stack merging and slot enforcement need careful indexes and mutation logic
|
|
- transfers should use transactions when they touch multiple item documents
|
|
|
|
## Suggested endpoints
|
|
- `GET /api/inventory/by-owner/{ownerType}/{ownerId}`
|
|
Return all item records currently owned by that container owner.
|
|
- `POST /api/inventory/by-owner/{ownerType}/{ownerId}/grant`
|
|
Create a new item record or add quantity into an existing compatible stack.
|
|
- `POST /api/inventory/by-owner/{ownerType}/{ownerId}/move`
|
|
Move an item or stack within the same owner inventory.
|
|
- `POST /api/inventory/transfer`
|
|
Move quantity from one owner inventory to another.
|
|
- `POST /api/inventory/items/{itemId}/consume`
|
|
Consume quantity from a specific item record.
|
|
- `POST /api/inventory/items/{itemId}/equip`
|
|
Equip a specific item into a character equipment slot.
|
|
- `POST /api/inventory/items/{itemId}/unequip`
|
|
Return an equipped item to a character inventory slot.
|
|
|
|
Notes:
|
|
- `equip` and `unequip` only make sense for character-owned items
|
|
- `transfer` is the core world interaction primitive for looting, dropping, trading, chest interaction, and auction handoff
|
|
- a future AuctionApi can reserve or re-own specific item ids without redesigning InventoryApi
|
|
|
|
## Item identity
|
|
Every inventory record should have a stable UUID string such as:
|
|
- `a8d4218b-5e20-4e47-8b5f-0f0f0b9d7e10`
|
|
|
|
Each record also carries an `itemKey` such as:
|
|
- `wood`
|
|
- `stone`
|
|
- `pistol`
|
|
- `small_health_potion`
|
|
|
|
Recommended distinction:
|
|
- `id`: unique item-record identifier used for ownership changes, auctions, and references
|
|
- `itemKey`: item definition identifier used to decide stackability and gameplay behavior
|
|
|
|
## Recommended stored shape
|
|
Each item document should include:
|
|
- `id`
|
|
- `itemKey`
|
|
- `quantity`
|
|
- `ownerType`
|
|
- `ownerId`
|
|
- `slot`
|
|
- `equippedSlot`
|
|
- `ownerUserId` when applicable
|
|
- `createdUtc`
|
|
- `updatedUtc`
|
|
|
|
Optional future fields:
|
|
- `durability`
|
|
- `rarity`
|
|
- `instanceData`
|
|
- `listingId`
|
|
- `reservedUntilUtc`
|
|
|
|
## MVP rules
|
|
- an item record must belong to exactly one owner at a time
|
|
- stackable items may share `itemKey` but should still be represented by one stack record per occupied slot
|
|
- non-stackable items must always have `quantity = 1`
|
|
- equipped items should set `equippedSlot` and clear `slot`
|
|
- unequipped bag items should set `slot` and clear `equippedSlot`
|
|
- slot occupancy must be unique per `(ownerType, ownerId, slot)`
|
|
- all mutating endpoints should be idempotent where practical
|
|
|
|
## Client shape
|
|
The Godot client should fetch all items for the currently relevant owner and group them into a bag view locally.
|
|
|
|
Good pattern:
|
|
- login/select character
|
|
- `GET /api/inventory/by-owner/character/{characterId}`
|
|
- when opening a stash or world container: `GET /api/inventory/by-owner/location/{locationId}`
|
|
- cache the returned item records locally
|
|
- call transfer/equip/consume endpoints using specific `itemId` values
|
|
- replace local state with the server response after each mutation
|