Skip to content

GMCP

GMCP (Generic MUD Communication Protocol) is an out-of-band channel between the MUD and the client. It carries structured data — character vitals, room info, inventory, chat, client identification — alongside the normal text stream. Oxidus implements GMCP in both directions: messages received from the client and messages sent to it.

The two directions use separate machinery:

  • Incoming (client -> server) is handled by the EXT_GMCP module on the player and login objects, which parses each message and routes it to a handler file by package name.
  • Outgoing (server -> client) flows through the GMCP daemon (GMCP_D), which routes a package to a daemon module that formats the payload and sends it.

All package names, keys, and values are defined in include/gmcp_defines.h and should be referenced by their GMCP_* macros rather than written as literal strings.

When a GMCP message arrives, the driver calls gmcp(string message) on the user or login object. That function lives in std/ext/gmcp.c (EXT_GMCP), inherited by both std/living/player.c and adm/obj/login.c. It does not gate on whether the player has GMCP enabled — this ensures the Core.Hello and Core.Supports handshake still runs during login.

The message is parsed by GMCP_D->convert_message() into a ClassGMCP, which splits the dotted name into its parts:

class ClassGMCP {
string name; // "Core.Supports.Set"
string package; // "Core"
string module; // "Supports"
string submodule; // "Set"
mixed payload; // JSON-decoded data, or null
}

It is then routed to std/handlers/gmcp/<Package>.c, calling the function named after the module:

  • Core.Hello -> Core.c::Hello(payload)
  • Core.Supports.Set -> Core.c::Supports("Set", payload)
  • Char.Items.Inv -> Char.c::Items("Inv", null)

If no handler file exists for the package, the miss is logged to system/gmcp.

PackageHandlerPurpose
Core.HelloCore.cStores client name/version (login only)
Core.Supports.Set/Add/RemoveCore.cTracks which packages the client supports
Core.PingCore.cEchoes a pong (rate-limited, 60s cooldown)
Char.Login.CredentialsChar.cAuthenticates and replies with Char.Login.Result
Char.Items.Inv/Room/ContentsChar.cSends inventory, room, or container item lists
External.Discord.Hello/GetExternal.cDiscord integration info

Handler modules inherit std/handlers/gmcp/gmcp_module.c, which provides the per-player cooldown system used to rate-limit requests like Core.Ping.

Game code sends data by calling the daemon:

#include <daemons.h>
#include <gmcp_defines.h>
GMCP_D->send_gmcp(tp, GMCP_PKG_CHAR_VITALS);

GMCP_D identifies the package and dispatches to the matching daemon module under adm/daemons/modules/gmcp/ (Char, Client, Comm, Core, Room). That module assembles the payload — when none is supplied it sends the full default set — and calls do_gmcp() on the target to JSON-encode and transmit it. So a bare send_gmcp(tp, GMCP_PKG_CHAR_VITALS) causes the Char module to gather and send the player’s current vitals.

Incoming, existing package: add a function named after the module to the handler file (e.g. Skills() in Char.c), and add any new GMCP_PKG_* define. Routing finds it automatically.

Incoming, new package: create std/handlers/gmcp/<Package>.c, inherit __DIR__ "gmcp_module";, and add the defines. The router locates it by file name.

Outgoing: add a function for the package to the appropriate daemon module under adm/daemons/modules/gmcp/, then call GMCP_D->send_gmcp() from game code.

FileRole
std/ext/gmcp.cEXT_GMCP — incoming entry point and do_gmcp() sender
std/handlers/gmcp/Incoming handler modules (Core, Char, External)
std/handlers/gmcp/gmcp_module.cHandler base class — cooldown system
adm/daemons/gmcp.cGMCP_D — parsing and outgoing routing
adm/daemons/modules/gmcp/Outgoing daemon modules (Char, Client, Comm, Core, Room)
std/classes/gmcp.cClassGMCP structure
include/gmcp_defines.hAll GMCP_* package, key, and value defines