AbstractConstructor - calls parent Stuff constructor.
ReadonlystuffRuntime ID for this object (generated using nanoid). This is NOT the MongoDB _id - it's a runtime identifier.
StaticsubscribableUniversal live-query subscribable fields — fields every Stuff
exposes regardless of mixin composition. Currently just
displayName, a derived render that delegates to
Stuff.getPresentation (Named's name or Visible's
shortDescription, falling through to the baked-in 'something').
Declared here rather than in a substrate-private synthetic
table because every Stuff genuinely owns the concept — there is
no "what if this Stuff has no displayable identity?" case. The
descriptor uses dependsOnFields to declare the leaf source
fields it depends on (name, shortDescription); the substrate
installs precise (FieldChangedEvent, 'field', dep) index
entries automatically. Shadow lifecycle support rides on
ShadowChangedEvent in changes (declared-but-unfired until
the shadow subsystem wires it).
Mixin layers above Stuff add their own subscribableFields for
mixin-owned state; the substrate's prototype-chain walk
hasOwnProperty-checks at every level and unions the
descriptors.
Future universal renders (pronoun, articleName, etc.) land
here too. Mixin-gated renders go on the mixin that owns the
gate.
Register m as a member. Idempotent. The SOLE writer of the
Pattern-B back-ref (alongside removeMember): when m composes
WarrenMemberMixin, stamps m.setWarren(this). A plain-Location
member just lives in the set.
Single-warren guard (Q2b): a member already owned by a DIFFERENT Warren is rejected — it stays where it is and we warn. The declared seed path can initiate a membership but never re-home an owned one; the owner always wins.
Returns true when m is a member of this Warren after the call.
AbstractadmitSeat an arrival when the host is over capacity — re-route it to an
eligible satellite or bud a new one. Called by the member-side
over-capacity witness. The sync prefix (before the first await)
should perform the move when an eligible satellite already exists,
so the re-seat completes within the triggering move (single sense).
Protected AbstractattachmentConsent seam for the residency (self-eviction) sweep. The sweep
asks each idle object whether it may be culled; the object decides,
reading its own knowledge. Default is cull ({ ok: true }) — a
fresh backing class is reclaimable by default and only becomes
sticky when its author deliberately vetoes, the correct bias for a
leak-plugger. Return { ok: false, reason } to veto.
Vetoes layer on the mixin/class that owns the relevant
relationship, composed via super.canEvict(context) — base Stuff
stays permissive and does not reach into Container/Shadow/Avatar/
Exit knowledge. The relational vetoes derive from the R2.x
ref-cleanup rules: an object in an owned/symmetric live-ref
relationship vetoes while its anchor is alive (see
docs/subsystems/residency.md).
Distinct from canDestruct: an object that permits eviction can
still canDestruct-veto, so the sweep's enforce path tolerates a
DestructError (logs + continues). The sweep calls this on the
raw target (via RAW_TARGET) so asking never counts as a touch.
Protected AbstractcreateClone one fresh member instance (the room template).
ProtectedcreateCreate one member through the per-Warren serialization chain so two concurrent creations never clone the same template path at once.
ProtecteddesignateMake m the host and wire its host-only fixtures. No exit changes
to other members (a fresh host has no satellites yet). Re-seats any
registered self-seating fixtures last — a no-op on the first-ever
designation (none registered yet), but it revives fixtures that died
with a previously lost host when the graph stands up fresh.
Destroy this object.
Locked down by @CallSecurity(ApiOnly) — only callers under
mud/api/ (in practice, StuffApi.destruct) may invoke it.
@Unshadowable because the unregistration path must always run;
a shadow that intercepts and skips it would leak the object into
the registry forever. @Final because subclass overrides would
defeat the same invariant — the loader hook throws
FinalViolationError at import time on any subclass redefinition.
Subclass cleanup belongs on the optional onDestruct() witness
(consulted by StuffApi.destruct while the target is still live);
refusal logic belongs on canDestruct(). This terminal destroy()
is the unshadowable mark-and-unregister step only.
ProtectedgetThe current host member (or null when empty / lost), pruned. For
subclass policy (reconcile) that must skip the host. Does NOT
create one — use getHost() for the create-if-absent kernel.
The reusable placement kernel. Returns the current host, creating
the graph's first instance (which becomes host) when empty, or
migrating the role to a survivor when the prior host was force-
destroyed. Both avatar sign-in (startLocation) and future
item-spawn share this.
Read the recency timestamp. Read by the residency sweep — which
calls it on the raw target (via RAW_TARGET) so the sweep's own
introspection never counts as a touch.
Live members, R2.3-pruned. A destructed member is dropped from the set on read (defends against any destruct path that bypassed the R2.4 cleanup).
Self-presentation — the casual-register render string for this object, the answer to "what does this Stuff call itself?" Three- step resolution:
Named.name if present and non-empty — the object's
proper name ("Alice", "Excalibur", "Town Square").Visible.shortDescription if present and non-empty —
the object's visual identity ("a heavy oak door").For a Globbable stack (quantity !== 1) the count folds in as
an affix — "30 coins" — pluralized via GrammarApi.pluralize
(which honors host-side getPluralForm() overrides for
irregulars). Named takes precedence over Visible so a
Named-with-description renders by its proper name; code that
needs the formal register calls getFullName() when typed as
Named.
Viewer-blind by design. This is the shared baseline every
Stuff exposes; the viewer-aware naming step (recognition /
identification — see
docs/subsystems/belief.md) composes on top
of it. Left shadowable (NOT @Final) so masking / disguise
effects can override the rendered identity via a method shadow.
Build the composable Mml fragment for this object's display name —
the Mml sibling of getPresentation. Mml.ref (and so every
<item> / <name> / … identity tag) renders this, not a raw
string, so a name joins the compose chain as a fragment like
everything else. The label is the already-resolved, viewer-aware
name (recognition runs in the render layer and hands it in).
Return null for the plain default — Mml.ref then wraps the label
in Mml.text, which escapes it exactly once, so player-authored
names / status decoration are safe by construction and the fragment
is never re-escaped downstream. Override to build a richer fragment
(a TPA terminal wraps its name in <color> to tint by state). The
plain-string getPresentation stays the surface for non-prose
consumers (logs, context.note, MQL scalars).
Read seam. Instance method, but unwraps via ProxyApi.unwrap
before reaching the # slot — this inside an instance method
called through the proxy is the proxy, and the # slot lives
on the raw target.
Get the spatial zone. Unwraps via RAW_TARGET because the
# slot lives on the raw target only and this inside an
instance method called through the proxy is the proxy.
Membership predicate.
Sync host check (no clone). True iff room is the current host.
Check if this object has been destroyed.
@Unshadowable: the destroyed-state read is a framework invariant —
any shadow that lied about it would let consumers touch a torn-down
Stuff. @Final: subclasses overriding this would defeat the same
invariant; the loader hook throws FinalViolationError at import
time on any subclass that redefines it.
ProtectedmigrateMigrate the host role to newHost after the prior host was force-
destroyed. The dead host's outbound hub exits were destructed by its
own Exitable.onDestruct, but the satellite-side inverses were left
BLOCKED (the asymmetry) — so for each survivor we explicitly remove
and destruct the dead exit toward the old host, then re-point every
other survivor's hub exit at newHost, swap the host-only fixtures,
and update the host pointer.
A member's HasInteractive population changed. Coalesces a
reconcile() onto a microtask so a burst of arrivals/departures
runs the policy once. Event-driven — no polling.
ProtectedoccupantsTerminal onDestruct no-op. Exists so subclasses and mixins
overriding onDestruct can call super.onDestruct() without
the cast-to-optional-callable dance — the chain is guaranteed
to bottom out here. StuffApi.destruct invokes the hook via
the optional-method dispatcher in api/stuff.ts; that path
still works (always finds a function on the prototype).
Override (not extend with super) at any layer that wants
cleanup; chain to super.onDestruct() from the override so
intermediate layers in a mixin chain run too.
Invoked by StuffApi.destruct (and forceDestruct) while
the target is still live, after canDestruct passes and before
shadow-detach + destroy(). Witness — the return value is
ignored (it cannot veto; canDestruct is the veto seam). Override
to release resources/listeners and chain super.onDestruct()
so mixin layers run.
ProtectedreapDestruct a member, draining any occupants to the host first and tearing down BOTH sides of its hub exit. Never reaps the host.
Order is load-bearing for the exit-teardown asymmetry: remove +
destruct the host-side exit FIRST (which clears the member-side
inverse pointer via Exit.onDestruct), so the member's own
Exitable.onDestruct doesn't try to block an already-dead inverse.
Protected AbstractreconcilePopulation-reactive reconcile (bud-high / merge-low).
Register a self-seating fixture so it re-seats when the host migrates
or the graph stands back up. Called by FixtureMixin.seatSelf when
its seatIn target is this Warren. Stores the fixture's template path
— the durable handle that survives the fixture's death-with-host; a
path-less fixture can't be revived and is logged.
Drop m from the member set. Clears the back-ref iff it still names
us (R2.2 symmetric). When m was the host and survivors remain, the
host role is lost and re-designated lazily on the next getHost().
Returns true when m was present.
ProtectedrequireNarrow a room to an Exitable container, failing early when it isn't.
A Warren that wires hub/fixture exits requires its rooms to compose
ExitableMixin; a non-Exitable room is a composition error worth
surfacing loudly rather than papering over with a cast.
Stamp this Stuff's templatePath and re-key the
byTemplatePath index so future findByTemplatePath lookups
see the new path. No-op when path matches the current value.
Locked down by @CallSecurity(ApiOnly) because flipping a
Stuff's identity post-clone would break FromTemplate
policies and any caller-side caching of template-path
identity. @Final @Unshadowable because the index update has
to run for every successful set — a subclass override that
forgot the index call (or a shadow that intercepted) would
silently desync byTemplatePath.
Unwraps via ProxyApi.unwrap so the #-slot access lands on
the raw target (see comment on #templatePath above).
Set the spatial zone. Gated by FromSpatialZone — only the
SpatialZone class and its subclasses (CartesianZone,
SphericalZone) may call this through the proxy. The
addLocation / removeLocation chokepoints on the zone side
are the legitimate callers; everyone else is rejected.
Clone-time seeding from StuffApi.#cloneInner doesn't go
through this method — it uses the caller-allowlisted
_stampZone seam below.
@Final @Unshadowable because the index of substrate
invariants that consult getZone() (containment's
cross-zone gate, Mobile.traverse, MQL scope walks) trusts
the slot's value; a subclass override or shadow that lied
about it could break those invariants. No legitimate
subclass needs to extend this anyway — the only legitimate
write paths are the SpatialZone chokepoints and clone-time.
ProtectedspawnClone a fresh satellite (via the policy createMember), ensure it
is registered, and wire its hub exit to the current host. Returns
the new member.
Tear the whole graph down — destruct every member (host last) and
clear all state. Used on shutdown / HMR; the Warren Idea itself
persists only as a definition and the graph reconstitutes lazily.
Get a string representation of this idea (for debugging). Subclasses should override to provide more specific information.
Refresh the recency timestamp to now. Timestamp-fixed (no caller-supplied value). Called on the raw target by the security gate on every successful dispatch (Phase 2) and by the residency presence walk.
Protected AbstractunwireRemove the host-only fixtures from whatever previously held them.
ProtectedunwireTear down the host-side half of m's hub exit and destruct that
Exit object (the asymmetry fix — removeExit alone leaks the Exit
and leaves a blocked dead entry). The member-side half is left to
m's own Exitable.onDestruct when m is being reaped; when this
is called for a still-living m the member-side inverse is cleared
by the host-exit's Exit.onDestruct.
Protected AbstractwireWire the host-only fixtures (external neighbors).
ProtectedwireWire a bidirectional hub exit between the current host and m,
recording the host-side direction so teardown can find it. No-op
when m IS the host or there is no host yet.
StaticcleanupR2.4 framework cleanup — tear the graph down when the Warren itself destructs so no member is orphaned.
Base class for incorporeal game objects with identity but no physical presence.