Skip to Content

MMCP Specification

MUD Master Chat Protocol (MMCP)

Sources:

MMCP is a decentralized chat protocol that allows MUD clients to communicate with each other over a TCP/IP connection.

Terminology

A peer is any remote MUD client with which a direct MMCP TCP/IP connection has been established.
The caller is the side that initiates the TCP/IP connection; the answerer is the side that accepts it. An “incoming connection” means the remote peer is the caller. An “outgoing connection” means the remote peer is the answerer.
The sender is the peer that transmits a particular command or message; the receiver is the peer that receives and processes that command or message.
A Command Byte is a single byte value (0-255).
An End of Command (CHAT_END_OF_COMMAND) is a single byte value of 255.

Establishing a Connection

Caller

When a TCP/IP connection is established, the caller begins a handshake sequence with the answerer by sending an initial string with the following format:
CHAT:<chat name>\n<ip address><port>
This consists of the literal string “CHAT:”, followed by the chat name, a line feed (\n), and the caller’s provided IPv4 address immediately followed by the caller’s provided port number.
The provided IPv4 address and port number are concatenated without a separator and can differ from the caller’s real IPv4 address and port number. The IPv4 address is a variable length, dotted-decimal string or the literal string <Unknown>. The port number is a fixed width, left-aligned value which occupies the final 5 characters of the handshake string, padded with trailing spaces if necessary. When parsing the initial string, the answerer MUST obtain the port number by reading the final 5 characters of the string and trimming any trailing spaces.
The sprintf format is: CHAT:%s\n%s%-5u

After sending this string, the caller waits for a response. The answerer replies with:

  • NO (without a newline character) to reject the connection, or
  • YES:<chat name>\n to accept it.

The caller then optionally sends its client version using a CHAT_VERSION command (described below). Version exchange is not required during handshake and may occur at any time.

Answerer

Upon detecting an incoming connection, the answerer accepts the socket and waits for the CHAT: string. To reject the connection, it sends NO (without a newline character). To accept, it sends YES:<chat name>\n.

The answerer then optionally sends its own client version using a CHAT_VERSION command.

Chat Data Blocks

A standard chat data block has the format:
<COMMAND BYTE><data><END OF COMMAND>

Command boundaries are determined solely by the CHAT_END_OF_COMMAND byte. All commands follow this structure except the initial handshake and CHAT_FILE_BLOCK data blocks, which omit the CHAT_END_OF_COMMAND byte.

Command Byte Values

In the following table, Portable indicates commands intended to function across multiple client implementations.

Command Name Byte Value Notes
CHAT_NAME_CHANGE 1 Portable; ALL clients SHOULD support.
CHAT_REQUEST_CONNECTIONS 2 Portable; ALL clients SHOULD support.
CHAT_CONNECTION_LIST 3 Portable; ALL clients SHOULD support.
CHAT_TEXT_EVERYBODY 4 Portable; ALL clients SHOULD support.
CHAT_TEXT_PERSONAL 5 Portable; ALL clients SHOULD support.
CHAT_TEXT_GROUP 6 Portable; ALL clients SHOULD support.
CHAT_MESSAGE 7 Portable; ALL clients SHOULD support.
CHAT_DO_NOT_DISTURB 8 Portable; most clients LACK support.
CHAT_SEND_ACTION 9 NOT portable; used by Mud Master.
CHAT_SEND_ALIAS 10 NOT portable; used by Mud Master.
CHAT_SEND_MACRO 11 NOT portable; used by Mud Master.
CHAT_SEND_VARIABLE 12 NOT portable; used by Mud Master.
CHAT_SEND_EVENT 13 NOT portable; used by Mud Master.
CHAT_SEND_GAG 14 NOT portable; used by Mud Master.
CHAT_SEND_HIGHLIGHT 15 NOT portable; used by Mud Master.
CHAT_SEND_LIST 16 NOT portable; used by Mud Master.
CHAT_SEND_ARRAY 17 NOT portable; used by Mud Master.
CHAT_SEND_BARITEM 18 NOT portable; used by Mud Master.
CHAT_VERSION 19 Portable; ALL clients SHOULD support.
CHAT_FILE_START 20 Portable; ALL clients SHOULD support.
CHAT_FILE_DENY 21 Portable; ALL clients SHOULD support.
CHAT_FILE_BLOCK_REQUEST 22 Portable; SOME clients MAY support.
CHAT_FILE_BLOCK 23 Portable; SOME clients MAY support.
CHAT_FILE_END 24 Portable; SOME clients MAY support.
CHAT_FILE_CANCEL 25 Portable; SOME clients MAY support.
CHAT_PING_REQUEST 26 Portable; ALL clients SHOULD support.
CHAT_PING_RESPONSE 27 Portable; ALL clients SHOULD support.
CHAT_PEEK_CONNECTIONS 28 Portable; ALL clients SHOULD support.
CHAT_PEEK_LIST 29 Portable; ALL clients SHOULD support.
CHAT_SNOOP_START 30 Portable; SOME clients MAY support.
CHAT_SNOOP_DATA 31 Portable; SOME clients MAY support.
CHAT_SNOOP_COLOR 32 NOT portable; used by Mud Master.
CHAT_SEND_SUBSTITUTE 33 NOT portable; used by Mud Master.
CHAT_SIDE_CHANNEL 40 Mudlet-specific; REQUIRES Mudlet.
CHAT_CHANNEL_DATA 240 Mudlet-specific; REQUIRES Mudlet.
CHAT_END_OF_COMMAND 255 Terminates all MMCP commands.

Note: Commands 8-18 (CHAT_DO_NOT_DISTURB through CHAT_SEND_BARITEM) are used to share MUD client objects (actions, aliases, macros, variables, events, gags, highlights, lists, arrays, and bar items). Support is optional and varies by client.

MMCP Commands

CHAT_NAME_CHANGE (1)

Sender

Sent when changing the local chat name.
Format: <CHAT_NAME_CHANGE><new name><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

The new name must be broadcast to all existing connections.

Receiver

Replace the stored name for this peer with the new name.

CHAT_REQUEST_CONNECTIONS (2)

Sender

Requests the receiver’s public connections for automatic outgoing connections.
Format: <CHAT_REQUEST_CONNECTIONS><CHAT_END_OF_COMMAND>
sprintf: %c%c

Receiver

Respond with a CHAT_CONNECTION_LIST containing all public connections.

CHAT_CONNECTION_LIST (3)

Sender

Sends a comma-delimited list of public connections (IP address and port pairs).
Format: <CHAT_CONNECTION_LIST><IP/port list><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

List format: <ip1>,<port1>,<ip2>,<port2>,...

  • Ports are not padded.
  • List must never end with a comma.
  • Unknown IPs are represented as <Unknown> (case-sensitive, including the angle brackets).
  • If no public connections exist, send an empty list.

Example: 28.25.102.48,4050,100.284.27.65,4000,<Unknown>,4050

Receiver

Store the list and attempt outbound connections to all valid entries.

CHAT_TEXT_EVERYBODY (4)

Sender

Broadcast message to all peers.
Format: <CHAT_TEXT_EVERYBODY><text><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

The sender generates the complete display text, including newlines and prefix.
Example: \n%s chats to everybody, '%s'\n

Receiver

  • If the message arrives on an incoming connection (i.e. the remote peer is a caller): relay to ALL other connections (both incoming and outgoing) except the original sender.
  • If the message arrives on an outgoing connection (i.e. the remote peer is an answerer): relay ONLY to incoming connections (callers being served).

A relayed message must never be sent back to the connection from which it was received.
The caller/answerer distinction enables “serving” mode so peers behind NAT/firewalls can still receive broadcasts.

CHAT_TEXT_PERSONAL (5)

Sender

Private message to one peer.
Format: <CHAT_TEXT_PERSONAL><text><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

The sender generates the complete display text, including newlines and prefix.
Example: \n%s chats to you, '%s'\n

Receiver

If the sender is not being ignored, display the text.

CHAT_TEXT_GROUP (6)

Sender

Message to a named group.
Format: <CHAT_TEXT_GROUP><group name><text><CHAT_END_OF_COMMAND>
The group name field is exactly 15 bytes, left-aligned, and padded with trailing spaces if necessary. When parsing the group name, the receiver MUST read exactly 15 bytes for the group name and trim any trailing spaces. The remainder of the payload is the message text.
sprintf: %c%-15s%s%c

The sender generates the complete display text, including newlines and prefix.
Example: \n%s chats to the group, '%s'\n

Receiver

  • If the message arrives on an incoming connection (i.e. the remote peer is a caller): Do NOT relay.
  • If the message arrives on an outgoing connection (i.e. the remote peer is an answerer): relay ONLY to incoming connections (callers being served). This is subject to local group filtering (see Note below).

A relayed message must never be sent back to the connection from which it was received.

Note (Implementation-specific behavior in MUSHclient)

MUSHclient loops through its connections and only sends the message to the connections the user manually tagged with that group name (case-insensitive), regardless of whether the user composed the group chat message or is simply relaying it. Group membership in MUSHclient is therefore asymmetric. Alice may have Bob tagged as “Warriors” while Bob has Alice tagged as “Friends”. If this happens, Alice sending a group chat message to “Friends” or Bob sending a group chat message to “Warriors” will result in the message failing to send.

CHAT_MESSAGE (7)

Sender

System/notification message.
Format: <CHAT_MESSAGE><message><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

The sender generates the complete display text, including newlines and prefix.
Example: \n<CHAT> %s has refused your connection because your name is too long.\n

Receiver

Display the message.

CHAT_VERSION (19)

Sender

Sends client name and version string.
Format: <CHAT_VERSION><version string><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

CHAT_PING_REQUEST (26)

Sender

Format: <CHAT_PING_REQUEST><timing data><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

Receiver

Echo the timing data unchanged in CHAT_PING_RESPONSE.

CHAT_PING_RESPONSE (27)

Sender

Returns timing data from the request.
Format: <CHAT_PING_RESPONSE><timing data><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

Receiver

Calculate round-trip time.

CHAT_PEEK_CONNECTIONS (28)

Sender

Requests public connection list for viewing only.
Format: <CHAT_PEEK_CONNECTIONS><CHAT_END_OF_COMMAND>
sprintf: %c%c

Receiver

Respond with CHAT_PEEK_LIST.

CHAT_PEEK_LIST (29)

Sender

Tilde-delimited list of public connections (IP, port, name triples).
Format: <CHAT_PEEK_LIST><list><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

The list always ends with a single tilde unless it is empty.
If no public connections exist, send an empty list.

Example: 204.285.28.18~4050~Omawarisan~<Unknown>~4050~Baalzebul~

Receiver

Display the list; do not attempt connections.

CHAT_SNOOP_START (30)

Sender

Requests start/stop of snooping (single command; acts as a toggle, receiver decides whether to honor it).
Format: <CHAT_SNOOP_START><CHAT_END_OF_COMMAND>
sprintf: %c%c

Receiver

Decide whether to honor the request.

CHAT_SNOOP_DATA (31)

Sender

Sends MUD output to snoop recipient.
Format: <CHAT_SNOOP_DATA><message><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

Receiver

Display the message. Never forward snoop data to avoid loops.

File Transfer Commands

CHAT_FILE_START (20)

Sender

Initiates file transfer.
Format: <CHAT_FILE_START><filename>,<length><CHAT_END_OF_COMMAND>
sprintf: %c%s,%lu%c

The filename must be the base name only; the length is the file size in bytes.

Receiver

Validate sender, filename, and length. Reject with CHAT_FILE_DENY if unacceptable. Otherwise accept and immediately request the first block with CHAT_FILE_BLOCK_REQUEST.

CHAT_FILE_DENY (21)

Sender

Rejects an incoming file transfer.
Format: <CHAT_FILE_DENY><reason><CHAT_END_OF_COMMAND>
sprintf: %c%s%c

Receiver

Display the reason and clean up any partial files.

CHAT_FILE_BLOCK_REQUEST (22)

Sender

Requests next block (receiver-driven transfer).
Format: <CHAT_FILE_BLOCK_REQUEST><CHAT_END_OF_COMMAND>
sprintf: %c%c

Receiver

Send next 500-byte block via CHAT_FILE_BLOCK. On EOF, send CHAT_FILE_END instead.

CHAT_FILE_BLOCK (23)

Sender

Fixed 500-byte data block (may be shorter on final block).
Format: <CHAT_FILE_BLOCK><raw bytes> (no END byte)

Receiver

Write data, then request next block with CHAT_FILE_BLOCK_REQUEST. Track total length from CHAT_FILE_START to determine final block.

CHAT_FILE_END (24)

Sender

Signals completion of transfer.
Format: <CHAT_FILE_END><CHAT_END_OF_COMMAND>
sprintf: %c%c

Receiver

Close the file.

CHAT_FILE_CANCEL (25)

Sender

Aborts an in-progress transfer (either side).
Format: <CHAT_FILE_CANCEL><CHAT_END_OF_COMMAND>
sprintf: %c%c

Implementation Notes

Character Set

The protocol does not mandate a character encoding. Historically, most implementations used ASCII or system-local 8-bit encodings. The original Mud Master client restricted chat names to printable ASCII characters 32-122 inclusive, excluding { | } ~. Other clients may permit additional characters.

String Sanitization Recommendations

  • Remove or replace 0xFF bytes in all user-controlled strings (except CHAT_FILE_BLOCK raw data) to prevent protocol stream corruption due to malformed commands.
  • Strip ANSI/VT100 terminal escape sequences from user input to avoid terminal interference.
  • Remove line feeds (\n) from chat names and group names.
  • Remove or replace tildes (~) in chat names to avoid delimiter collision and preserve CHAT_PEEK_LIST parsing.

Chat Name Length

Original Mud Master allowed <= 30 characters; TinTin++ enforces <= 20. Use the stricter limit of 20 for maximum compatibility.

Connection List vs. Peek List Formatting

Connection lists use commas as separators and never end with a comma. Peek lists use tildes and always terminate with one tilde.

IPv6 Support

The protocol does not support IPv6. All addresses exchanged in the handshake and connection lists use IPv4 dotted-decimal format or the literal string <Unknown>. Provided addresses may differ from the actual socket addresses due to firewall or NAT handling.

Message Loops

The protocol lacks built-in loop detection. Complex topologies can cause infinite relaying. MUSHclient mitigates this by discarding recently sent messages for a short period (currently 5 seconds). Relay-only implementations should replace real IP addresses with <Unknown> and apply rate limiting.