| 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: ... 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:
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: by: 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. 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: 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. All the available keys and values are: Trigger flags for AddAlias These are in the 'alias_flag' table. All the available keys and values are: Trigger flags for AddTimer These are in the 'timer_flag' table. All the available keys and values are: Custom colour flags for AddTrigger These are in the 'custom_colour' table. All the available keys and values are: Error codes - map error names to numbers These are in the 'error_code' table. You can use these to check individual error codes. eg. All the available keys and values are: Error codes - map error codes to descriptions These are in the 'error_desc' table. You can use these to give meaningful error messages. eg. All the available keys and values are: 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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.
The default, if omitted, is 6. eg. 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: 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. 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. 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. You can write a small Lua program to do the same thing that the md5sum program does (in Linux, Cygwin etc.): Compare this to the output from md5sum using Cygwin: 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. 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. 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. utils.fromhex (s) This converts the supplied hexadecimal string s back to a normal string. The converted string may contain binary zeroes. eg. 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. 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: 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:
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: Output: You pass utils.split 2 or 3 arguments:
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. Output: 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:
If the parsing fails, three results are returned:
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:
Example: Output: 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: 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.
Return value = (string) yes, no, ok, retry, ignore, cancel, abort Example: 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: The only required argument is the message text itself.
Return value = what they typed, or nil if cancelled Example: 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:
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.
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: The only required arguments are the message text and the table of choices (t).
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: Possible returned values would be:
(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. 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: Possible returned values would be:
The return value will be one of the following types:
Here is an example of using multilistbox: 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: 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:
The following are valid execution-time flags:
An example of using the flags would be: 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: 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: The third result returned is a table of capture patterns that have been matched. Example: 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: 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:
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: utils.filepicker This invokes the Windows standard "file picker" dialog box, which lets you choose a file for opening or saving. Usage is:
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. 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. 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:
The replacement is the replacement word - if the user chose "change" or "changeall" (or double-clicked a suggested word). eg. utils.info This returns a table with the following things in it:
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: Example of use: 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 |