|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Mon Mar 03, 2008 11:06 pm
[2.21] Setting duplicated while redefining disabled setting (found in 2.18) |
This problem, or a similar one, has been reported before (here, for instance), but I haven't yet found a thread showing this on the bug list, and it's still occurring in 2.18. So, here's a simple test case. Apologies if this is a repeat.
Defining a setting (e.g. a trigger) with the same ID and type as an existing setting should change the definition of the existing setting. If the existing setting is disabled, however, cMUD appears to create a duplicate instead.
Procedure
- Start cMUD
- Hit ESC to dismiss the Settings window
- At the command-line, type:
Code: |
#trig ft {foo} {#say trig 1}
#trig ft {foo} {#say trig 2}
#t- ft
#trig ft {foo} {#say trig 3}
#trig
#show foo
|
The output window will show:
Code: |
Triggers:
- foo -> #say trig 2
foo -> #say trig 3
foo
trig 3
|
The existence of two triggers named 'ft' (one disabled, one enabled) can be confirmed with the Package Editor.
Note that 'trig 2' overwrote 'trig 1' (because the trigger was enabled when the #trig command was issued). This is the expected behavior. However, 'trig 3' created a duplicate - I would have expected it to overwrite 'trig 2', and probably to set the trigger to enabled.
I have produced similar errors with alarms and other setting types, as well. The same problem will also occur if the setting is within a disabled class (e.g. '#var foo/bar baz;#t- foo;#var foo/bar bag' will create a duplicate variable). |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you!
Last edited by JQuilici on Sat Mar 29, 2008 4:02 pm; edited 1 time in total |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Sat Mar 29, 2008 4:00 pm |
Still present in 2.21 (not surprising, since I haven't seen it on the fix list).
|
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Tue Apr 01, 2008 6:43 pm |
Well, I think this is partly by design. When a setting is disabled, it is effectively invisible to your scripts. So it's like the trigger/variable/etc doesn't exist.
For example, imagine that you had a trigger named "ft" in your window, but also had a trigger named "ft" in a Global module in another package. Normally the "ft" trigger in your window will fire. But if you disable it, then the trigger in the global module will fire instead. And changing the "ft" trigger when the one in your window is disable causes the trigger in the global module to be changed instead.
This is inheritance and is the crude way that you can do some object-oriented programming in CMUD. By disabling a class folder, you can cause the inherited settings in another module to take over.
Now, with that said, getting duplicate settings with the same name in the same folder *is* bad and causes all sorts of problems. So I guess I need to think about exactly how to change this.
I'd actually be interested in exactly what zMUD does in this case if someone is willing to test it for me. |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Tue Apr 01, 2008 8:02 pm |
I did the same set of commands in a blank session in zMUD 7.21:
Code: |
#trig ft {foo} {#say trig 1}
#trig ft {foo} {#say trig 2}
#t- ft
#trig ft {foo} {#say trig 3}
#trig
#show foo |
and got
Code: |
Triggers:
+ foo -> #say trig 3
foo
trig 3 |
The Settings Editor confirms that only the final version of the trigger exists - there is no duplicate. So zMUD replaced the disabled trigger with the new version, and made it enabled.
Thinking on this further, I think the basic algorithm for, say, "#trig foo {}' ought to be:
- Attempt to locate an existing ENABLED trigger named 'foo', using all the existing scoping, etc. If it exists, replace it.
- If none is located, determine the default location for a new trigger.
- If a DISABLED trigger named foo exists in that default location, delete it.
- Create the new trigger 'foo' (enabled) in the default location.
Feel free to describe why this is wrong.
The algorithm outlined ought to prevent duplicate named settings from being created in the same class, at least, and it matches the behavior of zMUD in this case. Unfortunately, I think the model breaks down when the disabled thing is one of the containing classes, rather than the setting itself. More testing and noodling required. |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Tue Apr 01, 2008 8:14 pm |
Well, my routine for searching for a setting that is "inscope" can be called with an argument to ignore the "disabled" property. I think zMUD is doing something similar to just ignoring the Disabled property when overwriting an existing setting. So my modified version of your algorithm is this:
1. Attempt to locate an existing ENABLED trigger named 'foo', using all the existing scoping, etc.
2. If none is located, look for an existing DISABLED trigger named 'foo' using the existing scoping
3. If a trigger is found, replace it.
4. If no trigger is found, create it in the default location
That actually results in the fewest changes to the code, so should result in the fewest side-effects. And I think it will produce the same behavior as zMUD even when triggers are hiding in other classes. |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Tue Apr 01, 2008 8:18 pm |
Hmm, maybe not. In zMUD, if you do this:
#CLASS Test
#VAR InTest 123
#CLASS 0
#T- Test
#VAR InTest 456
then I think it creates a new variable called "InTest" within the main window. I don't think this overwrites the InTest in the disabled class. So I think that your algorithm might be more correct...it should only look for a disabled setting in the default location. |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Tue Apr 01, 2008 9:03 pm |
In that second case you describe the variable itself is not disabled, its containing class is. I believe it is applicable to change a disabled setting when the setting itself is disabled and no enabled setting has been found. When a containing class is disabled we would not want anything to be created or updated in it.
|
|
_________________ 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: Tue Apr 01, 2008 9:37 pm |
And an example to illustrate what I mean about disabling the classes:
Code: |
#trig ft {foo} {#say trig 1} {myClass}
#trig ft {foo} {#say trig 2} {myClass}
#t- myClass
#trig ft {foo} {#say trig 3} {myClass}
#trig
#show foo |
Note that I am disabling the class containing the trigger, not the trigger itself.
On zMUD 7.21, this gives:
Code: |
Triggers:
myClass: foo -> #say trig 3
foo |
And the SE confirms that only one copy of the trigger exists, inside myClass, which is still disabled.
On CMUD 2.21, this gives:
Code: |
Triggers:
- foo -> #say trig 2
- foo -> #say trig 3
foo |
And the PE confirms that two copies of the trigger exist, both inside myClass, which is still disabled.
IMHO, zMUD's behavior is the better of the two, and I am having a hard time thinking of any way to improve upon it. I think it is also the result we'd get from my proposed algorithm, suitably generalized:
- Attempt to locate an existing ENABLED trigger, using all the existing scoping, etc. (including any class structure specified in the new definition). If found, replace it.
- If none is located, determine the default location for a new trigger, taking any class structure specified in the new definition into account.
- If a trigger is found at the default location (regardless of whether it or any class containing it is disabled), delete the trigger found.
- Create a trigger in the default location and mark the trigger (only - not any classes) enabled.
Thus, any disabled class continues to be disabled after this process, but the trigger itself will be enabled, regardless. |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Tue Apr 01, 2008 9:54 pm |
Vijilante wrote: |
In that second case you describe the variable itself is not disabled, its containing class is. I believe it is applicable to change a disabled setting when the setting itself is disabled and no enabled setting has been found. When a containing class is disabled we would not want anything to be created or updated in it. |
I'm not sure that a silent failure to create a setting, as you describe, is the right thing to do, though I could be convinced. It would also break compatibility with zMUD, though anyone who is relying on this behavior in scripts should have his head examined.
The real difficulty is that this situation would most likely arise from either (a) coder error, or (b) two DIFFERENT coders (e.g. package authors) who happen to use the same name for a class they create. There's no need to do anything nice for case (a) - the coder should just fix his code to use different class names. For case (b)...there's probably no right answer. At least one of the two authors is not going to get what he expects, and I don't see any good way to fix it. |
|
_________________ Come visit Mozart Mud...and tell an imm that Aerith sent you! |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Tue Apr 01, 2008 10:52 pm |
I would actually expect the code example Zugg gave to create a new variable. There is no default class defined, and the final #VAR command in his example does not specify a class.
I would expect your original trigger example
Code: |
#trig ft {foo} {#say trig 1}
#trig ft {foo} {#say trig 2}
#t- ft
#trig ft {foo} {#say trig 3}
#trig
#show foo |
To result in only 1 trigger and for it to be enabled there at the end. Even if a class were added to the workings of this example I would still expect the trigger to be overwritten because of the ID of 'ft' that is used. Part of the concept behind ID's is that they are supposed to be unique, even though they don't actually have to be.
I guess what I am saying is that when there is a default class then it makes sense to overwrite a directly disabled setting within that class if no fully setting is found. I don't think zMud did that for variables, but it probably should have.
Having the containing class(es) disabled kind of changes that. I actually don't think it should be possible for the default class to be a disabled class. A quick test of this:
Code: |
#CLASS test
#CLASS 0
#T- test
#CLASS test
#VAR a 1 |
Results in the variable being created in the disabled test class. On a subsequent command line we can then do "#VAR a 2" and cause a second 'a' variable to be created again in the disabled class. I think the second issuance of "#CLASS test" in this example should have enabled the class, but I really don't know how far up the class tree we want to take that. Again a matter of the specific setting being disabled versus containing classes.
Continuing further from my above I would expect this to actually set the default class back to 0
Code: |
#CLASS test2
#T- test2
#VAR b 3 |
This once again is creating a situation where the default class is disabled and leads to problems. We really don't want such problems.
Then we have the usages of defining a class in commands with one of thier parameters, such as "#VAR c 1 {} {test3}"; this can again have problems. If the class test3 is disabled then duplicates of the variable c will be created.
Code: |
#VAR c 1 {} {test3}
#T- test3
#VAR c 2 {} {test3} |
Again a class has been specified and we expect the variable to be overwritten.
I think there are just a few more combinations that really have to be considered before Zugg should make any changes. Right now there is ID and class specifiers in some commands, the default class, and a generic name, containing classes, disabled settings, disabled parent classes, disabled modules. Part of whatever is done has to allow for expansion and overriding, in the same fashion as was worked out last time the whole scoping discussion got done. I actually really trust JQuilici to think through all of it, I remember he wrote the script for testing scoping before.
So far though I haven't really seen that the impact of any change has been considered with a broad enough perspective to suggest on what a proper formula should be. |
|
_________________ The only good questions are the ones we have never answered before.
Search the Forums |
|
|
|
Vijilante SubAdmin
Joined: 18 Nov 2001 Posts: 5182
|
Posted: Tue Apr 01, 2008 11:12 pm |
Another slightly more convoluted example enter at the command line
Code: |
#CLASS test
#CLASS 0
#ALIAS a {#WAIT 2000;#VAR b 1 {} {test};#SHOW {b is @b}}
#ALARM +1 {#T- test}
#EXEC {a} |
Here we see a script failure due to threading. What covers this case were there is no possible reason to expect the b variable to be inaccessible immediately after it was assigned?
Again the variable was created in a disabled class, but we really don't want the #VAR command to create a second 'test' class. Simply enabling the test class doesn't actually solve the problem if its parent class is disabled. I really have to ask where should we draw the line on what we are doing with a disabled setting? Any set of rules that is developed has to not only work for all setting types (even classes), it must also make clear sense without needing to be spelled out. |
|
_________________ 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: Wed Apr 02, 2008 1:29 am |
One of the key "rules" I can think of is that it should never be possible for CMUD to create a setting with the same name as another setting of the same type in the same class folder...ever. We know that if that happens, the internal hash table gets screwed up (because the hash table can only point to a single setting). I have recently improved the Settings Editor so that it is much harder to get duplicate settings with the same name. So scripting should also prevent this.
This was at the very core in zMUD. Since zMUD didn't have any database, it *only* had the hash table, so it couldn't create duplicate settings.
But we need to be *very* careful with these kind of changes. It would be really easy to completely screw up CMUD and compatibility with existing zMUD *and* CMUD scripts. So I really need to minimize the changes that I make to this low-level code.
It might just come down to doing a final check before CMUD creates any new setting. At the point of creating a new setting, CMUD already knows the correct name and class folder/module/package that the setting is going to be created in. At that time it could check to see if an existing setting with the same name already exists, and if so, just overwrite the existing setting regardless of whether it is enabled or disabled and regardless of whether the class containing it is enabled or disabled.
This kind of "last minute" check would ensure that duplicate settings don't get created. So this fixes any current situation that creates a duplicate setting (which we know is bad, so *must* be fixed), without changing any other existing behavior.
I really *don't* want to get into any side effects where using #CLASS might also enable the class. I think it is desirable to be able to use
#CLASS test
#T- test
#VAR a 123 // creates variable "a" in the disabled "test" class
In other words, people *should* be able to create variables in a disabled class without the side effect of enabling the class. Enabling the class might start causing triggers or alarms to run when all you want to do is reset a variable in the class. So I don't think I want to change that.
The problem that I need to fix is that doing
#CLASS test
#T- test
#VAR a 132
#VAR a 456
creates *two* variables named "a". That's the problem and bug that I need to focus on fixing.
So maybe I should just do that kind of fix and then see how things work after that and then we can decide if there are remaining cases that don't work the way we want them to. |
|
|
|
Zugg MASTER
Joined: 25 Sep 2000 Posts: 23379 Location: Colorado, USA
|
Posted: Thu Apr 03, 2008 10:43 pm |
OK, I have gone ahead and "fixed" this the way I described about. When creating a new setting, CMUD first searches (as before) for an existing setting that is visible in the current scope and enabled. Now, in v2.22, if nothing is found, CMUD also searches for a setting in the current context that is disabled. If a disabled setting is found, then the disabled setting is modified instead of creating a new setting with the same name. Here are some examples from v2.22:
Code: |
#VAR a "outer"
#VAR test/a "in test"
#SHOW @a // "outer"
#T- a
#SHOW @a // "in test"
#T- test
#SHOW @a // null
#VAR a "new"
#SHOW @a // still disabled, so null
#T+ a
#SHOW @a // "new"
#SHOW @test/a // still null since test class is disabled
#T+ test
#SHOW @test/a // still "in test"
#T- a
#T- test
#CLASS test
#VAR a "new in test"
#CLASS 0
#T+ test
#SHOW @a // "new in test"
#T- test
#VAR test/a "done"
#T+ test
#SHOW @a // "done" |
So, you can see that it never creates a duplicate variable. If you change to a disabled class and try to modify the variable within the class, then it properly changes the variable instead of creating a new one. If you are outside of the class but specify the full class/name path, it properly modifies the variable instead of creating a new one in the class. I think this is closer to the way it should work. If you have some other test cases you want me to try, let me know and I'll report the results. |
|
|
|
JQuilici Adept
Joined: 21 Sep 2005 Posts: 250 Location: Austin, TX
|
Posted: Fri Apr 04, 2008 4:44 pm |
Well, I've been doing some thinking on this issue for the last couple of days. Started about four posts, and then deleted them to think some more. I wish I could say I've come back with great insights, but mostly I'm coming up with more questions.
The one that's really bending my brain involves this:
Code: |
#var a val
#alias a {#show "alias a"}
#trig a {^a} {#show "trig a"}
#var a/b val2 //creates a class 'a'
#t- a |
Which 'a' (var, alias, trig or class) should be disabled by the last line? (CMUD 2.21 appears to ACTUALLY disable...none of them. )
I'd say let's go with Zugg's implementation above for 2.22, and ponder this all some more. I'll post my rambling thoughts in this thread when I get 'em organized. |
|
_________________ 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 Apr 04, 2008 7:28 pm |
The order in which #T+ and #T- search for settings is undefined. If you have multiple different setting types with the same name, you can specify the "type" in the optional second argument, like this:
#T- a "alias"
However, with that said, it usually will search for a Class folder first, and then look for other settings. Internally, it will probably find settings in the order shown by the Filter buttons in the settings editor, since that represents the numerical "type" field for the setting, and that is how the database is sorted internally. But you should *never* count on this order, except for the fact that Classes will usually be tested before any other setting.
I came up with another test today:
Code: |
#VAR test/a "in test"
#CLASS 0
#T- test
#VAR a "in main?"
#T+ test
#SHOW @a // shows "in main?"
#SHOW @test/a // still "in test" |
This shows a case where the disabled @a variable is not in the "current class". So rather than modifying that variable buried in a disabled folder, you can see that is will create a new variable in the current class. I think that is working the way most people would expect.
If you specifically modified the disabled variable with
#VAR test/a "new test"
then it would still properly modify the variable in the disabled class. But without a full path specifier, it's going to create a variable in the current class if it cannot find an existing enabled variable in the current scope. If the "test" class was still enabled and you did #VAR a "123" then it would modify the @a within the "test" class because that variable is still in scope, which is how zMUD worked. |
|
|
|
|
|
|
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
|
|