Fully enabled ATCP plugin

Posted by Twisol on Fri 17 Apr 2009 07:04 AM — 24 posts, 94,995 views.

USA #0
I've been working with Trevize's ATCP plugin for a while now, and recently I began modifying it, as you might've deduced from my recent posts here. I've modified it heavily, stripping out bits that weren't needed and updating the functionality a bit. The resulting plugin can now handle any incoming ATCP packet, and it implements a function for sending ATCP messages to the server as well.

The old plugin was restricted to only three messages, and it identified them in BroadcastPlugin() with simple numeric IDs. I changed this so that all message broadcasts had an ID of 1, and the message was packed in with the content in the format "<message>|<content>". I've also added support for enabling of various ATCP command modules, such as 'composer' and 'ping'. Because the plugin needs to know what to enable before connecting, it broadcasts an ID of 0, which client plugins can respond to by calling its EnableModule method. (Thanks to Nick for this technique)

When a module is enabled, various messages included in the module may be sent by the server, which will be broadcast to listening plugins. Modules may also contain client-side messages, such as 'keepalive' which prevents the client from timing out, and 'ping' which speaks for itself. As a basic form of validation, client-side messages can only be sent through the plugin if their associated modules have been enabled. (Basic argument type-checking is also performed on a per-message basis)

To aid in ATCP development, a 'header' file of sorts is included with the plugin, atcp_lua.xml. This file includes definitions such as a variable containing the ATCP plugin's ID, an EnableModuleATCP() function to more easily call the ATCP plugin's EnableModule function, a SendATCP() function that's much the same, and an UnpackATCP() function that splits the broadcast message into two variables (msg and content) and returns them. To use these utilities, add the following line into your plugin file, before the <script> tag:
<include name="atcp_lua.xml" />


Here's an example OnPluginBroadcast definition that shows how easy it is to work with the plugin:


OnPluginBroadcast - function(msg, id, name, text)
  if id == idATCP then
    if msg == 0 then
      EnableModuleATCP("room_brief")
    else
      msg, text = UnpackATCP(text)
      if (msg == "Room.Brief") then
        SetStatus(text)
      end
    end
  end
end


Download page: http://www.jonathan.com/atcp
My documentation of ATCP messages: http://www.jonathan.com/atcp/doc.html

References:
1. Trevize's ATCP plugin: http://www.gammon.com.au/forum/bbshowpost.php?id=8915
2. ATCP documentation: http://www.ironrealms.com/nexus/atcp.html


(If Nick, in his infinite wisdom, sees fit to add this plugin to his plugins page, I will be very, very happy. >_>)
Amended on Fri 17 Apr 2009 07:05 AM by Twisol
Australia Forum Administrator #1
The four files Twisol refers to can be obtained from here:

http://mushclient.com/plugins/Twisol/
USA #2
What is ATCP for? That documentation on ironrealms talks about details but not the motivation/purpose of ATCP (or even what the name means).
USA #3
It's basically similar to MXP, I suppose, and it was created because at the time, MXP didn't exist. All four (soon to be six, I believe) Iron Realms MUDs are ATCP-enabled.

ATCP stands for Achaean Transport Client Protocol. Another page that's not so clear on the details, but gives a brief overview, can be found here: http://www.ironrealms.com/rapture/manual/files/FeatATCP-txt.html


Nick: Wow, thanks. :D


EDIT: If you happened to see the previous EDIT message I had here, ignore it. My browser had a quirk displaying XML source...
Amended on Fri 17 Apr 2009 06:36 PM by Twisol
Netherlands #4
ATCP basically supplies extra information to clients about the game, and works a fair bit differently than MXP. Whereas MXP is negotiated through Telnet once and then mixes its tags (which describe layout, colours and whatnot) in the middle of regular output. With ATCP, any extra information is relayed through telnet data channels (IAC SB ATCP .. IAC SE ATCP if I recall properly) so it will never interfere with the regular output.

It's basically a pure two-way data channel that works with a simple key-like structure.
USA #5
That's pretty much correct, except that the ATCP is also only "negotiated" once; the start/end byte sequences are in the output text itself, so you have to look for them and remove the ATCP line from the packet before writing the packet data. I thought that was pretty much what MXP did too.

(Also, the end sequence doesn't have an extra ATCP, it's only two bytes)
Amended on Fri 17 Apr 2009 06:43 PM by Twisol
Netherlands #6
Correct. :) I wrote my own implementation quite some time ago - I bet this is one of those plugins tons of people keep rewriting in their own preferred method. :D

Technically, everything is in the same stream of bits and bytes. The only method to get at said data in MUSHclient is through OnPluginPacketReceived, where you can filter it out also.

On a protocol level however, descriptions of Telnet on RFCs which discuss telnet option negotiation (and suboption negotiation), the data discussed through IAC sequences is considered seperate. To be compliant with the telnet specification, clients -need- to ignore what is inbetween the options if they cannot understand it.

That is basically where ATCP and MXP differ technically, post-negotiation. One messes with input as the client receives it, while the other one continues to do its work below the radar. (On a sidenote - I hate the person who designed MXP, then deviated of it in its own implementation to the point where the 'borked' implementation became the standard... but I digress. :D)
USA #7
Interesting, thanks. I'm curious, did your implementation allow for sending as well as recieving? >_>

EDIT: Oh, interesting. I seem to have added a feature that they put in server-side too. It checks outgoing messages to be sure that the appropriate module is enabled, otherwise it doesn't send it - it's mentioned in that Rapture manual page I linked. :D
Amended on Fri 17 Apr 2009 07:08 PM by Twisol
Netherlands #8
Mine allows for sending too. It's not too difficult - it's pretty much the same except in the opposite way. SendPkt() is your friend.
USA #9
Yup. Okay, cool, thanks. =)
USA #10
What's the difference more or less between ATCP and ZMP? Probably just that the former was popularized by the Iron Realms games...
USA #11
Er, what's ZMP? <_<
USA #12
Heh. :-)
http://sourcemud.org/wiki/ZenithMudProtocol

After reading your description of ATCP, it looks like it's basically the same idea: a way to communicate information without using the main communication channel.
Netherlands #13
I think it is the same kind of reason you had VHS and Betamax, with the latter losing even though it predated VHS and was superior. It just gains more popularity because more people use it.
USA #14
Yeah, in fact ZMP looks almost identical to ATCP.
USA #15
Uploaded a new version to http://www.jonathan.com/atcp/atcp.xml, to fix a bug related to enabling modules more than once. (Would appreciate it if your mushclient.com version was replaced, too?)

EDIT: oops, -now- it's uploaded.
Amended on Thu 23 Apr 2009 01:09 AM by Twisol
Australia Forum Administrator #16
Uploaded now.

Suggest in future you update the version number in the plugin, like this:


<muclient>
<plugin
   name="ATCP"
   author="Soludra"
   id="7c08e2961c5e20e5bdbf7fc5"
   language="Lua"
   purpose="ATCP data"
   date_written="2008-09-18"
   date_modified="2009-04-23 12:15"
   requires="4.35"
   version="2.0"
   >
</plugin>

USA #17
Oops, yes, thanks. >_>
USA #18
I'm proud to release ATCP v2.0! This version replaces the old interface with a PPI-exposed pair of methods, allowing client code to register method callbacks. It also requires MUSHclient v4.46, in preparation for the public release of PPI, but you could get PPI separately and change the version requirement if you really wanted.

The full ATCP plugin is posted in the next post due to length. I've pasted my Roomname plugin here, as an example of how to use the new ATCP interface.

<!DOCTYPE muclient> 
 
<muclient> 
<plugin
   name="roomname"
   author="Soludra"
   id="e94f6bd8509a2b00d10cb226"
   language="Lua"
   purpose="Adds a Nexus room name to the status bar"
   date_written="2008-08-23"
   date_modified="2010-01-15 12:27:00"
   requires="4.35"
   version="2.0"
   > 
</plugin> 
<script><![CDATA[

-- Used to load the ATCP interface
PPI = require("ppi")

-- Will contain the ATCP interface
atcp = nil

-- Executed when you get an ATCP message!
OnRoomBrief = function(message, content)
  SetStatus(content .. ".")
end

-- Use this to load the ATCP library
OnPluginListChanged = function()
  local atcp, reloaded = PPI.Load("7c08e2961c5e20e5bdbf7fc5")
  if not atcp then
    -- Normally, you might put an error or a warning note here.
    -- Roomname won't do anything if ATCP isn't available, so
    -- it's safe to leave it running until ATCP comes online.
  elseif reloaded then
    -- Registers a function to call when Room.Brief is received.
    atcp.Listen("Room.Brief", OnRoomBrief)
    -- Sets the local 'atcp' variable to the global created before.
    _G.atcp = atcp
  end
end
 
]]></script> 
 
</muclient>
USA #19
Well, the plugin is too large for the 6000-character maximum even on its own, so here's a pastebin link:

http://mushclient.pastebin.com/fe1d0a12
Australia Forum Administrator #20
Twisol said:

Well, the plugin is too large for the 6000-character maximum even on its own, so here's a pastebin link:



The length restriction on your posts has been removed.
USA #21
Thank you, I appreciate it!
Australia Forum Administrator #22
Worstje said:

Technically, everything is in the same stream of bits and bytes. The only method to get at said data in MUSHclient is through OnPluginPacketReceived, where you can filter it out also.


Out of curiosity, is there any limit on the amount of data that can appear (from server to client) inside the telnet negotiation sequence? I saw a reference to 2048 bytes, but that was from client to server.
USA #23
I can't find an explicit limit, although I admit I didn't look too hard (looking through [1]). I do imagine that there would be a 'sensible' limit of 1024, or 2048 based on what you said.

[1]: http://www.omnifarious.org/~hopper/telnet-rfc.html