Lua script extensions
MUSHclient has some scripting extensions which are only available to Lua scripting. This page describes them. Lua "sandbox" To help block out dangerous functions, for example:
    os.execute "del mushclient.exe"
... MUSHclient has a 'preliminary script' box in its Global Preferences -> Lua section. This has code that disables some 'dangerous' functions (like 'os') by setting them to nil. If you are not planning to run untrusted scripts (eg. plugins) then you can edit that code and comment-out any parts you feel comfortable with having available to your scripts. The code in this box is executed every time the Lua script engine is instantiated, in other words for every world, and every plugin. There are suggestions in the default script for how you might modify it to block certain plugins (or worlds) but not others, from having access to dangerous commands. You can do this by using GetWorldID and GetPluginID to find the unique indentifier of the current world or current plugin. The default behaviour of the sandbox is to disable the following libraries: package.loadlib (so DLLs cannot be loaded, which may have malicious code) io (so files cannot be created or opened) os (some functions) (so operating system calls, such as "execute", cannot be made) The package library is modified so that the "require" statement will not load DLLs. You can modify the sandbox code to remove all restrictions, add more, or fine-tune them to your requirements. For example, if you wanted to use os.date and os.time (and others), but not os.execute, os.remove or os.rename, you would replace:
    os = nil -- no operating system calls
by:
    os.execute = nil  -- no executing OS commands
    os.remove = nil   -- no removing files
    os.rename = nil   -- no renaming files
print function To make it easier to use Lua examples in MUSHclient, the function "print" is defined to effectively call the world "Note" function. However, unlike Note, print adds a space between each argument (like the Lua "print" does). eg.
    Note "hello, world"
    print "hello, world"    --> does the same thing

    Note (1, 2, 3) --> 123
    print (1, 2, 3) --> 1 2 3
world functions available from global scope Although the normal MUSHclient script functions are defined in the "world" table, MUSHclient adds a metatable to the _G table and uses the __index entry to make the script functions available at global scope. Put another way, you can either write:
    world.Note "hello, world"  --> calls Note function in world table
    Note "hello, world"        --> does the same thing
Both methods call the world.Note script function. If you wish to change the behaviour of an inbuilt script function you must replace the world.XXX version (eg. change world.Note) rather than simply replacing the global version. Constants To make it easier to write scripts, MUSHclient's Lua interface has various built-in tables of constants. Trigger flags for AddTrigger These are in the 'trigger_flag' table.
    print (trigger_flag.OmitFromOutput) --> 4
All the available keys and values are:
      Enabled  -->  1
      ExpandVariables  -->  512
      IgnoreCase  -->  16
      KeepEvaluating  -->  8
      LowercaseWildcard  -->  2048
      OmitFromLog  -->  2
      OmitFromOutput  -->  4
      RegularExpression  -->  32
      Replace  -->  1024
      Temporary  -->  16384
Trigger flags for AddAlias These are in the 'alias_flag' table.
    print (alias_flag.AliasQueue) --> 4096
All the available keys and values are:
      AliasMenu  -->  8192
      AliasQueue  -->  4096
      AliasSpeedWalk  -->  2048
      Enabled  -->  1
      ExpandVariables  -->  512
      IgnoreAliasCase  -->  32
      OmitFromLogFile  -->  64
      RegularExpression  -->  128
      Replace  -->  1024
      Temporary  -->  16384
Trigger flags for AddTimer These are in the 'timer_flag' table.
    print (timer_flag.OneShot) --> 4
All the available keys and values are:
      ActiveWhenClosed  -->  32
      AtTime  -->  2
      Enabled  -->  1
      OneShot  -->  4
      Replace  -->  1024
      Temporary  -->  16384
      TimerNote  -->  16
      TimerSpeedWalk  -->  8
Custom colour flags for AddTrigger These are in the 'custom_colour' table.
    print (custom_colour.NoChange) --> -1
All the available keys and values are:
      NoChange  -->  -1
      Custom1  -->  0
      Custom2  -->  1
      Custom3  -->  2
      Custom4  -->  3
      Custom5  -->  4
      Custom6  -->  5
      Custom7  -->  6
      Custom8  -->  7
      Custom9  -->  8
      Custom10  -->  9
      Custom11  -->  10
      Custom12  -->  11
      Custom13  -->  12
      Custom14  -->  13
      Custom15  -->  14
      Custom16  -->  15
      CustomOther  -->  16
Error codes - map error names to numbers These are in the 'error_code' table. You can use these to check individual error codes. eg.
    if AddTimer (blah blah blah) ==
         error_code.eTimerAlreadyExists  then
      error ("That timer already exists")
    end
    print (error_code.eBadRegularExpression) --> 30021
All the available keys and values are:
      eAliasAlreadyExists  -->  30011
      eAliasCannotBeEmpty  -->  30012
      eAliasNotFound  -->  30010
      eAlreadyTransferringFile  -->  30052
      eArrayAlreadyExists  -->  30055
      eArrayDoesNotExist  -->  30056
      eArrayNotEvenNumberOfValues  -->  30057
      eBadDelimiter  -->  30059
      eBadMapItem  -->  30023
      eBadParameter  -->  30046
      eBadRegularExpression  -->  30021
      eCannotCreateChatSocket  -->  30042
      eCannotImport  -->  30062
      eCannotLookupDomainName  -->  30043
      eCannotPlaySound  -->  30004
      eChatAlreadyConnected  -->  30049
      eChatAlreadyListening  -->  30047
      eChatIDNotFound  -->  30048
      eChatPersonNotFound  -->  30045
      eClipboardEmpty  -->  30050
      eCommandNotEmpty  -->  30020
      eCommandsNestedTooDeeply  -->  30041
      eCouldNotOpenFile  -->  30013
      eErrorCallingPluginRoutine  -->  30040
      eFileNotFound  -->  30051
      eImportedWithDuplicates  -->  30058
      eInvalidObjectLabel  -->  30008
      eItemInUse  --> 30063
      eKeyDoesNotExist  -->  30061
      eLogFileAlreadyOpen  -->  30015
      eLogFileBadWrite  -->  30016
      eLogFileNotOpen  -->  30014
      eNoChatConnections  -->  30044
      eNoMapItems  -->  30024
      eNoNameSpecified  -->  30003
      eNoSuchCommand  -->  30054
      eNoSuchPlugin  -->  30034
      eNoSuchRoutine  -->  30036
      eNotAPlugin  -->  30035
      eNotTransferringFile  -->  30053
      eOK  -->  0
      eOptionOutOfRange  -->  30026
      ePluginCannotGetOption  -->  30033
      ePluginCannotSetOption  -->  30032
      ePluginCouldNotSaveState  -->  30037
      ePluginDisabled  -->  30039
      ePluginDoesNotSaveState  -->  30037
      ePluginFileNotFound  -->  30030
      eProblemsLoadingPlugin  -->  30031
      eScriptNameNotLocated  -->  30009
      eSetReplacingExistingValue  -->  30060
      eSpellCheckNotActive  -->  30064
      eTimeInvalid  -->  30022
      eTimerAlreadyExists  -->  30018
      eTimerNotFound  -->  30017
      eTriggerAlreadyExists  -->  30006
      eTriggerCannotBeEmpty  -->  30007
      eTriggerLabelNotSpecified  -->  30029
      eTriggerNotFound  -->  30005
      eTriggerSendToInvalid  -->  30028
      eTriggerSequenceOutOfRange  -->  30027
      eUnknownOption  -->  30025
      eVariableNotFound  -->  30019
      eWorldClosed  -->  30002
      eWorldOpen  -->  30001
Error codes - map error codes to descriptions These are in the 'error_desc' table. You can use these to give meaningful error messages. eg.
    status = AddTimer ("a", 0, 0, 0, 0, 0)
    if status ~= error_code.eOK  then
      error (error_desc [status]) -->  Time given to AddTimer is invalid
    end
All the available keys and values are:
     0  -->  No error
      30001  -->  The world is already open
      30002  -->  The world is closed, this action cannot be performed
      30003  -->  No name has been specified where one is required
      30004  -->  The sound file could not be played
      30005  -->  The specified trigger name does not exist
      30006  -->  Attempt to add a trigger that already exists
      30007  -->  The trigger "match" string cannot be empty
      30008  -->  The name of this object is invalid
      30009  -->  Script name is not in the script file
      30010  -->  The specified alias name does not exist
      30011  -->  Attempt to add a alias that already exists
      30012  -->  The alias "match" string cannot be empty
      30013  -->  Unable to open requested file
      30014  -->  Log file was not open
      30015  -->  Log file was already open
      30016  -->  Bad write to log file
      30017  -->  The specified timer name does not exist
      30018  -->  Attempt to add a timer that already exists
      30019  -->  Attempt to delete a variable that does not exist
      30020  -->  Attempt to use SetCommand with a non-empty command window
      30021  -->  Bad regular expression syntax
      30022  -->  Time given to AddTimer is invalid
      30023  -->  Direction given to AddToMapper is invalid
      30024  -->  No items in mapper
      30025  -->  Option name not found
      30026  -->  New value for option is out of range
      30027  -->  Trigger sequence value invalid
      30028  -->  Where to send trigger text to is invalid
      30029  -->  Trigger label not specified/invalid for 'send to variable'
      30030  -->  File name specified for plugin not found
      30031  -->  There was a parsing or other problem loading the plugin
      30032  -->  Plugin is not allowed to set this option
      30033  -->  Plugin is not allowed to get this option
      30034  -->  Requested plugin is not installed
      30035  -->  Only a plugin can do this
      30036  -->  Plugin does not support that subroutine (subroutine not in script)
      30037  -->  Plugin could not save state (eg. no state directory)
      30039  -->  Plugin is currently disabled
      30040  -->  Could not call plugin routine
      30041  -->  Calls to "Execute" nested too deeply
      30042  -->  Unable to create socket for chat connection
      30043  -->  Unable to do DNS (domain name) lookup for chat connection
      30044  -->  No chat connections open
      30045  -->  Requested chat person not connected
      30046  -->  General problem with a parameter to a script call
      30047  -->  Already listening for incoming chats
      30048  -->  Chat session with that ID not found
      30049  -->  Already connected to that server/port
      30050  -->  Cannot get (text from the) clipboard
      30051  -->  Cannot open the specified file
      30052  -->  Already transferring a file
      30053  -->  Not transferring a file
      30054  -->  There is not a command of that name
      30055  -->  That array already exists
      30056  -->  That array does not exist
      30057  -->  Values to be imported into array are not in pairs
      30058  -->  Import succeeded, however some values were overwritten
      30059  -->  Import/export delimiter must be a single character, other than backslash
      30060  -->  Array element set, existing value overwritten
      30061  -->  Array key does not exist
      30062  -->  Cannot import because cannot find unused temporary character
      30063  -->  Cannot delete trigger/alias/timer because it is executing a script
      30064  -->  Spell checker is not active
Colour names - map colour picker names to RGB values These are in the 'colour_names' table. You can use these to look up colour names (eg. "red") and find the corresponding RGB value. Extended colour codes - map colour selector numbers to RGB values These are in the 'extended_colours' table. You can use these to see what the RGB equivalent is for the 256 extended colours (keyed by 0 to 255). Bit manipulation library The Lua language does not contain native support for bit-wise manipulation of numbers (and, or, exclusive or etc.) MUSHclient has a few simple extensions that permit that. They are in the library (table) "bit". The following operations are supported: bit.shr - shift right This takes two arguments. Both are converted to unsigned 'long long' (64-bit unsigned integers). The first argument is shifted right the number of bits in the second argument. eg.
    print (bit.shr (1024, 6)) --> 16
bit.ashr - arithmetic shift right This takes two arguments. The first is converted to signed 'long long' (64-bit integer), the second to unsigned 'long long' (64-bit unsigned integer). The first argument is shifted right the number of bits in the second argument. Use this version for shifting signed numbers right, as it preserves the sign. (The other version, bit.shr, will shift the sign bit into the number part). eg.
    print (bit.ashr (-1024, 6)) --> -16
bit.shl - shift left This takes two arguments. The first is converted to signed 'long long' (64-bit integer), the second to unsigned 'long long' (64-bit unsigned integer). The first argument is shifted left the number of bits in the second argument. You can use this for signed or unsigned numbers, as the sign bit will still be preserved. eg.
    print (bit.shl (4, 6)) --> 256
bit.band - bitwise "and" This takes one or more arguments. All are converted to signed 'long long' (64-bit integers). The result is all arguments "and-ed" together bitwise. eg.
    print (bit.band (15, 7, 3)) --> 3
bit.bor - bitwise "or" This takes one or more arguments. All are converted to signed 'long long' (64-bit integers). The result is all arguments "or-ed" together bitwise. eg.
    print (bit.bor (1, 2, 8)) --> 11
bit.xor - bitwise "exclusive or" This takes one or more arguments. All are converted to signed 'long long' (64-bit integers). The result is all arguments "exclusive or-ed" together bitwise. eg.
    print (bit.xor (15, 1)) --> 14
bit.neg - bitwise "negate" (ones complement) This takes one argument. It is converted to a signed 'long long' (64-bit integer). The result is the ones-complement of the number (zero bits become one, one bits become zero). eg.
    print (bit.neg (1)) --> -2
    print (bit.band (14, bit.neg (8)))  --> 6  (clear 8-bit)
bit.mod - bitwise "modulus" (remainder after integer divide) This takes two arguments. Both are converted to signed 'long long' (64-bit integers). The result is modulus - the remainder after doing an integer divide of the first argument by the second argument. eg.
    print (bit.mod (17, 4))  --> 1
bit.tonumber (s, base) - convert a string into a number This takes a string, and converts it into a number. Unlike the standard Lua tonumber function this function will handle up to a 52-bit number (the default Lua number conversion will only go to 32-bit numbers). eg.
    print (bit.tonumber ("A7C5AC471", 16)) -->  45035996273
The base is optional and defaults to 10. The base can be in the range 2 to 36. Fractional numbers are not supported, nor are numbers with exponents (eg. 10.24e15). For such numbers use the standard Lua "tonumber" function. Because of limitations in the size of a floating point number, the maximum string value that can be converted is a 52 bit number, ie: hex FFFFFFFFFFFFF (decimal 4503599627370495). Leading whitespace is skipped. After that, there can be an optional + or - sign. bit.tostring (n, base) - convert a number into a string This takes a number, and converts it into a string to the given base, in uppercase. The base is optional and defaults to 10. The base can be in the range 2 to 36. Fractional parts are discarded, as the number is first converted to a 64-bit number internally. Negative numbers are OK, and will be converted with a leading "-" sign. eg.
    print (bit.tostring (45035996273, 16)) -->  A7C5AC471
Compression and decompression MUSHclient offers access to the zLib compression and decompression through the Lua scripting interface. These routines are 8-bit "clean", which means you can compress or decompress any data, including imbedded nul characters. utils.compress (s [, method] ) Compresses string s and returns the compressed form. Note that it may contain nulls (bytes with a zero value). The optional argument 'method' indicates the level of compression you want. 0 = no compression 1 = best speed .... 9 = best compression The default, if omitted, is 6. eg.
    comp = utils.compress ("this is a fine thing")
For short strings the compressed data may be longer than the uncompressed data because of a 12-byte "compression information" header that is prepended to the compressed data. For longer text (such as the section above about the Lua sandbox), the compression ratio is about 50%. utils.decompress (s) Decompresses string s and returns the decompressed form. Raises an error if decompression cannot be done (eg. bad compressed data). These two functions should be complementary, so that this should always be true:
    x = "some string" -- for any (string) data whatsoever
    y = utils.decompress (utils.compress (x)) --> y should be same as x
Hashing, base-64 encoding and decoding utils.hash (s) Returns a 40-character hex string which is the hash of the string 's'. The string 's' may contain the null byte (ie. hex 00). Otherwise, this is the same behaviour as the world.Hash function. eg.
    print (utils.hash ("Nick Gammon")) --> fe09b07227a4e006213ac005831d55b20508a568
    print (Hash ("Nick Gammon")) --> fe09b07227a4e006213ac005831d55b20508a568
utils.sha256 (s) This returns a 256-bit SHA hash (Secure Hash Algorithm) of the string s, which may contain binary zeroes. Unlike the utils.hash function this returns the result as a straight 32-byte (256-bit) field (that is, not converted to printable hex). If you want it in readable form you must then convert it yourself (eg. with utils.tohex). eg.
    print (utils.tohex (utils.sha256 ("nick gammon")))
    --> result: B3223193E1C89CB1E42E2BE2DF34874320F43E149DC315A381B08B7BC52849AD
This is a more secure hash than the standard utils.hash algorithm, which returns a 160-bit hash. utils.md5 (s) This returns a 128-bit MD5 hash of the string s, which may contain binary zeroes. Unlike the utils.hash function this returns the result as a straight 16-byte (128-bit) field (that is, not converted to printable hex). If you want it in readable form you must then convert it yourself (eg. with utils.tohex). eg.
    print (utils.tohex (utils.md5 ("nick gammon")))
    --> result: 9A380FD967D936AC99ED73B4A038CE8C
You can write a small Lua program to do the same thing that the md5sum program does (in Linux, Cygwin etc.):
    f = io.open ("docs/RegularExpressions.txt", "rb")
    if f then
      print (utils.tohex (utils.md5 (f:read ("*a"))))
      f:close ()
    end -- if

    --> result: 3764E22E2AC5BA67997C42C288253101
Compare this to the output from md5sum using Cygwin:
    $ md5sum RegularExpressions.txt
    3764e22e2ac5ba67997c42c288253101 *RegularExpressions.txt
The hash is the same, apart from not being in lower case, which you can change with the string.lower function if you want. utils.base64encode (s [, linebreaks] ) Encodes the string 's' in base64 encoding (suitable for emails etc.). If 'linebreaks' is true, there will be a carriage return/linefeed every 76 characters. The string 's' may contain the null byte (ie. hex 00). Otherwise, this is the same behaviour as the world.Base64Encode function. eg.
    print (utils.base64encode ("Nick Gammon")) --> TmljayBHYW1tb24=
The output string will be 4/3 times as large as the input string, plus some possible padding to make up the result to a multiple of 4 (the padding character is "="). Also, if you request linebreaks there will be a further 2 byte for every 76 bytes output (that is, every 57 bytes of input). The default is to not have linebreaks. utils.base64decode (s) Decodes the string 's' from base64 encoding to plain text. The decoded string may contain the null byte (ie. hex 00). Otherwise, this is the same behaviour as the world.Base64Decode function. Bytes that are invalid are skipped (eg. spaces, newlines, other junk). eg.
    print (utils.base64decode ("TmljayBHYW1tb24=")) --> Nick Gammon
If the source string is not a multiple of 4 bytes then the last few bytes of the decoded string will be lost (because decoding is done in batches of 4 input bytes to 3 output bytes). Converting strings to/from hex form utils.tohex (s) This converts the string s to hexadecimal (printable) form. The string may contain binary zeroes. Use string.lower to make a lower-case version if that is what you prefer. eg.
    print (utils.tohex ("Nick Gammon")) --> 4E69636B2047616D6D6F6E
utils.fromhex (s) This converts the supplied hexadecimal string s back to a normal string. The converted string may contain binary zeroes. eg.
    print (utils.fromhex ("4E69636B2047616D6D6F6E")) --> Nick Gammon
The supplied string may contain 'space' characters (0x09 – 0x0D or 0x20) which are ignored, otherwise if it contains characters other than A-F, a-f or 0-9 this function raises an error. If the number of characters is odd then the last character is treated as the low-order nibble of the final byte. eg.
    a = utils.fromhex ("ABC") --> same as utils.fromhex ("AB0C")
Note that "spaces are ignored" means that a sequence like "A B C D" is treated as the same as "ABCD" not "0A 0B 0C 0D". utils.readdir (s) - read a disk directory into a table You can use utils.readdir to read an entire directory on your PC into a Lua table, based on the wildcard you supply. For example:
    t, e = utils.readdir ("c:/mushclient/plugins/*.xml")

    assert (t, e) -- raises error on failure
If the directory specification is matched, the result from the call is a table of directory items, keyed by the filename. If the directory specification cannot be matched, or is invalid, it returns nil followed by an error message. You can simply test for non-nil, or call "assert" to report the error. For each file in the directory (that matches the wildcard) the following is returned: size - file size in bytes create_time - creation time (except for FAT filesystems, where it is omitted) access_time - last access time (except for FAT filesystems, where it is omitted) write_time - time written archive - true if archive. Set whenever the file is changed, and cleared by the BACKUP command. hidden - hidden file. Not normally seen with the DIR command normal - normal file. File can be read or written to without restriction. readonly - read-only. File cannot be opened for writing, and a file with the same name cannot be created. directory - Subdirectory. system - system file. Not normally seen with the DIR command. By detecting suddirectories you could conceivably recurse and find the contents of subdirectories as well. utils.split (s, delim) - split a delimited string into a table The function utils.split is intended to do the reverse of table.concat. That is, it takes a string and generates a table of entries, delimited by single-character delimiters (such as comma or newline). Example:
    test = "the,quick,brown,dog,jumped"
    t = utils.split (test, ",")
    tprint (t)
    print (table.concat (t, ","))
Output:
    1="the"
    2="quick"
    3="brown"
    4="dog"
    5="jumped"
    the,quick,brown,dog,jumped
You pass utils.split 2 or 3 arguments: The string to be split The single-character delimiter (optional) the maximum number of splits to do If the 3rd argument is not supplied, or is zero, then the entire string is split. Otherwise, it will be split the number of times you specify. eg.
    t = utils.split (test, ",", 2)
    tprint (t)
Output:
    1="the"
    2="quick"
    3="brown,dog,jumped"
In this case the remaining text is placed in the 3rd table item. utils.xmlread (s) - XML parser The function utils.xmlread uses MUSHclient's internal XML parser to parse an XML string you supply. This effectively would let you parse triggers, aliases etc. that you have copied to the clipboard as text (or created with ExportXML script routine), and see exactly what each value is set to. Or, by reading a MUSHclient world file into memory as a string, you could parse that. The XML parser is not necessarily 100% industry-standard XML parsing, however it is the method MUSHclient uses for its own XML documents, and should be reasonably compatible with standard XML unless you use some of the more fancy XML extensions. It should certainly parse the XML output by MUSHclient itself (eg. triggers, aliases, world files, plugins) as that is the same routine it uses to read them in. You pass to the parser a single string, which is the XML to be parsed. If the parsing is successful three results are returned: The root node (all other nodes are children of this node) The root document name (eg. "muclient") A table of custom entities in the document, or nil if no custom entities If the parsing fails, three results are returned: nil - to indicate failure The error reason The line the error occurred at You can pass the first 2 results to "assert" to quickly check if the parsing was successful. Each node consists of a table with the following entries: name - name of the node (eg. <trigger>foo</trigger> - the name is "trigger") content - contents of the node (eg. <trigger>foo</trigger> - the content is "foo") empty - boolean to indicate if the node is empty. (eg. <br/> is an empty node) line - which line in the XML string the node occurred on (eg. line 5) attributes - a table of attributes for this node, keyed by the attribute name (eg. "world_file_version"="15"). Attribute names have to be unique so we can used a keyed lookup to find them. The attributes table is not present if there are no attributes defined. nodes - a table of child nodes, keyed by ascending number (the order they appeared in). Each child node has the same contents as described above. Children are not necessarily unique (eg. there may be more than one <trigger> node in a document) so they are keyed by number, and not by node name. The nodes table is not present if there are no children of this node. Example:
    a, b, c = utils.xmlread ("<foo><bar x='2'/></foo>")
Output:
    "line"=1
    "name"=""
    "nodes":
      1:
        "line"=1
        "name"="foo"
        "nodes":
          1:
            "line"=1
            "name"="bar"
            "empty"=true
            "content"=""
            "attributes":
              "x"="2"
        "content"=""
    "content"=""
You can see from the above that the "root" node is really just an unnamed node which is the placeholder for the top level nodes (ie. the first "real" node is a child of the root node). In this case the node "foo" is the first child of the root node. utils.msgbox ( msg, title, type, icon, default ) This lets you display a Windows message box (very similar to MsgBox in VBscript). The intention is to allow you to display (in a small dialog box), information of an urgent nature, or ask a yes/no type question. The calling sequence is:
    result = utils.msgbox ( msg, title, type, icon, default )
The only required argument is the message text itself, the others default to their first possible value. The first 4 arguments are string arguments, the last is a number. msg = message to display (max 1000 characters) title = title of box - if nil, defaults to "MUSHclient" (max 100 characters) type = type of box (must be in lower case and exactly as shown here): "ok" - The message box contains one push button: OK. This is the default. "abortretryignore" - The message box contains three push buttons: Abort, Retry, and Ignore. "okcancel" - The message box contains two push buttons: OK and Cancel. "retrycancel" - The message box contains two push buttons: Retry and Cancel. "yesno" - The message box contains two push buttons: Yes and No. "yesnocancel" - The message box contains three push buttons: Yes, No, and Cancel. icon = type of icon: "!" - An exclamation-point icon appears in the message box. This is the default. "?" - A question-mark icon appears in the message box. "i" - An icon consisting of a lowercase letter i in a circle appears in the message box. "." - A stop-sign icon appears in the message box. default = default button (1 - 3) This sets the default button (the one with the focus) to be either button 1, 2 or 3. The default is the first button. Return value = (string) yes, no, ok, retry, ignore, cancel, abort Example:
    print (utils.msgbox ("You are being paged", "Warning!", "ok", "!", 1)) --> ok
    print (utils.msgbox ("You are being paged")) --> ok
    print (utils.msgbox ("Go ahead?", "Question", "yesno", "?")) --> yes / no
utils.inputbox ( msg, title, default, font, fontsize ) This lets you display a Windows message box and accept a free-format reply (very similar to InputBox in VBscript). The intention is to allow you to display (in a small dialog box) a question and accept a typed response. The calling sequence is:
    result = utils.inputbox ( msg, title, default, font, fontsize )
The only required argument is the message text itself. msg = message to display (max 1000 characters) title = title of box - if nil, defaults to "MUSHclient" (max 100 characters) default = default text - defaults to no text font = font to use in response field - defaults to standard Windows font fontsize = size of font to use (ignored if no font supplied) Return value = what they typed, or nil if cancelled Example:
    print (utils.inputbox ("What is your name?", "Query", "Nick", "Courier", 9)) --> Peter
Also see below for a similar function: utils.editbox utils.editbox ( msg, title, default, font, fontsize ) This is almost identical to utils.inputbox, except that the response field: is much larger - for entering large amounts of text has scroll bars - for scrolling through it Otherwise, the arguments are the same as for utils.inputbox. utils.choose (msg, title, tbl, default) utils.listbox (msg, title, tbl, default) utils.multilistbox (msg, title, tbl, defaults) These behave very similarly so they will be described together. These functions display a dialog box with a predetermined list of items for the user to choose from. If the user cancels the dialog box, or does not make a selection, nil is returned. Otherwise the key of the selected item is returned. utils.choose - displays a dialog box with a combo-box in it (drop-down list) utils.listbox - displays a dialog box with a list control in it - single selection utils.multilistbox - displays a dialog box with a list control in it - multiple selections allowed The utils.listbox function would be more suitable for longer lists, but that is probably partly personal preference. The utils.multilistbox function allows multiple selecions, so this is useful when you want the user to be able to select multiple items. The calling sequence is:
    result = utils.choose ( msg, title, t, default )
The only required arguments are the message text and the table of choices (t). msg = message to display (max 1000 characters) title = title of box - if nil, defaults to "MUSHclient" (max 100 characters) t - table of key/value pairs, as described below default = default key - defaults to no selection (table of defaults for multilistbox) Return value = the key of what they selected, or nil if cancelled, or nothing selected. For multilistbox the return value is a table of the selected keys, or nil if nothing selected. The third argument is a table of key/value pairs. The value is displayed, however the corresponding key is returned. The values are automatically sorted into ascending alphabetic order. The fourth argument is the key (string or number) which corresponds to the wanted default selection. If it does not correspond to any key in the table then no item will be selected. For multilistbox the fourth argument is a table of the keys of the wanted defaults. For no default selection just pass nil as the default. Example:
    print (utils.choose ("Your favourite", "Foods ...", { "apples", "bananas", "peaches", "cream" } ))
Possible returned values would be: nil - if no choice made or dialog cancelled 1 - apples chosen 2 - bananas chosen 3 - peaches chosen 4 - cream chosen (Note that peaches would actually be shown 4th in the list as the list is sorted). To convert from the key back to the value, simply index into your table. Eg.
    t = { "apples", "bananas", "peaches", "cream" }
    result = utils.listbox ("Your favourite", "Foods ...", t)

    if result then
      print ("You chose", t [result])
    else
      print "Nothing chosen"
    end -- if
Keys and values can be either strings or numbers. MUSHclient will distinguish between strings and numbers which are the same (eg. "10" and 10 are considered different keys). Here is an example of using string keys, and supplying a default choice:
    t = {
        fruit = "apple",
        vegetable = "potato",
        spice = "pepper",
        herb = "parsley",
        }
    result = utils.listbox ("Choose a food", "Foods ...", t, "fruit")

    if result then
      print ("You chose key", result, "which is", t [result])
    else
      print "Nothing chosen"
    end -- if
Possible returned values would be: nil - if no choice made or dialog cancelled "fruit" - apple chosen "vegetable" - potato chosen "spice" - pepper chosen "herb" - parsley chosen The return value will be one of the following types: nil - if no selection made or dialog cancelled string - if an item with a string key is selected number - if an item with a numeric key is selected Here is an example of using multilistbox:
    t = {
        fruit = "apple",
        vegetable = "potato",
        spice = "pepper",
        herb = "parsley",
        }

    defaults = {
        fruit = true,
        spice = true,
        }

    result = utils.multilistbox (
        "Choose a food",
        "Foods ...",
        t,
        defaults
        )
In this case if OK is pressed then the variable 'result' is a table of all of the selected keys (eg. {fruit = true, spice = true } if the defaults were taken). rex - PCRE regular expression library The functionality of the PCRE (Perl Compatible Regular Expression) library is available to Lua scripts. re = rex.new (pattern, flags) This compiles a pattern, returning a regular expression object that can be used to test regular expressions. For example:
    re = rex.new ("(.+) goes (.+)")
Flags are optional. If you want to use them you can use the rex.flags () function to convert various flags into numbers. flag_table = rex.flags () This returns a table of PCRE flags. You can index into this to get various compile and runtime flags. The following are valid compile-time flags: ANCHORED CASELESS DOLLAR_ENDONLY DOTALL EXTENDED EXTRA MULTILINE NO_AUTO_CAPTURE UNGREEDY UTF8 The following are valid execution-time flags: ANCHORED NOTBOL NOTEOL NOTEMPTY An example of using the flags would be:
    re = rex.new ("(.+) goes (.+)", rex.flags ().CASELESS)
This would make a caseless regular expression. start, end, substrings = re:match (string, pos, flags) This takes a regular expression object compiled previously with rex.new, and matches it against a target string. The "pos" argument is optional, and specifies a 1-relative starting point for the match. If omitted, the whole string is tested. You can also supply a negative number to count from the right, eg. -10 would start 10 characters from the end of the string. The "flags" argument is optional, and specifies execution flags, as described above. Example:
    re = rex.new ("(.+) goes (.+)")
    s, e = re:match ("Nick goes East")
    print (s, e) --> 1 14
If you are planning to do multiple matches against the same regular expression, it is faster to compile once only (ie. do rex.new once), and test multiple times. However for once-off tests you can combine them both into the same line:
    s, e = rex.new ("(.+) goes (.+)"):match ("Nick goes East")
    print (s, e) --> 1 14
The third result returned is a table of capture patterns that have been matched. Example:
    re = rex.new ("(?P<who>.+) goes (?P<where>.+)")
    s, e, t = re:match ("Nick goes East")
    print (s, e) --> 1 14
    table.foreach (t, print) --> see below
Output from table.foreach: 1 Nick 2 East where East who Nick This shows that the 2 capture patterns (the things in round brackets) have been captured in the table as index 1 and 2 (first and second pattern) and also under named indices "where" and "who" because we used named capture patterns in the regular expression. start, end, offsets = re:exec (string, pos, flags) This takes a regular expression object compiled previously with rex.new, and matches it against a target string. It takes the same arguments are re:match, however the table returned as the 3rd result consists of pairs of offsets, rather than the strings themselves. For example:
    re = rex.new ("(.+) goes (.+)")
    s, e, t = re:exec ("Nick goes East")
    print (s, e)  --> 1 14
    table.foreach (t, print) --> see below
Output from table.foreach: 1 1 2 4 3 11 4 14 In this case we see that the first capture was from columns 1 to 4, and the second capture was from columns 11 to 14. result = re:gmatch (string, fun, count, flags) The gmatch function: Tries to match the regex re against string up to count times (or as many as possible if count is either not given or is not a positive number), subject to execution flags "flags". Each time there is a match, fun is called as fun (m, t), where m is the matched string and t is a table of substring matches (this table contains false in the positions where the corresponding sub-pattern did not match.). If fun returns a true value, then gmatch immediately returns. gmatch returns the number of matches made.
    re = rex.new ("(\[A-Za-z\]+)")
    n = re:gmatch ("Nick goes East", function (m, t) print (m) end )
    print (n) --> 3
Output from function during execution: Nick goes East utils.functionlist This returns a table of all the internal MUSHclient function names (the same list used by the Help script function). The intention here is that you could use this table in an internal Notepad "global replace" to fix the capitalization of function names. You could also use it go generate keywords for use in text editors such as SciTE. Example:
    table.foreachi (utils.functionlist (), print)

    -->
    1 Accelerator
    2 AcceleratorList
    3 Activate
    4 ActivateClient
    5 ActivateNotepad
    6 AddAlias

    ...

    296 WorldName
    297 WorldPort
    298 WriteLog
utils.filepicker This invokes the Windows standard "file picker" dialog box, which lets you choose a file for opening or saving. Usage is:
    filter = { txt = "Text files", ["*"] = "All files" }

    filename = utils.filepicker (title, name, extension, filter, save)
title - title to appear on the dialog box (eg. "Name of plugin") name - default name of file to be loaded/saved extension - default extension to use filter - table of file filters, see example above save - true for a "save" dialog, false for a "load" dialog All arguments are optional. Returns nil if dialog dismissed, or the chosen filename if not. m1, m2 = utils.metaphone (word) This returns one or two metaphones (sound-alike codes) for the supplied word. This is the same behaviour as the world.Metaphone function, except that this one returns the secondary metaphone as a separate result. eg.
    print (utils.metaphone ("swordfish"))  --> SRTF XRTF
n = utils.edit_distance (word1, word2) This returns the Levenshtein Edit Distance between the two words. This is the same behaviour as the world.EditDistance function. eg.
    print (EditDistance ("food", "fodder"))  --> 3
utils.spellcheckdialog This invokes a GUI dialog box, intended for use with a spell checker. It is called with two arguments, the first being the misspelt word, the second being a table of suggested replacement words. If this dialog is cancelled, utils.spellcheckdialog return nil. Otherwise, it returns two things (which are strings): action, replacement The action can be one of: ignore ignoreall add change changeall The replacement is the replacement word - if the user chose "change" or "changeall" (or double-clicked a suggested word). eg.
    print (utils.spellcheckdialog ("fod", {"food", "fodder"} ))  --> change food
utils.info This returns a table with the following things in it: "current_directory" --> the current directory "app_directory" --> the directory in which MUSHclient.exe resides "world_files_directory" -->> the default world files directory "log_files_directory" --> the default log files directory "plugins_directory" --> the default plugins directory "startup_directory" --> the MUSHclient startup directory This function is primarily intended for situations where you do not have access to the world "Info" functions, such as in the spell-checker. progress This provides functionality for displaying a "progress" dialog box during length operations. First you create the progress dialog with: progress.new (description) That returns a userdata item that you can then use in subsequent operations:
    dlg:status (msg) --> set a status message (eg. "processing 'foo' ")
    dlg:range (low, high) --> sets the range of operations (eg. 1 to 100)
    dlg:position (pos)  --> sets where we are in the range (eg. 80)
    dlg:checkcancel ()  --> returns true if the user clicked on the cancel button
    dlg:setstep (amount)  --> sets the step amount (eg. 5)
    dlg:step ()  --> increase the position by the step amount
    dlg:close ()  --> dismiss the progress dialog
Example of use:
    local dlg = progress.new ("Loading things ...")
    dlg:range (0, 3)  --> 3 steps
    dlg:setstep (1)  --> step 1 per step
    dlg:status ("Processing foo")  --> first step
    dlg:step ()
    -- do something lengthy here
    dlg:status ("Processing bar")  --> second step
    dlg:step ()
    -- do something lengthy here
    dlg:status ("Processing fruit")  --> third step
    dlg:step ()
    -- do something lengthy here
    dlg:close ()  --> all done
The dialog box is a "modal" dialog, so make sure you arrange to close it, even on a script error, or it will lock out attempts to use the GUI interface. If necessary, use "pcall" on whatever it is you do while the dialog is open. See Also ... Topics Lua base functions Lua coroutine functions Lua debug functions Lua io functions Lua math functions Lua os functions Lua string functions Lua table functions Scripting Scripting functions list (Help topic: general=lua) Documentation contents page