Update item docs for per-item modules and registry
This commit is contained in:
@@ -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`.
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user