And Events Shall Be Raised Uninterruptible

It is reasonable to say that the flow of control in LSL is determined, at the top level, by the recognition of events. LSL code executes only when something happens. Of course, there is a variety of ways that something can happen in LSL. The obvious methods are by overt actions such as touch() or collision(). You can catch an event when some parameter of the object has changed(). There are events that trigger on ostensibly linguistic communication: listen(), link_message(), and email(). There are events raised when a system request is fulfilled, such as timer() or dataserver(). There are also what I call the administrative events, state_entry() and state_exit(). There’s even an event available when someone gives you money(). When events are triggered, the respective LSL code is performed.

But is it that simple? The pattern of code execution in LSL programs can be confusing: on the one hand, event code appears to be triggered when the action occurs, according to the interrupt-driven model; yet on the other hand, reliance on interrupt-driven coding techniques simply stall the program.

That’s because event-driven does not mean interrupt-driven, at least not in LSL. The reality is that LSL event code is not executed directly when the event is recognized. The paradigm is not interrupt-driven at the level of the script.

Here’s what does occur: when an event is raised, a request is placed in that task’s event queue, and it stays there until the program is in a position to service it. The program is in that position when it has completed any currently executing event code. The program then services event requests in order of the request.

This means that the coding techniques of interrupt-driven applications cannot be considered. Perhaps the most basic of these is the “busy-wait,” where the program loops until an interrupting event sets a flag. This would seem to be intuitive, because you “wait for this, and then do that,” hoping to confine these actions within one routine.

Let’s take a trivial example where an announcement is made upon the first touch of the object:

[lsl]
// A misapplied example of busy-wait.
// This will not work in LSL!

integer g_flag = FALSE;

default  {
   state_entry()  {
      while (g_flag == FALSE);    // Wait for a touch.
                                  // g_flag is now TRUE.
      llSay (0, "I've been touched!");
   }

   touch_start (integer numDetected)  {
      g_flag = TRUE;
   }
}
[/lsl]

This protocol would require that, upon touch, program execution be taken from the middle of the state_entry() routine and given to touch_start(); whereupon, after touch_start() completes, execution would return to wherever state_entry() left off. This is how the interrupt-driven paradigm works. This just does not happen in LSL.

But notice: if this had worked, then I would have effectively coded the action of a touch*() event in the state_entry() event. This is likely not the most philosophically elegant of situations; nevertheless, as you will see later, it may be expedient to do exactly this. File that away while I make my current point: event code cannot be interrupted by other event code.

Now, the first stab at a working LSL program for the above is quite simple:

[lsl]
default  {
   touch_start (integer numDetected)  {
      llSay (0, "I've been touched!");
   }
}
[/lsl]

Of course, these examples are not quite equivalent. In the former implementation, the object would speak only once; in the latter, it will speak every time a group of touches (in the amount of numDetected) has been recognized.

If you really want to ensure that the object speaks only once as originally intended, then you can disable the announcement and/or change state. This can be coded in a few different ways depending on your personal style or, more likely, the arbitrary whim of your immediate supervisor. Notice how the following examples are functionally equivalent:

[lsl]
integer g_flag = FALSE;

default  {
   touch_start (integer numDetected)  {
      if (! g_flag)  {
         llSay (0, "I've been touched!");
         g_flag = TRUE;
      }
   }
}
[/lsl]
[lsl]
default  {
   touch_end (integer numDetected)  {
      llSay (0, "I've been touched!");
      state TOUCHED;
   }
}

// ------------------------------------------------

state TOUCHED  {
   state_entry()  {
   }
}
[/lsl]
[lsl]
default  {
   touch_end (integer numDetected)  {
      state TOUCHED;
   }

   state_exit()  {
      llSay (0, "I've been touched!");
   }
}

// ------------------------------------------------

state TOUCHED  {
   state_entry()  {
   }
}
[/lsl]
[lsl]
default  {
   touch_end (integer numDetected)  {
      state TOUCHED;
   }
}

// ------------------------------------------------

state TOUCHED  {
   state_entry()  {
      llSay (0, "I've been touched!");
   }
}
[/lsl]

The example with the state_exit() event I find particularly interesting; others might find it particularly obtuse if they haven’t used state_exit() before. Just as state_entry() is performed, if it is defined, upon state entry, state_exit() will be performed, if it is defined, upon transition to another state.

That example sets up a cascade of events:

  1. Wait for a touch;
  2. Explicitly request a change of state;
  3. Automatically call state_exit() in this state;
  4. Perform the state change;
  5. Automatically call state_entry() in the new state.

And in these last two examples, the action intended to be performed upon touch–that is, the annoucement “I’ve been touched!”–is not placed in the touch event itself, but downstream in the cascade. The placement of the announcement itself is not critical in this example, as long as it occurs after the touch. This distribution, this separation, this organizational distance of consequence from its cause is a useful technique in the design and topological sorting of event cascades in LSL.

Not even the delays implicit in certain function calls will free up resources to allow other events to play out. Calling llInstantMessage() will delay the script for two seconds; calling llEmail() will delay the script for 20; and you can specify your own explicit delay with llSleep().

Nothing at the LSL code level happens during these delays. The rest of the event routine is on hold, and no other code is executed. Event requests will be queued during these delays, but not serviced. Only when the delay finishes, and event code is resumed and completes execution, are any other events, waiting in the event queue, serviced.

Code these delays carefully, however. The event queue holds up to 64 requests, after which events cannot be queued and are therefore effectively ignored.

I’ll continue this discussion in a future blog entry, which I will likely entitle A Series of [Un]Requested Events.

Originally posted 21 Dec 2008

Learn the Language.

There are, it appears, three types of errors to be found in any computer program, and LSL gives everyone ample opportunity to create scads of each type:

  1. syntax errors;
  2. semantic errors;
  3. program logic errors.

Syntax is the grammar of the language; a statement in LSL is like a sentence. Variable names, operators, string or number literals, and function parameters must be in their proper place within the statement. Common syntax errors in LSL include, for instance, missing terminating semicolons and unbalanced parentheses, braces, or double-quote marks.

Semantic errors occur when the syntax is correct but the meaning of the token or expression is disallowed.  These can be subtle and may require a bit more deduction from your little grey cells.

An easily apparent semantic error is the following global variable declaration:
[lsl]
integer x = "Hello!" ;
[/lsl]
Most grammars are general about the syntax; this statement’s syntax is probably checked as:
[lsl]
type-name  identifier  =  literal  ;
[/lsl]
where “identifier” is a token word to be used as a new variable name, and “literal” is a class of tokens usually encompassing explicit strings, numbers, vectors, and rotations.  Remember that global variables cannot be initialized with function calls or by expressions with operators.

So the above nonsensical statement passes the syntax check.  Then the compiler must check the meaning of the tokens encountered.  Was “x” defined before this in scope?  If so, it’s a semantic error! Even then, a string cannot be assigned to an integer variable in LSL, so compilation ends with a semantic error flagged when the compiler reads the string literal.

I’ll not dwell on logic errors here, except to say that those errors are discovered when compilation completes without error but the program doesn’t do what you expect.  These errors can be far subtler than semantic errors, and that’s when the real debugging starts!

So why have I bothered to state what most people learn in the first few days of their programming life?  Because some people don’t.

Syntax errors are by far the easiest of these three classes of errors to identify and correct, and yet so many people post–in group chat, no less–a line of code with an obvious grammatical error and then ask, “What’s wrong with this?”

Generally speaking, if the compilation reports a syntax error, then you should seriously consider the possibility that you have a syntax error.  The error is at or just before the reported line and position.  Unbalanced braces may not be caught until the start of the next event code, but even that is a clue to look for that specific error in the previous routine.

If you cannot identify a syntax error, then you don’t understand the very basics of the language.  Learn it.

Ideally there should be no reason to ask in group chat for others to point out a simple grammatical error. It shows that you’re not thinking and you’re not looking, and instead you are wasting other people’s time. Contrary to public wisdom–now there’s an oxymoron if I ever heard one!–there are such things as stupid questions.

That’s the ideal, of course.  And there’s one exception which we have all encountered.  There are times when you are told there’s a syntax error, and you look at it, and you look at it, and you look at it, and you just can’t see it, even though you’re told it’s right in front of your face.  If this hasn’t happened to you at least once in your life, you’re lying.

Then, and not before, is the time to ask for help.  Whomever you ask, whether in private or in group chat, don’t just ask what’s wrong.  Give them more information, including the wording of the error message.

Logic errors can be very difficult to find; semantic errors much less so.  But if you need help finding a simple syntax error, then be prepared to hear an [un]intentional laugh or two.  It may not be a nice thing to do, but they may not be unjustified.

Originally posted 05 Oct 2008
Last edited 06 Oct 2008

Undocumented Feature in llListen()

Don’tcha just love the phrase “undocumented feature”? I think I shall expound upon this term at a later date. Right now, I want to concentrate on a recent experiment of mine.

This week in the CSMS group, a question arose about the consequences of calling llListen() more than once in the same state, changing only the name parameter, without removing the previous listen first. The scripter intended to toggle listening to one person and then the other.

My initial response was (1) that you wouldn’t stop listening to the previous person, and (2) it’s bad form not to remove a listen you don’t want anyway.

From the explanation in the wiki, I had also assumed that, if you don’t bother removing any listens, each new llListen() call would register a new listen callback with a unique handle, resulting in “hearing” one person’s line of chat more than once.

Since I haven’t coded that particular scenario before, I decided to test it out. I began with two scripts for three objects: “George” and “Martha”, who do the talking, and “Florence” who does the listening.

George and Martha get the same script. When they speak, they are identified by their names, which are the names of their objects:
[lsl]
CHANNEL = -1234567;

// ====================================================

default {
state_entry() {
llSay (CHANNEL, "Starting up.") ;
llSetTimerEvent (10);
}

// —————————————————-

timer() {
llSay (CHANNEL, "Hello again.") ;
}
}
[/lsl]
The output, as you can tell, is George or Martha speaking one line of chat every 10 seconds. Here’s George speaking on that channel:

George: Starting up.
George: Hello again.
George: Hello again.
George: Hello again.

…and so on. How Martha’s side of the discussion differs from this is left to the reader as an exercise.

To test my hearing against George and Martha, I’m going to enable them in sequence with each touch to Florence:
[lsl]
integer CHANNEL = -1234567;
integer toggle = 0;
integer handle = 0;

// ====================================================

default {

touch_start (integer numDetected) {

string who = "George" ;
if (toggle) who = "Martha" ;

// A call to llListenRemove() really should go here.
handle = llListen (CHANNEL, who, "", "") ;
llOwnerSay ("Now listening to " + who +
" using handle #" + (string) handle + ".") ;

toggle = (toggle + 1) & 1;
}

// —————————————————-

listen (integer channel, string name, key id,
string message) {
llOwnerSay (name + " says: " + message) ;
}
}
[/lsl]
I should note here that this is a script not intended for production, and I usually isolate myself during tests like these, so I haven’t bothered with a loop calling the llDetected*() functions.

On the first touch to Florence, she begins to listen to George.

Florence: Now listening to George using handle #1.
Florence: George: Hello again.
Florence: George: Hello again.
Florence: George: Hello again.

On the second touch, she also listens to Martha. So far the output is:

Florence: George: Hello again.
Florence: George: Hello again.
Florence: Now listening to Martha using handle #2.
Florence: George: Hello again.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: Martha: Hello again.

Then I touch Florence once more. (By this time, Florence should have slapped me in the muzzle, but that’s another matter.) I ask Florence thereby to register another listen to George. I expect to hear:

Florence: George: Hello again.
Florence: Now listening to George using handle #3.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: George: Hello again.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: George: Hello again.
Florence: Martha: Hello again.

Instead I hear:

Florence: George: Hello again.
Florence: Now listening to George using handle #1.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: Martha: Hello again.

Clearly the second llListen() request for George has not been recognized. No, wait, that’s not right; the call to listen to George, using the identical parameters as the previous listen to George, has returned the previously registered handle.
I kept touching Florence to no avail:

Florence: George: Hello again.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: Now listening to Martha using handle #2.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: Martha: Hello again.
Florence: Now listening to George using handle #1.
Florence: George: Hello again.
Florence: Martha: Hello again.
Florence: George: Hello again.
Florence: Martha: Hello again.

I can now say with confidence that previously registered and active calls to llListen() using the same parameters are not re-registered. This would make sense for efficiency; after all, does it really make sense to listen twice to the same object on the same channel?

OK where does it say that in the wiki? I don’t believe it does.

The argument could be made that this has no need to be in the wiki, for you shouldn’t call llListen() repeatedly in this manner without first calling llListenRemove() first. Better still, you can set up one listen request, then move the filter for the object’s name to the event code:
[lsl]
integer CHANNEL = -1234567;
integer toggle = 0;

// ====================================================

default {
state_entry () {
llListen (CHANNEL, "", "", "") ;
llOwnerSay ("Now listening to George.") ;
}

// —————————————————-

touch_start (integer numDetected) {
toggle = (toggle + 1) & 1;
string who = "George" ;
if (toggle) who = "Martha" ;
llOwnerSay ("Now listening to " + who + ".") ;
}

// —————————————————-

listen (integer channel, string name, key id,
string message) {

if ((name == "George") && (toggle == 0))
llOwnerSay ("George says: " + message) ;

else if ((name == "Martha") && (toggle == 1))
llOwnerSay ("Martha says: " + message) ;
}
[/lsl]
And the result is a true listening toggle between objects in the same state:

Florence: Now listening to George.
Florence: George says: Hello again.
Florence: George says: Hello again.
Florence: George says: Hello again.
Florence: Now listening to Martha.
Florence: Martha says: Hello again.
Florence: Martha says: Hello again.
Florence: Now listening to George.
Florence: George says: Hello again.
Florence: George says: Hello again.

Though outside the scope of this blog entry, you could also use different states to control Florence’s selective hearing. That solution, though, works best in very simple programs, or in scripts that have been split off to accommodate a separate user interface. The old adage is true: within every small program, there’s a much larger program trying to get out.

Originally posted 29 Aug 2008
Last edited 30 Aug 2008

We Need a Complete Language Reference for LSL

For more than a year, I’ve been a member of the fine group College of Scripting, Music, and Science (hereafter CSMS), formed to support those learning the Linden Scripting Language (LSL). It’s a fun group that has gotten back on track after being inundated with script-beggars for the last several months.

People in the group are asking some darn good questions about the mechanics of the language. One such question this week dealt with the registration of llListen() calls. I had part of the answer and decided to test what I couldn’t answer. In fact, if I get my act together, I should be discussing that question in the next blog entry.

My tests produced a surprising result: though I was able to invalidate another’s hypothesis, I was not able to validate my own. I was under the impression that it worked differently. I checked the documentation again. Well, according to my interpretation of what’s there, I should have been right. (Of course!)

But what is the documentation? Ah, it’s an online wiki! That’s just like group chat where people are under the impression that a million heads are always better than one.

I put it to you that, when it comes to computer language design and specification, even two heads may not be better than one. LL should at least consider restricting wiki entry and modification to the acknowledged and authorized experts who design, implement, and maintain the language: LL employees.

But wait, there’s more! There’s not just one wiki, there are three! The other two are linked from the LSL Portal page (the official LSL wiki), and they appear to be near-mirror images of each other. The kicker is, they’re better wikis than the official one. They give you far more useful information, even though some of that information is now out of date. Because they’re not official, there’s no incentive to maintain them.

So when I need to look up an aspect of LSL, what do I do? I necessarily look in all three. That seems to be the only way to get any rounded information I can probably rely on. And yet all three of them are woefully incomplete. When I can’t find what I’m looking for, then I must devise tests to see what I can rely on. Hey, testing is inevitable and often fun, but the basic specs should be documented fully.

Linden Labs must produce a complete and authorized reference for the Linden Scripting Language. It’s fine if they want to keep the official reference online, but they must take it out of the open, collaborative paradigm and keep its development in-house.

And if they don’t want to make this a priority, then they must authorize another writer to produce it. I doubt I’m the best person for that, but I’m sure there are others chomping at the bit.

Oh, and before you publish the first edition, add state variables to the language, please.

Originally posted 29 Aug 2008
Last edited 30 Aug 2008

Template for Agent Detection in Events

There are three questions concerning the nature of events which a scripter should answer to code the event properly:

  1. How is the capture of the event set up or requested?
  2. Exactly how is the event triggered?
  3. What information is available to the scripter when the event is triggered?

Well I don’t know about you, but I’m getting really flustered seeing examples of LSL on the wiki and other places for the detection events–touch, collision, and sensor–where the detected agent is identified only by a subscript of zero:
[lsl]
touch_start (integer numDetected) {
key oneAgent = llDetectedKey(0);
// Do something with the agent’s key here.
}
[/lsl]
Yes, this will work to reserve data about one agent who has triggered the event. After all, the event will not fire if there hasn’t been at least one agent detected, and the indexes (actually offsets) for the agents start at zero. And I understand the need for simplicity when the focus of the discussion is the action that results from the detection, not the detection itself.

But the touch and collision events are able to sense more than one agent who has been detected since the last time the event was triggered. (Sensor detects only at the time of the request.) That’s the purpose of the formal parameter in the event header, which here I have named numDetected. This parameter answers the third question above: you are given the number of agents who have touched the object since the last trigger in the same state, and they are identified by the subscripts zero through (numDetected - 1).

Now, oftentimes there is only one avatar around to touch or collide with an object.  Even if there is more than one av around, usually only one av does the touching within the space of a full second–far enough time for the system to react to a touch event. And even if more than one av is touching an object close to the same time, many scripted reactions don’t need to know how many people touched the object. In this last case, you don’t even need the llDetected*() function calls.

But you cannot rely on isolated touches or collisions.  Attend a class at your favorite virtual university, for instance; the first thing to happen is the distribution of class materials, usually performed by having the students touch an object, each of whom should then receive the materials in the object’s inventory. When there are 30-plus students in the class, all of whom are touching the object at about the same time, the chances become good that numDetected will be greater than 1.

Assuming you don’t want to miss anyone in the process, you’ll need to use a loop. Here is the frame I use for any production-oriented detection-event code, with a line of example action:
[lsl]
touch_start (integer numDetected) {
integer av;

for (av=0; av < numDetected; ++av) {
llSay (0, llDetectedName(av) + " has touched me.");
}
}
[/lsl]
A common restriction to the above is to perform an action only when the object’s owner is detected.  In this scenario, other agents are usually ignored. I augment the template as follows:
[lsl]
touch_start (integer numDetected) {
integer av;

for (av=0; av < numDetected; ++av) {
if (llDetectedKey(av) == llGetOwner()) {
// Do your thing here.

// No need to check for anyone else.
return;
}
}
}
[/lsl]
If the code the owner executes is substantial, then for readability I might take a less structured approach to recognize him, as below. In school I was taught all the good habits of structured programming in the C-family languages: no gotos, breaks (outside of switch statements), or multiple exits from functions; then I got out into the real world, and grew up. It all comes down to some very old and sage advice for any discipline: know what the rules are before you try to bend or break them.
[lsl]
touch_start (integer numDetected) {
integer av;
key owner = llGetOwner() ;
for (av=0; av < numDetected; ++av)
if (llDetectedKey(av) == owner) jump allowed;

// Owner did not touch object; just leave.
return;

// Only the owner gets to execute the following code:
@allowed;
// Lots of code goes here.
}
[/lsl]
And while we’re here, let’s augment this for the case where touch access would be allowed by the owner and by members of the group to which the object is assigned. This particular version doesn’t discriminate between these choices, and so the llDetected*() functions would not apply. And I am given to understand than it’s best to employ separate targets for separate jumps–something about one of those long-standing LSL bugs.
[lsl]
touch_start (integer numDetected) {
integer av;
key owner = llGetOwner() ;

for (av=0; av < numDetected; ++av) {
if (llDetectedKey (av) == owner) jump alphaAvatar;
if (llDetectedGroup (av)) jump beta_Avatar;
}
return;

@alphaAvatar;
@beta_Avatar;
// Lots of code goes here.
}
[/lsl]

Certainly an even better, structured version of this would employ a user-written function, which in turn would allow us to use the llDetected*() functions. Notice that you can use the llDetected*() functions in a subroutine called by the detection events.

[lsl]
Authorized_Action (integer agent) {
key who = llDetectedKey (av) ;
// …et cetera.
}

// —————————————————

default {
touch_start (integer numDetected) {
integer av;
key owner = llGetOwner() ;

for (av=0; av < numDetected; ++av) {
if ((llDetectedKey (av) == owner) ||
llDetectedGroup (av)) {

Authorized_Action (av) ;

// Use if identification of the avatar is unnecessary:
return ;
}
}
}
}
[/lsl]

Whew! I think that’s enough for now.

Certainly I cannot be the only person who has developed this simple technique. But I honestly have never seen it posted. Now it is!

Originally posted 22 Aug 2008
Last edited 26 Jan 2009