MUSHclient with open-source tools
This guide outlines the implementation of a persistent Lua-based plugin for MUSHclient. It focuses on creating a data-driven system that captures MUD output via regular expressions, stores state across sessions using the built-in variable system, and utilizes lifecycle hooks for clean initialization.

Define the Plugin Metadata Header
Create a new .xml file in the /worlds/plugins directory. Every MUSHclient plugin requires a unique 24-character ID and specific attributes to identify itself to the client engine.
<include name="Constants.lua"/>
<plugin
name="HealthTracker"
author="Developer"
id="a1b2c3d4e5f6g7h8i9j0k1l2"
language="Lua"
purpose="Tracks HP and Mana via triggers"
save_state="y"
version="1.0"
>
</plugin>⚠ Common Pitfalls
- •Duplicate IDs will cause the client to ignore the plugin.
- •Ensure save_state is set to 'y' or variables will not persist on disk.
Initialize the Lua Script Block
Open a <script> tag within the XML. This is where the Lua logic resides. Use the OnPluginInstall callback to initialize global tables and load existing data.
<script>
<![CDATA[
function OnPluginInstall()
Note("HealthTracker plugin loaded.")
hp = tonumber(GetVariable("current_hp")) or 0
max_hp = tonumber(GetVariable("max_hp")) or 0
end
]]>
</script>⚠ Common Pitfalls
- •Variables retrieved via GetVariable are always strings; tonumber() is required for math operations.
- •CDATA tags are essential to prevent the XML parser from breaking on Lua symbols like < or &.
Implement Regex Triggers for Data Capture
Define triggers within the XML (outside the script block) to capture MUD output. Use named groups or wildcards to pass data to a Lua function.
<triggers>
<trigger
enabled="y"
match="^HP: (\d+)/(\d+) MP: (\d+)/(\d+)>"
regexp="y"
script="OnStatsUpdate"
sequence="100"
>
</trigger>
</triggers>⚠ Common Pitfalls
- •Incorrect sequence numbers can cause other plugins to intercept lines first.
- •Ensure the 'regexp' attribute is set to 'y' if using PCRE patterns.
Process Captured Data in Lua
Write the callback function defined in the trigger. This function receives wildcards from the regex match. Update the local state and perform logic like low-health warnings.
function OnStatsUpdate(name, line, wildcards)
hp = tonumber(wildcards[1])
max_hp = tonumber(wildcards[2])
if hp < (max_hp * 0.2) then
ColourNote("red", "black", "WARNING: LOW HEALTH!")
end
end⚠ Common Pitfalls
- •Wildcard indices start at 1 in MUSHclient Lua, unlike 0-indexed arrays in other languages.
- •Excessive use of Note() or ColourNote() in high-frequency triggers can cause client lag.
Configure State Persistence
To ensure data survives a client restart or plugin reload, use OnPluginSave to write variables back to the plugin's internal database.
function OnPluginSave()
SetVariable("current_hp", hp)
SetVariable("max_hp", max_hp)
end⚠ Common Pitfalls
- •SetVariable only accepts strings or numbers; tables must be serialized using a helper or JSON-like stringification.
- •If the plugin crashes before OnPluginSave is called, data from that session is lost.
Debug via the Global Trace Window
Use the built-in MUSHclient trace functionality to debug trigger matches. Press Ctrl+Alt+T or use script commands to inspect which triggers are firing and why they might fail to match.
-- Add this to your logic to force a trace during testing
SetEntityCheck(true)
Trace(true)⚠ Common Pitfalls
- •Leaving Trace(true) active in a production plugin will flood the output window and degrade performance.
What you built
By following this structure, you have created a modular MUSHclient plugin that separates trigger definitions from Lua logic while maintaining state. This architecture is scalable for complex UI overlays, automated combat systems, or inventory management scripts.