[Home] [Downloads] [Search] [Help/forum]


Register forum user name Search FAQ

Gammon Forum

[Folder]  Entire forum
-> [Folder]  MUSHclient
. -> [Folder]  General
. . -> [Subject]  Sending hidden text from the mud

Sending hidden text from the mud

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Posted by KaVir   Germany  (117 posts)  [Biography] bio
Date Tue 30 Jun 2009 12:04 PM (UTC)
Message
I'd heard good things about MUSHclient, but never got around to checking it out until recently, when I was updating the links on my mud's website. I downloaded it and gave it a whirl, and was very impressed - particularly by the plugin concept. I reckon it's flexible enough to let me create a fairly decent graphical front-end for my mud, despite my horrendous artistic skills.

However I do have a question, which cropped up shortly after I started looking at the existing plugins:

Is there some way to format text (within the mud itself) so that when it's sent to the client it can be used by plugins, but is never displayed to the user?

To give a common example...supposing I want to have a status bar, which displays health and mana. The approach I've seen in existing plugins is to search for a specific text pattern, extract the appropriate data, and use it to update the status bar.

But what I'd like to do is add a "MUSHclient status bar" config option within the mud, that (when switched on) sends updated health and mana values to the user whenever those values change. The player wouldn't need a specific prompt format (or indeed any prompt at all), and the values would simply be ignored if they didn't have the appropriate config option set (obviously other clients would get spammed with junk data, but that's there own fault for setting the config option in the first place).

I searched around, and noticed a plugin for gagging individual words, which I suppose would be one way of doing it. But I was wondering if there was anything built-in, or at least an easier way of handling it - something obvious that I may have overlooked, perhaps?
[Go to top] top

Posted by Bast   (78 posts)  [Biography] bio
Date Reply #1 on Tue 30 Jun 2009 01:51 PM (UTC)
Message
You could do this with telnet codes. Aardwolf implemented them to do this sort of thing.

Look at the telnet_options.lua plugin in the forums or the Aardwolf Mushclient. I don't have code to implement them in the server though.

Aardwolf also has a statmon option that some of the Aardwolf plugins use. It is a comma delimited string that has a whole bunch of information in it.

Aardwolf has also implemented a general tags feature that can be turned on or off depending on the user. Aardwolf uses this for things like spells, map, room, and char data.

Couldn't you also use something like MXP?

Bast

Bast

Scripts: http://github.com/endavis
[Go to top] top

Posted by Fadedparadox   USA  (91 posts)  [Biography] bio
Date Reply #2 on Tue 30 Jun 2009 05:06 PM (UTC)
Message
MXP would be easy to do this with, I'd think.
[Go to top] top

Posted by KaVir   Germany  (117 posts)  [Biography] bio
Date Reply #3 on Tue 30 Jun 2009 05:39 PM (UTC)
Message
Thanks for the tips, MXP looks promising.
[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #4 on Tue 30 Jun 2009 09:36 PM (UTC)
Message
Bast is correct, Aardwolf used telnet codes for some things, and a "statmon" command for others.

MXP could also be used.

I personally prefer the {stats} line for a few reasons...


  • It is simple for the server to implement - no telnet codes needed (nor the worry about whether the client will support them).

  • Implementing MXP is non-trivial, as you need to worry about "escaping" the use of < > and & everywhere text is sent to the MUD.


An example of what Aardwolf did with the {stats} line was:


{stats}39/30,16/13,13/13,28/25,29/25,13/10,93,100,100,44,34,You are Standing.,9999,532/567,510/510,880/880,3199,0,0,2500,241,25,5


1="39/30"     -->  current str / base str,
2="16/13"     -->  current int / base int,  
3="13/13"     -->  current wis / base wis,  
4="28/25"     -->  current dex / base dex,  
5="29/25"     -->   current con / base con,  
6="13/10"     -->   current luck / base luck,
7="93"        --> hp percent,
8="100"       --> mana percent,
9="100"       --> moves percent,
10="44"       -->  hitroll,
11="34"       --> damroll, 
12="You are Standing."  --> position, 
13="9999"     --> enemy pct (9999 if not fighting),
14="532/567"  --> hp / maxhp,
15="510/510"  --> mana / max mana,
16="880/880"  --> moves / max moves, 
17="3199"     --> gold,
18="0"        --> qp,
19="0"        --> tp,
20="2500"     --> align,
21="241"      --> exp to level,
22="25        --> current level
23="5"        --> position


(This was a single line).

Now a simple trigger, matching: {stats}*

... would capture this (and omit from output of course). Then a simple string.match could break the line up into its component parts.

I think next time I would use key/value pairs, which is more flexible for future expansion. eg.


{stats}str="39/30",int="16/13",wis="13/13" ... etc.


Then Lasher built into the MUD a command ("statmon on" I think) which turned on this display. Any client that wanted to use it would then just send "statmon on" and away it went.

A similar thing was done for the current room name, where you are on the overland map, and so on.

The only real problem we had with this was getting started - for example, for the client to make sure "statmon" status was on, it had to send "statmon on", however the client couldn't really be sure it was at "command prompt" level in the MUD. For example, the MUD might have made a MOTD announcement, and be showing "Press <return> when read.". Now if this was the moment the client sent "statmon on" then it wouldn't "take".

To work around this we used telnet codes, simply as toggles for the various extra information types.
This meant sending a telnet negotiation sequence like:


IAC, SB, AARDWOLF_TELOPT, which, TELOPT_ON, IAC, SE


(where AARDWOLF_TELOPT was 102, TELOPT_ON was 1, and which was 1 for statmon).

Now the server would catch this at a lower level than the command level, so it would always work, and set a flag appropriately for that player's descriptor.




As for MXP, it could be done that way, but adding MXP to the server is non-trivial. I did a page about that:

http://www.gammon.com.au/mushclient/addingservermxp.htm

You need to be careful, or you will be forever plagued by stray < and > characters in room descriptions, pages etc. which disappear from view depending on which client the players are using (and not all clients support MXP).

Also, MXP is really a mark-up language for MUD output (and it works fairly well for that), but isn't really designed for sending out-of-band data which isn't displayed. What I mean by that is, if you are receiving your current HP, MXP can colour it red, and make it a hyperlink by marking it up like HTML does. But to simply send data to the client, which isn't initially viewed, you really need to set up MXP variables with "entity" statements, which is a bit fiddly.




The plugins I wrote for Aardwolf, which parse the {stats} line, and display player status in various nice windows are all available from this site, if you want to look at them as a basis for your own design.

http://www.gammon.com.au/mushclient/plugins/Aardwolf/

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by David Haley   USA  (3,881 posts)  [Biography] bio
Date Reply #5 on Tue 30 Jun 2009 11:42 PM (UTC)
Message
It should be pretty easy to implement ZMP using a plugin, too, and then you could implement your own ZMP package to do exactly what you need.

David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone

http://david.the-haleys.org
[Go to top] top

Posted by KaVir   Germany  (117 posts)  [Biography] bio
Date Reply #6 on Wed 01 Jul 2009 03:45 PM (UTC)
Message
Thanks for the response, Nick. I took a look at your "stats detector" plugin, and if I understand it correctly, it seems to be a trigger which matches "{stats}*" and uses omit_from_output to stop that entire line of text from being displayed to the user. If so, that's exactly what I was after.

I already support a 'display' command which will no doubt make this easier (it echos the specified text back to the user, but can include a wide range of variables, rather like an expanded customisable prompt). So for example if I wanted a Diablo2-style graphical skill tree in one of the windows, I could retrieve the data via 'display' - the stats line would then only need to contain some sort of 'dirty' timestamp indicating the last time a skill had changed.

ZMP looks interesting, but I was rather hoping for a quick and simple solution, at least to start with. I'll worry about going back and cleaning up the server-side part if/when I can get something that looks pretty on the client end.
[Go to top] top

Posted by KaVir   Germany  (117 posts)  [Biography] bio
Date Reply #7 on Mon 19 Apr 2010 09:51 AM (UTC)
Message
Sorry to necro an old thread, but this is a follow-up to my post last year, as (after getting sidetracked with other things) I finally got back to looking at this issue. One of my players created a TinTin++ status bar script which works by pummeling the server with data requests, and I decided it was about time I came up with a more effective way for him to retrieve the data he wanted.

Although I did originally add ZMP, and even toyed with the idea of using the Aardwolf 102 protocol, I finally settled on using MSDP - and after reading about the new "out of bands" support, I realised that MUSHclient could use MSDP as well.

You can read about MSDP here: http://tintin.sourceforge.net/msdp/

The basic gist is that after establishing a handshake, the client can ask the server to send it one or more variables at any time. But more importantly, it can also ask the server to observe certain variables and send an update whenever they change. And the syntax is really simple, which keeps things easy for the plugin writer.

I've never programmed in Lua before and it feels strange to use something other than C or C++ after so long, so this may not be the best approach - but it's simple, it works, and it's proof of concept, so I thought it might be of interest to others (feel free to do whatever you like with it). I guess it would probably work with versions as early as 4.48, but I've only tried it with 4.51 (as I know later versions have included more work on negotiation).

<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Friday, April 16, 2010, 11:17 PM -->
<!-- MuClient version 4.51 -->

<!-- Plugin "MSDP_Support" generated by Plugin Wizard -->

<muclient>
<plugin
   name="MSDP_Support"
   author="KaVir"
   id="a2efb182be2df05bf58658b2"
   language="Lua"
   purpose="Switches on MSDP"
   date_written="2010-04-16 23:15:33"
   requires="4.51"
   version="1.0"
   >

</plugin>

<!--  Get our standard constants -->

<include name="constants.lua"/>

<!--  Script  -->

<script>
<![CDATA[

local MSDP = 69

function OnPluginTelnetRequest (type, data)
  if type == MSDP and data == "WILL" then
    Note ("Received IAC WILL MSDP" )
    return true
  elseif type == MSDP and data == "SENT_DO" then
    -- IAC SB MSDP response IAC SE 
    Note ("Sent IAC SB MSDP MSDP_VAR \"REPORT\" MSDP_VAL \"ACTIONS\" MSDP_VAL \"ACTIONS_MAX\" IAC SE")
    SendPkt ("\255\250\69\1REPORT\2ACTIONS\2ACTIONS_MAX\255\240")
  end -- if
end -- function OnPluginTelnetRequest

function OnPluginTelnetSubnegotiation (type, data)
  if type == MSDP then
    Note ("Received IAC SB MSDP " )
    endpos=string.len(data)
    for i=endpos,1,-1 do
      if string.byte(data,i) == 1 then
        variable = string.sub(data,i+1,endpos)
        StoreVariable(variable, value)
        endpos = i-1
      elseif string.byte(data,i) == 2 then
        value = string.sub(data,i+1,endpos)
        endpos = i-1
      end -- if
    end -- for
    Note ("IAC SE")
  end -- if
end -- function OnPluginTelnetSubnegotiation

function StoreVariable (MSDP_var, MSDP_val)
  Note ("MSDP_VAR \"", MSDP_var, "\" MSDP_VAL \"", MSDP_val, "\"")
  if MSDP_var == "ACTIONS_MAX" then
    MSDP_ACTIONS_MAX = tonumber (MSDP_val)
  elseif MSDP_var == "ACTIONS" then
    MSDP_ACTIONS = tonumber (MSDP_val)
  end -- if
end -- function StoreVariable

]]>
</script>

</muclient>


Of course that's only the basic data exchange (and if a variable sends multiple values it ignores the extra ones). But it's really easy to add it to existing plugins - I already tested it out using one of Nick's graphical bar plugins, stripping out the trigger and using MSDP to update the variables. It works great, and personally I think it looks much nicer to see the bars slowly going up even when the rest of the screen is stationary.

I'm thinking of using it for updating a map as well, and perhaps even have the map show other people moving around.

Nick: Do you mind if I modify some of your plugins to use MSDP and then make them available to my players? Although I don't know of any other muds that use MSDP, over a third of my active players use MUSHclient, so it would be really nice to be able to offer them a selection of plugins that take advantage of the new protocol.

[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #8 on Mon 19 Apr 2010 09:59 AM (UTC)
Message
No I don't mind at all. :-)

I am pleased that the basic idea of exchanging data between client and server is being put to practical use with a minimal amount of fuss.

Using the telnet protocol hides the data from players who may have disabled the plugin for some reason, so that is a good approach.

I'm not quite following the code in OnPluginTelnetSubnegotiation, there is probably a simpler way of doing whatever it is you are doing there, but a bit more playing around with Lua (see string.match or string.gmatch) and you will probably work out a better way.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by KaVir   Germany  (117 posts)  [Biography] bio
Date Reply #9 on Mon 19 Apr 2010 10:41 AM (UTC)
Message
Thanks - I'm still new to this, and I'm not aware of my experienced plugin writers among my playerbase, so it'll make it much easier to put an initial set of plugins together if I can copy and paste!

In regard to the code in OnPluginTelnetSubnegotiation, the server can send multiple variables at a time (with a variable preceded by MSDP_VAR=1 and a value preceded by MSDP_VAL=2), so although you might get a simple:

IAC SB MSDP MSDP_VAR "HEALTH" MSDP_VAL "100" IAC SE (i.e., \255\250\69\1HEALTH\2100\255\240)

You might also receive something like:

IAC SB MSDP MSDP_VAR "HEALTH" MSDP_VAL "100" MSDP_VAR "MANA" MSDP_VAL "50" MSDP_VAR "ACTIONS" MSDP_VAL "250" IAC SE

And it would even be perfectly valid for the server to send something like:

IAC SB MSDP MSDP_VAR "MUD_PORT" MSDP_VAL "3000" MSDP_VAL "3001" IAC SE

In which case the client may choose to use both values or discard one (I decided just to use the first value for now, to keep things simple).

I figured the easiest solution was just to loop backwards through the string until I hit an MSDP_VAR or MSDP_VAL, and then use string.sub to pull out the token and store it as a variable or value respectively. As a variable is always sent before its value, I can't store the value until I've also retrieved the variable name - and because there can be multiple values in a row I can't just assume that a value will be directly preceded by its associated variable.

Perhaps it's not the best way to do it, but I've never programmed in Lua before so I just used the easiest approach I could think of and googled for the Lua syntax. I'm open to any better solutions, and will no doubt rewrite it when I've had a bit of practice, but I was mostly just interested in seeing if it worked - and it does, perfectly. The MUSHclient interface documentation was clear enough that I was able to add MSDP support in an afternoon with no prior knowledge of Lua.
[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #10 on Mon 19 Apr 2010 10:07 PM (UTC)
Message
You could try this:


test = "\001health\002100\001mana\00264\001health\002500"

t = {}

for field, value in string.gmatch (test, "\001(%C*)\002(%C*)") do
  t [field] = value
end -- for

for field, value in pairs (t) do
  print (field, "=", value)
end -- for


Output:


health = 500
mana = 64


By using a Lua table in the initial pass, only the latest value is stored.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] top

Posted by Nick Gammon   Australia  (22,975 posts)  [Biography] bio   Forum Administrator
Date Reply #11 on Wed 21 Apr 2010 12:09 AM (UTC)
Message
To use the first value instead of the last, change to:


t = {}

for field, value in string.gmatch (test, "\001(%C*)\002(%C*)") do
  t [field] = t [field] or value
end -- for


The short-circuit "or" condition keeps the original value if there is one.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
[Go to top] 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.


34,651 views.

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

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

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at HostDash]