|
Danj Beginner
Joined: 12 Oct 2000 Posts: 17 Location: United Kingdom
|
Posted: Wed Mar 15, 2006 12:15 am
MUD prompt and callbacks? |
Is this likely to be possible in CMUD: creating a "prompt" package which allows other packages to "register" a callback with it, so that when the prompt occurs, functions containing actions that other packages want to happen at prompt-time will get called? Preferably without having to resort to things like storing alias names in variables and then using #EXEC or something? Alternatively, will there be any built-in special prompt functionality in CMUD which could be used in a similar manner?
|
|
_________________ --
Dan Jackson |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Wed Mar 15, 2006 1:43 am |
There is nothing special about a MUD prompt...it's just text. The MUD doesn't send any special markers or anything. So just create a trigger that matches the prompt and does something. No need to "register" any special actions. Any package can have it's own prompt trigger.
So I guess I'm wondering what you are looking for that you can't already do with a trigger? |
|
|
|
SilentDawn Beginner
Joined: 19 Jan 2006 Posts: 11 Location: Sydney, Australia
|
Posted: Wed Mar 15, 2006 5:26 am |
I think what Danj is asking for is a command that generates a "prompt event". Instead of creating a trigger to detect the prompt within every package, a single trigger can be used to detect the prompt. When the prompt is detected, this is broadcast to the packages. Any package that is setup to perform an action when the prompt is detected could listen for this broadcast.
This would indeed simply the implication of packages, and perhaps improve performance as well. It could allow packages to sit idle until they are needed, rather than have every line of text parsed by every package, simply looking for the prompt. |
|
|
|
Danj Beginner
Joined: 12 Oct 2000 Posts: 17 Location: United Kingdom
|
Posted: Wed Mar 15, 2006 11:17 am |
SilentDawn has put it better than I have, that's pretty much exactly what I meant. I hadn't considered the possibility of the "event" style approach - I was imagining that a package wishing to be notified when the prompt happened would pass the name of a function to be called to the "prompt" package, and then when the prompt was detected the "prompt" package would call all the functions of which it had been informed in this way.
|
|
_________________ --
Dan Jackson |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Wed Mar 15, 2006 3:05 pm |
You can already do this in ZScript:
Code: |
#CLASS {QLib|EventHooks} {enable|setdef}
#VAR qlNextPromptCmds {}
#AL AddNextPromptCmd {#ADDKEY qlNextPromptCmds {%1} {%-2}}
#AL DelNextPromptCmd {#DELKEY qlNextPromptCmds {%1}}
#AL DoNextPromptCmds {#IF {@qlNextPromptCmds} {#LOOPDB @qlNextPromptCmds {%val}} {}}
#AL ClearNextPromptCmds {qlNextPromptCmds={}}
#VAR qlPromptCmds {}
#AL AddPromptCmd {#ADDKEY qlPromptCmds {%1} {%-2}}
#AL DelPromptCmd {#DELKEY qlPromptCmds {%1}}
#AL DoPromptCmds {#IF {@qlPromptCmds} {#LOOPDB @qlPromptCmds {%val}} {}}
#TR PromptTrig {^<} {DoPromptCmds;DoNextPromptCmds;ClearNextPromptCmds}
#NOOP -- Similar code for other events removed for clarity of this example
#CLASS 0 |
Change the PromptTrig to catch your prompt reliably (mine is dead easy since nothing else from the mud has carets around it), and you're in business. Simply "AddPromptCmd <id> <command>" to set up a command that will fire on any prompt, and "AddNextPromptCmd <id> <cmd>" to set up one that fires only once, on the next prompt.
I have similar code for other events (connect/disconnect, combat start/stop, spell failures, etc), but this gives you the basic idea. I got the idea when I started wanting to write code for atconnect. As I understand it, you can only have ONE atconnect alias in the system, so I couldn't just stick one in any class that wanted to do something at connect time, but this works like a charm. |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Carabas GURU
Joined: 28 Sep 2000 Posts: 434 Location: USA
|
Posted: Wed Mar 15, 2006 4:52 pm |
Nice, jquilici. If you have not done so already, you should consider adding that to the finished scripts forum. I'm sure a lot of people will get some use out of it.
|
|
_________________ Carabas |
|
|
|
Danj Beginner
Joined: 12 Oct 2000 Posts: 17 Location: United Kingdom
|
Posted: Wed Mar 15, 2006 5:04 pm |
jquilici - nice code, but that's exactly the sort of thing I was hoping to be able to avoid with CMUD. I actually quite like SilentDawn's idea of events that packages can listen for - in fact I think it'd be a good idea if the idea was expanded so that any type of user-created event can be raised which packages will be notified of if they so choose. For example, a "prompt" package could raise a USER_PROMPT event, which could be caught by a "stats" package which could then raise other events such as USER_MANA_LOW or USER_HP_LOW which could be picked up by other packages wishing to perform actions related to those events. Basically the idea behind having these events is so that scripts can be made more modular and each package doesn't have to have its own "prompt" trigger.
|
|
_________________ --
Dan Jackson |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Wed Mar 15, 2006 5:46 pm |
Ahh, OK, now I understand.
Well, events are something that I'm considering. Right now in zMUD there are pseudo-events with the "atconnect", "atexit" etc aliases. These are really event handlers that zMUD fires when something happens (like getting connected to the MUD). Being able to have "user-defined" events, such as "OnPrompt" which could be fired by one package and then causing event handlers in other packages to fire is an interesting idea.
I'll definitely consider this during the CMUD beta process. |
|
|
|
Carabas GURU
Joined: 28 Sep 2000 Posts: 434 Location: USA
|
Posted: Wed Mar 15, 2006 6:00 pm |
Edit: nm :)
|
|
_________________ Carabas |
|
|
|
Larkin Wizard
Joined: 25 Mar 2003 Posts: 1113 Location: USA
|
Posted: Wed Mar 15, 2006 7:14 pm |
I would love to see an event handling architecture, too, though I understand the complexities and issues involved in such a framework. Developing applications in C# spoiled me with the ability to subscribe and unsubscribe by simply using += and -= with a function delegate. I implemented a pseudo-event handler in one of my latest scripts, but it is still limited in the parameters it will accept and the format of the commands.
Feel free to discuss with us how you plan to implement such an event handling framework, so we can provide examples of ways we intend to use it and help shape the design. :) |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Mar 16, 2006 6:33 pm |
Well, this is probably as good a place as any to discuss event handling. So feel free to post more details on what you will need.
First, remember that this needs to fit into the existing "zScript" framework. For example, we are not going to invent some entire new way to handle arguments and parameters. In zScript, there are no formal "declarations". The first argument is $1, the second is $2, etc.
Also, in zScript, an "alias" is really the same as a procedure or method in a C-like language. Consider the existing "atconnect" alias. This is called whenever the MUD makes a successful socket connection to the MUD. It doesn't take any arguments. To call this "method" from your own script, you just put "atconnect" as the command.
So, imagine that there was an event handler called "OnPrompt" that is fired whenever a prompt is detected. To implement this "event handler" in your package, you'd simply define an Alias called "OnPrompt". At this point we don't know what arguments there might be to this event.
Now, since CMUD doesn't have any way to detect a prompt itself, we need some way to "fire" this event from within some other code (like a trigger). As we learned above, to "call" an event handler, we just use the name of the alias:
#TRIGGER {Mud Prompt pattern} {OnPrompt}
So, from what I can tell, the only thing we need to change in zScript is to make a way for "OnPrompt" to call not just the "current" alias in the current class, but to call *all* of the OnPrompt aliases in all of the enabled packages and classes.
To do this, I can imagine creating a new zScript command called #EVENT which takes an alias name, along with a list of arguments, and calls each enabled version of the alias in the same order that the packages are loaded in. This would give us a simple solution where you would just change the above trigger to:
#TRIGGER {Mud Prompt pattern} {#EVENT OnPrompt}
and this would cause the trigger to call any of the event handler aliases called "OnPrompt" in any of the loaded packages.
Is something simple like this useful? Obviously, the simpler it is the faster you'll see it implemented. But I'd like to see a discussion of the pro's and con's of this simple method. |
|
|
|
Carabas GURU
Joined: 28 Sep 2000 Posts: 434 Location: USA
|
Posted: Thu Mar 16, 2006 7:29 pm |
Hmm, yeah. That would probably be the most universal and simplest way. Another thought. How about providing us with standard session variables for hitpoints, mana, etc. People name their variables many different ways. For example:
@hp @hitpoints @curhp @maxhp etc
@mana @curma @maxma etc
With a plug and play package repository, it would be really helpful if we had a standard variable name for these common variables. Perhaps something like this.
#NOOP %hitpoints(100)
#NOOP %max_hitpoints(200)
#SH %hitpoints
100
#SH %max_hitpoints
200
Of course, that is only a suggestion. I'll hopefully get the time to give this more thought later. |
|
_________________ Carabas |
|
|
|
Tech GURU
Joined: 18 Oct 2000 Posts: 2733 Location: Atlanta, USA
|
Posted: Thu Mar 16, 2006 8:03 pm |
Zugg the approach you described is solid. I would reccomend that we take that approach initially, and have it function and utility vetted out in beta. Since it's a fairly straightforward implementation we can get a better idea if it works or not. If it does great.. if not, you can consider a more complex architecture.
|
|
_________________ Asati di tempari! |
|
|
|
Danj Beginner
Joined: 12 Oct 2000 Posts: 17 Location: United Kingdom
|
Posted: Fri Mar 17, 2006 3:17 pm |
Carabas: the problem with that is that different muds represent stats in different ways. For example, on the mud I play on (Aardwolf) in your prompt you can choose to have hp displayed as a number or as a percentage. If hp is displayed as a percentage there's no way to calculate the current number of hp unless the max hp is already known previously - also, max hp may be changed by e.g. levelling or training. Also, while hp and mana are probably common to the majority of muds, I imagine some of them have wildly differing other stats.
|
|
_________________ --
Dan Jackson |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Fri Mar 17, 2006 3:26 pm |
I would vote for making event handlers as a special case of triggers, rather than a special case of aliases. In fact, I would specifically propose just adding an 'event' option to #TRIG and #COND, so that the pattern is matched against the names of thrown events (thrown using a new #EVENT command per Zugg above) rather than MUD output. Here are my reasons:
- Triggers fit the semantics of events - both things 'react to something that occurred'.
- Triggers already have 'if many match, fire all of them' behavior (which matches event handlers), whereas aliases mask one another.
- CMUD is introducing priority ordering for triggers (which event handlers could use, and would get for free).
- Syntax is easy - just add an 'event' option to triggers/states and a new #EVENT command.
- If syntax is set up that way, you can actually use event handlers as part of multistate triggers, with all the expressive power that brings.
- It allows you to have handlers that match a whole class of events, rather than just one (just as triggers can match multiple different strings).
I'm sure I could think of more, but that should get the discussion started. |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Tarn GURU
Joined: 10 Oct 2000 Posts: 873 Location: USA
|
Posted: Fri Mar 17, 2006 3:53 pm |
jquilici wrote: |
I would vote for making event handlers as a special case of triggers, rather than a special case of aliases.
|
I agree. Events feel a lot more like triggers (when X happens do Y) than user or code-invoked aliases. Having them available in things like #COND's would be great (if you see X text (#TRIG) and then the prompt event fires on the next line (#COND), do stuff). That saves a lot of bookkeeping code and variables that you'd be required to have available to an alias.
Also, for a defined event there should be a #RAISEEVENT command. That way a package can define its own events, put data in fields within the variable, and raise the event. To avoid changing the argument passing mechanism, a particular event might define the presence of a global variable with particular fields that viewers of the event can get at.
-Tarn |
|
|
|
Carabas GURU
Joined: 28 Sep 2000 Posts: 434 Location: USA
|
Posted: Fri Mar 17, 2006 6:51 pm |
Danj wrote: |
Carabas: the problem with that is that different muds represent stats in different ways. ... |
Right. I had not thought of it that way. Hmm. Back to the drawing board.
Edit: I vote for the trigger method.
/me wonders if CMUD will bring him out of retirement. |
|
_________________ Carabas |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Mar 17, 2006 9:14 pm |
Hmm, you are right...the trigger idea is better. And it's easy to just add a new trigger "type". Then the #RAISEEVENT command would just be used to fire event triggers that match the pattern. It also gives more flexibility on passing arguments by setting up the event pattern:
#EVENT {OnPrompt (%d) (%d)} {#VAR hp %1;#VAR mana %2}
which would create a special "event trigger", could then be fired with something like:
#TRIGGER {^~[(%d)hp (%d)mana~]} {#RAISEEVENT OnPrompt %1 %2}
as an example of passing arguments from the prompt to the OnPrompt events.
And yes, getting all of the power of multiple conditions would be interesting. I was just trapped in the "box" of the existing atconnect aliases. Well, that's what these forums are for...to discuss ideas and improve things. |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Fri Mar 17, 2006 10:58 pm |
Just to make sure I've made myself clear, I'm advocating having an 'event' option on #TRIG and #COND, so that I can write something like the following:
#TRIG {OnPrompt (%d) (%d)} {#VAR hp %1;#VAR mana %2} {} {event}
Having the #EVENT command would be an equivalent shorthand for this, much like #ONINPUT and the 'input' option.
The biggest reason that I'd want it this way is so that I could have multistate triggers where only some of the states are event handlers, and you can combine 'em with all the other fancy options on #TRIG and #COND. So, for instance, I could write an event handler that only fires if the event is thrown within 3 seconds after getting a certain line from the MUD:
#TRIG {a certain line from the MUD} {}
#COND {SomeEvent} {#NOOP -- do something} {} {event|dur|param=3000}
(Hope I remembered that syntax correctly)
Basically, I see this as 100% analogous to the 'input' option (or #ONINPUT), which basically makes a trigger match text sent to the MUD, rather than text received from the MUD. In the case of the 'event' option, the trigger only matches lines sent to an internal 'event stream', and any script can write a line to the event stream using #RAISEEVENT.
Using that model, you get tons of power. You can mix-and-match input, output, and event states in triggers. You can even have high-priority event handlers modify an event, consume it, etc. before the lower-priority handlers try to match against it.
And I'm betting its fairly straightforward to do in the code... |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Mar 17, 2006 11:49 pm |
Right...#EVENT is just a shortcut for this (like #ONINPUT)
The only trouble with the code right now is that currently a condition like "duration" is it's own trigger type. You can't currently do something like {oninput|dur|param=3000}. The "dur" type overwrites the "oninput" type. So that's where the harder part of the implementation is: fixing the trigger condition types so that they work better (which will also improve stuff like OnInput triggers). |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Sat Mar 18, 2006 2:58 pm |
As long as your mentioning changing around the trigger options, might it be possible to make the param obsolete and allow multiple options to be on the same trigger. A syntax example for something like that would be,
#TRIGGER {^(pattern%d)} {#ADDITEM Patterns {%1}} {loopat=99|dur=1000}
This would give a trigger that matches the pattern 99 times or until 1 second passes. I would say some of the options would be muttually exclusive still: alarm, input, the new event; these are obviously telling the trigger to fire off of different streams of data and are more of type then an option. Most likely something for a wishlist down the road. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
NowhereMan Beginner
Joined: 09 Dec 2004 Posts: 10
|
Posted: Fri Mar 24, 2006 7:54 pm |
i second that, jquilici.
i have been wishing for that exact thing in zmud. i am constantly simulating the functionality by storing commands in lists and using triggers to execute those commands at the right time. i hadn't thought about the possibility of multistate 'event' triggers, but that could have some interesting uses as well. |
|
|
|
|
|