Saxonberg Server API
    Preparing search index...

    Hierarchy

    • LoginBase
      • default
    Index

    Constructors

    Properties

    _clientState: Record<string, unknown> = {}

    Persistent UI state slot. Keys come from clientStateSchema; values are the JSON-shape the schema declares. Round-trips via the Hydrator like any other persistent field; populated wholesale on session-establish via the welcome payload, updated by client-state-write.

    _transientClientState: Record<string, unknown> = {}

    Transient client-state slot — keys whose schema entry is transient. In-memory only: deliberately NOT in persistentFields, so it never reaches the Hydrator and resets to defaults on a fresh login. Per-bar input modes live here (ephemeral input scoping that belongs to the session, not the character).

    interactives: Set<default> = ...

    Connected Interactives. Host-internal storage; external consumers use addInteractive / removeInteractive / getInteractives().

    stuffId: string

    Runtime ID for this object (generated using nanoid). This is NOT the MongoDB _id - it's a runtime identifier.

    _mixinName: string = 'CommandGiverMixin'
    clientStateSchema: ClientStateSchemaEntry<unknown>[] = ...

    Client-state schema. Declares every persisted UI key + its default. One flat array — no chain walker, no per-feature mixin layer. Add an entry to ship a new key.

    Scope rule: only put a key here if it's substrate-level (meaningful for every HasInteractive-bearing thing). If a key is owned by a narrower mixin, the right move is to promote a small registry / walker — but defer that until the array genuinely outgrows a single file.

    commandContributions: CommandContributions = ...

    Verb allowlist for the pre-world phase. The recency stack IS the sandbox — no world verbs (go/say/take) leak because Login composes none of the mixins that contribute them. (style rides along from HasInteractiveMixin; harmless.)

    DEFAULT_PORTRAIT: string = ''

    Empty portraitUrl sentinel — "no image; client renders a generated placeholder" (initials/icon from the name). We don't mint a fake asset URL server-side; the empty string is the honest "default" the client interprets.

    GUEST_RESERVED_WORD: "Guest"

    Reserved first word of every guest name (e.g. "Guest Mallow"). Two jobs: it makes guest-ness legible in plain text wherever the Named name appears (speech/emote attribution, look, logs — a UI badge can't reach those), and it is withheld from real character naming (the char-gen enroll denylist imports it) so a real player can't impersonate a guest. Lives here on the guest-mint site. Exact-word only; fuzzy/homoglyph near-misses are out of scope.

    persistentFields: string[] = ...
    settings: SettingsSchemaEntry<unknown>[] = ...

    Shell-level settings the command pipeline consumes.

    shell.parser selects which parser turns input text into a ParseResult. Today the only registered parser is 'msh', the Mud SHell tokenizer-driven parser; declared as an enum so the future LLM-backed parser can be added by appending to enumValues.

    subscribableFields: SubscribableFieldDescriptor[] = ...

    Universal 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.

    Methods

    • Run a pre-bound { command, model } through the dispatcher's bound tail — resolve → validator-preload → validators → execute — minting a fresh per-command CommandContext and returning it (with its accumulated notes). This is the scripting interpreter's dispatch primitive: a scripted command is resolved (scope re-checked), validated, and executed exactly like a typed one, but the envelope is the script driver's to assemble (the run aggregates per-statement notes), not this method's.

      Distinct from the inline bound branch in executeCommand (which runs on the outer ctx and emits the envelope); this mints its own ctx so each scripted statement carries its own attribution + note set. Mirrors that branch's resolve/validate/execute sequence.

      Parameters

      Returns Promise<CommandContext>

    • Clone-per-execution controller dispatch. The clone is destructed in finally regardless of outcome.

      Option E (per-subcommand controller): when the bound model carries a subcommand and the subcommand declares its own controller:, that template wins; otherwise falls back to the verb-level controller. A subcommanded verb with no resolvable controller (subcommand omitted, no verb-level fallback) returns a player-facing failure rather than throwing.

      Parameters

      Returns Promise<void>

    • Walk the verb's match list at the assemble stage. Shape errors fall through to the next match; the first claiming match (bind succeeds + validators pass) runs _executeOne exclusively. Bind errors stop the chain on the outer ctx.

      Returns the CommandContext whose accumulator the dispatcher uses for the dispatch-response envelope: a fresh per-attempt ctx for the claiming match, or the outer ctx for pre-match failures (unknown verb, all-shape-fall-through, bind error).

      Chain-of-responsibility lives at the assemble stage only; pass: true retired with CommandResult. Content patterns that need "I might handle this depending on state" use dynamic contributions on the recency stack.

      Parameters

      Returns Promise<CommandContext>

    • 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.

      Returns void

    • Run the entry procedure: take ownership of the connection, then branch on character count (0 → char-gen, ≥1 → roster).

      Returns Promise<void>

    • Mint a throwaway guest avatar from the seed and hand off to it. The guest gets a generated reserved-word name ("Guest Mallow"), spawns in the lounge (the seed's startLocation), persists nothing, and is reaped when its connection drops (Avatar.onLinkdead). Mirrors playCharacter's handoff shape, minus all the per-character template / roster / ownership machinery.

      Returns Promise<void>

    • Begin char-gen: seed the draft with the player's real name (for the name suggester) and emit the initial state frame by dispatching the bare enroll verb through the real command pipeline.

      Returns Promise<void>

    • Shadowable extension point for envelope frames. Default returns the envelope unchanged. Treat the input as immutable; return a fresh copy when transforming.

      Parameters

      • envelope: EnvelopeTemplate

      Returns EnvelopeTemplate | null

    • Shadowable extension point. Default checks per-frame modality attribution: when frame.meta.modality is set, drops the frame unless the recipient's PerceptionApi.sensorium includes the named modality. Actor self-frames (carrying audience:actor) always deliver — you always perceive your own acts.

      Game content shadows this to drop frames (return null) or transform them (return a modified copy — treat the input as immutable). Per-recipient interception, lifecycle bound to the shadow's host.

      Parameters

      • frame: MessageFrame

      Returns MessageFrame<unknown> | null

    • Walk the recency stack newest-first and pair every command with the resolved Stuff that affords it and its bucket. The 'self' sentinel resolves to the giver instance, so every record's source is a concrete Stuff. Standing state — depends only on the current stack, not on any command being in flight. The dispatch chain decides ordering; we don't dedup.

      Lazily seeds the 'self' entry on first read so callers that skip postRegister (test helpers like makeStuff, ad-hoc scripts) still see the giver's own contributions. Production code goes through postRegister; this branch is the safety net.

      Returns Affordance[]

    • 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.

      Returns number

    • Self-presentation — the casual-register render string for this object, the answer to "what does this Stuff call itself?" Three- step resolution:

      1. Named.name if present and non-empty — the object's proper name ("Alice", "Excalibur", "Town Square").
      2. Visible.shortDescription if present and non-empty — the object's visual identity ("a heavy oak door").
      3. The baked-in fallback (DEFAULT_PRESENTATION).

      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.

      Returns string

    • 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).

      Parameters

      • _label: string

      Returns Mml | null

    • 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.

      Returns string | null

    • Subclass-override delivery hook for envelope frames. Avatar overrides to multiplex to its connected Interactives; NPCs and other Sensors default to no-op.

      Parameters

      • envelope: EnvelopeTemplate

      Returns void

    • SensorMixin delivery — multiplex frames to the connected Interactive(s).

      Parameters

      • frame: MessageFrame

      Returns void

    • 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.

      Returns boolean

    • Mark the schema-subscription gate open. The first connection attach is when client-side schema delivery becomes meaningful — earlier pushes are pre-subscription bookkeeping.

      Parameters

      Returns void

    • Terminal 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.

      Returns void

      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.

    • Envelope template method — DO NOT override. Override handleEnvelope instead, or shadow filterEnvelope for content-driven interception. Parallels onMessage.

      Parameters

      • envelope: EnvelopeTemplate

      Returns void

    • Template method — DO NOT override in subclasses. Override handleMessage instead, or shadow filterMessage for content- driven interception.

      Parameters

      • frame: MessageFrame

      Returns void

    • Hand off to a chosen character. Validates ownership, transfers the Interactive, starts the avatar's session, and destructs Login. Invoked by PlayController for play <playerId>.

      Parameters

      • playerId: string

      Returns Promise<boolean>

    • Build the 'self' entry once, at registration. Walks the class chain (concrete first, then mixins prototype-bottom-up) and dedupes by YAML filename so a mixin and its consumer can't double-contribute the same command.

      Parameters

      • Optional_context: unknown

      Returns Promise<void>

    • Push an authoritative client-state value out to every connected Interactive on this host. Parallel to the existing client-state-write inbound flow but flips the direction — server mutates, client follows. Used by the style verb (and future server-initiated client-state changes) so the client re-renders without waiting for a reconnect snapshot.

      Caller MUST have already called setClientState(key, value)

      • save(). This is the push half; persistence is upstream.

      Parameters

      • key: string
      • value: unknown

      Returns void

    • Drop every entry whose bucket is 'environment' or 'peers'. Used during a self-move; ContainmentApi follows up with the appropriate pushCommandSource calls for the new environment.

      Parameters

      • reason: "self-moved"

      Returns void

    • 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).

      Parameters

      • path: string

      Returns void

    • 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.

      Parameters

      Returns void

    • Get a string representation of this idea (for debugging). Subclasses should override to provide more specific information.

      Returns string

    • 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.

      Returns void

    • Generate a recognizable guest name: the reserved word plus a surname drawn from the real common NameBank ("Guest Mallow"). No parallel name list — when the bank is unseeded (a content gap, or no DB in a unit test) the guest is simply "Guest", with no fabricated surname. Pure read; safe to call before mint.

      Returns Promise<{ name: string; surname?: string }>