Guides

GMCP with open-source tools

This guide outlines the technical implementation of the Generic MUD Communication Protocol (GMCP), also known as Telnet Option 201. GMCP allows servers to send structured JSON data to modern clients like Mudlet or BeipMU, enabling features like graphical health bars, auto-mapping, and inventory windows without interfering with the human-readable text stream.

4-8 hours6 steps
GMCP with open-source tools hero illustration
1

Initiate Telnet Option 201 Negotiation

The server must signal its intent to use GMCP during the initial Telnet handshake. Send the 'IAC WILL GMCP' sequence (255 251 201). The server should only send GMCP data if it receives 'IAC DO GMCP' (255 253 201) from the client. Store a boolean flag on the user's connection object to track this state.

telnet_negotiation.c
#define TELOPT_GMCP 201
const unsigned char gmcp_will[] = { 255, 251, 201 };
write_to_descriptor(d, (const char*)gmcp_will, 3);

⚠ Common Pitfalls

  • Sending GMCP payloads to clients that have not sent DO GMCP can cause 'garbage' characters to appear in their terminal.
  • Failure to handle WONT GMCP may lead to infinite negotiation loops.
2

Implement the Subnegotiation Parser

Incoming GMCP data is wrapped in a subnegotiation block: IAC SB 201 [Payload] IAC SE. The payload follows the format 'Package.Message [JSON]'. Your parser must identify the package name, the message, and then hand the remaining string to a JSON decoder.

protocol_handler.py
def handle_gmcp_input(data):
    # data is the content between IAC SB 201 and IAC SE
    parts = data.split(' ', 1)
    cmd = parts[0]
    payload = json.loads(parts[1]) if len(parts) > 1 else {}
    return cmd, payload

⚠ Common Pitfalls

  • Some clients send the package name and message without a space before the JSON object.
  • Buffer overflows if the incoming GMCP string exceeds expected length limits.
3

Establish Core Handshake

Once negotiated, the client usually sends 'Core.Hello'. The server should respond with its own 'Core.Hello' and use 'Core.Supports.Set' to define which packages it intends to send (e.g., Char 1, Room 1, Comm 1). This prevents the client from being overwhelmed by unused data.

server_response.json
Core.Supports.Set [ "Char 1", "Room 1", "Comm 1" ]
4

Send Character Vitals (Char.Vitals)

Implement a function to broadcast character stats whenever they change. To optimize bandwidth, only send updates when values actually change or at a fixed heartbeat interval. Common keys include 'hp', 'maxhp', 'mp', 'maxmp', and 'stamina'.

act_info.c
/* Send IAC SB 201 Char.Vitals { "hp": 100, "maxhp": 120 } IAC SE */
void send_gmcp_vitals(CHAR_DATA *ch) {
    char buf[MAX_STRING_LENGTH];
    sprintf(buf, "Char.Vitals { \"hp\": %d, \"maxhp\": %d }", ch->hit, ch->max_hit);
    write_to_gmcp(ch->desc, buf);
}

⚠ Common Pitfalls

  • Sending vitals every single pulse can cause lag on high-latency connections.
  • Forgetting to escape quotes in the JSON string if not using a library.
5

Implement Room.Info for Mapping

To support Mudlet's auto-mapper, send 'Room.Info' whenever a player moves. This should include a unique numeric 'num', the room 'name', 'area', and 'exits'. This allows the client to build a visual representation of the world.

room_data.json
Room.Info {
  "num": 1205,
  "name": "The Town Square",
  "area": "Midgaard",
  "exits": { "n": 1206, "s": 1204, "w": 1200 }
}

⚠ Common Pitfalls

  • Using non-unique IDs for rooms will break client-side mapping.
  • Inconsistent exit naming (e.g., 'north' vs 'n') can confuse client scripts.
6

Validate and Debug with Client Consoles

Use the Mudlet 'errors' and 'debug' views to verify incoming payloads. In Mudlet, you can run 'lua display(gmcp)' in the command line to see the current state of all received data. Ensure that the JSON is well-formed and that no telnet control codes are leaking into the visible text buffer.

⚠ Common Pitfalls

  • JSON syntax errors (like trailing commas) will cause modern clients to silently discard the entire packet.
  • Hidden characters in the byte stream can cause the Telnet state machine to hang.

What you built

Successful GMCP implementation moves your MUD from a pure text interface to a rich, data-driven experience. Start with Char.Vitals and Room.Info, as these provide the most immediate value to players using modern clients. Always prioritize using a robust JSON library over manual string concatenation to ensure protocol stability.