New custom status bar window

Posted by Nick Gammon on Tue 30 Nov 2004 09:31 PM — 60 posts, 242,764 views.

Australia Forum Administrator #0
Once again I am revisiting the idea of having a standalone custom status bar. This subject comes up from time to time, and it always seems a bit problematic to get it working properly.

A while back I did a Visual Basic plugin that showed an external "status bar" window, but that had various problems ...

  • To compile it you needed Visual Basic, which costs money, and takes time to learn
  • You need to register the control with the Windows registry, something that didn't always work for me
  • It used COM objects, which seem to have a habit of popping in and out of memory when you don't want them to


"There must be a better way", I thought.

So this time, I looked at doing it in a way that could be easily reproduced, using free compilers, such as Cygwin. Also, that didn't need to be registered in the Registry.

I toyed with the idea of doing it in Lua, but abandoned that principally because Lua is standard C, not a system for writing Windows GUI programs.

Then I thought of MFC (Microsoft Foundation Class) libraries, but again, you need to own them, and Visual Studio, which isn't a free solution.

Finally I reached for my old copy of "Programming Windows 95" by Charles Petzold, and looked at doing a small Windows program in C "from scratch".

This turned out to be surprisingly easy. Using an example program from Chapter 2, it was possible to do a simple external status bar program (see screen shots below).

The entire source is in a single file of just over 700 lines. The source is available in the download, so you can modify it, or just use the precompiled version if that does the trick for you.

They are both available here (33 Kb):


http://www.gammon.com.au/files/utils/statusbar.zip


The status bar window is designed to be configurable on-the-fly by MUSHclient (or another program) by sending "commands" to it, as will be discussed shortly. The commands would be to do things like:

  • Set window title
  • Set size and position of window on the screen
  • Define text colour
  • Define background colour
  • Define font for drawing text
  • Define a set of labels for status bars (eg. hp, mana, movement, xp)
  • Set current values as a percentage (eg. 80,40,70,20)
  • Set colours for drawing each bar - both "bar" colour and filler colour
  • Define how big each bar is (width and height) and gaps between linees
  • Tell the window to quit


In the next post is a screenshot of it in operation.

Australia Forum Administrator #1

Here is what the status bar window can look like (the exact appearance can be configured heavily as mentioned above) ...

Australia Forum Administrator #2
Communicating with the status bar

The trick with any system like this is to get messages to it - tell it to update the bar widths, for example.

I wanted to avoid using COM, which always seems to cause problems, you need to register controls, make sure they exist, worry about what happens if the control window is closed an so on.

Then the idea occurred - use UDP (User Datagram Protocol).

UDP is a "connectionless" protocol for sending packets around the Internet. It is quick because you don't need to establish a connection, however there is a danger that packets may be lost. This is not a big issue with a status bar running on your own PC. Sending a packet from one program to another on the same PC is unlikely to take very long, and is unlikely to get lost. Even if it does, it just means the status bar might be slighly out of date for a moment.

The great thing about UDP is it doesn't matter if the status bar goes away, the packets will simply be discarded. Also you can leave the status bar program running all the time. If MUSHclient sends it information it will update, and if not, no matter.

Indeed you can have a bit of fun, by using a "broadcast address" at the sending end, any PC on the same subnet can receive the same packet. So you could conceivably have multiple PCs all showing the same information.

Getting started

If you download the status bar program above, and run the statusbar.exe file, you will see a window like the one above except without text, and with a white background.

It can be moved and resized in the usual way.

Now even before using MUSHclient we can "talk" to it with any program that sends UDP packets. To help you play with it, I have written a small "UDP send" program. The source and executable (19 Kb) are available from:


http://www.gammon.com.au/files/utils/udpsend.zip


Use a command window to start udpsend like this:


udpsend 127.0.0.1 4111


This tells it to send packets to localhost (your own PC) at port 4111, which is the default port for statusbar.exe.

Now you can send commands to it, eg.


magic,title,hello there,


All being well, the title on the status bar window should update.

Then you can try changing the background colour:


magic,backcolour,25600


The window background should change to dark green.

What is all this 'magic' stuff?

The intention was to make sure that packets received by the statusbar program were intended for it. Thus, each packet is checked for a "magic string" at the start, which defaults to the word "magic". You can configure that when you execute the statusbar program. For example, if you had multiple worlds each with their own status bar, you could use the world name as the magic string.

Amended on Tue 30 Nov 2004 09:48 PM by Nick Gammon
Australia Forum Administrator #3
A more concrete example of talking to the status bar is this example, which I used to do the screenshot above. It uses the Lua socket library to send UDP packets:


dofile "lua.lua"
socket = require "socket"
s = socket.udp ()
s:setpeername ("127.0.0.1", 4111)

s:send ("magic,title,Status Bar - BabbleMUD")
s:send ("magic,config,10,5,100,15,60,320,20")
s:send ("magic,size,10,10,350,140")
s:send ("magic,textcolour," .. ColourNameToRGB ("indigo"))
s:send ("magic,backcolour," .. ColourNameToRGB ("lightsteelblue"))
s:send ("magic,labels,HP,Mana,Move,XP")
s:send ("magic,values,10,20,30,80")
s:send ("magic,font,Comic Sans MS,10,0,700")
s:send ("magic,colours," .. 
        ColourNameToRGB ("darkgreen") .. "," .. 
        ColourNameToRGB ("darkblue") .. "," .. 
        ColourNameToRGB ("saddlebrown") .. "," .. 
        ColourNameToRGB ("dimgray"))
s:send ("magic,fillcolours," .. 
        ColourNameToRGB ("lightgreen") .. "," .. 
        ColourNameToRGB ("lightblue") .. "," .. 
        ColourNameToRGB ("tan") .. "," .. 
        ColourNameToRGB ("gainsboro"))

s:send ("magic,text,5,88,Darkhaven Square")

s:close ()


However since not everyone will want to use Lua to talk to status bars, MUSHclient 3.56 now has a UdpSend function added to it. This simply lets you send a single UDP packet to the network.

Here is an example of using the UdpSend function (in Lua again, but it could just as easily be VBscript) to capture an inventory and display it:


function multiline_trigger (name, line, wildcards)

  local function udp (msg)
    UdpSend ("127.0.0.1", 4111, "m," .. msg)
  end  

  udp ("title,Inventory")
  udp ("size,10,10,300,300")
  udp ("config,10,5,100,15,80,300,20")
  udp ("font,Harem,12")
  udp ("textcolour,0")
  udp ("backcolour,14804223")
  udp ("text,5,5," .. wildcards.inventory)

end -- function


The above example uses a "helper" function to send to the correct address and port, and put the magic word at the start of the line. It illustrates, by the way, the method of declaring "local" functions in Lua, which are functions that are visible only inside another function.

The code above sets the window title and size, sets the font to Harem, defines the text and background colours, and sends the inventory as multi-line text. See below for how it looks:


Amended on Wed 01 Dec 2004 04:06 AM by Nick Gammon
Australia Forum Administrator #4

This is the inventory window done with the above script ...

Australia Forum Administrator #5
This is the multi-line trigger used to update that window:


<triggers>
  <trigger
   enabled="y"
   lines_to_match="20"
   match="(?P&lt;inventory&gt;You are carrying:\n(([ ]{5}.*\n)+)\n)\z"
   multi_line="y"
   regexp="y"
   script="multiline_trigger"
   sequence="100"
  >
  </trigger>
</triggers>

Australia Forum Administrator #6
If you want to add more customisation, then simply update the source for the statusbar window, and then recompile, either using Visual C++ or Cygwin (or your own favourite compiler). To compile under Cygwin:


g++ statusbar.cpp -lwsock32 -lgdi32 -o StatusBar.exe


For example, you might add the ability to play sounds, change the style of the window (maybe make it semi-transparent?), make the labels right-justified, the possibilities are endless.
Greece #7
Nice little program, I'll go test it right now.
By the way, if you need a better(?) UDP program, NetWorkshop on my page (www.poromenos.org) works and it's free.
Greece #8
Hm, there appears to be a small problem with CrLf appearing as two squares, no biggie.
Greece #9
I wonder how hard it would be to make it show a two dimensional progress dialog, to show you how your health or whatever changes over time... Interesting, I'll have to work on it.
Greece #10
By the way, since MC has a UDP send function, could you add a OnUDPReceived(pckPacket) event? It would be great if we could make MC communicate with other instances/people around the world/whatever. Imagine if you could write a plugin to see your friend's status in realtime in a MC window. This would also allow callbacks, since programs could send a packet with the function name and your plugin would call it. I'm not very big on adding extra non-mud functionality, but since we already have UDP send let's be symmetric and enable receive.
Note to developers about something that has not yet even been created: If you want to communicate through UDP I would suggest you did a GetUniqueID and then sent it to the other side as a handshake, then use it as a prefix for every packet you want to receive. This way you make sure the packet is coming from the original source.
Which also leads me to think, which plugin of which world would receive the packet? They could all get it and then choose if they want it, but I'll leave that up for discussion.
Amended on Tue 30 Nov 2004 10:34 PM by Poromenos
Australia Forum Administrator #11
Quote:

NetWorkshop on my page (www.poromenos.org) works and it's free.


Yes, thanks. Mine is free too. :)

The one I wrote comes with source, it is only 168 lines of code, so if you want to incoporate it into a bigger project, it is simple to see how it is done.

As for the cr/lf appearing as boxes, you can suppress that by using a trailing comma, as the strtok function used in the code just looks for commas as delimiters.

In a plugin (eg. a trigger) you wouldn't have cr/lf anyway.
Australia Forum Administrator #12
See UdpListen.
Greece #13
Quote:

Yes, thanks. Mine is free too. :)

Don't be so antagonistic :P I didn't say yours wouldn't work, I was just providing an alternative (and plugging my site!).
Quote:

As for the cr/lf appearing as boxes, you can suppress that by using a trailing comma, as the strtok function used in the code just looks for commas as delimiters.

Yes yes, I noticed right after I had written the comment :)
Quote:

See UdpListen.

Niiice, do they all receive every packet coming? Sounds good.
Australia Forum Administrator #14
I didn't mean it to sound like that. I thought your comment might imply I was charging for mine. You seem to have some interesting stuff on your site. :)

Er yes, it should receive every packet directed at that port, from the IP address you specify.
Greece #15
Ah no, I was just pointing out that mine was also free. This is great, now we can make clients communicate in previously unimaginable ways!
Australia Forum Administrator #16
Quote:

Which also leads me to think, which plugin of which world would receive the packet? They could all get it and then choose if they want it, but I'll leave that up for discussion.


I didn't want this idea to go crazy, it remembers which plugin installed the listener, and that one gets it. However you could have multiple plugins that listen on different ports.
Greece #17
Oh, I see. Well, there are 65000 ports, so it'll take a while for someone to bump into two plugins that use the same port. Does it return an error if another application is listening to the port, or does UDP allow that? I'm a bit rusty on it, but that could account for erratic behaviour. The best way is for the plugin writer to prefix each text they send with the plugin's name or another string and discard everything else.
USA #18
eBadParameter: The port is already in use by another plugin, or the UDP socket could not be created

Returns that if being used elsewhere.
Australia Forum Administrator #19
Well, you can go further than that. ;)

Here is what I have tried. First make a little plugin that displays the incoming packets:


<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE muclient>
<!-- Saved on Tuesday, November 30, 2004, 3:15 PM -->
<!-- MuClient version 3.56 -->

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

<muclient>
<plugin
   name="udptest"
   author="Nick Gammon"
   id="03f4d34b49652923953d9c19"
   language="VBscript"
   purpose="testing udplisten"
   date_written="2004-11-30 15:13:41"
   requires="3.56"
   version="1.0"
   >

</plugin>


<!--  Script  -->


<script>
<![CDATA[
sub GotPacket (which)
  ColourTell "white", "green", which
end sub

sub OnPluginInstall ()
 status = UdpListen ("0.0.0.0", 4222, "GotPacket")
 if status <> 0 then
    ColourNote "white", "red", _
      "UdpListen not installed, error code " & status
 else
    ColourNote "white", "darkgreen", "UdpListen installed"
 end if
end sub
]]>
</script>


</muclient>



Now go to a different PC (or the same PC, or whatever) and pipe a Unix command to your MUSHclient PC...


 ls | ./udpsend 10.0.0.3 4222


Then you see the directory listing on your MUSHclient world. Now you can see various possibilities here. You might have something installed on your MUD server box (maybe hosted at a different site) and that could send you a UDP packet if, say, the disk is filling up, or the server has crashed, or whatever.
Amended on Wed 01 Dec 2004 04:29 AM by Nick Gammon
Australia Forum Administrator #20
Tests would seem to indicate that multiple copies of the status bar listening on the same port on a particular PC will not necessarily all get the message. So if you want to have multiple ones (eg. an inventory, and a health bar) choose different ports.

You can specify the port when you run the statusbar program:


StatusBar port ip magic

eg.

StatusBar 4222 0.0.0.0 gandalf



However if you broadcast, the same message can appear on different PCs. For example in my network I have a private IP address range 10.0.0.1 onwards. If I use the udpsend program to send to 10.0.0.255 then all PCs listening on that port get the packet at the same time (roughly).
Australia Forum Administrator #21
Also, tests have shown that re-installing the plugin seems to fail every second time. It would appear that re-using the port in quick succession causes an "address in use" message from the Windows sockets. You normally wouldn't do that anyway, once you have the plugin installed it would stay there.

However I amended the plugin code above to show an error message, so you can see if it installed or not.

If you really need to reinstall it, I would suggest using some technique to remove the old listener, wait a second, and then install a new one.

eg.


'
'  remove the old one
'

UdpListen ("0.0.0.0", 4222, "")

'
'  use a timer here to wait a second  
'

' ------ timer delay ------

'
'  install the new one
'

UdpListen ("0.0.0.0", 4222, "GotPacket")

#22
Soooo, how do you make the statusbar react to changes in the mud? I don't have a clue about how to make MC send UDP packets. Also, the MUD I play on has this nifty thing for MXP. First it sends this:

<STAT MUD_HP MAX=MUD_MAXHP CAPTION=" HP: ">
<STAT MUD_SP MAX=MUD_MAXSP CAPTION=" SP: ">
<STAT MUD_EP MAX=MUD_MAXEP CAPTION=" EP: ">
<STAT MUD_MONSTER_NAME CAPTION=" Monster Name: ">
<STAT MUD_MONSTER_HEALTH CAPTION=" Monster Health: ">
<STAT MUD_ACTION CAPTION=" Action: ">
<!ENTITY MUD_MAX_MON_HEALTH 100 PUBLISH>
<GAUGE MUD_HP MAX=MUD_MAXHP caption=HP>
<GAUGE MUD_SP MAX=MUD_MAXSP caption=SP>
<GAUGE MUD_EP MAX=MUD_MAXEP caption=EP>
<GAUGE MUD_MONSTER_HEALTH MAX=MUD_MAX_MON_HEALTH caption=MONSTER>

Then every tick after, it sends this:

<!ENTITY MUD_HP 1534 PUBLISH>
<!ENTITY MUD_MAXHP 1534 PUBLISH>
<!ENTITY MUD_SP 2687 PUBLISH>
<!ENTITY MUD_MAXSP 2687 PUBLISH>
<!ENTITY MUD_EP 1207 PUBLISH>
<!ENTITY MUD_MAXEP 1207 PUBLISH>
<!ENTITY MUD_MONSTER_NAME "" PUBLISH>
<!ENTITY MUD_MONSTER_HEALTH 0 PUBLISH>
<!ENTITY MUD_ACTION "" PUBLISH>

Would this be possible to use to make a MXP thing or script which updates the bars manually? :)
USA #23
You can use GetEntity (http://www.gammon.com.au/scripts/function.php?name=GetEntity) to get all the values, which you can then send to the bar/window/whereever.

The first part looks like it just sets up the bar area, and then the second actually has all the info. So you could do something similar in the window. You'd just need a trigger that gets sent with the MXP info (the tick perhaps?) to set off the update. Does the info get sent before or after the tick?
Australia Forum Administrator #24
After a bit of mucking around I got a timer to do it, like Flannel suggested. I did it in Lua, so either switch your scripting to Lua, or make a plugin out of it. If you have trouble doing that let me know and I'll do one.

I decided it was easier to hard-code the names of each bar rather than trying to interpret the STAT and GAUGE MXP elements, which MUSHclient doesn't currently support.


<timers>
  <timer enabled="y" second="5"    send_to="12"
>
  <send>do

t = t or {}
local old_t = {}

-- make copy of current values
for k, v in t do
  old_t [k] = t [k]
end

t.mud_hp = GetEntity ("mud_hp")
t.mud_maxhp = GetEntity ("mud_maxhp")
t.mud_sp = GetEntity ("mud_sp")
t.mud_maxsp = GetEntity ("mud_maxsp")
t.mud_ep = GetEntity ("mud_ep")
t.mud_maxep = GetEntity ("mud_maxep")
t.mud_monster_name = GetEntity ("mud_monster_name")
t.mud_monster_health = GetEntity ("mud_monster_health")
t.mud_max_mon_health = GetEntity ("mud_max_mon_health")

-- see if changed
local changed = false
for k, v in t do
  if old_t [k] ~= t [k] then
    changed = true
  end -- if
end

-- exit if nothing changed
if not changed then
  return
end -- if

-- function to send UDP packets with magic keyword and port number
local function udp (msg)
  UdpSend ("127.0.0.1", 4111, "magic," .. msg)
end  -- function udp

-- function to calculate percent, with guard against divide by zero
local function percent (amount, max)
  amount = tonumber (amount)
  max = tonumber (max)

  if not (amount and max) then
    return 0
  end -- if no values supplied

  if max &lt;= 0 then
    return 0
  end -- if too low

  if amount &gt; max then
    return 100
  end -- if too high

  return math.floor (amount / max * 100)
end -- function percent 

-- title, colours, bar sizes, labels
udp ("title,Status")
udp ("textcolour,0")
udp ("backcolour,14804223")
udp ("config,10,5,100,15,70,320,20")
udp ("labels,HP,SP,EP,Monster")

-- bar "foreground" colours
udp ("colours," .. 
        ColourNameToRGB ("darkgreen") .. "," .. 
        ColourNameToRGB ("darkblue") .. "," .. 
        ColourNameToRGB ("saddlebrown") .. "," .. 
        ColourNameToRGB ("dimgray"))

-- bar "background" colours
udp ("fillcolours," .. 
        ColourNameToRGB ("lightgreen") .. "," .. 
        ColourNameToRGB ("lightblue") .. "," .. 
        ColourNameToRGB ("tan") .. "," .. 
        ColourNameToRGB ("gainsboro"))

-- percentages
udp ("values," ..
     percent (t.mud_hp, t.mud_maxhp) .. "," ..
     percent (t.mud_sp, t.mud_maxsp) .. "," ..
     percent (t.mud_ep, t.mud_maxep) .. "," ..
     percent (t.mud_monster_health, t.mud_max_mon_health)
    )

-- monster name
udp ("text,5,95," .. t.mud_monster_name)

end -- do</send>

  </timer>
</timers>



What this does is, every 5 seconds, check the entity values, and if they have changed, send an update to the status window. You need to download the status bar window as mentioned above and start it up (ie. execute the .exe) manually.

What you should see 5 seconds later is the status bar with the 4 bars drawn, and then as the updates arrive from the MUD the length of the bars will update. You can see the names of the colours for each bar in the timer above, you can customise that to your choice.

Just drag the window around to a convenient location on the screen.
#25
So can you use this to capture channel conversations?

Also, will it scroll if too many lines show up?

Additionally, How would I use it for an external statusbar for my prompt? Considering I know what % life and what not I'm at. I don't need to calculate it out.
Australia Forum Administrator #26
No, this particular implementation is not scrolling. I was trying to keep it simple, and do a bar graph.

To capture a conversation, just use "send to notepad".
#27
How complicated would it be to make this bar scrolling?
Australia Forum Administrator #28
Why do you want a scrolling bar? It would be a different sort of window, a text window with scrollbars.
#29
Thanks _ALOT_ for the help with the statusbar Nick!
Though I seem to have a rather odd problem now. After an hour or two the statusbars disappear leaving a white window with only the labels left. I downloaded ShellEnhanced to make the Statusbar always on top, other than that I have no clue about what might be messing with it :/
Australia Forum Administrator #30
I don't think it is the ShellEnhanced stuff. It happened to me once yesterday, and I just reproduced it.

I don't quite know why, the program itself is so simple it shouldn't go wrong. However I am guessing from what seems to be happening is that sometimes when it tries to draw on its little window it somehow grabs the entire desktop and draws on that, hence the desktop changes colour. Why, I'm not sure. For one thing, Windows should not allow programs to interfere with each others like it seems to be doing.

I'm trying to Google my way to finding an article that explains if there is some sort of exception situation that should be handled differently.
Australia Forum Administrator #31
OK, I think I fixed it. Download the new source/executable from the same spot as before.

In case anyone is wondering, the problem seemed to be that it was not freeing resources, and what eventually happened was that Windows ran out of drawing resources, and in my case at least, the desktop went crazy. This is the change (in a few places):

Before


   FillRect (hdc, &rect, CreateSolidBrush (colours [i]););


After


  HBRUSH barbrush = CreateSolidBrush (colours [i]);
  if (barbrush)
    {
    FillRect (hdc, &rect, barbrush);
    DeleteObject (barbrush);
    } // end of having brush


What the original was doing was CreateSolidBrush for each bar drawn, and each background of the bar, plus the background for the whole window, every time the window was redrawn. This meant that it would have used around 9 brushes per redraw (for 4 bars).

The new version frees each resource as it uses it, so it should never use them up.
#32
Rock on Nick, just one more thing.
I didn't really get how to print multiple textlines in the window. I'd like to put some statistics as well as some affecting protections information, wether spells are up or not etc. To make it a bit easier to keep track off :)
Australia Forum Administrator #33
OK, it's that last line with "text" in it that is the key. If you change:


-- monster name
--udp ("text,5,95," .. t.mud_monster_name)


... to:


udp ("text,5,95," ..
[[You stand at the entrance to the Darkhaven Academy.  A large flight of
stone stairs lead down into the massive subterranean structure, which
has the responsibility of training the young adventurers of the world.
A small cobbled street leads eastward to a shop catering to the needs
of these adventurers.  To the north, you see the lengthy Horizon Road.
]])


Then you will see (especially if you resize the window a bit) all that text. The "text" line lets you send multi-line text to appear that the nominated pixel onwards (horizontal 5, vertical 95 in this case).

So, you can append a few things together as required, with \r\n at the end of each line for a line break. The multi-line literal in Lua does that automatically for you.

For example:


udp ("text,5,95," ..
     "blah blah\r\n" ..   -- something
     "lalalala I'm not listening\r\n"  .. -- something else 
     GetUniqueID ()  -- something that changes
  )

Amended on Sat 18 Dec 2004 01:00 AM by Nick Gammon
#34
Well, a Scrolling Text window would basically be what I've been looking for for channel capturing... I know, Notepad works too but I don't like that way.

Basically, with this window it looks like what I'm looking for, if it could just scroll like the output window.

Anyways, So that's why.
Amended on Sat 18 Dec 2004 01:06 AM by David Berthiaume
Australia Forum Administrator #35
With the status bar window, if you have a comma in your text, then whatever is after the comma will not display (in the "text" section). This is because it uses a comma as a delimiter.

This could be worked around with a few changes, however for now, if your text has commas in it replace them with something else if possible (eg. replace-all comma with semicolon).
#36
ok, I'm having some trouble with this. Iv'e dl'd both the http://www.gammon.com.au/files/utils/udpsend.zip and http://www.gammon.com.au/files/utils/statusbar.zip
, the statusbar coems up fine, but when I use the updsend prog, it just flashes a black screen real fast and that's all. I've unzipped both and tried to alter the IP source in the statusbar by hand but that didn't help. I've redone everything, reinstalled and everything to start over and still have problems. I've been trying to do this in VBscript and used the
function multiline_trigger (name, line, wildcards)

local function udp (msg)
UdpSend ("127.0.0.1", 4111, "m," .. msg)
end

udp ("title,Inventory")
udp ("size,10,10,300,300")
udp ("config,10,5,100,15,80,300,20")
udp ("font,Harem,12")
udp ("textcolour,0")
udp ("backcolour,14804223")
udp ("text,5,5," .. wildcards.inventory)

end -- function

part as a script and loaded it. It just gives me errors though and has no effect on the statusbar, Iv'e also tried setting up and alisa to send this information to script. I'm probally just doing this all wrong, so if anyone can help me through this I'd appreciate it. I'm completly lost now, so if someone can lead me through this....

Also, I've seen Zmud users with buttons and bitmaps used with information sent from variables to set these up. Is this basically the same thing?
USA #37
Well, you need to send the magic text when sending through UDP. So its m,title,Inventory
or whatever.

What errors?

That UDP program isn't required, its just a standalone UDP thing. MC has UDP capabilities built in.

Yes, that stuff should be sent to script, in either a script file, or from an alias/trigger (this one should be in a trigger, since it uses wildcards in the last line).

Oh, wait. No, in VB you need to ditch most of that stuff, and just use the UDPSend built in to MC:

UDPSend "127.0.0.1", 4111, "m,title,Inventory"
would be your first line, for instance.
If you wanted to make a subroutine (to take care of the IP, port, and magic text) you could do that.

sub udp(sSend)
UDPSend "127.0.0.1", 4111, "m," & sSend
end sub

Put that in your script file, then you can use those lines already (except the inventory one, for that you just substitute the wildcard).
Amended on Tue 28 Dec 2004 09:00 PM by Flannel
Australia Forum Administrator #38
Quote:

Also, I've seen Zmud users with buttons and bitmaps used with information sent from variables to set these up. Is this basically the same thing?


Yes, basically. I think zMUD has some of that built into the client, but this program illustrates how you can expand MUSHclient to add these sorts of things as stand-alone add-ons.

My original examples were in Lua, that should have been mentioned on the page somewhere. If you are not commited to VBscript you could switch your script language to Lua and then they should work unchanged.
USA #39
Or put them in a plugin.

And yes, zMud has this sort of thing. Or at least it started with the capabilities to draw things and such, someone still had to code everything as a plugin. And it's capabilities are more limited than MCs currently (since you can only do what it has functions for, with MC you can do literally anything). There just isn't a plugin/program already built for MC.
Amended on Tue 28 Dec 2004 08:20 PM by Flannel
#40
ok, I set the following in an alias and had it send to script with the status bar up:

UdpSend "0.0.0.1", 4111, "m," .. msg
end

udp ("title,Inventory")
udp ("size,10,10,300,300")
udp ("config,10,5,100,15,80,300,20")
udp ("font,Harem,12")
udp ("textcolour,0")
udp ("backcolour,14804223")

and got this error:

Error number: -2146827263
Event: Execution of line 1 column 31
Description: Expected end of statement
Line in error:
UdpSend "0.0.0.1", 4111, "m," .. msg
Called by: Immediate execution

in the source for the status bar I have the IP set as 0.0.0.1

I'm just not getting this for some reason. I've tried other options aswell, taking out parts and adding and using the text found in hte contents file, but nothing changes the statusbar
USA #41
You need to have the IP be 127.0.0.1, since it references your own computer.

Youre using VBscript, so you have to rewrite a lot of that stuff to VBScript.

And I believe you need to put that subroutine in your main script file. Although I may be mistaken, it might be able to go in an alias. However it would make sense to put it in your main script, so other aliases/triggers can call it as well.
#42
Ok, Flannel walked me through this and it's fixed, but still having problems seeing any text when written and sent to the statusbar
China #43
hi, nick.
status is good job. and i want to load it at the OnPluginInstall function.
how can i do it?
Australia Forum Administrator #44
Which part are you asking about? To start up the status bar window your plugin would probably need to do os.execute (in Lua) to start the program running.
China #45
yes, i want to auto load state bar window at my plugin. i use os.execute("StatusBar.exe") to do it, and it open a dos window to open state bar window, state bar does not work until i close dos window. i don't know why, and how can i do it automatically.
the second question is path of os.execute use, i have to put the file StatusBar.exe at mc directory, if not do this, status bar window can not open.
Australia Forum Administrator #46
This should start it up from the MUSHclient executable directory, and not keep a DOS window open:


os.execute ("start " .. GetInfo (66) .. "\\" .. "statusbar.exe")


To do it automatically put that inside a function called OnPluginInstall inside a plugin. Eg. in Lua:


function OnPluginInstall ()
  os.execute ("start " .. GetInfo (66) .. "\\" .. "statusbar.exe")
end -- function OnPluginInstall 
Amended on Thu 04 Oct 2007 07:20 AM by Nick Gammon
China #47
thank nick for your answer.
my MUSHclient executable directory is "C:\Program Files\MUSHclient\", so when i use start command, it does not work. and i changed it like
os.execute ("start \"\" \"" .. GetInfo (66) .. "\statusbar.exe\"")
it is OK.
it works well now, thank you!
China #48
i have new problem now, when i load status bar window at the function called OnPluginInstall, the MUSHclient lost focus, current window change to status bar window. i want to reactive MUSHclient automatically, how can i do it?
Australia Forum Administrator #49
http://www.gammon.com.au/scripts/doc.php?function=ActivateClient
China #50
great! it work very well. thanks alot!
China #51
hi nick, i have a new problem now, that is when the MCL file which have load status bar window can not be save.
it always say "access to XXX.MCL was denied".
what is this mean, and how can i solve it?

and more question is, how can i close the status bar window when i close one session of the MUSHclient?
i think to use send a close message to status bar window, and status bar window close itself, but i am not a programmer, and can not modifie the C file of status bar window. can u help me?

thanks a lot!
Australia Forum Administrator #52
If you edit the statusbar.cpp file that came with the download, it explains what each of the commands are, in comments near the top. You want the "quit" command.

I don't know why it won't save, try saving under a different name and then quitting MUSHclient or rebooting the PC.
China #53
oh, yeah! it have "quit" command. thanks!
#54
I'm not sure how the status bar is set up. I've dl'ed the statusbar.zip file and unzipped it. The link to the additional Lua files providing socket support in Nick's original post did not work, so I did a google search and found the latest file here:
http://luaforge.net/frs/?group_id=23&release_id=837
Where do I put the .lua files from luasocket-2.0.2-lua-5.1.2-Win32-vc6.zip? The zip file has subfolders which complicates things. The instructions given by the programmer are here: http://www.tecgraf.puc-rio.br/~diego/professional/luasocket/installation.html

I would greatly appreciate it if someone looked into this for me.
Australia Forum Administrator #55


I got it working like this:

  • As mentioned on the MUSHclient downloads page:

    http://www.gammon.com.au/downloads/dlmushclient.htm

    Download the file with the custom status bar program in it, namely:

    http://www.gammon.com.au/files/utils/statusbar.zip
  • Download the amended LuaSocket stuff as described here, about halfway down the page:

    http://www.gammon.com.au/forum/?id=8319

    Specifically you want the file:

    http://www.gammon.com.au/files/mushclient/lua5.1_extras/luasocket-2.0.1.zip

    Unzip that into the same directory as MUSHclient.exe (eg. c:\Program Files\MUSHclient if that is where you put it).
  • Manually start up StatusBar.exe (for now) by double-clicking it, and accepting it as a valid program if your firewall complains that it is trying to access the Internet (that is because it opens a "listening" UDP port).

    You should see a blank (white) status bar window.
  • In MUSHclient's File -> Global Preferences -> Lua, make sure "Allow DLLs to be loaded" is checked (as luasocket is a DLL). Make sure your current world is trusted (in the script on that page), or make the first line:

    
    trust_all_worlds = true
    


    If you had to change it, go to the Game menu -> Reload Script File.
  • Copy and paste the example from page 1 of this thread, minus the 'dofile "lua.lua"' part, as follows, into an Immediate window (Ctrl+I):

    
    
    socket = require "socket"
    s = socket.udp ()
    s:setpeername ("127.0.0.1", 4111)
    
    s:send ("magic,title,Status Bar - BabbleMUD")
    s:send ("magic,config,10,5,100,15,60,320,20")
    s:send ("magic,size,10,10,350,140")
    s:send ("magic,textcolour," .. ColourNameToRGB ("indigo"))
    s:send ("magic,backcolour," .. ColourNameToRGB ("lightsteelblue"))
    s:send ("magic,labels,HP,Mana,Move,XP")
    s:send ("magic,values,10,20,30,80")
    s:send ("magic,font,Comic Sans MS,10,0,700")
    s:send ("magic,colours," .. 
            ColourNameToRGB ("darkgreen") .. "," .. 
            ColourNameToRGB ("darkblue") .. "," .. 
            ColourNameToRGB ("saddlebrown") .. "," .. 
            ColourNameToRGB ("dimgray"))
    s:send ("magic,fillcolours," .. 
            ColourNameToRGB ("lightgreen") .. "," .. 
            ColourNameToRGB ("lightblue") .. "," .. 
            ColourNameToRGB ("tan") .. "," .. 
            ColourNameToRGB ("gainsboro"))
    
    s:send ("magic,text,5,88,Darkhaven Square")
    
    s:close ()
    

  • Your status bar should now look like the screen dump on page 1 of this thread.


Australia Forum Administrator #56
However an important point is that you don't need Luasocket to make it work - that was for the example, and is one way of going about it. In this example (also from page 1):


function multiline_trigger (name, line, wildcards)

  local function udp (msg)
    UdpSend ("127.0.0.1", 4111, "magic," .. msg)
  end  

  udp ("title,Inventory")
  udp ("size,10,10,300,300")
  udp ("config,10,5,100,15,80,300,20")
  udp ("font,Harem,12")
  udp ("textcolour,0")
  udp ("backcolour,14804223")
  udp ("text,5,5," .. wildcards.inventory)

end -- function


That simply uses UdpSend which is built into MUSHclient. If you do a trigger like that, you can skip all the steps about installing Luasocket, letting MUSHclient read DLLs, and so on.
Amended on Fri 06 Jun 2008 09:23 PM by Nick Gammon
Australia Forum Administrator #57
I got a couple of triggers to work nicely, it was a bit trickier than I thought, as I haven't looked at it for a while, and things have changed a bit.


<triggers>
  <trigger
   enabled="y"
   match="&lt;*/* hp */* m */* mv */* xp&gt;*"
   name="status_prompt"
   send_to="12"
   sequence="100"
  >
  <send>

-- calculate percentages, put them in a table 
status_table = {
   %1/%2 * 100,  -- hp
   %3/%4 * 100,  -- mana
   %5/%6 * 100,  -- movement
   %7/%8 * 100,  -- xp
     }

if not status_window_open then
  status_window_open = true
  
  udp = function (msg)
    UdpSend ("127.0.0.1", 4111, "magic," .. msg)
  end  

  os.execute ('start "statusbar" "' .. GetInfo (66) .. 'StatusBar.exe"')

  -- it might take a second to start up StatusBar.exe, so do this a second later
  DoAfterSpecial (1, [[
    udp ("title,Status Bar - SMAUG")
    udp ("config,10,5,100,15,60,320,20")
    udp ("size,10,10,350,140")
    udp ("textcolour," .. ColourNameToRGB ("indigo"))
    udp ("backcolour," .. ColourNameToRGB ("lightsteelblue"))
    udp ("labels,HP,Mana,Move,XP")
    udp ("font,Comic Sans MS,10,0,700")
    udp ("colours," .. 
            ColourNameToRGB ("darkgreen") .. "," .. 
            ColourNameToRGB ("darkblue") .. "," .. 
            ColourNameToRGB ("saddlebrown") .. "," .. 
            ColourNameToRGB ("dimgray"))
    udp ("fillcolours," .. 
            ColourNameToRGB ("lightgreen") .. "," .. 
            ColourNameToRGB ("lightblue") .. "," .. 
            ColourNameToRGB ("tan") .. "," .. 
            ColourNameToRGB ("gainsboro"))
    udp ("values," .. table.concat (status_table or {}, ","))
    ActivateClient ()  -- get focus back
  ]], 12)
else
  udp ("values," .. table.concat (status_table, ","))
end -- first time
</send>
  </trigger>

  <trigger
   enabled="y"
   keep_evaluating="y"
   match="Exits: *"
   send_to="12"
   sequence="100"
  >
  <send>

if udp then
  udp ("text,5,88,%0")
end -- status window available

</send>
  </trigger>
</triggers>




To make this work, first I set my SMAUG prompt to show all the stuff it needs:


prompt <%h/%H hp %m/%M m %v/%V mv %x/%X xp> 


That is, current and max HP, current and max Mana, current and max movement points, xp and XP to go. This lets me calculate percentages for the status bar.

Then after installing the statusbar.exe from:

http://www.gammon.com.au/files/utils/statusbar.zip

It was ready to work. What the first trigger does is calculate the percentages, and then the first time around, it uses os.execute to start up the statusbar.exe program. (You need to make the world "trusted" for this to work). It then uses DoAfterSpecial to configure the status window a second later. The reason for this is to give the status bar program time to start.

From then on, each prompt updates the status bar.

The second trigger updates the current exits.
Amended on Fri 06 Jun 2008 10:42 PM by Nick Gammon
Australia Forum Administrator #58

This is what it looked like:

Australia Forum Administrator #59
A nice trick is to make the status bar window auto-position itself adajacent to the world window where it is being used. To do this replace the line above:


udp ("size,10,10,350,140")


... by this:


    -- where is the world window?
    local mc_location = GetWorldWindowPosition (1, 1)
    local width = 350
    local height = 140
    local t = {
      mc_location.left - width - 10,
      mc_location.top - 30,
      width,
      height,
      }
    udp ("size," .. table.concat (t, ","))


What this does is use GetWorldWindowPosition to find the position of the world window, in global screen co-ordinates. We then subtract the width of the status window (350 in this case) from the left of the MUSHclient window, which puts it exactly to the left of it. I added a "fudge" factor of 10, to allow room for the window edge.

Then it takes the top of the MUSHclient window, and subtracts 30 to allow for the width of the title bar, so both title bars line up.