This page can be quickly reached at http://mushclient.com/scripting
An introductory tutorial video on scripting is at http://vimeo.com/80333485.
Why use scripting?
Scripting is optional, but if you choose to do it you can extend the power of the client by doing such things as:
- Conditional tests (eg. warn if HP low)
- Arithmetic (count things you kill)
- Fancy displays (different colours)
- Stats rollers
- Automated attacks
- Auto healer
- Health bar
- Level timer
- Wilderness walker
- Random socials
- Lots more ...
Setting up for scripting
The first thing you need to do for each MUSHclient "world" is to enable scripting in the world configuration dialog box. This does a number of things:
- Indicates you wish to use scripting
- Declares which script language you are going to use
- Initializes the appropriate scripting "engine"
- Declares which script file (to hold your scripts), if any, you want to use
- Defines a "scripting prefix" for typing script commands into the main command window
- Optionally lets you specify script handlers for "events" such as when you connect to a MUD
Example:
The important things to configure here are:
- Language - I have chosen Lua, however you can choose from (at present):
- VBscript (a form of Visual Basic)
- JScript (a form of JavaScript)
- PerlScript (Perl scripting language)
- Python
- Tcl
- Lua (recommended)
- PhpScript
- Enable scripting - this tells MUSHclient to "turn scripting on".
- Script file - this selects the name of the file you are storing scripts in. This is optional and can be omitted initially. A script file is simply a text file you create with a text editor, and save with the appropriate suffix (eg. .lua for Lua files, .vbs for VBscript files).
- Script prefix - this is a character string that you will use for entering script commands into the main command window. The default is "/", which the examples below will assume you are using.
Which language?
If you already are familiar with a language, such as VBscript or JScript then it probably makes sense to choose that one. If not, I recommend choosing Lua, for the following reasons:
- Lua is easy to learn, yet very powerful
- The Lua script DLL is shipped with MUSHclient - you can be sure the other MUSHclient users (assuming they have a reasonably recent version) will also have Lua.
- The scripting support for Lua lets you do some more powerful things (like named wildcards in triggers) very easily
- Lua's "table" structure, and inline functions are very powerful and let you write some very complex scripts
- Lua is cross-platform, so if you ever want to use MUSHclient on Unix (under the Wine program) you can still use your Lua scripts
The documentation for Lua is at Lua Documentation.
Where do I put my scripts?
Script commands can go in one or more of 4 main places:
- Type into command window
- The "Immediate" scripting window
- Directly into triggers, timers, aliases ("Send to script")
- Your script file
You can use any or all of the above. Typically the command and intermediate windows are used for quick tests of script commands or short scripts.
Also you can put scripts into plugins, in which case they can also go directly into triggers, aliases and timers (send to script), or into the script part of the plugin.
Let's look at each one in turn. I will demonstrate a "hello, world" script for each one. These examples will use Lua, although they would look very similar in other languages.
Command window
Note that in some languages, such as Lua, script function names and keywords are case-sensitive, so you must use 'Note' rather than 'note'.
If you have images disabled, the graphic shows:
/Note "Hello, world!"
in the Command window.
|
In this example the script command is simply typed into the MUD "command" window, prefixed by the scripting prefix, in this case a "/" symbol. This is useful for testing commands to see how they work, eg.
If you have images disabled, the graphic shows:
/ColourNote ("white", "green", "how does ColourNote work?")
in the Command window.
|
The command window is most suitable for single-line commands, although you can do multiple lines by pressing Ctrl+Enter to insert linebreaks into the command window. Multiple-line commands are more easily entered in the Immediate window, described below.
Immediate window
If you have images disabled, the graphic shows:
Note "Hello, world!"
in the Immediate window (dialog box).
|
You access the Immediate window from the Game -> Immediate menu (or press Ctrl+I). This opens a dialog box, as shown above, that lets you type in multi-line script commands, and then press "Run" to execute the script. The contents of the window are remembered for the current MUSHclient session, when you click on the Close button.
For ease of editing you can click on the "Edit..." button to open a larger, resizable, dialog box that has a monospaced font.
The Immediate window is handy for testing larger script snippets, where you might want to test a loop or function definition. If you are planning a lengthy script for a trigger, alias or timer, it could be helpful to test your ideas out first in the Immediate window.
The Immediate window and command window share the same "script space" so that variables or functions declared in one are accessible in the other one (and also in the script file, and "send to script").
An example of what you might try in the Immediate window is:
If you have images disabled, the graphic shows:
colours = { "red", "green", "blue", "magenta", "cyan" }
for i = 1, 5 do
ColourNote ("white", colours [i], "Test number " .. i)
end
in the Immediate window and also in the "Edit Immediate expression" window, accessed by clicking on the Edit button.
|
This example shows a multi-line script, and also shows the "Edit" dialog box being opened for easier editing. I have resized it a bit to make the screen dump smaller, but it shows the general idea. Again you can see that by using Note (or ColourNote or similar) you can display information in the main MUSHclient world window.
In triggers, aliases, timers (send to script)
The third method is to script directly in a trigger, alias or timer. This is known as the "send to script" method, because the text in the trigger/timer/alias is not sent to the MUD but to the script engine.
This method is an easy way of scripting your triggers/timers/aliases because it does not require the use of a separate script file.
Let's start of with our "hello, world" example ...
If you have images disabled, the graphic shows:
Note "Hello, world!"
in the Send box of a newly created alias. Also the word "test" is in the Alias box. The "Send To" combo-box has "Script" selected. Every other entry is the default.
|
The example above shows that the script command is now in the "send" box of the alias, and the "send to" field is set to "script". Thus the command is sent to the script engine. So when I type the word "test" in the command box, the alias matches it, and sends the script command to the script engine, thus "hello, world" appears in the output window.
In the case of "send to script" the script commands are not executed (and thus checked for syntax) until the alias matches, so any syntax errors are only detected then. Thus you need to make sure you check your aliases, triggers and timers after writing scripts for them.
If you have images disabled, the graphic shows:
if %1 < 100 then
ColourNote ("white", "red", "Warning! - health is low")
end -- if
in the Send box of a newly created trigger. Also the text "<*/*hp */*m */*mv */*xp> *" is in the Trigger box. The "Send To" combo-box has "Script" selected. Every other entry is the default.
|
The more elaborate example above shows a trigger designed to test for low health. It uses an "if" test on wildcard 1 (%1 in the script) to see if your health is below 100, and if so, to do a red warning in the output window.
The asterisks in the trigger match text are to match the variable information (ie. hp, mana, movement points, xp).
In the "send" box is the script, however since it is too big to easily edit there, I have clicked on the "..." button which opens up a larger, monospaced font, editing window.
Once again, "send to script" is chosen, so the script commands are executed whenever the trigger matches.
In order to test triggers, it is handy to "force through" something you know will match. Rather than waiting for it to occur in the heat of battle, you can use the "Debug simulated world input" dialog box to pretend that the relevant line has arrived. Press Ctrl+Shift+F12 to open the dialog box. If your trigger matches on specific colours or special text (such as bold) then you can use the "specials" dropdown menu and "insert special" button to insert the desired ANSI codes into your dialog window.
If you have images disabled, the graphic shows:
<50/1000hp 100/100m 110/110mv 2000/31581675xp>
in the "Debug simulated world input" dialog box. There is a blank line before and after this text (entered by pressing Ctrl+Enter).
|
Above is an example of how I tested the trigger described earlier. Press Ctrl+Enter to enter multiple lines.
In your script file
The final approach to scripting is to put your scripts into a script file (or in the case of plugins, the script area of the plugin). This has the advantages of:
- Centralising your scripts into a single place, where they can be easily inspected.
- Share one script routine between multiple triggers, aliases, and timers.
- Have "helper" scripts that are shared between other scripts (ie. subroutines)
- Handle special events like:
- On world open
- On world close
- On getting the focus
- On losing the focus
- On connecting to the MUD
- On disconnecting from the MUD
- On world save
- Other things, inside a plugin
- Various MXP-related events, such as a variable being set
- You can also initialise variables when the script loads, by placing scripts outside of any function.
To put your script in a script file, first you have to create the file (an empty file if necessary), and save it using the appropriate filename suffix for your chosen language (eg. myscript.lua). Then choose that file as your "script file" in the scripting configuration tab, as shown earlier up. Once you have done that you can edit the file using MUSHclient's text editor by using Game -> Edit Script File menu item (or pressing Shift+Ctrl+H), or simply edit it in an external editor of your own choosing.
Whenever the script engine is initialised the script file is read in. If you make changes to it you must reprocess the script file. MUSHclient tries to detect when you do that and offer to reload it automatically, but you can force it to do that by using Game -> Reload Script File menu item (or pressing Shift+Ctrl+R).
When using a script file in this way, any syntax errors in your script are immediately detected, so you can fix them before trying to test the scripts. However runtime errors (such as divide by zero) will not be detected.
Once, again, let's do our "hello, world" example, using a script file...
If you have images disabled, the graphic shows a new alias with "test" in the Alias box. It also has "test_alias" as the function name in the Script box. All other entries are the defaults.
The graphic also shows the contents of the script file (myscripts.lua) being edited. It contains:
function test_alias (name, line, wildcards)
Note "Hello, world!"
end -- function
|
In this case we are using a script file function, so the "send to" field is still set to "world" (the default). In practice we can use the "send" box to send things to wherever we want and call the script function.
The important part is that the "script" box is filled in with the name of the script function. This is the name used in the function declaration inside the script (in this case, "test_alias").
The function declaration in the script file must follow the conventions documented for the type of thing calling it.
In the case of triggers and aliases, that is:
- Name - the name of the trigger or alias (the contents of the "label" field)
- Line - the entire line that matched the trigger or alias
- Wildcards - an array of the matching wildcards (if any)
In the case of timers, it is:
- Name - the name of the timer (the contents of the "label" field)
The purpose of passing down the name of the trigger, alias or timer, is to let the script function share the same function between different things, and let the function detect which one called it by testing the name.
Below is a more elaborate example, that shows how you can process wildcards passed down to an alias, and also pass down a name to it.
If you have images disabled, the graphic shows a new alias with "^target (?P<target>.*?)$" in the Alias box. It has "target_alias" as the function name in the Script box. The Regular Expression checkbox is checked. It has "target_someone" as the name of the alias in the Label box. All other entries are the defaults.
The graphic also shows the contents of the script file (myscripts.lua) being edited. It contains:
function target_alias (name, line, wildcards)
Note ("Name = " .. name)
Note ("Line = " .. line)
for k, v in pairs (wildcards) do
Note ("Wildcard " .. k .. " = " .. v)
end -- for
SetVariable ("target", wildcards.target)
end -- function
The command windows show me testing it with "target nick" as the command. The Output window shows the results, which are:
Name = target_someone
Line = target nick
Wildcard 1 = nick
Wildcard target = nick
Wildcard 0 = target nick
|
Interesting points here are:
- The script function is now in a separate file - the script file, shown in the upper window. This file could be shared between different MUSHclient world files.
- The alias has a name (a label) which is "target_someone". That is not the name of the script it calls. Names must be unique - that is, only one alias can have a particular name. The name is passed down to the script. In this case it simply displays it. The name is highlighted above in green.
- The alias calls a script whose function name is "target_alias". Many aliases can call the same script, however only one function of that name can be in the script file. The script name is highlighted above in yellow.
- I have used a regular expression as the match text so I could use a named wildcard. In this case the wildcard is named "target". This is passed down in the wildcards array (as wildcards.target) so it can be directly used to set a variable. This named wildcard is highlighted above in blue. Named wildcards are a powerful feature that removes the worry of "counting wildcards". You can access a named wildcard directly from the wildcards array in Lua, as shown above. In other languages use the GetAliasWildcard or GetTriggerWildcard script function.
- Wildcard 1 and wildcard "target" are the same thing - the first wildcard.
- Wildcard 0 is the entire text that matches the regular expression
- The "line" argument is the entire matching line that triggered the trigger. In this case wildcard 0 and "line" are identical, however if the regular expression only matched part of the line they would not be.
We can check that the script functioned correctly and set the variable by looking at the variables configuration screen ...
If you have images disabled, the graphic shows the variables configuration window, with "target" as the name of the variable, and "nick" as the contents.
|
Another approach would be to simply type "/Note (GetVariable ("target"))" into the command window and check that the word "nick" was echoed in the output window.
Using variables
Scripts can use two type of variables:
- Variables supported by the script language itself
- MUSHclient variables
Each has their pluses and minuses.
Script language variables
These are built into the script language, and are typically:
- Numbers
- Strings
- Booleans (true or false)
and may also include:
- Arrays
- Lists
- Dates/times
- COM objects
- Functions
Using the inbuilt variables is easy, you simply script them the way your language supports. The drawbacks are:
- Variables are lost when the script engine is re-initialised, which might happen a lot during testing
- Variables are not saved when you close your world
- Variables are not loaded next time you open your world
MUSHclient variables
MUSHclient variables are accessed by using GetVariable and SetVariable script routines. They are automatically loaded when you open your world file, and saved with your world file when you save it (if you save it, of course).
The drawbacks of MUSHclient variables are:
- Clumsier to access - you have to use GetVariable and SetVariable to use them
- They are only strings - thus things like numbers and dates have to be converted to/from strings, and special structures like lists and arrays are harder to store.
You can work around the drawbacks above by scripting appropriately. For example, you can load script variables from MUSHclient variables on world open, and save them back again on world close.
Scripting functions you can call
Apart from the functions you write yourself, and those provided by the script language itself, there are many functions built into MUSHclient that you can call (about 295 of them). They are all documented in the help file that ships with MUSHclient, and also on at MUSHclient scripting functions and MUSHclient online documentation.
As an example, here is the documentation for ColourNote.
The documentation is extensively cross-referenced, for example the ColourNote documentation references other ways of doing notes (eg. not in colour) and also various ways of manipulating colours.
Handling errors
It would be a rare scripter that does not make some sort of mistake during development. The first type is syntax or compile-time errors. An example is below ...
If you have images disabled, the graphic shows the Immediate window with this script in it:
for i = 1, 5 do
blah (i)
end -- for
The scripting error dialog box has popped up, containing:
[string "Immediate"]:2: attempt to call global 'blah' (a nil value)
stack traceback:
[string "Immediate"]:2: in main chunk
|
In this case I have deliberately called a non-existant function "blah". It is worthwhile reading the error message, as it usually tells you a fair bit. In this case it tells me the error is on line 2 (which I can find by counting down my script), and also the nature of the error, in this case that "blah" is a "nil value". In Lua parlance, that means "blah" is not defined.
Strictly speaking, the above error is not a syntax error but a runtime error. However the general idea is the same, you will see a dialog box similar to the one above.
Recent versions of MUSHclient have an extra option in the scripting configuration dialog that lets you redirect error messages to the output window, rather than a dialog box, which can be more useful if errors occur during a battle.
Logic errors can be harder to find, that is if the script simply isn't doing the right thing. However judicious use of Note can be helpful to display values from inside your script to see what is going wrong.
|