|
Larkin Wizard
Joined: 25 Mar 2003 Posts: 1113 Location: USA
|
Posted: Tue Mar 18, 2008 2:38 pm
[2.20] Priority for variables, functions, etc |
It's not only possible to alter the priority of variables and functions (through %priority), it's quite useful. For example, I have a function to calculate the amount of health I should lose before I sip a health potion. I have a default version of this function in my combat system package, but I have an override version of the same function in my personal Lusternia package that has a higher priority. I'm really hoping to leverage this behavior in my combat scripts by setting low priorities on things such as this and allowing the end users to easily override them in their own main package.
The problem, however, is that the only way to set the priority on some settings is through the %priority function or through the XML. There is no visible priority field for variables, functions, macros, aliases, etc in the PE forms. |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Wed Mar 19, 2008 6:13 am |
I think the idea is that scope and enabled/disabled that determines which variable/function to use. It's surprising that priority makes a difference, actually.
|
|
|
|
Larkin Wizard
Joined: 25 Mar 2003 Posts: 1113 Location: USA
|
Posted: Wed Mar 19, 2008 11:28 am |
I understand what you're saying, but I must say that I feel priority is a better way to handle this particular scenario. It's not very realistic for someone to override a function in my combat system by disabling the function in the system and implementing their own in its place. It's easier to just have them create the function and have it work because I've set my function's priority very, very low.
|
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Wed Mar 19, 2008 11:47 am |
In some respects, I agree - it gives you a certainty that just isn't present with scoping rules, because they melt even Zugg's brain. But it's also less intuitive, because unless you tailor all your scripts carefully, it's entirely possible that, if you had an alias and a variable in the same class, the alias would use a variable somewhere totally different, which creates all kinds of problems. It's a difficult choice, but I can understand why scoping was chosen, certainly.
I don't think zScript was ever designed to allow hooking like that - I think it's expected that if they're going to be mucking around with your code, they'll be replacing the function in the GUI. If you take your sentence on its own - "for someone to override a function in my combat system by disabling the function in the system and implementing their own in its place" - it sounds pretty sensible to me, actually.
One possible way of adding their own code while still leaving the original handler would be to use more events. Then if their event handler has a higher priority than yours, it's run first, but yours is still run at the end. It doesn't seem unreasonable to me that if they want your code to be ignored, that they'd have to disable or delete the function in question. |
|
|
|
Larkin Wizard
Joined: 25 Mar 2003 Posts: 1113 Location: USA
|
Posted: Wed Mar 19, 2008 12:15 pm |
I guess I don't want people "mucking" around with my system. Heh. This priority system would also mean that I could push out updated packages that wouldn't break their overrides because they keep their overrides in their own session package.
To me, this is one of the primary reasons to set priorities on settings. I know we most often think of multiple triggers firing on the same line and needing a certain order to their execution, but that's a limited view. In MUSHclient, I built plugins that could override aliases and macros in some creative ways, mostly through the use of priorities.
Events are at a level of complexity that I'd rather not expose to the user in this case. The logic of the healing is a core part of the combat system, and sure I have a lot of events firing and I use them as hooks, but here I just want the user to be able to dynamically define when to sip health. I use a function because the calculation is dynamic (based on max health, of course) and can involve other factors, such as a person's abilities or artifacts. |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Wed Mar 19, 2008 2:55 pm |
I see, and I agree that events aren't the way to do that. You basically want the user to be able to define a single line of code and to not overwrite it when you patch. Definitely a knotty problem.
|
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Wed Mar 19, 2008 8:10 pm |
Unfortunately Fang is correct. CMUD is designed with some very specific "scoping" rules that have taken a lot of time and effort to get right. And part of the purpose is to allow the end-user to override anything they want. I know that as a Developer you might not want users to override something, but that has always been the philosophy in zMUD and CMUD.
Priorities for aliases, functions, etc *are* stored in the package database so it might be possible to use them in the future. But right now the priority of aliases, functions, variables are undefined and you should not count on them to work in any particular way.
So, something to consider for the future, but for now I'll just be happy if the current scoping rules work ;) |
|
|
|
Larkin Wizard
Joined: 25 Mar 2003 Posts: 1113 Location: USA
|
Posted: Wed Mar 19, 2008 9:05 pm |
In that case, can you suggest a way to use scoping to override the function given in my example? I've got a combat system with a sip_health() function that returns a calculated amount of health to start sipping health potion. If the function is both defined and used in the combat system package, how can a user override it using scoping rules without modifying my package?
|
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Wed Mar 19, 2008 10:05 pm |
There really is no easy way without a user modifying your package. The closest you can get is to keep user specific data out of your package. This data would be held in variables, and by not putting those variables in your package they will be created under the users window in thier package. This also helps with multiplaying because the variable is created for each window as it is needed, and then stays with that window.
Basically the scoping rules make it so that user specific overrides are best done with variables that your code interprets. Since variables can still be used as simple functions via the #VARFUNC some things might be definable that way. For example
Code: |
#EVENT onLoad {#IF (!%defined(SipHealth)) {#VARFUNC SipHealth {%eval(@HP*4/3)}}} |
Of course onload is currently bugged and causes variable to be created in the module where the onLoad is defined rather then following the scoping rules. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Larkin Wizard
Joined: 25 Mar 2003 Posts: 1113 Location: USA
|
Posted: Wed Mar 19, 2008 10:16 pm |
Fair enough. That's basically what I do already, so I'll just have to add a few more things to my "persistent, user-configurable" settings script. Thanks for the feedback!
|
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Thu Mar 20, 2008 2:21 am |
You can extend Vijillante's strategy to aliases and functions of arbitrary complexity. I've come up with (but not fully tested) two different strategies for putting in overridable aliases/functions:
- Use indirection. Your package uses onLoad (when it's fixed) to create the setting within the character window for each overridable alias/function (if it does not yet exist) to do nothing but call your internal, default version. E.g.
Code: |
#event onLoad {#IF (!%defined(sipHealth)) {#class myPkgOverrides;#function sipHealth($arg) {#return @sipHealthDefault($arg)};#class 0} |
Then the user can redefine sipHealth to do whatever he wants (including call your function and modify the results!), or leave it alone so it gets your default behavior. Even better, if he ever blows away the override setting entirely, it reappears (with default behavior) on the next load. The only disadvantage is a small speed penalty, since there is an extra alias/function call to do the indirection.
Let scoping work FOR you. Distribute your main package, and a second package with the overridable settings. Since an alias in the char window will be found before an alias in the second package (per the scoping rules), the user can override anything in the second package simply by creating an alias with the same name in their char window. Again, blowing away the end-user alias results in the default behavior being restored. And this should be slightly faster than #1, since there is only the single alias call. However, the distribution and setup of the package(s) is more complicated, and it is nearly impossible to call your default version and modify the results.
All in all, I prefer #1 unless speed is really going to be an issue, but either one should work. YMMV, of course. |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Thu Mar 20, 2008 3:02 am |
I always feel a bit weird about creating settings from within settings. The worry is that a bug or typo will cause it to fire a bunch of times and create a load of settings. Picking the extra settings out of your package is almost as annoying as getting those tiny bits of paper out of your clothes when you've accidentally left a receipt in your jeans pocket and washed it. Grr.
Custom functions is definitely the way to go about this, because then your "See if I can sip health" logic can just be #if (@SipHealth()) {} - but it seems like in cases like this, where they won't be hooking your function but replacing it, it'd be easier to just disable your function (or perhaps not enable it) in your OnLoad event. |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Thu Mar 20, 2008 4:34 pm |
I guess I drank the settings-within-settings Kool-Aid a while ago, when I realized that the only correct way to have a multi-state trigger in a multi-play environment was to put a copy of the trigger into each character window. Which means a published package that wants to handle multi-play would have to create such a thing within an onLoad handler.
|
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
|
|