Update item docs for per-item modules and registry

This commit is contained in:
Jage9
2026-02-21 22:04:17 -05:00
parent 0d0b36e680
commit 97caaef001
3 changed files with 74 additions and 79 deletions

View File

@@ -49,7 +49,7 @@
- Persisted state stores only instance data. - Persisted state stores only instance data.
- Global/type-level properties are loaded from server registry in `server/app/item_catalog.py`. - Global/type-level properties are loaded from server registry in `server/app/item_catalog.py`.
- Per-type use/update validation and message behavior are handled in `server/app/item_type_handlers.py`. - Per-type use/update validation and message behavior are implemented in per-item modules under `server/app/items/` and wired in `server/app/items/registry.py`.
- Client-side add/edit metadata is handled in `client/src/items/itemRegistry.ts`. - Client-side add/edit metadata is handled in `client/src/items/itemRegistry.ts`.
- End-to-end add-item template: `docs/item-type-template.md`. - End-to-end add-item template: `docs/item-type-template.md`.

View File

@@ -1,58 +1,70 @@
# Item Type Template # Item Type Template
This page is a practical, copy/paste template for adding a new item type with the current registry-based system. This page is a practical template for adding a new item type with the current per-item module + single registry system.
Use this when you want a new item type without editing one huge `if/elif` chain.
## Plain-English Flow ## Plain-English Flow
When a new item type is added, you wire it in four places: When a new item type is added, wire it in these places:
1. Server catalog (`item_catalog.py`) 1. Server item module (`server/app/items/<name>.py`)
- Defines global defaults shared by all instances of that type: - Define item metadata constants:
- default title - label/tooltip
- default params - editable properties
- use sound / emit sound - defaults/capabilities/sounds/cooldown/range/directional
- cooldown - property metadata
- Implement behavior:
- `validate_update(item, next_params)`
- `use_item(item, nickname, clock_formatter)`
2. Server behavior (`item_type_handlers.py`) 2. Server registry (`server/app/items/registry.py`)
- Defines what happens when users edit params (`validate_update`). - Add one module entry in `ITEM_MODULES`.
- Defines what happens when users press `use` (`use`). - Update `ITEM_TYPE_ORDER` if needed.
3. Shared type unions (`models.py`, protocol/state types) 3. Shared item type unions
- Adds the new type name to type literals/unions so packets validate. - Add the type in:
- `server/app/models.py`
- `client/src/network/protocol.ts`
- `client/src/state/gameState.ts`
4. Client item registry (`itemRegistry.ts`) 4. Client fallback metadata
- Defines add-menu order, editable properties, and optional property dropdown choices. - Add defaults in `client/src/items/itemRegistry.ts`:
- `DEFAULT_ITEM_TYPE_SEQUENCE`
- `DEFAULT_ITEM_TYPE_EDITABLE_PROPERTIES`
- `DEFAULT_ITEM_TYPE_GLOBAL_PROPERTIES`
That is enough for a first working item type. That is enough for a first working item type.
## Example: `counter` ## Minimal Server Module Example: `counter`
This sample item increments a number each time it is used. `server/app/items/counter.py`:
### 1. Server Catalog (`server/app/item_catalog.py`)
```py ```py
ItemType = Literal["radio_station", "dice", "wheel", "clock", "counter"] from __future__ import annotations
ITEM_DEFINITIONS: dict[ItemType, ItemDefinition] = { from typing import Callable
# ...existing...
"counter": ItemDefinition( from ..item_types import ItemUseResult
default_title="counter", from ..models import WorldItem
capabilities=("editable", "carryable", "deletable", "usable"),
use_sound=None, LABEL = "counter"
emit_sound=None, TOOLTIP = "Counts up each time you use it."
default_params={"value": 0}, EDITABLE_PROPERTIES: tuple[str, ...] = ("title", "value")
use_cooldown_ms=1000, CAPABILITIES: tuple[str, ...] = ("editable", "carryable", "deletable", "usable")
), USE_SOUND: str | None = None
EMIT_SOUND: str | None = None
USE_COOLDOWN_MS = 1000
EMIT_RANGE = 15
DIRECTIONAL = False
DEFAULT_TITLE = "counter"
DEFAULT_PARAMS: dict = {"value": 0}
PROPERTY_METADATA: dict[str, dict[str, object]] = {
"title": {"valueType": "text", "tooltip": "Display name spoken and shown for this item."},
"value": {"valueType": "number", "tooltip": "Current counter value.", "range": {"min": 0, "max": 9999, "step": 1}},
} }
```
### 2. Server Handler (`server/app/item_type_handlers.py`)
```py def validate_update(_item: WorldItem, next_params: dict) -> dict:
def _validate_counter_update(_item: WorldItem, next_params: dict) -> dict:
try: try:
value = int(next_params.get("value", 0)) value = int(next_params.get("value", 0))
except (TypeError, ValueError) as exc: except (TypeError, ValueError) as exc:
@@ -63,53 +75,31 @@ def _validate_counter_update(_item: WorldItem, next_params: dict) -> dict:
return next_params return next_params
def _use_counter(item: WorldItem, nickname: str, _clock_formatter: Callable[[dict], str]) -> ItemUseResult: def use_item(item: WorldItem, nickname: str, _clock_formatter: Callable[[dict], str]) -> ItemUseResult:
current = int(item.params.get("value", 0)) next_value = int(item.params.get("value", 0)) + 1
next_value = current + 1
return ItemUseResult( return ItemUseResult(
self_message=f"{item.title}: {next_value}", self_message=f"{item.title}: {next_value}",
others_message=f"{nickname} uses {item.title}: {next_value}", others_message=f"{nickname} uses {item.title}: {next_value}",
updated_params={**item.params, "value": next_value}, updated_params={**item.params, "value": next_value},
) )
```
Then register it in `server/app/items/registry.py`:
ITEM_TYPE_HANDLERS: dict[ItemType, ItemTypeHandler] = { ```py
# ...existing... from . import clock, counter, dice, radio, wheel
"counter": ItemTypeHandler(
validate_update=_validate_counter_update, ITEM_TYPE_ORDER: tuple[str, ...] = ("clock", "counter", "dice", "radio_station", "wheel")
use=_use_counter,
), ITEM_MODULES: dict[str, ItemModule] = {
"clock": clock,
"counter": counter,
"dice": dice,
"radio_station": radio,
"wheel": wheel,
} }
``` ```
### 3. Type Unions
Update item-type unions/literals in:
- `server/app/models.py`
- `client/src/network/protocol.ts`
- `client/src/state/gameState.ts`
Add `"counter"` anywhere item types are enumerated.
### 4. Client Registry (`client/src/items/itemRegistry.ts`)
```ts
export const ITEM_TYPE_SEQUENCE: ItemType[] = ['clock', 'counter', 'dice', 'radio_station', 'wheel'];
const ITEM_TYPE_EDITABLE_PROPERTIES: Record<ItemType, string[]> = {
// ...existing...
counter: ['title', 'value'],
};
export const ITEM_TYPE_GLOBAL_PROPERTIES: Record<ItemType, Record<string, string | number | boolean>> = {
// ...existing...
counter: { useSound: 'none', emitSound: 'none', useCooldownMs: 1000 },
};
```
No dropdown options are needed here because `value` is a numeric text field.
## Checklist Before Commit ## Checklist Before Commit
1. Add/adjust server tests for `use` and `update` validation. 1. Add/adjust server tests for `use` and `update` validation.

View File

@@ -112,15 +112,20 @@ This is behavior-focused documentation for item types and their defaults.
## Adding A New Item Type (Registry V1) ## Adding A New Item Type (Registry V1)
Item types are currently code-registered on both server and client so new types are additive instead of editing one large branch. Item types are currently code-registered on both server and client. Server item logic is split per item module and wired through one registry.
For a full copy/paste example with plain-English explanation, see `docs/item-type-template.md`. For a full copy/paste example with plain-English explanation, see `docs/item-type-template.md`.
1. Server catalog: add global defaults in `server/app/item_catalog.py` (`ITEM_DEFINITIONS`). 1. Server item module: add a new file under `server/app/items/` with:
2. Server handlers: add `validate_update` + `use` logic in `server/app/item_type_handlers.py` and register it in `ITEM_TYPE_HANDLERS`. - defaults/capabilities
- property metadata/options
- `validate_update` and `use_item`
2. Server registry: add one entry in `server/app/items/registry.py`:
- `ITEM_MODULES`
- `ITEM_TYPE_ORDER` (if ordering changes)
3. Server models: extend `ItemType` literals in `server/app/models.py` and any packet enums that list item types. 3. Server models: extend `ItemType` literals in `server/app/models.py` and any packet enums that list item types.
4. Client registry: add type metadata in `client/src/items/itemRegistry.ts` (`ITEM_TYPE_SEQUENCE`, editable properties, and global property hints). 4. Client fallback registry: add type defaults in `client/src/items/itemRegistry.ts` (`DEFAULT_ITEM_TYPE_SEQUENCE`, editable/global fallback metadata).
5. Client protocol types: update item-type unions in `client/src/network/protocol.ts` and `client/src/state/gameState.ts`. 5. Client protocol/state types: update item-type unions in `client/src/network/protocol.ts` and `client/src/state/gameState.ts`.
6. Tests: add or update server tests under `server/tests/` for use/update validation and cooldown behavior. 6. Tests: add or update server tests under `server/tests/` for use/update validation and cooldown behavior.
### Example Shape ### Example Shape