Skip to content

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. - DoorOwnableId 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 moneyEconomy's job; PropertyStore calls IEconomyService.CanAfford/ DebitAsync, it never touches a balance directly. - A container's contentsInventory'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. - IPropertyServiceRegisterAsync(OwnableId, string displayName), Find(OwnableId), PurchaseAsync(Character buyer, Door door), SetLockedAsync(Character, Door, bool), SetSealedAsync(Character, Door, bool), SetGroupAsync(Door, OwnableId groupId). - IWorldContainerServiceRegisterAsync(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.