Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Proposal for a llWaitForMessage() function.

Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
11-30-2003 16:27
One thing I have noticed is lacking in LSL, is the ability to halt execution and wait for a message response. This can of course be done by leaving an event, and waiting for a message using the link_message event, but this breaks the execution flow. Given that LSL is much more efficient when a program is divided into many individual scripts, each communicating with each other using message passing, I believe a function to wait for a message is in order.

Here is what I propose:

list llWaitForMessage(integer sender, integer channel, string mesg, key id, float time_out);

Sets a filter for waiting for a link message. The first four parameter are much like the listen parameters, in that they set up a criteria for triggering a response. If a message is received that meets the specified criteria, a list is returned containing the data sent in the message. After time_out seconds, an empty list is returned instead. A time_out of 0.0 means to wait forever.


Example usage:

CODE

/////////////////// Script A //////////////////////
integer REQUEST_CHANNEL = 1;
integer RETURN_CHANNEL = 2;

SomeFunction() {
// Ask a different script to perform calculations
// on some data.
llMessageLinked(LINK_SET, REQUEST_CHANNEL, "some data", "");

// This waits for up to 5 seconds for a response.
list Response = llWaitForMessage(LINK_SET,
RETURN_CHANNEL, "", "", 5.0);

// Check to see if we timed out.
if (llGetListLength(Response) == 0) {
llSay(0, "Message timed out.");
return;
}

// Otherwise we can now use the return data.
integer Sender = llList2Integer(Response, 0);
integer Channel = llList2Integer(Response, 1);
string Message = llList2String(Response, 2);
key ID = llList2Key(Response, 3);

// (Do some work on return data here)
}


/////////////////// Script B //////////////////////
integer REQUEST_CHANNEL = 1;
integer RETURN_CHANNEL = 2;

link_message(integer sender, integer channel, string mesg, key id) {
// Wait for a request.
if (channel == REQUEST_CHANNEL) {
// (do some work on sent data here)

// Send back a response.
llMessageLinked(sender, RETURN_CHANNEL, "some return data", "");
}
}




If it is determined that the time between llMessageLinked and llWaitForMessage is not long enough in same cases (in that a message response might be sent back before the WaitForMessage filter is in place), then a new message function which combines the functions of both llMessageLinked and llWaitForMessage might be needed. (Although I think if llWaitForMessage is called directly after sending a message, it should be in place in time to catch the response).

Using this, we could create true libraries. We could then use a wrapper function to simulate an in-script function.

Example:

CODE

integer REQUEST_SEARCH_NOTECARD = 1;
integer RETURN_SEARCH_NOTECARD = 2;

integer SearchNotecard(string notecard, string pattern) {
// This function asks the script SearchNotecard.lsl
// to look for a string in a notecard.
llMessageLinked(LINK_SET, REQUEST_SEARCH_NOTECARD,
llList2CSV([notecard, pattern]), "");
// Wait for the script to finish searching the notecard.
// (wait forever, or until it is done)
list Response = llWaitForMessage(LINK_SET,
RETURN_SEARCH_NOTECARD, "", "", 0.0);

// Use the string parameter as the return value.
string FoundPattern = llList2String(Response, 2);

// Return true if the pattern was found, false if not.
if (FoundPattern == "yes")
return TRUE;

return FALSE;
}


// Use of the wrapper function.
SomeFunction() {
// Look for the word "test" in the notecard "Some Note"
if (SearchNotecard("Some Note", "test") == TRUE)
llSay(0, "Found match!");
else
llSay(0, "No match found.");
}




This is just one example of the many uses of a wait for message function. The utility of such a function is huge.

Xylor
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
11-30-2003 17:17
::gets out pom poms::

::does cheer::

YES! YES! YES!

WE WANT
L-L-W-A-I-T-F-O-R-M-E... oh fudge it. :D

I want this!!! YES! ANYTHING to help encapsulate things!! PLEASE!! The sim could even use the extra time slices for other scripts!

==Chris
Oedefe Silverman
Registered User
Join date: 3 Oct 2003
Posts: 54
11-30-2003 18:20
bump!

with a slight change...

time_out == 0: don't wait; queries for message existance. If you want a super-large timeout, use a super-large value, such as 60 * 60 * 365 * 100 (100 yrs). Hmmm, I wonder how long my scripted objects will live :)
Mezzanine Peregrine
Senior Member
Join date: 14 Nov 2003
Posts: 113
11-30-2003 20:39
Adding a timeout to a wait is always useful.

This may even free up CPU cycles since a 'waiting' script can just be frozen until messages are recieved...
Rhysling Greenacre
Registered User
Join date: 15 Nov 2003
Posts: 132
12-02-2003 02:08
Correct me if I'm wrong, but that code can be rewritten to work fine by moving the message processing code into the link_message handler. If you did that the program wouldn't be wasting ticks while waiting for link_message event, so the argument that CPU ticks would be saved isn't true.

That being said; I'm not against this at all! Blocking I/O calls would simplify scripts a lot. It would be a lot easier to call code from other modules by sending a linked message. I'd like to see something similar with regular chat messages.
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
12-02-2003 03:23
Yes, those are simple examples and can easily be done without a llWaitForMessage function.

From: someone
This can of course be done by leaving an event, and waiting for a message using the link_message event, but this breaks the execution flow.


Using this method becomes very unwieldy when a function must be split many times to wait for a result from a seperate script. Perhaps another example is in order:

CODE

// This script uses an imaginary matrix library.
// It treats a list as a matrix and performs operations
// on these matrices. In order to do this, it needs
// to know the dimensions of the matrix. The
// dimensions are kept in elements 0 and 1 of
// the matrix list. These are also used to determine
// how many elements following the dimensions
// header are part of the matrix, so that more than
// one matrix can be held in a list.

// Example Matrix:
// [ 0 4 1; 3 4 1; 4 5 8]
// This 3x3 matrix would be held in a list as follows:
// [3, 3, 0, 4, 1, 3, 4, 1, 4, 5, 8]
// Where the 0th and 1st elements, (3, 3), tells
// the dimensions of the matrix.

// Matrix library constants.
// Note: Since a llMessageLinked does not
// trigger a link_message event in the calling
// script, you can usually use the same channel
// as a return channel.
integer MATRIX_ADD = 1
integer MATRIX_MULTIPLY = 2;
integer MATRIX_SCALAR_MULTIPLY = 3;
integer MATRIX_DETERMINATE = 4;
integer MATRIX_REDUCED_ROW_ECHELON = 5;
integer MATRIX_INVERSE = 6;
integer MATRIX_EYE = 7;
integer MATRIX_TRANSPOSE = 8;

// Response element indices.
integer RESPONSE_SENDER = 0;
integer RESPONSE_CHANNEL = 1;
integer RESPONSE_STRING = 2;
integer RESPONSE_KEY = 3;

// Wrapper functions.
list MatrixAdd(list a, list b) {
// Since the dimension information is embedded
// in each list, we can safely just combine the two
// lists into one list and send this list as our
// parameter. The matrix library will split these
// back into two matrices on its end.
llMessageLinked(LINK_SET, MATRIX_ADD,
llList2CSV(a + b), "");

// Wait forever, or until done.
list Response = llWaitForMessage(LINK_SET, MATRIX_ADD,
"", "", 0.0);

// Take the string parameter.
string Result = llList2String(Response, RESPONSE_STRING);

// Convert to a list and send it back.
return llCVS2List(Result);
}

// Using the same general procedure, assume the
// following help functions:

list MatrixMultiply(list a, list b);
list MatrixScalarMultiply(list a, float scalar);
float MatrixDeterminate(list a);
list MatrixReducedRowEchelon(list a);
list MatrixInverse(list a);
list MatrixEye(integer num_diag);
list MatrixTranspose(list a);

// Now, lets use the matrix library to perform some
// useful things.
float TetrahedronVolume(vector a, vector b,
vector c, vector d) {
// This function computes the volume of a tetrahedron
// given by the 4 vertices a, b, c, and d.
list Matrix = [ 4, 4,
a.x, a.y, a.z, 1,
b.x, b.y, b.z, 1,
c.x, c.y, c.z, 1,
d.x, d.y, d.z, 1 ];

// Ask the matrix library for the determinate.
float Det = MatrixDeterminate(Matrix);
// Return the volume.
return llFabs( Det / 6 );
}

ConsumerPreferenceModel() {
// This is an example of a real-world problem
// which can be solved using the matrix library.

// Problem:
// Two competing companies offer cable television
// service to a city of 100,000 households.
// Company A now has 15,000 subscribers and
// Company B has 20,000 subscribers.
// (65,000 don't have cable).

// The matrix representing the given transition
// probabilities is:
list ProbMatrix = [3, 3,
//////// From ////////
// A B None
0.70, 0.15, 0.15, // A \
0.20, 0.80, 0.15, // B | To
0.10, 0.05, 0.70 ];// None /

// The state matrix representing the current
// population in each of the three states is:
list StateMatrix = [3, 1,
15000, // A
20000, // B
65000 ]; // None

// Now lets find the state matrix representing
// the population in each of the three states after
// 5 years.
integer Year;
for (Year = 1; Year <= 5; Year++) {
StateMatrix = MatrixMultiply(ProbMatrix, StateMatrix);
}

// StateMatrix can now tell us how many subscribers
// each company has. It should now read:
// [ 3, 1,
// 32411, -- A
// 43812, -- B
// 23777] -- None
}



All of these can, of course, be done without llWaitForMessage, but they require using global variables and saving states between message passings. In cases when multiple library calls are used in one function, this saving of states and waiting for a link_message to continue a calculation can become very messy.

With the availablility of llWaitForMessage, scripters could start writing more modular library scripts that others could just 'plug' in, and use with very little effort. This would greatly increase scripting efficiently, and allow even a novice scripter to use an advanced library to do complex operations. Some other possiblities are Regex libraries, ease of initializing rezzed objects, extended vector math helper functions, etc etc the list goes on.

Xylor
Mezzanine Peregrine
Senior Member
Join date: 14 Nov 2003
Posts: 113
12-02-2003 03:36
Isn't that what states are for?

I mean, couldnt the code be nicely segmented up... since technically, you are in a wait state...

I mean... a clever use of the state system already present in SL could make things a little nicer...

HOWEVER I think the ability to inline a wait is a good way to clarify code, as you mentioned above.

But perhaps we are looking at the wrong problem and solution pair then?

Perhaps we need something more powerful, such as the ability to call 'library' functions that live in another script object. Woudln't that be nifty?

IE,

ResultList = CallFunction("DoMatrixOp",ParameterList);

--> SL would look in the current object's inventory, and call the DoMatrixOp function feeding ParameterList as the parms, on any script object that had a function called DoMatrixOp..

Ie, all 'externally callable functions' would have to return a List, and take a List as their only parm.

Then we could really, REALLY start building some clean scripts.

I have a feeling it would be faster and less message-queue-spammy than the current system of link messages anyway.

Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
12-02-2003 03:55
From: someone
Isn't that what states are for?


Not really. A state change is mostly useful for switching which events you are handling. You still have to save all function variables into global variables, and read them back once you are ready to continue working on a segment of the function. In limited instances you could have a different link_message event handler in another state with another 'piece' of the function, but even this can get out of hand very quickly. Having more than one library function call in your script can cause you to have a LOT of states, and forget trying to read the code to figure out whats going on ^_~.

state switching isn't really a good solution. Might as well just read your current variable state info and use the same link_message handler. Then, at least, you don't have to worry about losing other event handlers while you are switched out of your default state doing a calculation. (Example: Someone touches your script while it is doing a state-switch based calculation. The touch wouldn't be queued since the current state wouldn't have a touch event registered).

From: someone
Perhaps we need something more powerful, such as the ability to call 'library' functions that live in another script object. Woudln't that be nifty?


That all I really want ^_~. If someone can come up with a good solution, that would be great. But until then, I believe this would be an acceptable way to accomplish this.

Xylor
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
12-02-2003 04:03
From: someone
IE,

ResultList = CallFunction("DoMatrixOp",ParameterList);

--> SL would look in the current object's inventory, and call the DoMatrixOp function feeding ParameterList as the parms, on any script object that had a function called DoMatrixOp..


I could live with that.

From: someone
I have a feeling it would be faster and less message-queue-spammy than the current system of link messages anyway.


It would actually take longer to process than the llWaitForMessage() approach, howver it would eliminate the need for helper functions (except for those who want a transparent interface and would make a quick 'normal' function to call CallFunction).

It would require the server to search through the object's inventory for the object with that name. That is the extra overhead. After that, it would basically send a message to the library script (trigger, call function, whatever you call it it boils down to pretty much the same thing).

However, I would be happy for either of these methods to be implemented. Anything that works, is fine by me!

Xylor
Mezzanine Peregrine
Senior Member
Join date: 14 Nov 2003
Posts: 113
12-02-2003 12:35
Maybe to eliminate the search-thru time, when the object is compiled, it searches through the inventory for that link or something, not during runtime.

It would mean the library has to be there before the thing that uses it is saved and compiled, but thats no biggie.
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
12-13-2003 11:02
*bump*

This would really help a lot of my current projects ^_~. Any Lindens have a reason this could not be implemented? =D

Xylor
Xylor Baysklef
Scripting Addict
Join date: 4 May 2003
Posts: 109
12-30-2003 07:41
*puppy dog eyes*

Please? =)

Xylor
si Money
The nice demon.
Join date: 21 May 2003
Posts: 477
12-30-2003 12:20
With all of the many things LSL really needs that are difficult to work around, sometimes nearly impossible, I think this is more a small convenience feature, and is not all that important.

It's very easy to redesign the flow of your script to accomodate this behavior. States are actually not a bad way to do it at all.
_____________________
Like a soul without a mind
In a body without a heart
I'm missing every part

-- Progress --
Catherine Omega: Yes, but lots of stuff isn't listed. "Making UI harder to use than ever" and "removing all the necessary status icons" things.... there's nothing like that in the release notes. :)
Carnildo Greenacre
Flight Engineer
Join date: 15 Nov 2003
Posts: 1,044
12-30-2003 15:05
Easy? What about situations where the 16k limit forces you to put a function in a separate script? How about if that function is called from two separate locations within a loop? How would you handle that without llWaitForMessage or llCallFunction?
_____________________
perl -le '$_ = 1; (1 x $_) !~ /^(11+)\1+$/ && print while $_++;'
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
12-30-2003 15:18
From: someone
Originally posted by si Money
With all of the many things LSL really needs that are difficult to work around, sometimes nearly impossible, I think this is more a small convenience feature, and is not all that important.

It's very easy to redesign the flow of your script to accomodate this behavior. States are actually not a bad way to do it at all.


Well. No.

Yes, I agree with your statement that states can solve this problem, but using more then a few positively DESTROYS readability. This would not be a convinance feature IMO, because it allows great amounts of encapsulation, hundredfold more then states and even event handles within the same state would.

Global variables suck. I utterly *loathe* storing data in global variables, because there is no clean-up for them, the memory that the globals use (for initilization) is simply gone all throughout the script, never ever to be reclaimed again. These variables are needed for state2state transfer of data, and even event2event transfer of data!

I make an effort to use as many local variables that replace globals as possible, because they are completely cleaned up after theyre not needed. Having an llWaitForMessage() function would really help that cause, because you can:

1. Encapsulate - Dont need to save anything to a global when your out of your event or loop.

2. Easily program in external functions. This is actually the *hardest* thing to code around. Im currently learning Java, and the community there is drowning in encapsulation and its totally elegant. With this feature you can create scripts that act as though theyre being called like a function!

I would totally dig this function being implimented. There are uses for it that can completely counteract the 'slowness' of LSL. I would even go for it more then I would an external function calling implimentation, since the latter would cause delays in script execution.

Well... my 2 Yen

==Chris
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
07-07-2006 21:57
For people who are still wanting this, take a look at the SynchronousDescReturn module I posted to the wiki library.
==Chris
_____________________
October 3rd is the Day Against DRM (Digital Restrictions Management), learn more at http://www.defectivebydesign.org/what_is_drm