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
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
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} }
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)
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!