Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ MUSHclient ➜ Plugins ➜ working with custom control codes

working with custom control codes

Posting of new messages is disabled at present.

Refresh page


Posted by Victorious   (89 posts)  Bio
Date Sun 20 Nov 2016 04:21 PM (UTC)
Message
Batmud has a custom client they've implemented. It communicates with the server using special control codes, some of which they've documented at the link below. How can I make mushclient display these, and write plugins that trigger off these?

http://www.bat.org/forums?a=view_post&postid=477&page=&group_name=archive16
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #1 on Sun 20 Nov 2016 08:37 PM (UTC)
Message
Er, with quite a bit of coding. :)

Using OnPluginPacketReceived callback in a plugin you can get incoming packets, detect and remove the sequences they describe, and then forward the rest of the packet with the sequences removed for the client to display.

You probably need to allow for their stuff to span multiple packets, to work reliably. You can always turn on "Debug Packets" (Edit menu) to see exactly what is arriving.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #2 on Sun 20 Nov 2016 09:44 PM (UTC)

Amended on Sun 20 Nov 2016 10:40 PM (UTC) by Nick Gammon

Message
Something like this could get you started:

Template:saveplugin=BatMUD_Analyzer To save and install the BatMUD_Analyzer plugin do this:
  1. Copy between the lines below (to the Clipboard)
  2. Open a text editor (such as Notepad) and paste the plugin into it
  3. Save to disk on your PC, preferably in your plugins directory, as BatMUD_Analyzer.xml
  4. Go to the MUSHclient File menu -> Plugins
  5. Click "Add"
  6. Choose the file BatMUD_Analyzer.xml (which you just saved in step 3) as a plugin
  7. Click "Close"



<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>
<plugin
   name="BatMUD_Analyzer"
   author="Nick Gammon"
   id="cff91f04c0d78149c0dd6258"
   language="Lua"
   purpose="Detects BatMUD sequences"
   date_written="2016-11-21 08:20"
   requires="4.80"
   version="1.0"
   >
<description trim="y">
Detects incoming packets in the BatMUD format.
</description>

</plugin>

<!--  Script  -->

<script>
<![CDATA[

local ESC = "\027"
local REGEXP = ESC .. "<(%d%d)(.-)" .. ESC .. ">%1"  -- ESC < nn (args) ESC > nn

-- do something with the message from the MUD
function handleMessage (code, args)
  ColourNote ("yellow", "", "Got code " .. code)
  ColourNote ("green", "", "Got args " .. args)
end -- handleMessage

function processSequence (code, args)
  -- handle embedded sequence
  args = string.gsub (args, REGEXP, processSequence)  -- recursive call

  -- put into a table for later processing
  -- insert at position 1 because the embedded ones will be detected first
  table.insert (incoming, 1, { code = code, args = args } )
  
  return ""  -- omit from packet
end -- processSequence

function OnPluginPacketReceived (s)
  incoming = { }
  s, count = string.gsub (s, REGEXP, processSequence)
  if count > 0 then
    for k, v in ipairs (incoming) do
      handleMessage (v.code, v.args)
    end -- for each one
  end -- some codes found
    
  return s  -- return fixed packet
end -- function OnPluginPacketReceived
]]>
</script>

</muclient>


This doesn't allow for the special stuff spanning packets, however it might work.

Now all you have to do is put code into handleMessage to do things with the incoming messages.

Untested (except for my own test data) - I'm not sure how live data will work.




Example test data from the BatMUD page:


\1b<20FFFFFF\1b|\1b<210000FF\1b|Test output, white on blue\1b>21\1b>20


This was in the "Debug simulated world input" window, where \1b represents ESC.

Response from plugin:


Got code 20
Got args FFFFFF|
Got code 21
Got args 0000FF|Test output, white on blue

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #3 on Sun 20 Nov 2016 09:48 PM (UTC)
Message
I'm presuming there is some negotiation with the MUD to turn this stuff on. That doesn't seem to be documented, but doing a packet debug should show what it is.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Victorious   (89 posts)  Bio
Date Reply #4 on Mon 21 Nov 2016 08:51 AM (UTC)
Message
Thanks for the sample code. Is there any documentation about the protocol and how control codes work? For example, when I look at this packet, I see a lot of hex values between a channel send.

Incoming packet: 9 (60 bytes) at Monday, November 21, 2016, 12:25:20 PM

Valdi [chat]: In 56 61 6c 64 69 20 5b 63 68 61 74 5d 3a 20 49 6e
the not too dis 20 74 68 65 20 6e 6f 74 20 74 6f 6f 20 64 69 73
tant future next 74 61 6e 74 20 66 75 74 75 72 65 20 6e 65 78 74
friday AD.. 20 66 72 69 64 61 79 20 41 44 0d 0a
Top

Posted by Victorious   (89 posts)  Bio
Date Reply #5 on Mon 21 Nov 2016 09:47 AM (UTC)
Message
I need to send the following packet data to the server to turn it on:
#define BC_ENABLE "\033bc 1\n"

But the following doesn't seem to work.
SendPkt("\033bc 1\n")
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #6 on Mon 21 Nov 2016 09:01 PM (UTC)
Message
Because of the difference between Lua and C you need to change the code from octal to decimal. In other words:


SendPkt("\027bc 1\n")


I've confirmed that works, providing you do it before logging on. For example, detect one of the initial messages in a trigger, like:


               Welcome to BatMUD, Online since 14th April 1990!


Then send the response.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #7 on Mon 21 Nov 2016 10:37 PM (UTC)
Message
I've made a bit of progress interpreting the codes.

Template:saveplugin=BatMUD_Analyzer To save and install the BatMUD_Analyzer plugin do this:
  1. Copy between the lines below (to the Clipboard)
  2. Open a text editor (such as Notepad) and paste the plugin into it
  3. Save to disk on your PC, preferably in your plugins directory, as BatMUD_Analyzer.xml
  4. Go to the MUSHclient File menu -> Plugins
  5. Click "Add"
  6. Choose the file BatMUD_Analyzer.xml (which you just saved in step 3) as a plugin
  7. Click "Close"



<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>

<muclient>
<plugin
   name="BatMUD_Analyzer"
   author="Nick Gammon"
   id="cff91f04c0d78149c0dd6258"
   language="Lua"
   purpose="Detects BatMUD sequences"
   date_written="2016-11-21 08:20"
   date_modified="2016-11-22 09:30"
   requires="4.80"
   version="1.1"
   >
<description trim="y">
Detects incoming packets in the BatMUD format.
</description>

</plugin>

<!--  Script  -->

<script>
<![CDATA[

local ESC = "\027"
local REGEXP = ESC .. "<(%d%d)(.-)" .. ESC .. ">%1"  -- ESC < nn (args) ESC > nn
local ARG_DELIMITER = ESC .. "|"

-- breaks up a line into AAAAA ESC| BBBBB
function splitArgs (args)
  local a, b = string.match (args, "^(.*)" .. ARG_DELIMITER .. "(.*)$")
  return a, b
end -- splitArgs

knownWindows = { }

function showInfoWindow (name, contents, left, top, fgColour, bgColour)
  local win = GetPluginID () .. "_" .. name
  local font = "f"
  WindowCreate (win, left, top, 1, 1, miniwin.pos_top_left, 0, 0)
  WindowFont (win, font, "Courier", 10, true)
  height = WindowFontInfo (win, font, 1)  + 10
  width = WindowTextWidth (win, font, contents) + 10
  WindowCreate (win, left, top, width, height, miniwin.pos_top_left, miniwin.create_absolute_location, ColourNameToRGB (bgColour))
  WindowText (win, font, contents, 5, 5, 0, 0, ColourNameToRGB (fgColour))
  WindowShow (win, true)
  knownWindows [win] = true  -- remember it exists
end -- showInfoWindow

function ResetOutput (args)
end -- ResetOutput

function ConnectedOK (args)
  ColourNote ("green", "", "Connected to BatMUD OK")
end -- ConnectedOK

function ConnectionFailed (args)
  ColourNote ("red", "", "Failed to connect: " .. args)
end -- ConnectionFailed

function OutputMessage (args)
  local msgType, message = splitArgs (args)
  if msgType == 'spec_prompt' then
    showInfoWindow ("prompt", 
                  message,
                  0, 50, "saddlebrown", "papayawhip")
  else
    ColourNote ("cyan", "", msgType .. ": " .. message)
  end -- if
  
end -- OutputMessage

function InGameLink (args)
  local command, where = splitArgs (args)
  ColourNote ("tomato", "", "Typing " .. command .. " gives " .. where)
end -- InGameLink

function PlayerHealth (args)
  local hp, hpmax, sp, spmax, ep, epmax = string.match (args, "^(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)%s+(%d+)$")
  if not hp then
    ColourNote ("white", "red", "Cannot interpret PlayerHealth line: " .. args)
    return
  end -- if not recognized 
  showInfoWindow ("health", 
                  string.format ("HP: %d, MaxHP: %d, SP: %d, MaxSP: %d, EP: %d, MaxEP: %d",
                                  hp, hpmax, sp, spmax, ep, epmax),
                  0, 0, "blue", "bisque")
                                 
end -- PlayerHealth

function PlayerName (args)
  local name, surname, race, level, gender, experience = string.match (args, "^([%a%d]+)%s+([%a%d]+)%s+([%a%d]+)%s+(%d+)%s+(%d+)%s+(%d+)$")
  if not name then
    ColourNote ("white", "red", "Cannot interpret PlayerName line: " .. args)
    return
  end -- if not recognized 
  
  if surname == '0' then
    surname = "<none>"
  end -- if
  
  if gender == '0' then
    gender = "Neutral"
  elseif gender == '1' then
    gender = "Male"
  elseif gender == '2' then
    gender = "Female"
  end -- if gender 

  showInfoWindow ("name", 
                string.format ("Name: %s, Surname: %s, Race: %s, Level: %d, Gender: %s, Experience: %d",
                    name, surname, race, level, gender, experience),
                0, 25, "darkorange", "papayawhip")

end -- PlayerName

function PlayerStatus (args)
  unconscious, stunned, dead = string.match (args, "^(%d+)%s+(%d+)%s+(%d+)$")
  if not unconscious then
    ColourNote ("white", "red", "Cannot interpret PlayerStatus line: " .. args)
    return
  end -- if not recognized 
  if unconscious ~= '0' then
    ColourNote ("yellow", "", "Unconscious")
  end -- if
  if stunned ~= '0' then
    ColourNote ("yellow", "", "Stunned")
  end -- if
  if dead ~= '0' then
    ColourNote ("yellow", "", "Dead")
  end -- if
end -- PlayerStatus

handlers = {
  [0] = ResetOutput,
  [5] = ConnectedOK,
  [6] = ConnectionFailed,
  [10] = OutputMessage,
  [31] = InGameLink,
  [50] = PlayerHealth,
  [52] = PlayerName,
  [54] = PlayerStatus,

} -- end of handlers

-- do something with the message from the MUD
function handleMessage (code, args)
  handler = handlers [code]
  if handler then
    handler (args)
  else
    ColourNote ("yellow", "", "Got code " .. code)
    ColourNote ("green", "", "Got args " .. args)
  end -- if
end -- handleMessage

function processSequence (code, args)
  -- handle embedded sequence
  args = string.gsub (args, REGEXP, processSequence)  -- recursive call

  -- put into a table for later processing
  -- insert at position 1 because the embedded ones will be detected first
  table.insert (incoming, 1, { code = tonumber (code), args = args } )
  
  return ""  -- omit from packet
end -- processSequence

-- strip BatMUD stuff out of packet, handle it separately
function OnPluginPacketReceived (s)
  incoming = { }
  s, count = string.gsub (s, REGEXP, processSequence)
  if count > 0 then
    for k, v in ipairs (incoming) do
      handleMessage (v.code, v.args)
    end -- for each one
  end -- some codes found
    
  return s  -- return fixed packet
end -- function OnPluginPacketReceived

-- when connected, activate the client protocol
function OnPluginConnect ()
  SendPkt("\027bc 1\n")   -- enable BatClient stuff
end -- function

-- hide all miniwindows on disconnect
function OnPluginDisconnect ()
  for k, v in pairs (knownWindows) do
    WindowShow (k, false)
  end -- for
end -- OnPluginDisconnect

]]>
</script>

</muclient>


We don't seem to be getting location information, but what I do see when I move around is:


spec_map: NoMapSupport


I was expecting to see message 60 (player location).

There must be another code to enable mapping.

Example screenshot:


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #8 on Tue 22 Nov 2016 05:34 AM (UTC)
Message
Victorious said:

Thanks for the sample code. Is there any documentation about the protocol and how control codes work?


What do you mean? The link you provided in the original post has all the documentation.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Victorious   (89 posts)  Bio
Date Reply #9 on Tue 22 Nov 2016 12:09 PM (UTC)

Amended on Tue 22 Nov 2016 12:20 PM (UTC) by Victorious

Message
It seems to assume some prior knowledge. E.g, what is ESC and nn? Is this part of the underlying telnet protocol? Also, additional data is appended after sending a packet. E.g from the send to enable batclient stuff,

Sent packet: 1 (6 bytes) at Tuesday, November 22, 2016, 8:15:44 PM

.bc 1. 1b 62 63 20 31 0a

My understand of how muds send information is that its sent in ascii plaintext.
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #10 on Tue 22 Nov 2016 08:35 PM (UTC)

Amended on Tue 22 Nov 2016 08:36 PM (UTC) by Nick Gammon

Message
Victorious said:

It seems to assume some prior knowledge. E.g, what is ESC and nn?


Have you looked at the plugin I posted? ESC is a standard way of describing the "Escape" byte, known as hex 0x1b (octal 33 and decimal 27).

nn refers to two digits (eg. 42).

Quote:

Is this part of the underlying telnet protocol?


It's not Telnet, it's their own protocol. But yes, their protocol is as they described it.

Quote:

Also, additional data is appended after sending a packet. E.g from the send to enable batclient stuff,

Sent packet: 1 (6 bytes) at Tuesday, November 22, 2016, 8:15:44 PM

.bc 1. 1b 62 63 20 31 0a


The packet debug shows the packet twice. Once in ASCII on the left, and repeated on the right in hex. That is because some of the characters (eg. ESC) are not printable. So you can see that it is sending ESC (0x1B), "bc 1" (0x62, 0x63, 0x20, 0x31) followed by newline (0x0a).

Quote:

My understand of how muds send information is that its sent in ascii plaintext.


Not in this case, their use of ESC allows them to send "out of band" messages. It's a common enough technique. The client interprets the out of band stuff separately, and strips it from what is displayed on the screen.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,122 posts)  Bio   Forum Administrator
Date Reply #11 on Fri 25 Nov 2016 05:05 AM (UTC)
Message
For anyone reading this page: Further testing shows that the plugin does not correctly process more advanced information, like the in-game map. This is because the plugin displays the out-of-band stuff first, and then sends the packet to MUSHclient's input processor. For certain types of lines this jumbles up the output.

It needs to be rewritten to process things exactly in the sequence they are received, probably by doing everything in OnPluginPacketReceived (including ordinary text) and then returning an empty packet.

This will have various implications (for example, handling Telnet negotiations and colours).

It won't be trivial to do, but it probably can be done.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Victorious   (89 posts)  Bio
Date Reply #12 on Fri 25 Nov 2016 05:18 AM (UTC)
Message
Thannks Nick. I'm trying to see if the developers are willing to release a more updated specification, as that post is over 3 years old.
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


30,095 views.

Posting of new messages is disabled at present.

Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.