dev:scriptref:gmcp

GMCP

Generated from smudgy v0.4.2-dev (smudgy-core.d.ts @ 5110ac599715). Index: scriptref.

Read structured GMCP messages as shared state, check protocol status, and react to connection lifecycle events.

import gmcp from "smudgy:state/gmcp";
 
const hp = gmcp.value?.Char?.Vitals?.hp;
gmcp.watch("Char.Vitals", vitals => console.log(vitals));

GMCP names and payloads vary by game. The contracts below describe common shapes without hiding unrecognized messages. For negotiation, sending messages, game-specific typing, and MSDP, see Read GMCP and MSDP.

Default export

The module's default export (gmcp).

const gmcp: StateConsumer<GmcpTree>;

The live GMCP tree, one entry per message name (see GmcpTree): read the latest value with gmcp.value, subscribe with gmcp.watch(path, ...), and wire widgets with gmcp.bind(path). Each message the server sends is committed as its own update, so a watcher at or under the message's path runs once per message, repeats included.

The message shapes

GmcpTree

export interface GmcpTree {
  Char?: {
    Vitals?: { hp?: number; maxhp?: number; mp?: number; maxmp?: number; [field: string]: unknown };
    Status?: { level?: number; [field: string]: unknown };
    Name?: { name?: string; fullname?: string; [field: string]: unknown };
    [message: string]: unknown;
  };
  Room?: {
    Info?: {
      num?: number;
      name?: string;
      area?: string;
      zone?: string;
      environment?: string;
      terrain?: string;
      exits?: Record<string, number>;
      [field: string]: unknown;
    };
    [message: string]: unknown;
  };
  Comm?: {
    Channel?: { chan?: string; player?: string; msg?: string; [field: string]: unknown };
    [message: string]: unknown;
  };
  [pkg: string]: unknown;
}

Everything the server has sent over GMCP, one entry per message name. import gmcp from "smudgy:state/gmcp" serves the live tree: gmcp.value.Char.Vitals.hp is the latest reading, and gmcp.watch("Char.Vitals", ...) runs on each vitals message.

Paths reach inside payloads too: gmcp.watch("Char.Vitals.hp", ...) hands the handler just the number. It runs on every message that covers the path, so a vitals update that left hp unchanged still fires; compare against gmcp.previousValue to react to change alone.

Message names are matched case-insensitively, so Char.Vitals finds the data whether the server spells it Char.Vitals or char.vitals.

The declared entries are a widely-implemented set of GMCP state objects. Games send others, and every message the server sends appears in the tree whether or not it is declared here; an undeclared message reads as unknown. A script can type the messages of the game it supports by extending this interface and casting the handle. A game that adds a Room.Weather message keeps the declared Room.Info typing by intersecting:

import gmcp from "smudgy:state/gmcp";
import type { StateConsumer, GmcpTree } from "smudgy:core";
 
interface FenworldGmcp extends GmcpTree {
  Room?: NonNullable<GmcpTree['Room']> & {
    Weather?: { temp?: number; rain?: boolean };
  };
}
 
const fenGmcp = gmcp as StateConsumer<FenworldGmcp>;
const temp = fenGmcp.value?.Room?.Weather?.temp;  // number | undefined

Protocol status

gmcp

export const gmcp: {
  readonly enabled: boolean;
  onReady(callback: () => void): void;
  send(name: string, data?: unknown): void;
  enableModule(name: string, version?: number): void;
  disableModule(name: string): void;
  mergeKeys(...names: string[]): void;
};

GMCP protocol status and control for the current session.

  • enabled — Whether GMCP is active on the current connection.
  • onReady — Runs callback once GMCP is ready. When GMCP is already active, the callback is called immediately, before onReady returns; otherwise it runs once when the server next completes GMCP negotiation. Code that runs at load time gets its callback whether it loads before or after the connection.
  • send — Sends a GMCP message to the game. Call gmcp.send("Char.Items.Inv") with only a name, or pass JSON-serializable data as the second argument. Messages are dropped (with a one-time notice) while GMCP is not active; use onReady to wait for it.
  • enableModule — Asks the server to turn a GMCP module on — gmcp.enableModule("IRE.Rift") — so its messages start arriving. version defaults to 1. Modules are shared: the server keeps a module on while anything still uses it, and turning one on that another script already enabled costs nothing. A module enabled before the connection is requested as part of the GMCP handshake.
  • disableModule — Releases this script's use of a GMCP module. The server is asked to turn the module off only when no other script still uses it.
  • mergeKeys — Marks message names whose payloads merge into the retained value instead of replacing it — for servers that send only the changed fields after an initial full send. Char.Status is always treated this way; gmcp.mergeKeys("Char.Defences") adds more.

Lifecycle events: smudgy:events/gmcp

ready

export const ready: EventConsumer<Record<string, never>>;

Fires once GMCP negotiation completes and the handshake has been sent; GMCP data starts flowing from this moment. For code that may load after the connection, gmcp.onReady from smudgy:core covers both orders.

closed

export const closed: EventConsumer<Record<string, never>>;

Fires when GMCP stops on a live connection: the server withdrew it, or the connection dropped while it was active. The last-received data stays readable through smudgy:state/gmcp.


Script API reference · ← smudgy:core — Shared state & events · MSDP (experimental) → · Scripting manual