|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Thu Nov 08, 2007 2:34 am
To scope or not to scope |
I again read all the old design topics for packages, modules, and windows. I still couldn't find anything about the behavior of aliases, which including checking the version history to see if there is a note about them changing. The list of old reading:
http://forums.zuggsoft.com/forums/viewtopic.php?t=24065
http://forums.zuggsoft.com/forums/viewtopic.php?t=24097
http://forums.zuggsoft.com/forums/viewtopic.php?t=24146
Extremely long post follows.
Let me start with a simple example of what I consider to be the problem, keep CMud open I will add more things to this as the post goes along. Using a blank session enter at the command line
Code: |
#WINDOW test;#MODULE shared;#TRIGGER {do test} {#VAR c %window;#VAR;alias};#ALIAS alias {#SHOW hello %window;#VAR};#MODULE 0;#MODULE test;#VAR a 1;#MODULE 0;#VAR b 2;#ALL "#SHOW do test" |
You can see that the trigger that is shared knows all about the variable in the window, but the alias that is shared does not. Windows are private to each other, but a script of any kind needs to have access to that windows variables when it executes within that window. With this test we see the variables suddenly become inaccessible, this puts the novice user into the position of having to pass those variable to the alias the way they would be passed to a procedure in a more formal language. Interestingly the alias knows what window it is talking to.
---------------------
To give an example of what a novice user might do we have to look at the first things they want to use, and the problems they will encounter as it gets more complex. Our user has just created her first character on a mud ever a cleric, she made the wise choice of getting CMud. Among the first things she sees is that the rules of this mud allow multiplaying, a user may have up to 3 characters logged in at a time and working together. She isn't quite interested in that just yet, because she has to learn a bit about play itself. After a short while she decideds she needs an easier way to see her health and do the correct healing spell. A guage, 2 variables, 1 trigger and 1 alias later she has it. Now a simple short command instead issues the right longer healing command. Feeling confident in her ability to heal faster, and understanding the combat better she goes to a much tougher area and rapidly dies. Now she decides to give multiplaying a try, figuring that with 2 characters she could take out that mob and get some really nifty gear. Her second character is a priest, and getting both to log in together and the window layout isn't hard. In the process she starts to figure out what these packages are for. So now she creates a package with a global module, she moves all 5 of her settings into the global module. Immediately the guages flicker different values based on using the same variables for the 2 characters. She then makes seperate variables for each window and everything looks fine. One window doesn't know the other window has variables of the same name, everything is happy and sensible. She has been playing for 5 hours straight and her second character has developed rapidly. She feels confident again about trying to take on the evil mob. She goes there starts the fight and tries her healing alias. It does nothing, both her characters die this time. So she comes to the forums and explains what she has and asks us how to do it.
We have to tell her to put the alias into each window, some settings just don't work when shared between multiple windows. This means that her global module is now just a trigger and a guage...isn't the whole point of having a distributable package with a global so that it works with all the windows? Of course she is confused by this, and all we can tell is that some time around version 1.10 aliases in a global module couldn't tell what window they are running in so they can't use or work with any windows private settings. Her response is obviously, "But the trigger and the guage know what window they are working with and can use the variables correctly." At this point, I would tend to just not respond to her posts, I would figure let Fang handle it and have the headache of trying to explain it.
-----------------
Now another beginning user, this one dove right in and created 2 characters. He makes a simple auto eating script in a 3rd package's global module for them, and everything looks to be working fine, the triggers function in both windows correctly. He realizes though that it would be easier to just use an alarm to have them eat periodically. He creates his alarm in the global module and expects it to work for both windows. Sadly it does not. He comes to the forums like a good user, and is told to make the alarm in each window that needs it. I just look at how he phrased his question and know that he is going to cause a headache, another one for Fang to handle.
Again we have created a situation where it is not possible to use some settings in distributed packages. In the original package/module/window design discussions global alarms were brought up, but never resolved. I do think they are actually needed, and I will get to how to handle them.
-----------------
First lets start with the stuff that doesn't break the existing system or compatibility.
Quote: |
With this definition of context, a script without a window (like an alias within a module) still has a context, even though #SHOW might never be used in that script. In the case of a setting with no window (like an alias within a module), using #SHOW will send the output to the currently focused window (since there isn't anyplace else for it to go). |
Since you still have my little example from above enter into the command line
Code: |
#ALARM {+10} {alias} |
and set the focus to be on the test window. You will see that the alias displays where someone would actually expect it to (where the alarm was and ran). Again the script is running within the window.
Quote: |
"FindSetting" searches the database for a setting of a given name. First is looks in the current class of the current module (which is the same as the "context" of the current script that is running). Then it looks in the parent class, then it's parent class until it gets to the root of the current module. Then it looks in any enabled class within a Global or Local module within the same package. |
This is the point at which I would propose that a script would check for settings in the window it is executing in.
Quote: |
Then it looks within a Global or External module within any other package. The order in which it searches these other modules is not defined, but it always looks in the current package before looking in other packages. |
Inserting the test at that point makes reasonable sense from an OOP view. You define one object, equivalance is roughly a module, it has its variables and procedures. Then you define another object that inherits the lower level object, it adds some procedures, overrides other, keeps a few the exact same, and may change some of the variables values. When you use one of the low levels procedure virtually from the high level object you expect that the procedure will operate from the scope of the higher level object. This is the expectation when entering an alias at the command line, our windows are the high level objects. This is also the expectation when another window uses the same object. Ideally, and this would break everything I don't want it now, when a script is executed "in scope" would be list of everything available to the window executing the script; search it in the same order you do now, but search everything that window has access to even if it is not directly available to the source of the script. Naming conflicts with distrubuted packages doesn't make that possible which is why I put the window the script is executed in after the entire source package. I am not saying use the window's "in scope" at this time, and possibly not ever. There are other ways to structure packages rather than having a piece of script in a global module of 1 package able to work with a piece of script in a local module of another package. The window is not private to itself is my main aim.
Instead of tweaking each individual settings search method make it the same for all of them. I do not believe this is going to break the scripts of anyone that is heavily involved in beta testing, and we can fairly safely say that they are the most complex around. Please speak up if this change would break your settings!
--------------
Next to tackle is setting changes and creation. Again this currently behaves differently for different types of settings. I can't actually think of a reason to do this. Part of the original discussion was a 3 character session with packages that defined a few triggers, guages, aliases and variables. Most of the packages were shared between the 3 character windows. That discussion was this topic http://forums.zuggsoft.com/forums/viewtopic.php?t=24065 You can see in of your early posts there (27 Jul 2006 18:27) that you described almost exactly what it is now, but then there was this
Quote: |
In other words, when the WarriorWindow receives the MUD prompt, the published trigger in the Health Package fires. But it should update the @HP and @Mana variables within the WarriorWindow itself, and not within the Health Package. Then, the Gauges created in the WarriorWindow would get their values from the variables within that same WarriorWindow. |
That is the source of the all of these settings behaving differently. When it comes to a package someone else is publishing I don't actually want it to modify my existing package in anyway. And you didn't either in the original post of that topic
Quote: |
We do *not* just want to put the Auction Package "within" one of the other character packages. Remember that each package should be self-contained. We shouldn't need to modify the Warrior Package, for example, by adding the Auction Package to it. |
The triggers should modify existing variables if they find them, and if none are found they are created in the module that contains the trigger. Let the multiplaying user move the variable to the specific character windows. I would happily answer posts for support about that because it actually makes sense.
--------------
Finally I get the hardest question, the global alarm. Let me start by once again referring to that original post regarding the design
Quote: |
But what we see is that each Package somehow needs to be told what windows apply to it. For example, we need a way to tell the Auction Package to only run triggers on the Window for Character 1. |
This ultimately lead to the List of Packages enabled for this Module. There is one small thing that didn't carry through from that original design discussion to the point that modules came along, windows were defined as a module with preferences and display areas, and packages were finally defined as a large container for modules. That is the enabled list should be List of enabled Modules for this Module.
I will start back at the basic capture trigger problem, the global capture trigger would make a loop because it fired in the window it had just captured to. So here we add the External flag to a module. We still want a global trigger to do #CW on all windows so we have the Global flag for a module. Everything else is Local to the package. This mostly came from the discussion http://forums.zuggsoft.com/forums/viewtopic.php?t=24146 and shows pretty much how the current system of global, local, and external came about.
In the original discussion we had a health package and multiplaying with it will illustrate where the list is lacking. We distribute our health package with a General module (global) containing triggers and guages for all classes, a Priest module for thier specific healing, a Warrior module for them, and a Thief module. All of class specific modules are external and do automatic healing and depend on the General module of the package. We didn't want someone to have complex package dependencies. When we look to bring the package into our specific sessions just enabling and disabling the entire package doesn't work. It works in a single character by disabling the extra modules. In a multiplay though we can't just turn off the modules, because then they aren't available to the other windows. The user literally has to split the package into multiple files to use it. With the change to allowing the enabled package to disable at the module level instead of the package level the multiplaying user can set it correctly.
How does this fix the global alarm you ask, it doesn't actually. It helps though. So how do we run a global alarm and have it act on all windows sensibly? Keeping from my previous example do this at the command line
Code: |
#MODULE test;#ALIAS what {why did the alarm cross the road;untitled:};#MODULE 0;#ALIAS what {to give Vijilante a headache;#T- alarm};#MODULE shared;#ALARM "alarm" *2 {#SHOW this joke brought to you by window @c;what};#MODULE 0;#WINDOW test |
Evidently the alarm already knows how, we just need to be able to tell it the right alias to use in each window.
----------------
All comments invited, and again I wish I had been able to participate in the original discussions. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Nov 08, 2007 7:50 pm |
Nice post. It's going to take me a while to get my brain into this thinking mode again. I'll probably dive into this more next week after I get this new debugging module released tomorrow.
I definitely see the problem with aliases that you are talking about. And I cannot find the post that led me to changing how aliases work either. So maybe I'll put it back and then see what happens ;)
I'll read this post a few more times over the next few days and sit down with those concrete examples from the earlier posts and see what I can do to make this work more consistently. Back then I didn't have the "instancing" stuff for buttons/statusbars and that might have been driving some of these limitations. Now that I have instancing, maybe things can be more consistent.
Quote: |
At this point, I would tend to just not respond to her posts, I would figure let Fang handle it and have the headache of trying to explain it. |
ROFL! That one had me laughing for a while. |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
|
|
|
Fang Xianfu GURU
Joined: 26 Jan 2004 Posts: 5155 Location: United Kingdom
|
Posted: Thu Nov 08, 2007 10:06 pm |
Zugg wrote: |
Quote: |
At this point, I would tend to just not respond to her posts, I would figure let Fang handle it and have the headache of trying to explain it. |
ROFL! That one had me laughing for a while. |
You think he's joking, too
Scoping discussions are annoyingly hard for me to get my head round. I can tell when it's not working properly, but I'm pants at suggesting alternatives and, as such, I can't really tell if Vijilante's suggested order is better or not. I'll give it a ponder, though, and check in if anything strikes me. |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 09, 2007 12:12 am |
Quote: |
Scoping discussions are annoyingly hard for me to get my head round |
Same for me here too unfortunately. It's one of those things that you know is working when you never have to think about it. But trying to think about exactly how it works gets very complicated. |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Fri Nov 09, 2007 7:39 pm |
Since I mentioned it here in the first place I will also add something to the global alarm statements, and why I think the focus targetting of alarms needs to be removed.
First, what I see as the most common usage of alarms in scripting is to preform a timeout function. Meaning the triggers did not receive the expected response within a reasonable time frame and some other action is required turning classes on/off, sending another command, etc. The are all things that we would want to do from the perspective of a session window. Remember our Tells window will not be able to see those settings, and sending a command to the mud from the Tells window is problematic. In this usage we want our alarm to always fire in a session window, and as a package distrubuter I do not know if my package will be used by someone playing 1 character at a time or 20 at once.
The second most common usage I have seen is the time of day usage. This could be for automatically closing and starting a new log, or it could be meant to log a character on and off at specific times. I used to actually have a script that connected at 1am and disconnected at noon, and would run 5 days a week while I was out of town. This type of usage can be satisfied with a temporary alarm created directly in the window in which we need it. I actually can't see much need to have this usage in a distributed package, which means the user is going to create it in the right window to start with.
So this leaves our more common usage. Interestingly we already have a way to tell an alarm to work only when connected. If this was exteneded to be 'only work in windows that are connected' then nearly all instances of the alarm going to the wrong place are solved.
Code: |
#MODULE share;#ALARM -5 {#SHOW Hello};#MODULE 0;#WINDOW test;#SESSION achaea.com 23 1;#CONNECT;test: |
The current rule of using focus obviously causes the alarm to fire in the test window, when that window has "No network connection", and is clearly not connected. Sometimes simple solutions are best. Also I can't be bothered to make a seperate bug report for that usage of #SESSION not enabling the alarm properly.
Which leaves us with the instances where we are sending a command to the mud from the alarm, and multiplaying. Some of these like the simple stay connected are handled by the -time solution. Where we do have a problem is when the alarm is occuring as a timeout and sending a command in replacement of the triggers. Which of our characters needs to have that command sent? For that matter, which of our characters turned the class/alarm on? What happens when a trigger advances from state0 to state1 for our Warrior, and then state1's matching text is received by our Thief before the Warrior gets the state1 match? Oh! That's right both the Warrior and the Thief characters are now working with incorrect information.
Basically what I am saying is it is bad design to distribute a package that uses multistate triggers and #Tą when there is any expectation of multiplaying. The state problem might be something that could be handled on a per window basis since it is not stored in the package database, but I can't imagine any way to handle having portions of the script suddenly turned off by the Priest while the Warrior still needed them. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Seb Wizard
Joined: 14 Aug 2004 Posts: 1269
|
Posted: Fri Nov 09, 2007 9:19 pm |
Instead of having the #ALARM interact directly with a window, it could raise an event, which is handled in a window to do whatever. It's a bit obscure and round-about though, not to mention inefficient, performance-wise.
|
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Fri Nov 09, 2007 10:58 pm Re: To scope or not to scope |
I wholeheartedly agree with nearly everything I read in Vijilante's original post (I haven't fully understood the stuff about global alarms yet - more noodling required). And thank you for the list of previous discussions. Very eye-opening, and I certainly understand much better the motivation for the design we have today.
Vijilante's proposal amounts to what I was trying to suggest in my post here, but did not articulate in nearly this level of detail (plus I didn't account for the 'current class' issue as well as I should have). Basically, all settings should search and create settings using the same set of scoping rules (rather than different ones for triggers and aliases), all settings should be able to access both settings within the class that defines them and settings within the window to which they are bound at runtime (if they are different), and the default behavior should cause settings in the defining package to trump settings in the window.
Vijilante wrote: |
Next to tackle is setting changes and creation. Again this currently behaves differently for different types of settings. I can't actually think of a reason to do this. Part of the original discussion was a 3 character session with packages that defined a few triggers, guages, aliases and variables. Most of the packages were shared between the 3 character windows. That discussion was this topic http://forums.zuggsoft.com/forums/viewtopic.php?t=24065 You can see in of your early posts there (27 Jul 2006 18:27) that you described almost exactly what it is now, but then there was this
Quote: |
In other words, when the WarriorWindow receives the MUD prompt, the published trigger in the Health Package fires. But it should update the @HP and @Mana variables within the WarriorWindow itself, and not within the Health Package. Then, the Gauges created in the WarriorWindow would get their values from the variables within that same WarriorWindow. |
That is the source of the all of these settings behaving differently. When it comes to a package someone else is publishing I don't actually want it to modify my existing package in anyway. And you didn't either in the original post of that topic
Quote: |
We do *not* just want to put the Auction Package "within" one of the other character packages. Remember that each package should be self-contained. We shouldn't need to modify the Warrior Package, for example, by adding the Auction Package to it. |
The triggers should modify existing variables if they find them, and if none are found they are created in the module that contains the trigger. Let the multiplaying user move the variable to the specific character windows. I would happily answer posts for support about that because it actually makes sense. |
I would like to point out that there is a perfectly robust way for a package writer to access variables in the window without requiring the end user to do a thing:
Code: |
#var "//"%window/varname foo |
(The quotes around "//" seem to keep the parser from whining at me, but it'd be nice to do without 'em. Hint, hint). This allows a package-writer to deal with 'instance variables', like the @HP and @Mana vars quoted above, that should be stored in the window rather than with the code, without having to know the name of the window, and without requiring a manual-move-to-the-window step. Triggers, aliases, and other sorts of scripts that do not share the built-in instancing for buttons can still work with character- or connection-specific variables rather easily. This syntax will also continue to work properly regardless of what scope search order we adopt, so long as the window is in there somewhere.
A simple example in the vein of Vijilante's examples above:
- In an empty session, type the following into the command line:
Code: |
#module 0;#var /name untitled;#window test;#module test;#var /name test;#module showStuff;#alias showBad {#show bad @name};#alias showGood {#show good @{"//"%window/name} } |
Then, type:
Code: |
test:showBad;test:showGood;untitled:showBad;untitled:showGood |
And for the record, Vijilante's joke example does NOT work for me with 2.10 - I just see the punch line in the untitled window (but not the setup in the test window).
|
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Sat Nov 10, 2007 12:48 am |
The 2.11 version that I'm uploading right now changes the Aliases so that they no longer change the context in which they are executing. So, they will execute in whatever context they are being called from (like from a trigger).
I put this in at the last minute so that we could test this context change. I didn't make any other scoping or context changes. I still need to think about alarms.
Anyway, let me know if this change in 2.11 causes any unexpected side effects or problems. I just figured it wouldn't hurt to change it in one beta version so we could play with it and decide if that was what we wanted. |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Sat Nov 10, 2007 1:00 am |
The joke example didn't work because of a bug in the current way an alarm uses the focus to determine where it works. I found out as I typed the posted up that #WINDOW does not properly set the focus in a way which the alarm recognizes, instead one must do window: You can see I put it that way in the test window's what alias, but forgot to do the same for the final #WINDOW test. I hate having to dodge bugs just to get a point acrossed, especially when I spent about 6 hours of thought, reading, thought, careful word selection, read again for the right quotes, more thought...
I actually had a massive headache long before I got to the end of writing that post, and it lasted 2 days. I think it will be worth it though, especially if I convince Zugg to make alarms work globally. I already have basically everything figured out for how to make window specific alarms using all the nifty thread stuff. This will actually let me do timeout type things with multiple character windows and never creating a setting in the characters window. I will of course publish those building blocks if they become useful, and I am still thinking about how to make states work in the multiplayer scenarios. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Sat Nov 10, 2007 1:14 am |
Quote: |
#var "//"%window/varname foo |
I really frown on doing anything like that. If you need to create variables within the window, it's better to use an OnLoad event in your package. The context for OnLoad is the window being loaded, so you can easily create variables there.
About alarms:
Maybe what I can try to do is to attach the alarm to the main CMUD window handle, instead of the window handle of a specific session window. Then, when the timer expires, the main CMUD window can loop through all modules/windows and perform a "Alarm.IsInScope?" test for each one. Then it can execute the alarm in whatever modules/windows can "see" that alarm.
If the alarm was created within a specific session window, then it's only going to fire within that window because nobody outside of the window can see the settings within it. But if the alarm is created in a Global module, then all of the windows will see it.
Is that basically what you are asking for Vijilante?
Oh, and don't give yourself a headache for 2 days! I know your life is tough enough these days. I certainly don't want this to make it worse.
I've been working hard on v2.11 today and the past few days, so I haven't actually spent as much time trying to understand your post as I should. On Monday I'll give it a couple of more careful reads and try all of your examples. |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Sat Nov 10, 2007 1:40 am |
The Alarm.IsInScope sounds good. Also consider what I said about having an alarm like #ALARM -30 {whatever} make a check of whether each window it is firing in is actually connected. Having those 2 tests done on alarms makes it very possible to have alarms in a distributed package work with any number of character windows.
I am hoping at least a few other testers chime in before you seriously consider making such a major change. The use focused window for alarms has been around for a long time, and changing it will affect users migrating from 1.34. While I see it as being 1 step closer to the original idea behind packages, and a very logical change; others may better be able to count the cost in irritated users and support questions we will have to answer.
Actually it was a good headache, it told me quite clearly that my brain was fully focused and devoted to the task. I haven't been able to have that kind of focus in 2 years. It was refreshing. Also I think it lasted 2 days because that second long post was being typed somewhere in there while slept and did other things. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Nov 15, 2007 9:08 pm |
Starting to tackle this scoping stuff again. I'm going to play with changing the alarms later today. But I wanted to answer some of your other comments:
Quote: |
The triggers should modify existing variables if they find them, and if none are found they are created in the module that contains the trigger. |
No, I still disagree with this. And the various improvements that have been made recently based upon other scoping discussions contradict this. A simple example: Imagine a package that handles the complicated MUD prompt of a specific MUD. People playing on that MUD would download the Prompt Package for their MUD and install it. Obviously this module would contain a trigger to capture the info on the prompt. And when it captures something like HP, you want it to create the @HP variable in the *window* that received the prompt text. If you have multiple windows open that are all sharing this Prompt Package, then each window would get it's own @HP variable, which is exactly what you want. You do not want the @HP variable created in the Prompt Package in this case.
So I still think the current way that this is working is correct.
With the change to Aliases in v2.11, I think settings are more consistent now.
However, I think I figured out why Aliases were originally changed. Consider the following external package:
Code: |
#VAR Count 0
#ALIAS DoWork {
#ADD Count 1
}
#TRIGGER {text to fire} {DoWork} |
Here, we are defining a variable called "Count" within the package. We have an alias in the package that does some work, and part of that is to increment this package variable. The Trigger is used to fire the package when the text is received in a MUD window.
Now, when the trigger fires, it gets executed within the context of the window that received the text. This is correct.
But when the trigger executes the DoWork alias, in v2.10 DoWork would execute in it's own context and properly have access to @Count. But in 2.11, DoWork no longer changes the context, so it executes within the window that received the text. In this context, it *might* find the Count variable. But if there is a Count variable within the window itself, then that will get used. And if there isn't any Count variable in the window, then CMUD will start searching other external packages in an *undefined* order! So depending upon what other packages are loaded, the DoWork alias might not access the correct variable.
I think this gets to the point that Vijilante raised in his post:
Quote: |
Quote: |
"FindSetting" searches the database for a setting of a given name. First is looks in the current class of the current module (which is the same as the "context" of the current script that is running). Then it looks in the parent class, then it's parent class until it gets to the root of the current module. Then it looks in any enabled class within a Global or Local module within the same package. |
This is the point at which I would propose that a script would check for settings in the window it is executing in.
Quote: |
Then it looks within a Global or External module within any other package. The order in which it searches these other modules is not defined, but it always looks in the current package before looking in other packages. |
Inserting the test at that point makes reasonable sense from an OOP view. |
But I'm not sure Vijilante stated it quite correctly. The "current context" is already the same as the "window it is executing in". What we need is a check within the context that the alias was defined.
So, the new "search for setting" path would be:
1) Search the current module/class, then check the Parent class, then the Parent class, etc until you get to the root class of the current module.
2) Search the module/class where the setting was defined (alias, trigger, etc). Then check parent, etc, until you get to the root class of the defining module.
3) Search within any Global or Local module (but not Windows) within the current package (undefined order)
4) Search within any Global or External module within other packages (undefined order)
Step (2) is the new step.
With this change, the example given above would properly access the @Count variable within the external package because that's the module that the alias was defined in. If @Count isn't found with the steps above, then it still gets created in step (1) within the current context. So a Prompt Package would still create the @HP variable within the window as desired. But if the package creator put a @HP variable within the Prompt Package, then this variable would be updated instead of creating a new variable within the window.
So, before I get to alarms, let's just discuss the change of adding step (2) shown above to the setting search routine. Anyone see any problems with it that I haven't thought of? |
|
|
|
Arminas Wizard
Joined: 11 Jul 2002 Posts: 1265 Location: USA
|
Posted: Thu Nov 15, 2007 9:23 pm |
Sounds good to me. I was using aliases verses triggers to try and accomplish this order anyway.
|
|
_________________ Arminas, The Invisible horseman
Windows 7 Pro 32 bit
AMD 64 X2 2.51 Dual Core, 2 GB of Ram |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Nov 15, 2007 9:30 pm |
Hmm, now I've confused myself again. I did some testing on v2.12 and what I found is that triggers are already searching step (2) *first*. In fact, if I display %module from within a trigger, and then put that trigger in an external package, the trigger is executing in the context of the module that the trigger is defined and *not* within the window that triggered it.
But, creating a new variable *does* get created within the window that received the text.
So this is more what Vijilante was talking about. If a trigger in an external package accesses a variable, CMUD *does* look in the external package first, and *then* looks within the window that caused the trigger. So steps (1) and (2) from the post above are currently reversed. And I'm not sure that it's incorrect. I need to think about this a bit more. I feel that headache coming again ;) |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Thu Nov 15, 2007 10:06 pm |
[EDIT: I had nuked a long post so I could more-fully consider what Zugg cross-posted with me above. The order that Vijilante has proposed the same one I originally proposed in another thread, except that I included a step which checked stuff inside the package where the setting is defined (reliably ahead of #4). However, since Zugg quoted the post I nuked, I've restored it below.]
Hrm. We thus have two 'special' modules (current window, and module where setting is defined), and one 'special' package (package containing the current window) that are ahead of all of the 'non-special' modules/packages. I think it's close, but still missing something.
I agree wholeheartedly that the default location for a new setting should be in the current module (e.g. the session window) rather than the package/module/class where the currently-executing code is defined. As you point out, most of the time, you WANT @HP to be a window-specific variable, not something global to the session, and the same thing is true when creating other settings on the fly (e.g. calling #TEMP in your Warrior window should NOT create a one-time trigger that might fire on text in your Cleric window, under most circumstances). If you need a global-ish variable, you can still do it using @//myPackageModule/HP instead. So far so good.
This more-or-less requires that your #1 in the search order be first. If you create a new variable with '#var HP value', then the reference @HP HAS to find that one first, or chaos ensues.
I also agree that #2 (stuff in the module where the currently-executing code is defined) should be special, and should be ahead of other random packages (e.g. #4), for all the reasons you gave.
Now consider #3 - the package containing the current context (usually the current window). For clarity let's call it the 'current package'. I agree that it should come before #4 (other random packages). However, I ALSO think that the package containing the definition should be reliably ahead of #4 (see below). For consistency, it should come after #3, also. So call it step 4, and push 'all the other packages' to step 5.
To understand why additional step is necessary, consider a package structured as follows:
Code: |
+MyPackage
+GlobalMod
+MyAlias
+MyWindow
+MyLocalTrigger
+ExternalMod
+MyExternalTrigger
|
If MyLocalTrigger catches something, and calls MyAlias, it should be able to count on getting the MyAlias in its own package, right? That's already handled by Zugg's #3 (since MyWindow is the context, MyPackage will be the current package). However, if MyExternalTrigger catches something in the main session window, and calls MyAlias, it should ALSO be able to count on getting the MyAlias in its own package. That's what my #4 ought to ensure.
Thus, the search path becomes:
- Search the current module/class, then check the Parent class, then the Parent class, etc until you get to the root class of the current module.
- Search the module/class where the setting was defined (alias, trigger, etc). Then check parent, etc, until you get to the root class of the defining module.
- Search within any Global or Local module (but not Windows) within the current package (undefined order)
- Search within an Global or External module (but not local or Windows) within the package where the setting was defined. (undefined order)
- Search within any Global or External module within other packages (undefined order)
The list above is renumbered, of course.
NOTE: No matter what we do, there will be circumstances in which settings hide other settings when we don't expect it. For example, in Zugg's example, the script is going to get into trouble using @Count, if someone happens to have defined a variable 'Count' in the window. The only ways to avoid this are:
- Swap #1 and #2, and swap #3 and #4, thus forcing package writers to use @//%window/var to access 'instance' variables. Zugg has already expressed abhorrence for this idea. And it makes the normal case more complicated. In addition, I think it's a mistake to have triggers use one order and aliases another - if you make the swap, swap it for everthing.
- Use @//MyMod/Count (or @//%module/Count) if you really want a variable in the package you're writing (rather than the current window). I still maintain that this would be the most robust way for a programmer to ensure that she's accessing the thing she expects.
- Use a more-unique name than 'Count' (e.g. 'MyPkg_Count'), and just hope nothing else conflicts. Easy, and probably works. But only probably.
NOTE #2: However, in the course of writing that post, I had a horrible thought that I will have to test at some point - where is the state of a multi-state trigger stored? (consider a global module with '#trig foo {#say foo};#cond bar {#say bar}' in it...what happens if you get 'foo' in one window and 'bar' in another? What SHOULD happen?). Ow. |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you!
Last edited by JQuilici on Fri Nov 16, 2007 3:12 am; edited 1 time in total |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Nov 15, 2007 10:17 pm |
Quote: |
This more-or-less requires that your #1 in the search order be first. If you create a new variable with '#var HP value', then the reference @HP HAS to find that one first, or chaos ensues. |
The key to this is "If you create a NEW variable...". Then yes. But what if @HP already existed in the package? Then it seems like the alias should always return the result of @HP within the package first, ignoring what might be also defined within the window.
Quote: |
Swap #1 and #2, and swap #3 and #4, thus forcing package writers to use @//%window/var to access 'instance' variables. Zugg has already expressed abhorrence for this idea. And it makes the normal case more complicated. In addition, I think it's a mistake to have triggers use one order and aliases another - if you make the swap, swap it for everthing. |
Yes, I'm starting to think that #1 and #2 need to be swapped. New variables are created in the current window by default using #VAR. But remember that #NEWVAR creates the variable within the first search context. So you can always use #NEWVAR to create a variable within your package if you want, while #VAR creates a new variable within the window. And rather than using @//%window/var to access "instance" variables, you just need to make sure that your package doesn't contain variables with the same name as the instance variables. In other words, if @HP is an instance variable, make sure @HP is only defined in the window and is not defined in the package. Using unique variable names is always a better way to deal with these issues.
A trigger state is currently stored within the trigger setting itself. And there is only one of these. So yes, multi-state triggers within a package are not going to work correctly with multiple session windows. And I'm afraid that's going to be a hard one to fix. So, in your example above tested in v2.12:
Code: |
#SHOW foo;:test:#SHOW bar |
you get "foo" displayed twice in the main window (one from the #SHOW and one from the trigger), and then you get "bar" displayed twice in the "test" window (again, one from the #SHOW and one from the trigger). Not sure if this is how is *should* be or not, but that's how it works right now and changing this would be a huge effort, so it's not going to happen for this public release. |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Thu Nov 15, 2007 10:26 pm |
I know that headache. I was having a definite problem before because some settings had no access to the variables in the window, and we need the variables to be in the window for multiplaying.
I really think there are 3 seperate items, and all 3 need to be adressed seperately
1. What settings should be visible/findable to a given script while it executes (displaying status and buttons should be considered executing)
2. What order should settings of the same name be used
3. When a setting is created where out of all the places available to a script should it go
The main thing that realy give me a headache is considering some very messy things that can be done for the visible/findable question. An example of a mess is
Code: |
Package A Package B
Module A1 Local Window B1
Var A1V Alias B1A
Alias A1A Module B2 External
Module A2 Global Var B2V
Trigger A2T |
Now the trigger A2T gets fired in the only window there is (and don't tempt me to add Package C ). Technically all the aliases and variables should be available to that trigger, by viture of the trigger being in PackageA it gains access Module A1 and Module B2, by being run in the Window B1 it gains access to the alias B1A owned in that window, Module A2 is global and also the home module of the trigger.
The command line for the window should not be able to use the alias A1A, nor should it be able to access the variables A1V and B2V.
I am not saying anything about what order multiple settings with the same name should be found in such a circumstance as this. Just what should be visible/findable. If the triggers commands were #VAR;#ALIAS, then I would expect to see 2 of each show up.
----------------
If that didn't make your headache worse, or cause you to decide that I really did fall off my rocker; then we might be getting somewhere. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 16, 2007 12:35 am |
OK, this is good. I think I've taken care of the "2. What order should settings of the same name be used" in the Scope Testing thread. Take a look at that and it makes it pretty clear exactly what order multiple settings with the same name are searched.
So I think we can check (2) off the list (or at least talk about it in the other thread).
(3) is actually easy. It always creates in the current window, assuming that it has a window context to work with. If there isn't any window context for the script, then it creates within the class/module that the script is defined in. But mostly everything has a window context associated with it.
Using #NEWVAR will override this. #NEWVAR always creates the variable within the class/module that the script is defined within. So, #NEWVAR within your A2T trigger will create a new variable in the "Module A2 Global", whereas #VAR would create a new variable within the "Window B1" window.
So, we can check (3) off the list.
That leaves us with (1)...what settings should be visible/findable. This is related to (2), but slightly different. What's tricky about testing this is that you can't just use the "#VAR" command to display a list of variables like you've done in some other tests. The #VAR command is running in the current window (because it's displaying output) and this is why #VAR normally creates a variable in the window. So using "#VAR" to display the list of variables that are accessible is just going to show the variables that WindowB1 can access, regardless of whether you execute it in your trigger or alias or on the command line.
The only way to test what is accessible is to try and actually access the variable or run the alias.
So, in v2.12 I have created your PackageA and PackageB. For the A2T trigger, I have the script: "#SHOW A2 fired;A1A;B1A;#SHOW a1: @A1V;#SHOW b2: @B2V". Now, in WindowB1 I execute "#SHOW A2T". And I get the result:
Code: |
A2 fired
A1A executed
B1A executed
a1: A1V
b2: |
You can see that the trigger *does* have access to all of the aliases and variables, except the @B2V variable. That might be a bug that I can fix.
Also, if I type "A1A" on the command line, it doesn't execute the alias...it's properly hidden from the window. And #SHOW @A1V doesn't show anything. Alias B1A works fine from the window.
Anyway, I think 2.12 is *really* close. All that I see broken is the ability to access the "B2 External" module from within the A2T trigger. |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Fri Nov 16, 2007 12:57 am |
Wow! I am absolutely amazed by this. I also can agree that using the #VAR and #NEWVAR as a determining factor for creation location sounds very good. I think the use of variable=value works as #VAR so it all seems good.
I am not sure which is more amazing: that you can make all the seperate rule sets so quickly and have them playing nice with each other, that you agree with my crazier thoughts on this, or that it took a year since the first set of rules were done for anyone to notice there was a problem.
It might be interesting after all the tests are made and put together to run them in 2.10 or 1.34 to see just how much actually got changed. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Fri Nov 16, 2007 3:31 am |
Zugg wrote: |
Quote: |
This more-or-less requires that your #1 in the search order be first. If you create a new variable with '#var HP value', then the reference @HP HAS to find that one first, or chaos ensues. |
The key to this is "If you create a NEW variable...". Then yes. But what if @HP already existed in the package? Then it seems like the alias should always return the result of @HP within the package first, ignoring what might be also defined within the window. |
Yes, this occurred to me on the drive home. It's a new variable, so there's nothing in the package that matches (or we'd have found it), so the @HP will find the one in the window. Good. And since the package author is completely in control of whether his PACKAGE has names that mask names in the window, and can control the location of new variables (via #VAR vs #NEWVAR), this actually works really well. I like it. |
|
_________________ 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 Nov 16, 2007 5:50 pm |
Quote: |
I am not sure which is more amazing: that you can make all the seperate rule sets so quickly and have them playing nice with each other, that you agree with my crazier thoughts on this, or that it took a year since the first set of rules were done for anyone to notice there was a problem. |
Heh. Well, it helps that I've been "living" in this scoping code for the past several weeks. Day and night. Dreaming about bugs. I'm really up-to-speed on the scoping code right now and it's all in one place and pretty well documented. But it *is* very complicated, and that's why I want to get it working now. In 6 months I'll forget everything and then I'll have a much harder time changing the code.
The key was to distinguish between the "current context" (controlled by what window fires the trigger, and changed via #class/#module) and the "defined context" which is the class/module the script is defined (which doesn't change). Once I separated those two concepts, then it started working much better.
Also, the fact that your complex visable/findable example worked almost perfectly once I had the rest of the rules defined really helped make me feel that we've finally gotten something right. The rules in the code are well-defined and don't have any exceptions in them anymore. With this clean code working as well as it does, I hope it will actually work in real-life scripts and packages. We'll see with the 2.12 release.
Now the trick will be to document this in a way that makes it easy to understand and doesn't give people headaches ;) But I think we have a good start on that and some useful examples in our various forum posts so far. |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Fri Nov 16, 2007 10:17 pm |
OK, I've got the reference to @B2V working now. When it was searching other packages, it was doing it from the context of the current window. Now when it looks in other packages it first tries from the context of the module that the setting is defined in. If that fails, then it looks in other packages from the context of the current window.
|
|
|
|
Guinn Wizard
Joined: 03 Mar 2001 Posts: 1127 Location: London
|
Posted: Fri Nov 16, 2007 10:22 pm |
This sounds like good news, even though my head hurts just from reading the scoping discussions
|
|
_________________ CMUD Pro, Windows Vista x64
Core2 Q6600, 4GB RAM, GeForce 8800GT
Because you need it for text... ;) |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Sat Nov 17, 2007 8:04 am |
OK, the alarms are still driving me crazy. But I think I'll release v2.12 with what I've got.
Here is what I've done for the alarms:
Alarms now loop through the list of modules/windows and execute within whatever module/window can "see" the alarm. So, if the alarm is created within a window (which should be most of the time), then only that window will fire the alarm.
But, if an alarm is created in a global module, then when the alarm fires, it will execute in each of the other windows.
In Vijilante's alarm tests above, he has the following:
Code: |
#MODULE test;#ALIAS what {why did the alarm cross the road;untitled:};#MODULE 0;#ALIAS what {to give Vijilante a headache;#T- alarm};#MODULE shared;#ALARM "alarm" *2 {#SHOW this joke brought to you by window @c;what};#MODULE 0;#WINDOW test |
Notice that the "what" alias in the "untitled" module (module 0) has a "#T- alarm". So, when the alarms runs in the untitled window, it is disabled.
However, you don't actually know what order the windows get executed. Actually, they are executed in the order that they were created. Since "untitled" was created first, it runs the alarm first, and disables it. So you never see anything in the "test" window from the global alarm.
If I remove the #T- from the "what" alias, then the alarm properly executes within both windows until you turn it off.
Another side effect of global alarms is that I've gone back (again) to the situation where an alarm will wait until it is finished before executing in the next window. This was needed because if the alarms executed as parallel background threads, then clicking on a window could change the current global focus in the middle of the alarm execution. And this could mess up the alarm. So, alarms no longer create parallel threads (unless you use #WAIT 0 to put it into the background). I'm sure changing this back *again* will probably aggravate Fang
I also implemented Vijilante's suggestion regarding alarms that start with a '-'. These alarms will only execute in a window that is currently connected.
I've played around a bit with alarms, and I'm still not sure they are correct. I think this is going to take a bit more thinking and more work to decide how you would actually use a global alarm. |
|
|
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|