Generated reference. This page mirrors
Code/World/README.md, this module's source-of-truth documentation (Standards/02_DOCUMENTATION_STANDARDS.md §1). Edit it there, not here -- this file is regenerated on every docs build and any local edit is silently overwritten.
World¶
Purpose. The ownable world entity types: a door, and a lockable container. Ties
Ownership (who owns it), Economy (what a door costs), and Inventory (what a container
holds) together. The door-purchase flow was
ROADMAP.md Milestone 6's
named build item, traced end to end in
07_NETWORKING.md §7
and Legacy/13_DEPENDENCY_GRAPH.md §3.
Locks, seals, and a second container type are
ROADMAP.md Milestone 7's
"rest of World", built per
20_WORLD_LOCKS_AND_CONTAINERS.md.
Responsibilities.
- Door — OwnableId Id, DisplayName, IsLocked, IsSealed, a computed
IsEffectivelyLocked, and (ADR-0013) GroupId : OwnableId?; see
08_DATA_MODEL.md §6 for why its
id is its Ownership.EntityId, not a separate id space,
20_WORLD_LOCKS_AND_CONTAINERS.md §1
for why sealing doesn't also set IsLocked, and
29_HOUSING.md for GroupId.
- DoorConfiguration — the operator-tunable purchase price (PurchasePrice, default 150,
legacy Door Cost; Legacy/07_ECONOMY.md §4)
and interaction range (InteractionRange, default 150 — no legacy precedent, a new value;
see that property's own doc comment), following
05_CONFIGURATION.md's per-module
GameResource pattern.
- IPropertyService / PropertyStore — registers a placed door's persisted record
(RegisterAsync, load-or-create, called from DoorComponent.OnAwake), PurchaseAsync:
the atomic (by ordering, not by a real transaction — see its own doc comment) validate,
assign-ownership, debit-money, publish-event sequence, SetLockedAsync/SetSealedAsync
(owner/access-gated and "world.seal"-gated respectively), and (ADR-0013) SetGroupAsync —
links a door into a property group's GroupId, called only by Housing's purchase flow.
SetLockedAsync and PurchaseAsync's already-owned check both resolve ownership via
door.GroupId ?? door.Id, deferring to the group's single Ownership record once a door is
grouped — see 29_HOUSING.md.
- DoorComponent — the placed-in-map entity: [Property] DoorId (generated once at spawn,
following WorldContainerComponent.ContainerId's precedent), [Sync(FromHost)] IsOwned /
OwnerName / IsLocked / IsSealed, and [Rpc.Host] RequestPurchaseDoor() /
RequestLockDoor(bool) / RequestSealDoor(bool) — this project's first gameplay RPCs
(Character's RPC was account creation). All three now carry a real distance check
(CallerWithinRange, DoorConfiguration.InteractionRange against
IPawnLocator.PositionOf) — Milestone 11, see
ADR-0007.
- WorldContainer / IWorldContainerService / WorldContainerStore — the same
ownable-and-lockable shape as Door, minus a purchase price (containers are never bought),
and (ADR-0013) the same GroupId : OwnableId? / SetGroupAsync shape as Door.
- LockableContainerComponent — a second IInventoryHolder (alongside Inventory's
WorldContainerComponent), ownable and lockable, following WorldContainerComponent's
OnAwake/load shape plus DoorComponent's Lock/Seal RPCs.
- WorldModule — registers IPropertyService, IWorldContainerService, and the
DoorConfiguration schema.
Not responsible for.
- Who owns a door or container, or access resolution — Ownership's job; PropertyStore/
WorldContainerStore call IOwnershipService, they do not duplicate its logic.
- Moving money — Economy's job; PropertyStore calls IEconomyService.CanAfford/
DebitAsync, it never touches a balance directly.
- A container's contents — Inventory's job; LockableContainerComponent calls
IInventoryService, the same way WorldContainerComponent already does.
- Assigning ownership to a container. Unlike a door, there is no purchase flow, and no
/setowner-equivalent — it would need target-resolution-by-name, the same blocked gap
behind Crime's /warrant and Chat's /pm. A container's ownership claim starts, and
stays, unowned this pass, so its lock/seal RPCs are real and tested but currently
unreachable by any player. See
20_WORLD_LOCKS_AND_CONTAINERS.md §5.
- Padlocks as physical entities, breach/jam timing, autoclose, door tax and 50% resale — all a
larger scope than this pass, not blocked by a missing capability; see
20_WORLD_LOCKS_AND_CONTAINERS.md §2.
Master/slave door linking, named there too, is now built as property groups — see the
Housing module and 29_HOUSING.md; World
contributes only GroupId/SetGroupAsync to that, per §3 above.
- /seal as a global chat command — sealing targets a specific door/container the player
already holds a client-side reference to; see
20_WORLD_LOCKS_AND_CONTAINERS.md §3.
- A distance check on LockableContainerComponent's Lock/Seal RPCs — Milestone 11 landed the
binding and applied it to DoorComponent only; containers are unowned and unreachable this
pass regardless (see "Assigning ownership to a container" above), so there was no live path
to exercise it against yet. Same one-line addition once a container is ever reachable.
Dependencies. Core (IServiceRegistry, IServiceResolver, IPersistenceBackend,
ISchemaRegistry, IEventBus, IGameLog), Character (Character, ConnectionExtensions.GetCharacter()),
Economy (IEconomyService), Ownership (IOwnershipService, OwnableId, OwnerRef),
Inventory (IInventoryHolder, IInventory, IInventoryService, for
LockableContainerComponent), Movement (IPawnLocator, for DoorComponent's distance check),
the S&box engine (Component, GameResource, Rpc, Sync).
Public API.
- IPropertyService — RegisterAsync(OwnableId, string displayName), Find(OwnableId),
PurchaseAsync(Character buyer, Door door), SetLockedAsync(Character, Door, bool),
SetSealedAsync(Character, Door, bool), SetGroupAsync(Door, OwnableId groupId).
- IWorldContainerService — RegisterAsync(OwnableId, string displayName),
Find(OwnableId), SetLockedAsync(Character, WorldContainer, bool),
SetSealedAsync(Character, WorldContainer, bool), SetGroupAsync(WorldContainer, OwnableId groupId).
- DoorComponent, LockableContainerComponent.
Events published.
- DoorPurchasing — cancellable, published by PropertyStore once eligibility (unowned,
affordable) is confirmed but before ownership is assigned or money moves. A subscriber may
veto the purchase; see that type's doc comment. The worked example for extension point 2 in
03_EXTENDING_THE_FRAMEWORK.md §3.
- DoorPurchased(Door, Character) — published by PropertyStore once ownership has been
assigned (see that type's doc comment for exactly when, relative to the debit, and why).
Persistence. DoorDocument { Id, DisplayName, IsLocked, IsSealed, GroupId } under
doors/{id}, schema version 2 (ADR-0013 added GroupId; version 1 → 2 is an identity
migration, DoorDocumentV1ToV2Migration). WorldContainerDocument { Id, DisplayName, IsLocked,
IsSealed, GroupId } under containers/{id}, schema version 2, same shape
(WorldContainerDocumentV1ToV2Migration). Ownership is a separate document under
ownership/{entityId}, owned by the Ownership module — a property group's Ownership is no
different, keyed by its own OwnableId, see
29_HOUSING.md; a container's contents are a
separate document under inventories/{id}, owned by the Inventory module — this module never
writes either collection directly.
Future improvements.
- Padlocks, master/slave linking, breach/jam, autoclose, door tax and repossession — see
20_WORLD_LOCKS_AND_CONTAINERS.md §2.
Master/slave linking specifically is now built, as property groups — see
29_HOUSING.md and the Housing module.
- Extending the real distance check to LockableContainerComponent, once a container is ever
reachable — see "Not responsible for" above.
- Selling a door back (legacy: 50% refund) — not built this pass; nothing exercises it yet.
- An ownership-assignment flow for containers, once target-resolution-by-name exists
anywhere in this codebase.