GMCP setup for TinTin++

Prerequisites

Before we can use GMCP we need to let the server know that our client supports this. In order to achieve this, first we need to create some variables that will store the bytes used for negotiation with the server trough the telnet protocol

#VAR {telnet[iac]} {\xFF};
#VAR {telnet[do]} {\xFD};
#VAR {telnet[sb]} {\xFA};
#VAR {telnet[se]} {\xF0};
#VAR {telnet[gmcp] {\xC9};

On their own, these are useless, so we create an alias that will aid us in sending GMCP commands to the server

#ALIAS {gmcp}
{
    #SEND {$telnet[iac]$telnet[sb]$telnet[gmcp]%0$telnet[iac]$telnet[se]};
}

With this alias we can send GMCP commands like this: gmcp Core.Ping

Enable GMCP

When the client connects to the server, there is a telnet level negotiation of capabilities. Amongst many options the server will “ask” the client if it supports GMCP. To handle this, we need to create a telnet event and respond with some basic information

#EVENT {IAC WILL GMCP}
{
    #NOP Enable GMCP
    #SEND {$telnet[iac]$telnet[do]$telnet[gmcp]};
    
    #NOP Send some information about the client
    gmcp core.hello {"client":"$info[SYSTEM][CLIENT_NAME]","version":"$info[SYSTEM][CLIENT_VERSION]"};
    
    #NOP Notify the server about supported packages
    gmcp core.supports.set ["Room 1","Room.Info 1","Char 1","Char.Items 1","Char.Vitals 1","Char.Status 1"];
}

From this moment forward, the server will send us GMCP messages. To see these messages, turn on telnet debugging with:

#CONFIG {DEBUG TELNET} {ON}

Example debug output:

RCVD IAC SB GMCP
SKIP SB (3)
RCVD IAC SB GMCP
SKIP SB (529)
RCVD IAC SB GMCP
IAC SB GMCP Room.Info IAC SE

The last line from the output is exactly the name of the event we are going to create to handle information the server sends

Handling events

Knowing the events we are interested in from debugging telnet, now we can create events and process the information as we see fit

#EVENT {IAC SB GMCP Room.Info IAC SE} {
    #VAR roominfo {%1};
}

All GMCP events have the first argument %1 set to a value that can be used directly in a variable. The second argument %2 is a JSON that can be used with a 3rd party script.

Room info example:

#VARIABLE {roominfo}
{
    {area} {giedi prime}
    {environment} {outdoors}
    {exits}
    {
        {east} {9c5db8bd76cc853faa53631e9d1a4928}
        {north} {66a833124afab82ce30f5b3b975bf6ef}
        {northeast} {86830bfbf2ab8212807525ca2fbd63dc}
        {northwest} {c6d0904a9303b3fd26a8db4d6b84956e}
        {south} {2d87f8f423954bbc65b9bc6cbf414b81}
        {southeast} {797d375df742b54e1cf72e02ce7cf2b9}
        {southwest} {27c28b1106b01e1119a3b4069b0fce4d}
        {west} {c07dbd96d862f9d2b5bbc5591bd2d539}
    }
    {name} {Giedi Prime Astro Port}
    {num} {b6c8cc8712e0e855ab4702574e5b8e6d}
}

GMCP Events

Some event names and what they might return

Char.Vitals { “string”: “H:4197\/3735 M:1097\/1032”, “maxsp”: 1032, “maxhp”: 3735, “hp”: 4197, “sp”: 1097 }
Char.Name { “fullname”: “Fufa the inexperienced Reverend Mother”, “name”: “fufa” }
Char.Stats { “con”: 109, “wis”: 119, “int”: 60, “dex”: 116, “qui”: 118, “str”: 114 }
Char.MaxStats { “maxstr”: 92, “maxqui”: 96, “maxdex”: 97, “maxint”: 43, “maxwis”: 100, “maxcon”: 92 }

There are a lot more and as always you can enable even more by tweaking the package names in core.supports.set (keep in mind that they need to be enabled/implemented on the server also)

Examples

Latency checker

A rough Round Trip Time measurement for network latency in milliseconds based on Core.Ping GMCP command

#ALIAS {latency} {
    #FORMAT {LATENCY_TIME_SENT} {%U};
    gmcp Core.Ping;
}

#EVENT {IAC SB GMCP Core.Ping IAC SE} {
    #FORMAT {LATENCY_TIME_EVENT} {%U};
    #MATH {LATENCY_TIME_TRIP} {($LATENCY_TIME_EVENT-$LATENCY_TIME_SENT)/1000};
    #SHOWME {Latency: ${LATENCY_TIME_TRIP}ms}
}
Usage Output
latency Latency: 263ms

Happy Scripting!