Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

listen() event handlers broken in multistate scripts

Kex Godel
Master Slacker
Join date: 14 Nov 2003
Posts: 869
03-29-2004 13:19
I haven't seen anyone else post this here yet, so I figured I'd start a thread.

If:
- you create a listener via llListen() in default
- change to another state "foo"

The state foo's listen() event handler does not recieve messages as expected from the listener created in state default.

An example of this bug can probably be found in the Shooting Gallery balloon game, as it no longer reports score.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
03-29-2004 16:56
I just talked to a Linden and they said they were having script problems.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Malachi Petunia
Gentle Miscreant
Join date: 21 Sep 2003
Posts: 3,414
03-29-2004 17:01
Confirmed Kex; touch_* events are also lost which is funny because there is no listener setup although timer events do work. I have submitted this and sent code samples to Doug.
Hank Ramos
Lifetime Scripter
Join date: 15 Nov 2003
Posts: 2,328
03-29-2004 17:40
Ben Linden told me that listens persisting across state changes was actually a "bug" that they have now fixed.

Unfortunately, some people (including myself) relied on that "bug" in our scripts, and now they are broken.
Panthar Orlowski
I didn't break it.
Join date: 24 Sep 2003
Posts: 10
03-29-2004 17:44
Here is a problem with it being a "fixed" bug...
I have many scripts that have several states that get swapped frequently. A nice way to keep things compartmentalized, I thought.
I originally had a lllListen() starting in each state. But, over time, they stacked up and caused nice "Too many Listens" errors.
So, I, and I assume many others, changed to having just one llListen() (or set of them) in the default state and just the listen handlers in the other states, to avoid this overflow.
I have written quite a few scripts in the past 6 months, and this could be quite a pain for us to "fix" to match the LSL "fix".
Nexus Nash
Undercover Linden
Join date: 18 Dec 2002
Posts: 1,084
03-29-2004 17:52
With this i'm the one who bitched... I logged in like the minute they opened 1.3 and I checked my database I tehn found that my FOR Loop has 5 second intervals between calls, this is in the touch_start and listens() for some reason it affects the WHOLE script! Anyways, this is the #1 thing to fix! I expect a patch today or tomorrow on this, touch_start are the 2nd and 3rd most used events!

Panther... use llListenRemove() this is not a bug nor was caused due to a fix of this. Plus there is no fix, the listens stack up if you don't release them.
_____________________
Siggy Romulus
DILLIGAF
Join date: 22 Sep 2003
Posts: 5,711
03-29-2004 17:54
From: someone
Originally posted by Malachi Petunia
Confirmed Kex; touch_* events are also lost which is funny because there is no listener setup although timer events do work. I have submitted this and sent code samples to Doug.



Yep, trying to track down bugs in my hottub too, I narrowed it down to listen and touch.. my scripts are pretty messy on account of my just starting out - but they don't cross states (yet). I'll keep pluging at it and see if I can find exactly whats going wrong.

Siggy.
_____________________
The Second Life forums are living proof as to why it's illegal for people to have sex with farm animals.

From: Jesse Linden
I, for one, am highly un-helped by this thread
Panthar Orlowski
I didn't break it.
Join date: 24 Sep 2003
Posts: 10
03-29-2004 18:07
So, even though the listens do not carry between states now, they still stack up?

From: someone
Originally posted by Nexus Nash

Panther... use llListenRemove() this is not a bug nor was caused due to a fix of this. Plus there is no fix, the listens stack up if you don't release them.
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
03-29-2004 20:20
From: someone
Originally posted by Hank Ramos
Ben Linden told me that listens persisting across state changes was actually a "bug" that they have now fixed.

Unfortunately, some people (including myself) relied on that "bug" in our scripts, and now they are broken.


what!?

Wtf are llListenRemove and llListenControl for then!?

Come on... I mean, this 'fix' breaks EVERY SINGLE ONE of my scripts that use listens.

==A very very very pissed off Chris

I mean, at the very least they could have notifyed us!
Kex Godel
Master Slacker
Join date: 14 Nov 2003
Posts: 869
03-29-2004 22:41
Agreed. This was not in the release notes, and should have been if it was a deliberate design change decision.

I can only hope they were mistaken to assume that this change was delibreate.
Carnildo Greenacre
Flight Engineer
Join date: 15 Nov 2003
Posts: 1,044
03-30-2004 00:42
If any of my original flock of birds are still out there (fat chance!), they'll be somewhat broken by this. Fortunately, most of my scripts since then have used either listens, or multiple states, but not both.
_____________________
perl -le '$_ = 1; (1 x $_) !~ /^(11+)\1+$/ && print while $_++;'
Bob Brightwillow
Technologist
Join date: 7 Feb 2003
Posts: 110
Re: listen() event handlers broken in multistate scripts
03-30-2004 09:01
From: someone
Originally posted by Kex Godel
If:
- you create a listener via llListen() in default
- change to another state "foo"

The state foo's listen() event handler does not recieve messages as expected from the listener created in state default.


That's the correct way for states to work, isn't it? I expected states to work this way when I first started scripting, and I just scratched my head when told that listens persist across state changes. It's not a very good state machine if it depends on what states it's been in previously!

Yes, of course it breaks existing scripts that depend on this. But those scripts are broken, because they're working around something that's broken. And yes, maybe it should have been announced. Let's howevr endure this pain once so that the script interpreter can be unbroken for good.
Tcoz Bach
Tyrell Victim
Join date: 10 Dec 2002
Posts: 973
03-30-2004 10:24
I can give you a perfect example of why this behavior won't work. Events HAVE to fully clear, like it says in the lsl guide.

Say you have an attachment that will eject you from land when a counter hits 0. This counter is based on shouts from the bullets that hit you. Every time a shout is received, the script deducts points.

So, say you get hit three times after that counter hits 0, but before the switch to a waiting state that the object uses to let you get back into the game but avoid getting hit again until you do (the delay can come from sending out a couple of link messages or even a couple of tells saying "you've been killed";).

When you get back, and the object switches to its working state, the user will take the damage from the bullets that hit him even though it was 3 minutes ago and there have been two state changes.

With that model, it's not possible to build real time action without extenstively coding around that problem. I ran head first into this in the Vorago and realized right away that there's no way this behavior could be intended, as there is almost no practical way to code around it. The only way was to use event handlers in the new state that were empty, which meant you can't remove the listeners and such on state exit (for instance, it's not unusual, in fact it's very useful, to register a listen on state entrance and remove it on state exit).

If you need to retain state or receive calls above and beyond a state change, process and store it in another script; I've actually been working on a little timestamping thing for queuing. I use the multiscript model extensively now and it's letting me do things I used to be a bit unsure how to do in one script.

The alternative would be to leave the behavior and add a llFlushEvents method or something, but since that's the intended behavior and isn't working anyway, I don't see that happening.

In general, I've found that if a mechanism in an API is causing you to write code to avoid something it does, it's more than likely a bug or design flaw.
_____________________
** ...you want to do WHAT with that cube? **
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
Still outraged.
03-30-2004 15:15
Still, no one has addressed my burning question, why have llListenRemove and llListenControl been implemented, when they obviously focus attention on listen-cross-state capability.

Hey everyone! Lets go break something that is so crucial in almost all listen-state changers!

Personally, I think enabling and disabling a listen upon entry-exit of a state is incredibly wasteful. It even requires use of a global variable. Now, I hate wastefully using global variabes, since they're for data that is supposed to be transferred around globally!

If you previously coded using this method, yay! Good for you!

However, I dont have time nor even want to change every single one of my state-change-listeners to this new system. Its blatently wasteful and even MORE confusing IMO.

Tcoz, I still don't understand what was so bad about the old system. If it was bugged why havent the lindens notifyed us!? In the many threads where people were confused about listens carrying over, they have NEVER responded admitting that the functionality was a bug. If a person can find a thread concerning this listen-resident 'problem' with a Linden responding that it is/was a bug, Ill pay that person 1000 L$.
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
Re: Re: listen() event handlers broken in multistate scripts
03-30-2004 15:24
From: someone
Originally posted by Bob Brightwillow
That's the correct way for states to work, isn't it? I expected states to work this way when I first started scripting, and I just scratched my head when told that listens persist across state changes. It's not a very good state machine if it depends on what states it's been in previously!


I always thought listens were more of a 'global' thing, where creating them caused your entire script to be added to a 'listeners' list on the server backend. That's the way they have been made out to be for the past YEAR. Linden Lab, espicially evident in LSL, has not followed traditional, strict, computer-science based conventions. If we had true state machienes, then we should already have state-specific variables. If they added in that kind of functionality with this change, I could understand it, but, as you can see, they haven't. I cant find any valid reason for this change other then it was a very unexpected malforesight.

From: someone
Originally posted by Bob Brightwillow
Yes, of course it breaks existing scripts that depend on this. But those scripts are broken, because they're working around something that's broken. And yes, maybe it should have been announced. Let's howevr endure this pain once so that the script interpreter can be unbroken for good.


I really cant see what good this change has brought. If the change breaks more things then it fixes, in my book, its a bug.

==Chris
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
03-30-2004 15:37
From: someone
Originally posted by Tcoz Bach
I can give you a perfect example of why this behavior won't work. Events HAVE to fully clear, like it says in the lsl guide.

Say you have an attachment that will eject you from land when a counter hits 0. This counter is based on shouts from the bullets that hit you. Every time a shout is received, the script deducts points.

So, say you get hit three times after that counter hits 0, but before the switch to a waiting state that the object uses to let you get back into the game but avoid getting hit again until you do (the delay can come from sending out a couple of link messages or even a couple of tells saying "you've been killed";).


This really has nothing to do with the way listen events specificly, worked in the past. During the time period while the script is switching states, all events are cleared, that's the way Cory said it worked while addressing my Bounds Check problem (search the Scripts and Scripting forum for that one). And the script cannot process events, until it loaded the new set of event listeners in its new state. In summery, the event switching process should completely fry a script's event listeners, unregistering all of them, and loading shiny new ones for the new state. Having llListen() cross states is a slight inconsistancy, but its different in that the programmer has to explicitly register the script. If it wasn't made to do that, it should have had a one-state range since the beginning, or at the very least, have been fixed during beta. It makes NO sence whatsoever to fix such a massive "bug" at this point. Notice the quotes in the previous sentence. This change should NOT have been implemented, it breaks wayy too many things, and its too late in the game. As a curtosy to scripters, LL should have at the very least depricated listen() and introduced a completely new event with the "correct" functionality.

Stale events should not be fired in the old state after a state change. That seems like a completely seperate bug.

==Chris
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
03-30-2004 15:42
From: someone
Originally posted by Tcoz Bach
I can give you a perfect example of why this behavior won't work. Events HAVE to fully clear, like it says in the lsl guide.


Mind pointing out where? I really dont feel like searching through it right now :-/
==Chris
Jake Cellardoor
CHM builder
Join date: 27 Mar 2003
Posts: 528
03-30-2004 19:08
In lsl_guide.html, "Chapter 5. States," it says

"When state changes, all callback settings are retained and all pending events are cleared. For example, if you have set a listen callback in the default state and do not remove it during state_exit(), the listen callback will be called in your new state if a new listen event passes the filter set in the default state."

Not that this would be the first time the documentation was inaccurate, though.
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
03-30-2004 19:15
w00t, thanks Jake :D

That quote basicly states that, if you create a listen handle (call llListen()) in your default state, and change to another state, if the object recieves a message conforming to what was specified in the handle declaration, it will execute the listen() in your non-default state, unless you remove it in default's state_exit().

Summery: It states that listens are script-wide; not specific to one state machiene.

Im confused as to what is your position on this Tcoz...
Tcoz Bach
Tyrell Victim
Join date: 10 Dec 2002
Posts: 973
03-30-2004 19:55
/sigh Chris.

No. It's a bug. Cory said so. My example is the one that I used to consistently duplicate the bugged behavior. It has everything to do with it, and is the reason I discovered the bug. Listens and collisions are BROKEN. It makes PERFECT sense to fix it. I have also posted the quote from the LSL guide in threads that you have responded to.

JEEZUS. You really do think I'm stupid don't you.
_____________________
** ...you want to do WHAT with that cube? **
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
03-30-2004 19:57
From: someone
Originally posted by Tcoz Bach
No. It's a bug. Cory said so.


Im only so mad about it because it breaks every single one of my scripts.

Please please please post a link to the post made by Cory about this.

In responce to your edits:
From: someone
Originally posted by Tcoz Bach
You really do think I'm stupid don't you.


No I dont. I just think your quick tempered and confused about what I said (I admit it was confusing, I tryed to clarify it in the post below).
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
03-30-2004 21:03
Here's what T'coz is refering to:

From: someone
Originally posted by Cory Linden
2. On state change, events do not clear, they suspend, even though the manual says otherwise.

Events do clear on state change, but there are still some bugs around the internal state of collisions, listens (and perhaps a few others). This will be fixed for 1.3.


Gah, Ive been confusing people!

Let me fully explain my position here.

Ill use this script as refrence:

CODE

integer ourListen;
default {
state_entry() {
ourListen = llListen(0, "", "", "");
}
touch_start(integer n) {
llCode();
llMoreCode();
llEvenMoreCode();
state foo;
}
listen(integer c, string n, key id, string m) {
llEmail();
llChangeSelf();
llFeedObject();
}
}
state foo {
state_entry() {
llCode();
llConfusingFunction();
llWierdNamedThing();
}
listen(integer c, string n, key id, string m) {
llDoSomethingWith(c);
llFeedObject();
}
}


There are three pieces of data which are handled during all this event flinging.

1. The event's functionality. This is the code you declare within the method you specify. For example, the functionality for default's touch_start() event is:
CODE

{
llCode();
llMoreCode();
llEvenMoreCode();
}


2. The event's queue identifyer. This is the special piece of data that is inserted in the event queue of a state. Ill refer to them like this: touch_start() event queue identifyer is: "Q:$touch_start"

According to Cory, each state machiene has its own event queue.

3. The event's callback. This is also called the event's handler. What this piece does, is tell the server that the state machiene wants to be notifyed if the event occurs.

All event callbacks, except the listen, email and timer events', are implicit. This means, declaring an event within a state automaticly registers it with the server.

You dont need to put in llMakeObjectTouchable() in your script, you just put in a touch_start() event in your object, and that automaticly registers the object.

Now, the listen event's callback is explicit. You need to actually make a call to llListen() for the server to register your script to recieve listen events.

There are a few problems with two of the three pieces of events, the callbacks and the queue identifyers.


Ill run through the daily life of the script I declared above.

When our script is compiled, it immediately loads up its default state machiene. As it loads, it tells the server:

1."Hey! I have a state_entry() event in here! Notify me if I enter into my state!"
2. "Hey! I have a touch_start() event in here! Notify me if anyone touches me!"

The server goes "OK." And registeres the state_entry and touch_start callbacks.
Then the state finishes loading up. The server then tells the state machiene "Hey! You finished loading up! Ill put this event in your queue."
The server puts "Q:$state_entry" in the default state machiene's queue.

The state machiene realises that its event queue was changed. Since it was previously empty, "Q:$state_entry" is the first entry. It sees this, and sends its state_entry functionality down to the VM (where it is executed).

The VM sees that the script wants to register a listen event. The server acknowledges the script's request, and registeres a callback for its listen events.

Then, the state machiene checks to see if it has any more events in its queue, sees none, and relaxes for awhile.

A few minutes later, Joe User strolls up and touches the object containing our script. He expects the object to do something miraculous, like dance the electric slide, but when it sits there, he keeps on touching it. He gets frustrated, and yells "die!".

Right when Joe touched the object, the server goes, "oh! I see that something registered was touched!"

The server puts "Q:touch_start" in default's queue.

default sees this, and sends touch_start's functionality to the VM.

Because Joe user was in range of the object, and the script had a listener registered, it adds "Q:listen" to default's event queue.

Now, Joe user is touching this object like mad, he wants it to do something, anything! Little does he know, that llCode takes a very long time to process.

Every time Joe touches the object, the server adds "Q:touch_start" to default's queue.

default trys to execute the events in its queue as fast as it can, but can only execute an event after the VM is finished processing its previous event. Since llCode takes long, the events start to build in default's queue.

Suddenly, the VM freezes default. It has come to the state foo; statement in the first touch_start that default sent to it. The VM then tells foo to start up, and (is supposed to) clear default's event queue. It also is supposed to clear default's non-explicit callbacks, so foo can load new callbacks.

foo starts up, and goes through basicly the same steps default used to set itself up. Joe is still touching the object, but having no effect, since foo has no touch_start callback registered. However, when Joe suddenly yells "Gahhh!!" The server, seeing that the script still has the listen callback set up, and still has a listen event functionality in state foo, puts "Q:listen" on foo's event queue.

foo sees this, and sends its listen event's functionality to the VM.

The VM executes the listen functionality's code, until it gets to the 'state default' statement. It then freezes foo.

It tells default to begin starting up, and (supposedly) clear's foo's event queue and non-explicit callback queue.

Now, because of the bug Tcoz describes, the VM accidentally leaves default's event queue full when starting up state foo. When default is entered again, the machiene executes that stale event queue. This is where the bug is, the state is supposed to have been cleaned out when it was exited.

The change in v1.3, was that the VM clears out the explicit *and* non-explicit callbacks of a state. I for one, have been coding since day 1 expecting the explicit callbacks (namely listen's) to stay intact.

Cory mentions that there are a few bugs concerning the internal state of collisions, listens, and perhaps a few other events in his post. This is where the ambiguity of his statement comes into play. We/I dont know if he meant to change the VM so that it cleared explicit callbacks or not. Keep in mind, the callbacks are not the same thing as the queue. "Events do not clear, they suspend" referrs to the event queue for a state. It is not cleared on exit, rather suspended.

Gah sorry about the length of this. There's still alot of unsolved mysteries about how everything works behind the scenes of LSL. Im just making my best guesses here. And forgive my ignorance of grammer, I think I actually mush around verb tenses/plural nouns/first-second-third person pov alot. :eek:

We need Cory to respond to this really. No one knows if the 'internal state' bugs he mentioned in his post, supposedly fixed in this version, were connected with the clearing of explicit callbacks.

==Chris

EDIT: Attempt at correcting grammer.
Cory Linden
Linden Lab Employee
Join date: 19 Nov 2002
Posts: 173
listen info
03-31-2004 18:25
Listens are now removed at state transitions, which has the advantages of a) removing the need to clean up listens on state transitions, b) correctly separates behavior between states, c) eliminates the bugs around chats showing up in the wrong state, and d) actually doing what was supposed to do.

Unfortunately, this does break some scripts in the world that were relying on having listens stick around. Mea culpa on not doing a better job of broadcasting the impact of the change. Apologies as well for the documentation bug.

I realize that some scripts will need to be modified as a result of this change, but with a small change current scripts will function as intended and scripts that relied on complicated multi-state listens will actually work as opposed the current situation where they mostly work but occasionally fail for impossible to debug reasons.

Also, llListenRemove and llListenControl were created for people who were writing scripts without multiple states but still needed to turn listens on and off.
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
03-31-2004 18:38
Weeee. Lots of work to do.

Lemme make sure I know how the system works now.

Cory, take the following script:

CODE


default {
state_entry() {
llListen(0, "", llGetOwner(), "");
}
listen(integer c, string n, key id, string m) {
llSay(0, "Foo");
state foo;
}
}
state foo {
state_entry() {
state default;
}
}


Upon entering default for the first time, a listen event callback is registered. Once anything within range is said by the owner, the object says "Foo" and switches to state foo. Does the listen callback get removed automatically? Or do we have to stick its identifyer in a global and remove it in state_exit?

In fact, Ill test this scenerio in world, and get back to everyone in a few minutes.
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
Test completed.
03-31-2004 18:56
I used this script to test:

CODE

default {
state_entry() {
llSay(0, "Entered state default.");
llListen(0, "", llGetOwner(), "");
}

touch_start(integer total_number) {
llSay(0, "Touched, switching states.");
state foo;
}
listen(integer c, string name, key id, string message) {
llSay(0, "listen event executing.");
}
}
state foo {
state_entry() {
llSay(0, "Entered state foo.");
state default;
}
}


Got this kind of responce from it:
<script compiled>
Object: Entered state default.
You: f
Object: listen event executing.
You: f
Object: listen event executing.
<object is touched>
Object: Touched, switching states.
Object: Entered state foo.
Object: Entered state default.
You: f
Object: listen event executing.
You: f
Object: listen event executing.
You: f
Object: listen event executing.

And this behavior continued reguardless of the number of times I touched the object. Basicly, no need for llListenRemove anymore now and no matter how many times I enter default, I dont get a Too Many Listens runtime error.

This makes things slightly less confusing, but still breaks alot of my stuff.

Cory, you have some problems with your state switching code, I performed an unrelated test, and occasionally got repeat state_entry's. Ill start a new thread, I dont want to hijack this one.

==Chris