Help with Triggers, Is This Possible?...

Posted by Onoitsu2 on Sat 25 Feb 2006 07:13 AM — 38 posts, 152,118 views.

USA #0
I am attempting to "Omit from Output" only 1 "word" in the same mannar that you would do to color it, and am having no luck. I have looked over all the MC documentation and have not seen a specific limitation spelled out, nor have I seen it put in to example of how to do such a thing ...

for the time being I have used it to color the "word" as black on black so I do not see it. I want this as I have a magical vision that shows things like:

(Magical) (Blue Aura) (Glowing) (Humming) <ItemNameHere>[1]

The glowing and humming I want there but the magical and blue aura i do not wish to see ... and sometimes an item will not have all, or not all items will have the magical at the front, it may be in any position for that matter ... just depending on the order of spells cast on the item.

I hope that someone is able to assist me on this matter ...

Thanks,
Onoitsu2
Australia Forum Administrator #1
One of the characteristics of MUSHclient is that triggers cannot actually change lines, one of the reasons being they have already been written to the screen before the trigger fires.

What you can do however, is call a script, which mangles up the original line (eg. omitting parts, changing others) and output that using the world.Note function.

Combine that with omitting the original line, and you effectively can do what you want.
USA #2
Isn't there also now something that allows you to send a line back to Mushclient, so its treated as though it came from the mud? So, you could use the packet capture functions to get the line, change it, then send it back to Mushclient to be processed normally. It would mean making your own matching functions, instead of using Mushclient's exiting trigger system, but it should now be possible, as of the last few version.

Or was I just imagining things? ;) lol
Australia Forum Administrator #3
The "simulate input" function was really intended for debugging. Potentially your suggestion could either cause MUSHclient to go into a loop, reprocessing the same line, or simply process things out of sequence.

I think my suggestion will work fairly well.
USA #4
Hmm. Yeah, in some cases it might loop. Depends on how you design the code for it. Your idea unfortunately is a) more complicated to make work right, b) won't correctly match on any triggers after the one that omits it, especially if the trigger matches the "final" version that removes/adds a word {though this could be considered bad trigger design anyway} and c) gets damned complicated when you need to preserve colors. Using simulate might not have been intended for doing it, but it does solve all three issues with trying to use omit, when you are significantly changing a lines contents. Since you would most likely need to use OnPluginPacketRecieved to do it correctly, does Simulate fee dthrough that anyway? If it does, then there may still be solutions, like dropping ESCESCESCm at the start. This is invalid ANSI, but is unlikely to come from a mud, shouldn't effect how the line gets processed and would be easilly detectable in the packet testing stage, thus showing that the line had already been altered. You just look for it, and return True, without any other testing or alterations.
#5
hi hi.

just a heads up, but I am a complete noob. :)

I want to omit my 'send' info for a trigger. If I click the omit from output box it omits both the send info and the actual trigger. I just want to omit the send info. Anyone?
Australia Forum Administrator #6
Change the send text to:

SendNoEcho "whatever you want to send"

Change "send to" to "Script".

Make sure scripting is enabled. I would recommend Lua as the language, however this example will also work with VBscript.

The quotes are required. Here is my test that will "sigh" whenever it sees the word "naga":


<triggers>
  <trigger
   enabled="y"
   match="naga"
   regexp="y"
   send_to="12"
   sequence="100"
  >
  <send>
SendNoEcho "sigh"</send>
  </trigger>
</triggers>

#7
Thank you Mr. Gammon ^_^
USA Global Moderator #8
Nick Gammon said:

One of the characteristics of MUSHclient is that triggers cannot actually change lines, one of the reasons being they have already been written to the screen before the trigger fires.

I've often wondered about that. Why aren't triggers processed before writing to the screen?
USA #9
Fiendish said:

Nick Gammon said:

One of the characteristics of MUSHclient is that triggers cannot actually change lines, one of the reasons being they have already been written to the screen before the trigger fires.

I've often wondered about that. Why aren't triggers processed before writing to the screen?

In a similar vein, I'e always been a little curious why the (after omit) send-to isn't the default behavior.
Australia Forum Administrator #10
Fiendish said:

I've often wondered about that. Why aren't triggers processed before writing to the screen?


I have to write to the screen as text arrives. Version 0.64 of MUSHclient added this feature:

http://www.gammon.com.au/scripts/showrelnote.php?version=0.64&productid=0

Without it, whilst MUSH games (which it was originally written for, hence the name) worked fine. Whole lines always arrived.

But the first thing a Diku MUD does is say:


Enter your character's name, or type new: 


So without writing to the screen, you didn't see that. And you sat and waited. Then you wrote to Nick Gammon and complained that his client "doesn't work".

So, text is processed (eg, for colours) and then written to the screen immediately. So you see login prompts, and "press <return> to continue" etc. And command prompts for MUDs that have them.
Australia Forum Administrator #11
Twisol said:

In a similar vein, I'e always been a little curious why the (after omit) send-to isn't the default behavior.


Backwards compatibility. There are always better ways of doing things if you don't mind breaking existing scripts / configurations.
USA #12
Nick Gammon said:

Twisol said:

In a similar vein, I'e always been a little curious why the (after omit) send-to isn't the default behavior.


Backwards compatibility. There are always better ways of doing things if you don't mind breaking existing scripts / configurations.

I understand that, I'm just missing what (after omit) would break.
Australia Forum Administrator #13
Read here:

http://www.gammon.com.au/forum/?id=6554

From about point 12 downwards ...

The sequence is completely different. For example "normal" send still has the text in the output window. It might want to capture the last 10 lines from the output buffer, say, including the matching line(s).

It isn't yet logged.

OnPluginScreendraw has not yet been called.

Output from (other) triggers that "send to output" hasn't been done yet.
USA #14
Ahh. Thanks.
USA Global Moderator #15
Nick Gammon said:

But the first thing a Diku MUD does is say:


Enter your character's name, or type new: 


So without writing to the screen, you didn't see that. And you sat and waited. Then you wrote to Nick Gammon and complained that his client "doesn't work".

So, text is processed (eg, for colours) and then written to the screen immediately. So you see login prompts, and "press <return> to continue" etc. And command prompts for MUDs that have them.

Nuts.
Wait... does this mean you can't trigger on a login prompt? That seems like a bug. :\

I only bring this up now because processing triggers before display would seem to make it possible to modify text in place rather easily.
Amended on Wed 04 May 2011 04:58 AM by Fiendish
USA #16
Fiendish said:
Wait... does this mean you can't trigger on a login prompt? That seems like a bug. :\

Well, MUSHclient is heavily line-oriented. Telnet lines in particular end in "\r\n". If the content we've received so far doesn't have a "\r\n" at the end, the safest thing to do is assume the whole line hasn't been received yet. And you can't trigger on a partial line.

Most login prompts don't end with \r\n, because they expect your input to be echoed at the end of that line. If there was an \r\n after the prompt, your input would be below it.
Australia Forum Administrator #17
Fiendish said:

Nuts.
Wait... does this mean you can't trigger on a login prompt? That seems like a bug. :\


Well you see Fiendish, a careful attention to design makes the difference between a program that works some of the time, and one that works all of the time. ;)

TCP/IP is stream-oriented. That is, you get data streaming in, you don't get definite transactions. So in the absolutely worst case, instead of a single packet with "Enter your character's name, or type new: ", you would get something like this:


E
<1 second pause>
n
<1 second pause>
t
<1 second pause>
e
<1 second pause>
r
<1 second pause>
 
<1 second pause>
y
<1 second pause>
o
<1 second pause>
u
<1 second pause>
r
<1 second pause>
...


In other words, each letter in its own packet. Now MUSHclient's state machine can handle that. The ANSI codes, even if they are split across packets, are just processed as they arrive, and then when the state transitions to "end of colour information" the colour is recorded.

Some clients don't do that. And they are they ones that have the occasional "bad colour" because the colour code happened to be split across packets.

Now for a slightly less extreme example:


Enter 
<1 second pause>
 your 
<1 second pause>
 chara
<1 second pause>
acter'
<1 second pause>
's nam
<1 second pause>
me, or
<1 second pause>
r type
<1 second pause>
e new:
<1 second pause>
: 
<1 second pause>


Do you want at trigger to fire after "Enter" and then another after " your" and then another after "chara" and so on? Do you not see where this is going to lead you? How does this hypothetical trigger match something like "Enter your"?

In the same way that we process colour codes (like ESC [40m) at the "m" and not before, we invoke triggers when the line has "arrived", that is, at the newline character.

In any case, there is a plugin handler for people that want to match on "login" prompts and do something. Search the plugin callbacks for "OnPluginPartialLine". That is an extra call, at the end of a packet when no more packets are available, so you can "pre-trigger" on the line.
USA Global Moderator #18
Quote:
Search the plugin callbacks for "OnPluginPartialLine".

Ooh, interesting.
USA Global Moderator #19
I want to revisit this thread, because I actually still want to do exactly what Onoitsu2 and Shadowfyr were talking about.

The requirements are:
1) replace all or part of a line of text. (Omit from output + Note works great)
2) have other triggers fire on the result. (no way without Simulate)

I've been looking at the code for Simulate and Note and they both end up in DisplayMsg which then leads to StartNewLine and ultimately we get down to ProcessPreviousLine. And that's where the current state of things breaks down from what would be REALLY nice for a lot of people who talk to me about switching to MUSHclient. Because triggers don't fire in ProcessPreviousLine on text that has replaced a line after an omit. And I really want it to. I understand concerns about creating infinite loops, so it would be prudent to only allow a given trigger to fire once per server line, but other triggers should be allowed to fire on replacement text.

And I can't really think of any negative effects from making this the standard behavior for Note as long as it only happens in triggers that omit from output. Notes in an omitting trigger essentially show the player a different view than what the server sent. The player should be able to later trigger on their current world view without having to know that somewhere hidden in the background, invisible to them, the server is sending something else.
USA #20
I'm in agreement with Fiendish regarding the requirements.

I will say that a simple way to substitute partial text within the line (and retain color for the rest of it) a la z/CMUD would be wonderful. In fact, a function like 'suboutput(pattern, newoutput, [enabletrigger])' would be extremely simple and useful in a lot of cases. Of course, the 'enabletrigger' bit would be true or false, depending if you want to be able to trigger off the new output, and would allow a secondary script to run on the replacement text.

The way I have it envisioned is it would turn something like:

( 4) (K)(M)(G)(H) a bag of Aardwolf (146)

into:
( 4) (KMGH) AardBag (146)

The first trigger would be one to change the flags (K)(M)(G)(H) on any item to (KMGH) and the second would be to change "a bag of Aardwolf" to "Aardbag."

I do understand it has potential to create infinite loops, but that really should fall on the person doing the scripting, not the author of a client.

Of course, it's also late in the night for me, and while this makes absolute sense to me, it might not be what Fiendish is looking for, and for that, I apologize. Either way, I'd really love to see a simple way to sub output and return the rest of the line undisturbed.
Australia Forum Administrator #21
A while ago, during testing, infinite loops weren't just a possibility, they happened all the time. Consider the "match-all" trigger (ie. matching "*") which outputs something (which the match-all trigger then matches again).

But let's say for a moment that we put in a limit (eg. no triggering more than once on a line) then would you say you would want to trigger more than once? For example, trigger "A" changes "Fiendish" to "That Rascal Fiendish" and trigger B changes "5 HP" into ("<low HP> 5 HP").

If we only match once (on the newly displayed line) then only one change gets made. If we match more than once, what limit do you impose?

Indeed, if a "prompt" trigger matches more than once there is the potential for "xp counting" triggers to be wrong (if it matches twice you count the XP twice).

Then you have the issue, if a line matches, and gets omitted and redisplayed, what line do subsequent triggers match on? The original? Or the modified one?

I'm not saying I refuse to change it. But I think you need to clearly think through what the change would be, exactly. To just modify it to match "note" lines would certainly send virtually every world file and plugin into a loop, because the "match all" idiom is used so much.

Take a look at this:

http://www.gammon.com.au/forum/?id=6554

(How MUSHclient processes text arriving from the MUD)

Then specify where in that flow process you would make changes, and what they would be. Also any additional flags you might want (eg. match only original lines, match only "note" lines, only match once, protect a particular "note" line from matching, etc.).

USA Global Moderator #22
Quote:
infinite loops weren't just a possibility, they happened all the time
I see the nb comment about stack overflows.

Quote:
If we only match once (on the newly displayed line) then only one change gets made.
Precisely this.

Quote:
If we match more than once, what limit do you impose?
I would impose the limit that a given trigger can only fire once (the repeat on same line checkbox should re-fire as many times as necessary until done and then not fire again after that) for each new line index. Rather than checking all triggers, check only the ones that have not yet fired from a listing that resets upon each new line.

Quote:
Indeed, if a "prompt" trigger matches more than once there is the potential for "xp counting" triggers to be wrong (if it matches twice you count the XP twice).
One reason why any given trigger should only be allowed to fire once per line.

Quote:
Then you have the issue, if a line matches, and gets omitted and redisplayed, what line do subsequent triggers match on? The original? Or the modified one?
Subsequent trigger checks should be matched against the new line, not the original line. The once-per-line limit should not reset until new unrelated data is shown.

Example for above statements:

Say there are four triggers, numbered in order of sequence...
1) match="{channel_tag}*", omits from output, sends SimNote("%1")
2) match="Fiendish says*", omits from output
3) match="*Fiendish*", omits from output, sends SimNote("%1jerkface%2")
4) match="*", omits from output, sends SimNote(string.gsub("%1","pants","Fiendish"))

Now the first trigger is in a plugin. As far as the user knows, it doesn't exist. As far as the user knows, the server sends the output of the resulting Note. The second trigger is created by the user to just block all instances of me saying something. The third is created to replace my name in other instances with something more suitable. The fourth trigger is only there because I'm not feeling hugely creative right now and am having a hard time thinking of more legitimate loops from reprocessing.

So when the mud sends:
Quote:
{channel_tag}Fiendish says your pants are on backwards.
Trigger 1 fires, removing the {channel_tag} and leaving just
Quote:
Fiendish says your pants are on backwards.
Trigger 2 fires, eliminating the line. Triggers 3 and 4 do not fire.

And when the mud sends:
Quote:
{channel_tag}Your pants say that Fiendish is on backwards.
Trigger 1 fires again. Trigger 2 does not fire. Trigger 3 fires and produces
Quote:
Your pants say that jerkface is on backwards.
Trigger 4 fires and produces
Quote:
Your Fiendish say that jerkface is on backwards.
Trigger 3 does not fire again. Trigger 4 does not fire again.

Now I know that Etzli wants a new function/option that handles substitution as a special case. And maybe that's the best way to deal with this. Or maybe Simulate isn't so bad for this particular use case?
Amended on Fri 19 Aug 2011 05:18 AM by Fiendish
USA #23
This probably isn't helpful in the context of MUSHclient, but I've thought a little about how I would implement this within Aspect. In my concept, triggers listen on a line source, and act as line sources themselves. By default, a trigger listens on the primary source (the lines coming in directly from the MUD). However, you could have a trigger listen to another trigger, allowing changes to propagate. Unrelated triggers are left unaffected.
USA Global Moderator #24
Quote:
In my concept, triggers listen on a line source, and act as line sources themselves.
As far as I can tell, this is what Simulate does. But I keep hearing that Simulate is to be avoided.
USA #25
No, Simulate pipes a line back to the primary source. Triggers should act as a line source themselves. Only triggers that know what they're getting into (i.e. are chained to this trigger) receive the newly-emitted line from the first trigger.
Australia Forum Administrator #26
Out of curiosity, how do other clients handle this issue? If possible give examples, and describe how they avoid issues of looping.

I don't mean specifically "triggering on notes" as they may not have the concept of notes. But how do they handle the underlying problem that is trying to be solved?

Assuming for the moment (which may not be correct) that they allow textual substitution in the incoming line (the line you are triggering on), which alters how further triggers "see" the line, how do you handle things like multiple plugins both wanting to see (say) "{channel_tag}*" - if one trigger removes "{channel_tag}"?

Also if textual substitution is permitted, how to they handle colour boundaries on the source text?
USA #27
The way they are handled is through priority. Example:

Trigger1 and Trigger2 have the same trigger patterns, but send different scripts for whatever reason. The trigger with the lower priority number will always fire first, and therefore, the trigger with the higher priority number will fire last. This can be seen in CMUD with the priority showing in the xml (and on the options tabs).

I imagine color boundaries are handled by "remembering" the location of the block of text being substituted. For instance, say the block of text was:

Quote:
This is a text block which I am substituting.


and you were wanting to substitute 'text block' with 'block of text.' The client sees that 'text block' starts at position 11 in the string (though with ansi codes, it'll count the beginning of the previous escape sequence) and ends on position 20. The rest of the string is stored in a temporary variable, preserving the ansi codes with it. The text is then substituted at position 11, therefore changing it to:

Quote:
This is a block of text which I am substituting.


Again, with the ansi escape sequences, the actual position will be much higher (e[1;34m, for example, adding another 7 characters) and the client would have to account for this.

My statement on color boundaries is pure speculation, as this is how I would imagine it works, but since I don't have the actual code itself, I cannot accurately give the proper procedure. That's the downside of non-open sourced clients.

As for how other clients prevent infinite loops, it's really on the part of the scripter to prevent infinite loops. What I mean by this is given a trigger that substitutes a line, if a scripter substitutes that line back to the original, that'll no doubt cause the infinite loop. Again with guessing, but it wouldn't surprise me if other clients put the text in a queue to be processed before being printed on the screen, something like below:

Quote:
Step one: Text received from MUD, place in internal queue.


Quote:
Step two: Is received text supposed to trigger substitution? If so, execute trigger. If not, proceed to step four.


Quote:
Step three: Is new text supposed to trigger a substitution? If so, repeat step two.


Quote:
Step four: Display text on screen.


Again, this is all speculation, and in a fantasy world, I'd be 100% right about the procedure, but maybe it'll serve as a ground point to implement what's being asked here today.
Amended on Sat 20 Aug 2011 06:22 AM by Etzli
Australia Forum Administrator #28
Etzli said:

The way they are handled is through priority. Example:

Trigger1 and Trigger2 have the same trigger patterns, but send different scripts for whatever reason. The trigger with the lower priority number will always fire first, and therefore, the trigger with the higher priority number will fire last.


So if Trigger1 changes the text, then Trigger2 won't fire at all? Because now the text is different?
USA #29
Nick Gammon said:

So if Trigger1 changes the text, then Trigger2 won't fire at all? Because now the text is different?


Correct. Here's an example of how it goes:


TriggerID:        Test1
Trigger Pattern:  This is a test.
Trigger Output:   Sub with "This test is complete."
Trigger Priority: 0

TriggerID:        Test2
Trigger Pattern:  This is a test.
Trigger Output:   Print "This trigger should not fire."
Trigger Priority: 10


Now print to the screen "This is a test." Test1 will fire, subbing it with "This test is complete." Since Test2 has a higher priority, it never fires because the text is no longer "This is a test." Reverse the priorities, and both triggers fire.
Australia Forum Administrator #30
I see. So the net effect is that triggers no longer can be designed to fire on what arrives from the MUD (which is probably well-defined). They now have to fire on whatever-it-is that previous triggers changed the line to. And this is somewhat dependent on trigger evaluation order.
USA Global Moderator #31
Etzli said:
As for how other clients prevent infinite loops, it's really on the part of the scripter to prevent infinite loops. What I mean by this is given a trigger that substitutes a line, if a scripter substitutes that line back to the original, that'll no doubt cause the infinite loop.
Not entirely true. zMUD will not easily let you get into an infinite loop, because a given trigger won't re-sub for the same line index. If you have one trigger that replaces "apple" with "pear" and another that replaces "pear" with "apple", the first trigger will not fire again.

Nick Gammon said:

I see. So the net effect is that triggers no longer can be designed to fire on what arrives from the MUD (which is probably well-defined). They now have to fire on whatever-it-is that previous triggers changed the line to. And this is somewhat dependent on trigger evaluation order.

Well, MUSHclient does already have a set evaluation order.
Australia Forum Administrator #32
Yes I understand that. But at present if two triggers fire on "apple" (and are set to keep evaluating) they both fire. One can't change the line (eg. change apple to pear) so that the other doesn't fire at all.
Australia Forum Administrator #33
Fiendish said:

zMUD will not easily let you get into an infinite loop, because a given trigger won't re-sub for the same line index.


Does zMUD let you substitute the actual contents? In other words, change the line "in situ" so that the next trigger matches the changed line? If so, the line index wouldn't come into it, would it? THe trigger has already been processed.
USA Global Moderator #34
Nick Gammon said:

Fiendish said:

zMUD will not easily let you get into an infinite loop, because a given trigger won't re-sub for the same line index.


Does zMUD let you substitute the actual contents? In other words, change the line "in situ" so that the next trigger matches the changed line? If so, the line index wouldn't come into it, would it? THe trigger has already been processed.


Yes it does. If the server is sending "I am Fiendish", triggering on the pattern "Fiendish" (zMUD patterns implicitly begin and end with glob * unless specified otherwise) with the script "#sub awesome" makes it look like the server had sent you "I am awesome" instead of "I am Fiendish". And if you have that AND a subsequent trigger on the pattern "awesome" with the script "#sub delighted" it will look like the server had instead sent "I am delighted". But if you have another trigger that #subs "delighted" into "Fiendish" again, the first trigger will not fire a second time. Thus, no loop.
I think the line index comes into it in the sense that at some point you need to decide to allow the first trigger to fire again. I guess this might be at each newline received from the server.
Amended on Sun 21 Aug 2011 06:56 AM by Fiendish
Australia Forum Administrator #35
Fiendish said:

I think the line index comes into it in the sense that at some point you need to decide to allow the first trigger to fire again. I guess this might be at each newline received from the server.


I don't fully understand this part. The triggers are always evaluated once, per line received. Why is a line index required?
USA Global Moderator #36
Quote:
The triggers are always evaluated once, per line received. Why is a line index required?
Triggers are evaluated once per line "seen", not necessarily per line "received". I am distinguishing here between lines from the server (what I call received) and lines that appear on the screen (seen). If I have a script in zMUD that echos text to the screen, triggers will fire on that text as well, even though it didn't get "received" from the server. This is all because zMUD apparently does not distinguish between echoing and printing mud output in the same way that MUSHclient does. In fact, if you have a trigger that echos a line of text that it also matches on, you *will* get into an infinite loop in zMUD because the echo is a new line. This is obviously something very easy to avoid in practice, but it gives an insight into the inner workings.

I will also note that zMUD detects infinite loops and offers an opportunity to kill the runway script.
Amended on Mon 22 Aug 2011 12:06 AM by Fiendish
USA #37
It should probably also be noted that in CMUD, there is now a #PRINT command that will print the message on the screen but will not fire any triggers. This is similar to the Note functions in MUSH (as far as I can tell).