Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Due to spam on this forum, all posts now need moderator approval.
Entire forum
➜ MUSHclient
➜ General
➜ Multi-line triggers
It is now over 60 days since the last post. This thread is closed.
Refresh page
| Posted by
| Nobody
(38 posts) Bio
|
| Date
| Sat 20 May 2006 12:46 PM (UTC) |
| Message
| I've been trying to make a specific multi-line trigger, reading all the docs and forum posts I can find. The trouble I'm having is with the "lines to match" bit.
The problem, simply put, is that there are any possibility of number of lines matching, from 0-50. If I set the number of lines high, then it matches past incidents as well. If I set it too low, I miss stuff.
Consider the following output:
Spells you can cast:
spell1 spell2
spell3_long_name
spell4 spell5 and spell6
Spells you can't cast:
"firey death" and "eroticism"
Mana left: 500/1000000000
Now, in this output, there are 3 (well, 2) easily definable start and end points. The middle bit (spells you cant cast) might not show up if there's nothing in it. What I want to do is a trigger that matches on the "spells you can cast" line, and then keeps reading the trigger until it hits the last line.
I realise I can probably do this easier by using simple triggers to turn it on and off, but it's actually easier for me if all the data in between gets sent to a script as one big hunk of text. I don't want to have to screw around with concatenating them myself and making sure the newlines are in the right place.
It would be nice if I could have a regexp "^Spells you can cast\n(.*)\nMana left" that would correctly match the multiple lines, and chuck everything in between into %1. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #1 on Sat 20 May 2006 11:51 PM (UTC) Amended on Mon 22 May 2006 06:19 AM (UTC) by Nick Gammon
|
| Message
| There is a very lengthy discussion of the philosophy behind the design of multi-line triggers here:
http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=4087&page=4
What the multi-line trigger matching does is apply a regular expression (that you supply) over the last "x" lines (where "x" is a number from 2 to 200, that you supply).
Now clearly if you have a multi-line trigger like:
^You see a bear\nIt growls at you$
... and you specify 50 lines, then potentially it will match about 48 times, as the text is in the 50-line buffer every time the test is done, gradually moving from the bottom to the top.
The character ^ only specifies the start of a line, not the start of the 50-line buffer.
What you need, to prevent multiple matches is one of:
- Make the number of lines small enough to prevent multiple matches, if possible.
- Anchor the regular expression to the start of the buffer with \A. For example:
\AYou see a bear\nIt growls at you$
Now it can only match if "You see a bear" is at the start of the 50-line buffer. This will certainly prevent multiple matches, but has the drawback that you need another 48 lines to arrive to "push" the target text to the top of the buffer.
- Anchor the regular expression to the end of the buffer with \Z or \z. For example:
^You see a bear\nIt growls at you\z
Now it can only match if "It growls at you" is at the end of the 50-line buffer. This will also prevent multiple matches, and also has the advantage that it will match as soon as the buffer has the text in it (that is, sooner than anchoring to the start).
Things get a little more complicated if there is no clear-cut "end of match" text. The best you can really do there is to find something that won't be in the matching sequence. For example, an inventory list:
You are carrying:
a magic mushroom
a torch
a dragonskin
a bag
recall scroll
<21/21hp 143/143m 110/110mv 0/10000xp>
Now in this example there is no string that terminates the inventory list, except that it is not something beginning with 5 spaces.
So, this regexp will work:
^You are carrying:\n(([ ]{5}.*\n)*)[^ ]\Z
Now we are looking for "You are carrying:" followed by any number of lines that start with 5 spaces, followed by a line that does not start with a space, anchored to the end of the multiple-line buffer. This final test is what stops the multiple matches.
In your case, you are lucky as you have known text at the end ("Mana left") which is what you can anchor with.
This regexp works for me:
(?s)^Spells you can cast\:\n(.*)Mana left: \d+/\d+\Z
This does what you want, puts everything in the middle into the %1 wildcard. Note the \Z at the end to anchor it, and also the "(?s)" specification which says "dots match all", that is it makes ".*" match everything including newlines.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Nobody
(38 posts) Bio
|
| Date
| Reply #2 on Sun 21 May 2006 05:10 AM (UTC) |
| Message
| Indeed it does work, I had something similar working for me the other night. But the problem remains that "lines to match" option, because the length of the output of that spells command can range from a minimum of 1, up to about 20 or 25. When I set the lines to 25, then everytime I hit enter (and get a new prompt from the mud) it matches again on the old list. I don't want it to keep matching on something that's no longer necessary.
I guess the multi-line trigger just isn't going to work in this situation, I'll have to just use standard triggers and hope I can script it properly.
Thanks anyway. | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #3 on Sun 21 May 2006 06:28 AM (UTC) |
| Message
| | No, if you do what I suggested it will match once, not many times. Can you post the trigger you are using, that doesn't work please? |
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
| Posted by
| Nobody
(38 posts) Bio
|
| Date
| Reply #4 on Mon 22 May 2006 04:12 AM (UTC) |
| Message
| The trigger is as follows:
<triggers>
<trigger
enabled="y"
group="Multi Line"
lines_to_match="100"
match="^Spells\:$((?s)(.*?))^Mana left(.*?)$\Z"
multi_line="y"
regexp="y"
send_to="2"
sequence="100"
>
<send>'%1' Matched!</send>
</trigger>
</triggers>
The problem is this. Let's say I do the command, and get the output. If it's a small output, the first time, it works ok. Then I do something else, and then I do that command again, then the trigger matches from the first time I hit it, all the way to the end of the second time I hit it.
Do you see what I'm saying? | | Top |
|
| Posted by
| Nick Gammon
Australia (23,173 posts) Bio
Forum Administrator |
| Date
| Reply #5 on Mon 22 May 2006 06:28 AM (UTC) |
| Message
| Indeed I do. I see what you mean. You have even used non-greedy wildcards, which was going to be my next suggestion.
I think what is happening is that once the regexp matcher finds "Spells:" it considers it to be potentially part of the pattern, and the .* wildcard then grabs everything after it, including further "Spells:" lines.
My example of the inventory works, because the inventory lines match the case of having leading spaces. I don't suppose the spells lines can be identified, or are they free-format?
I've tried various combinations but can't get them to work. All I can suggest is either:
- Use the multiple trigger method, turning on a trigger at the start of the spell list, and looking for something that indicates its end; or
- Once you have the wildcard from your multi-line trigger, break it into lines (eg. utils.split) and then scan backwards until you hit the first line with "Spells:" on it, thus eliminating earlier, false, matches.
You probably need to break it into lines anyway to extract out the spell names, so this is probably not a big deal.
|
- Nick Gammon
www.gammon.com.au, www.mushclient.com | | Top |
|
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.
22,375 views.
It is now over 60 days since the last post. This thread is closed.
Refresh page
top