Difference between revisions of "Manual:Advanced Lua"
(→Advanced Lua: fixed syntax highlighting) |
|||
Line 23: | Line 23: | ||
If you add this script to the trigger: | If you add this script to the trigger: | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
showMultimatches() | showMultimatches() | ||
− | </ | + | </syntaxhighlight> |
The script, i.e. the call to the function showMultimatches() generates this output: | The script, i.e. the call to the function showMultimatches() generates this output: | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
------------------------------------------------------- | ------------------------------------------------------- | ||
The table multimatches[n][m] contains: | The table multimatches[n][m] contains: | ||
Line 45: | Line 45: | ||
key=5 value=brave | key=5 value=brave | ||
------------------------------------------------------- | ------------------------------------------------------- | ||
− | </ | + | </syntaxhighlight> |
The function showMultimatches() prints out the content of the table multimatches[n][m]. You can now see what the table multimatches[][] contains in this case. The first trigger condition (=regex 1) got as the first full match "You have not completed any quests". This is stored in multimatches[1][1] as the value of key=1 in the sub-table matches[1] which, in turn, is the value of key=1 of the table multimatches[n][m]. | The function showMultimatches() prints out the content of the table multimatches[n][m]. You can now see what the table multimatches[][] contains in this case. The first trigger condition (=regex 1) got as the first full match "You have not completed any quests". This is stored in multimatches[1][1] as the value of key=1 in the sub-table matches[1] which, in turn, is the value of key=1 of the table multimatches[n][m]. | ||
The structure of the table multimatches: | The structure of the table multimatches: | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
multimatches { | multimatches { | ||
1 = { | 1 = { | ||
Line 70: | Line 70: | ||
matches[m] of regex n } | matches[m] of regex n } | ||
} | } | ||
− | </ | + | </syntaxhighlight> |
The sub-table matches[n] is the same table matches[n] you get when you have a standard non-multiline trigger. The value of the first key, i. e. matches[1], holds the first complete match of the regex. Subsequent keys hold the respective capture groups. For example: Let regex = "You have (\d+) gold and (\d+) silver" and the text from the MUD = "You have 5 gold and 7 silver coins in your bag." Then matches[1] contains "You have 5 gold and 7 silver", matches[2] = "5" and matches[3] = "7". In your script you could do: | The sub-table matches[n] is the same table matches[n] you get when you have a standard non-multiline trigger. The value of the first key, i. e. matches[1], holds the first complete match of the regex. Subsequent keys hold the respective capture groups. For example: Let regex = "You have (\d+) gold and (\d+) silver" and the text from the MUD = "You have 5 gold and 7 silver coins in your bag." Then matches[1] contains "You have 5 gold and 7 silver", matches[2] = "5" and matches[3] = "7". In your script you could do: | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
myGold = myGold + tonumber( matches[2] ) | myGold = myGold + tonumber( matches[2] ) | ||
mySilver = mySilver + tonumber( matches[3] ) | mySilver = mySilver + tonumber( matches[3] ) | ||
− | </ | + | </syntaxhighlight> |
However, if you’d like to use this script in the context of a multiline trigger, matches[] would not be defined as there are more than one regex. You need to use multimatches[n][m] in multiline triggers. Above script would look like this if above regex would be the first regex in the multiline trigger: | However, if you’d like to use this script in the context of a multiline trigger, matches[] would not be defined as there are more than one regex. You need to use multimatches[n][m] in multiline triggers. Above script would look like this if above regex would be the first regex in the multiline trigger: | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
myGold = myGold + tonumber( multimatches[1][2] ) | myGold = myGold + tonumber( multimatches[1][2] ) | ||
mySilver = mySilver + tonumber( multimatches[1][3] ) | mySilver = mySilver + tonumber( multimatches[1][3] ) | ||
− | </ | + | </syntaxhighlight> |
What makes multiline triggers really shine is the ability to react to MUD output that is spread over multiple lines and only fire the action (=run the script) if all conditions have been fulfilled in the specified amount of lines. | What makes multiline triggers really shine is the ability to react to MUD output that is spread over multiple lines and only fire the action (=run the script) if all conditions have been fulfilled in the specified amount of lines. | ||
Line 91: | Line 91: | ||
Lua has its own, fast and lightweight pattern matching built in - see [https://www.lua.org/pil/20.2.html 20.2 – Patterns]. Should you need proper regex however, Mudlet has [http://rrthomas.github.io/lrexlib/manual.html lrexlib] available - which works as a drop-in replacement; replace ''string.'' with ''rex.'' - for example ''string.gsub'' to ''rex.gsub''. [http://rrthomas.github.io/lrexlib/manual.html See manual] for documentation. | Lua has its own, fast and lightweight pattern matching built in - see [https://www.lua.org/pil/20.2.html 20.2 – Patterns]. Should you need proper regex however, Mudlet has [http://rrthomas.github.io/lrexlib/manual.html lrexlib] available - which works as a drop-in replacement; replace ''string.'' with ''rex.'' - for example ''string.gsub'' to ''rex.gsub''. [http://rrthomas.github.io/lrexlib/manual.html See manual] for documentation. | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
-- example: strip out trailing .0's from text using a regex | -- example: strip out trailing .0's from text using a regex | ||
local stripped = rex.gsub("1.0.0", [[(\.0+)+$]], '') | local stripped = rex.gsub("1.0.0", [[(\.0+)+$]], '') | ||
print(stripped) | print(stripped) | ||
− | </ | + | </syntaxhighlight> |
== Coroutines == | == Coroutines == | ||
Line 103: | Line 103: | ||
Here's an example - add this code as a new script: | Here's an example - add this code as a new script: | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
-- mudlet.supports didn't always exist, so if it's not already defined - define it | -- mudlet.supports didn't always exist, so if it's not already defined - define it | ||
mudlet = mudlet or {} | mudlet = mudlet or {} | ||
Line 120: | Line 120: | ||
send("sacrifice goat") | send("sacrifice goat") | ||
end | end | ||
− | </ | + | </syntaxhighlight> |
Make a ''^ritual$'' alias - which seems big, but that's just because there's a lot of explanation inside it: | Make a ''^ritual$'' alias - which seems big, but that's just because there's a lot of explanation inside it: | ||
− | <lua> | + | <syntaxhighlight lang="lua"> |
-- coroutines weren't supported before 3.2, so don't do anything if this alias | -- coroutines weren't supported before 3.2, so don't do anything if this alias | ||
-- is used in an older Mudlet | -- is used in an older Mudlet | ||
Line 142: | Line 142: | ||
ritualcoroutine = nil | ritualcoroutine = nil | ||
end | end | ||
− | </ | + | </syntaxhighlight> |
Now try doing the '''ritual''' command. You'll see that the ''send()'s'' are being sent one at a time, instead of all at once as they would have been without the yields. Cool, huh? | Now try doing the '''ritual''' command. You'll see that the ''send()'s'' are being sent one at a time, instead of all at once as they would have been without the yields. Cool, huh? |
Revision as of 05:21, 29 June 2017
Advanced Lua
Lua tables
A good overview of tables is available on Lua's wiki in the TablesTutorial. Nick Gammon has also written a nice overview on how to deal with Lua tables.
How to use multilinematches[n][m]
multilinematches[n][m]
is the compliment of matches[n]
when matching multi-line triggers. multilinematches[n][m]
stores its matches by lines, inside each line are the relevant matches to it. TThe following example can be tested on the MUD batmud.bat.org:
In the case of a multiline trigger with these 2 Perl regex as conditions:
^You have (\w+) (\w+) (\w+) (\w+)
^You are (\w+).*(\w+).*
The command "score" generates the following output on batMUD:
You have an almost non-existent ability for avoiding hits. You are irreproachably kind. You have not completed any quests. You are refreshed, hungry, very young and brave. Conquer leads the human race. Hp:295/295 Sp:132/132 Ep:182/181 Exp:269 >
If you add this script to the trigger:
showMultimatches()
The script, i.e. the call to the function showMultimatches() generates this output:
-------------------------------------------------------
The table multimatches[n][m] contains:
-------------------------------------------------------
regex 1 captured: (multimatches[1][1-n])
key=1 value=You have not completed any quests
key=2 value=not
key=3 value=completed
key=4 value=any
key=5 value=quests
regex 2 captured: (multimatches[2][1-n])
key=1 value=You are refreshed, hungry, very young and brave
key=2 value=refreshed
key=3 value=young
key=4 value=and
key=5 value=brave
-------------------------------------------------------
The function showMultimatches() prints out the content of the table multimatches[n][m]. You can now see what the table multimatches[][] contains in this case. The first trigger condition (=regex 1) got as the first full match "You have not completed any quests". This is stored in multimatches[1][1] as the value of key=1 in the sub-table matches[1] which, in turn, is the value of key=1 of the table multimatches[n][m].
The structure of the table multimatches:
multimatches {
1 = {
matches[1] of regex 1
matches[2] of regex 1
matches[3] of regex 1
...
matches[m] of regex 1 },
2 = {
matches[1] of regex 2
matches[2] of regex 2
...
matches[m] of regex 2 },
... ...
n = {
matches[1] of regex n
matches[2] of regex n
...
matches[m] of regex n }
}
The sub-table matches[n] is the same table matches[n] you get when you have a standard non-multiline trigger. The value of the first key, i. e. matches[1], holds the first complete match of the regex. Subsequent keys hold the respective capture groups. For example: Let regex = "You have (\d+) gold and (\d+) silver" and the text from the MUD = "You have 5 gold and 7 silver coins in your bag." Then matches[1] contains "You have 5 gold and 7 silver", matches[2] = "5" and matches[3] = "7". In your script you could do:
myGold = myGold + tonumber( matches[2] )
mySilver = mySilver + tonumber( matches[3] )
However, if you’d like to use this script in the context of a multiline trigger, matches[] would not be defined as there are more than one regex. You need to use multimatches[n][m] in multiline triggers. Above script would look like this if above regex would be the first regex in the multiline trigger:
myGold = myGold + tonumber( multimatches[1][2] )
mySilver = mySilver + tonumber( multimatches[1][3] )
What makes multiline triggers really shine is the ability to react to MUD output that is spread over multiple lines and only fire the action (=run the script) if all conditions have been fulfilled in the specified amount of lines.
Regex in Lua
Lua has its own, fast and lightweight pattern matching built in - see 20.2 – Patterns. Should you need proper regex however, Mudlet has lrexlib available - which works as a drop-in replacement; replace string. with rex. - for example string.gsub to rex.gsub. See manual for documentation.
-- example: strip out trailing .0's from text using a regex
local stripped = rex.gsub("1.0.0", [[(\.0+)+$]], '')
print(stripped)
Coroutines
Mudlet supports Lua's coroutines starting with 3.2.0, which opens up a whole lot of possibilities for the way you program your scripts. A pretty technical description and a tutorial is available, but for a quick explanation, think of coroutines allowing you to pause and resume running a function. If you're familiar with other clients, it is something like a #wait statement where a script will stop running, except unlike a #wait which auto-resumes the script later, you resume it when it yourself whenever you'd like.
Here's an example - add this code as a new script:
-- mudlet.supports didn't always exist, so if it's not already defined - define it
mudlet = mudlet or {}
mudlet.supports = mudlet.supports or {}
function ritual()
send("get wood")
-- think of coroutine.yield as yielding (giving away) control,
-- so the function will stop here and resume on making fire
-- when called the next time
coroutine.yield()
send("make fire")
coroutine.yield()
send("jump around")
coroutine.yield()
send("sacrifice goat")
end
Make a ^ritual$ alias - which seems big, but that's just because there's a lot of explanation inside it:
-- coroutines weren't supported before 3.2, so don't do anything if this alias
-- is used in an older Mudlet
if not mudlet.supports.coroutines then return end
-- create a coroutine that'll be running our ritual function
-- or re-use the one we're already using if there is one
ritualcoroutine = ritualcoroutine or coroutine.create(ritual)
-- run the coroutine until a coroutine.yield() and see
-- if there's any more code to run
local moretocome = coroutine.resume(ritualcoroutine)
-- if there's no more code to run - remove the coroutine,
-- so next time you call the alias - a new one gets made
if not moretocome then
ritualcoroutine = nil
end
Now try doing the ritual command. You'll see that the send()'s are being sent one at a time, instead of all at once as they would have been without the yields. Cool, huh?
You can also install the demo as a package - paste this into Mudlet:
lua function downloaded_package(a,b)if not b:find("oci",1,true)then return end installPackage(b)os.remove(b)cecho("package installed!\n")end registerAnonymousEventHandler("sysDownloadDone","downloaded_package")downloadFile(getMudletHomeDir().."/oci.mpackage","http://wiki.mudlet.org/images/3/36/Ritual-coroutine-demo.zip")
Note that if you'll be using coroutines as part of a package you'll give to others, remember about the if mudlet.supportscoroutines then return end
bit. Older Mudlets that don't support coroutines might crash, which sucks. Newer ones that do support them are completely fine, however!
Coroutines have many uses: finite state machines, running intensive tasks (yielding every once in a while so Mudlet isn't frozen), and so on.