Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Best way to send lists to child prims?

Ceera Murakami
Texture Artist / Builder
Join date: 9 Sep 2005
Posts: 7,750
07-21-2009 12:11
What is the best way to send a list to child prims in a linkset?

I have three things I need to send from the root prim to multiple child prims, so scripts in the child prims can act on that data. There is a vector for the scale of the root prim, a vector for the position of the root prim, and a float for the hollow of the root prim.

By using the following code in the root prim, I can make a list that works fine in the root:

default
{
touch_start(integer total_number)
{
list params = llGetPrimitiveParams([PRIM_TYPE, PRIM_SIZE, PRIM_POSITION]);
llSay(0, "Param List is " + (string)params);
llSay(0, "Hollow is set to " + (string)llList2Float((list)params,3));
llSay(0, "Size is " + (string)llList2Vector(params,7));
llSay(0, "Position is " + (string)llList2Vector(params,8));
llMessageLinked(LINK_SET, 3, (string)params, NULL_KEY); // send frame size
}
}

Index position 3 in the list is the float for my Hollow, position 7 is the vector for the size, and position 8 is the vector for the position. But when I use llMessageLinked, I have to typecast the list as a string, to send it to one or more child prims.

In the child prims, I have this code:

default
{
link_message(integer sender_number,integer number,string params,key id)
{
llSay(0, "Param List in child is " + params);
if(number == 3) { // import frame size
llSay(0, "Hollow in child is " + (string)llList2Float((list)params,3));
}
}
}

It says the full string just fine. But the atempt to parse out the float for the hollow gives a value of 0.00000.

I suspect I need to do something to parse the string of data back into List format? But what? Would that be llParseString2List ? The WIKI is unclear on how to typecast a list to string and bring it back again as a usable list.
_____________________
Sorry, LL won't let me tell you where I sell my textures and where I offer my services as a sim builder. Ask me in-world.
Innula Zenovka
Registered User
Join date: 20 Jun 2007
Posts: 1,825
07-21-2009 12:36
There may well be a better way, but I use the pair of functions here http://lslwiki.net/lslwiki/wakka.php?wakka=ExampleListConversion to turn my lists into strings and back again pretty painlessly.
Tali Rosca
Plywood Whisperer
Join date: 6 Feb 2007
Posts: 767
07-21-2009 12:47
While I haven't tested passing lists that way, I'd guess it should be
(float)llList2String((list)params,3)

Lists are strongly typed, and this one probably shows up as a list of strings. If you attempt to pick the wrong type out, it'll give you the default null value rather than cast. You need to pick out a string and do the cast explicitly.

ETA: I usually explicitly separate my data (typically with the $ sign), and then llParseList2String and pick it out from that with llList2String and a cast, but that's because I often gather the data from various places; not just a single list.
Darien Caldwell
Registered User
Join date: 12 Oct 2006
Posts: 3,127
07-21-2009 13:42
I always use llDumpList2String with something like "+" as the delimiter, and then use
llParseString2List or llParseStringKeepNulls to separate it back out on the receiving end, again using "+" to properly seperate it back out. Of course you must be sure your delimiter isn't used anywhere in your data.
_____________________
Tali Rosca
Plywood Whisperer
Join date: 6 Feb 2007
Posts: 767
07-21-2009 13:44
Ok, I just checked it, and it doesn't take kindly to casting a list that way. Casting to string just dumps all item in one run-on string. That can't be cast back, having no actual hint about where each element is. (I didn't know if casting to and from lists did something clever internally).
Easiest in this case is to use llDumpList2String, to put some separator char into your list, which you can then parse around with llParseString2List (or llParseStringKeepNulls).
So, for example:

llDumpList2String(params,"$";)
and then
paramList = llParseStringKeepNulls(params, ["$"], [])


ETA ...and I typed as Darien posted :-)
Ceera Murakami
Texture Artist / Builder
Join date: 9 Sep 2005
Posts: 7,750
07-21-2009 15:00
Cool! Many thanks, all. Later tonight I will try these ideas. Makes sense.
_____________________
Sorry, LL won't let me tell you where I sell my textures and where I offer my services as a sim builder. Ask me in-world.
Ceera Murakami
Texture Artist / Builder
Join date: 9 Sep 2005
Posts: 7,750
07-21-2009 16:36
Innula's method worked great!

Script in root prim (prim named "sendlist test";):
// Start script
string slc_sep = "#!$"; // funky name for clean namespaces

string list_2_string(list l) {
list result = [];
integer len = llGetListLength(l); // optimization
integer i;
for (i = 0; i < len; ++i) {
result += [llGetListEntryType(l, i)] + llList2List(l, i, i);
}
return llDumpList2String(result, slc_sep);
}

default
{
touch_start(integer total_number)
{
list params = llGetPrimitiveParams([PRIM_TYPE, PRIM_SIZE, PRIM_POSITION]); //get info
// Show what was collected
llSay(0, "Param List is " + (string)params);
llSay(0, "Hollow is set to " + (string)llList2Float((list)params,3));
llSay(0, "Size is " + (string)llList2Vector(params,7));
llSay(0, "Position is " + (string)llList2Vector(params,8));
// Parse and send
llMessageLinked(LINK_SET, 3, list_2_string(params), NULL_KEY); // send prim info
}
}
// End Script

Script in child prim (Named "Child Prim";):

// Start Script
string slc_sep = "#!$"; // funky name for clean namespaces

list string_2_list(string s) {
list l = [];
list result = [];
l = llParseStringKeepNulls(s, [slc_sep], []);
integer len = llGetListLength(l);
integer i;
for (i = 0; i < len; ++i) {
integer type = (integer) llList2String(l, i);
++i; // CLEVERNESS WARNING...BE VERY AFRAID
if (type == TYPE_INTEGER) result += (integer)llList2String(l, i);
else if (type == TYPE_FLOAT) result += (float)llList2String(l, i);
else if (type == TYPE_KEY) result += (key)llList2String(l, i);
else if (type == TYPE_VECTOR) result += (vector)llList2String(l, i);
else if (type == TYPE_ROTATION) result += (rotation)llList2String(l, i);
else result += llList2String(l, i);
}
return result;
}

default
{
link_message(integer sender_number,integer number,string message,key id)
{
llSay(0, "Param List in child is " + message); // What was sent
if(number == 3) { // get proot prim info
list params = string_2_list(message); // Parse it
// Display it
llSay(0, "Hollow is " + (string)llList2Float((list)params,3));
llSay(0, "Size is " + (string)llList2Vector(params,7));
llSay(0, "Position is " + (string)llList2Vector(params,8));
}
}
}
// End Script

The forum's stupid filters won't allow me to post the result, but it does work!
_____________________
Sorry, LL won't let me tell you where I sell my textures and where I offer my services as a sim builder. Ask me in-world.
Tali Rosca
Plywood Whisperer
Join date: 6 Feb 2007
Posts: 767
07-21-2009 16:50
Rather overkill if you actually know what types you're sending at which position in the list, though.
-And if you don't, you're unlikely to use the correct llList2... for that position anyway.

Notice how what the script is doing is reading what the type is, so it can cast the string to the correct type and insert it in the list:
[...bunches of stepping through lists and checking...]
if (type == TYPE_FLOAT) result += (float)llList2String(l, i);

Then, in your script, you know that you have to pick it out with a
llList2Float(params,3);

You might as well just parse the text with built-in llParseStringKeepNulls, rather than that extra function, and then just pick up
(float)llList2String(params,3);
That would save you bunches of code and list stepping in both scripts, making them both faster and more readable.
Innula Zenovka
Registered User
Join date: 20 Jun 2007
Posts: 1,825
07-21-2009 17:00
I agree it probably is overkill, which is why I wondered if there was a better way to do it.

I use it a lot, because it's so convenient; you can just drop those two functions into the top of the sender and receiver scripts and know that stuff's going to come out in the right type.
Tali Rosca
Plywood Whisperer
Join date: 6 Feb 2007
Posts: 767
07-21-2009 17:06
From: Innula Zenovka
I agree it probably is overkill, which is why I wondered if there was a better way to do it.

I use it a lot, because it's so convenient; you can just drop those two functions into the top of the sender and receiver scripts and know that stuff's going to come out in the right type.

...but you still need to know what those types are, to use the correct llList2... versions, which sort of negates the whole gain. It may be useful in situations where you truly do not know which types to expect and need to have your code react dynamically to it, but those are probably very rare.
Innula Zenovka
Registered User
Join date: 20 Jun 2007
Posts: 1,825
07-21-2009 17:16
Point taken, and I shall certainly try it this way in future.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
07-21-2009 18:54
since you only want three elements, and already know what their types will be, you could send just those with something like
llMessageLinked( LINK_ALL_OTHERS, 0,
llList2String( params, 3 ) + ";" +
llList2String( params, 7 ) + ";" +
llList2String( params, 8 ), "" );

and then catch them with
catch = llParseString2List( message, [";"], [] );

and use these to address them
(float)llList2String( catch, 0 )
(vector)llList2String( catch, 1 )
(vector)llList2String( catch, 2 )

or to send the whole list...
llMessageLinked( LINK_ALL_OTHERS, 0, llDumpList2String( params, ";" ), "" );

and catch similarly just the ones you need out of it.

catch = llParseString2List( message, [";"], [] );
using the similar setup to address them
(float)llList2String( catch, 3 )
(vector)llList2String( catch, 7 )
(vector)llList2String( catch, 8 )


";" should be plenty safe, since it's not going to be in any of the datatypes you'll use, and the cast from string to type should be safe since you'll know what to expect

if you'll be reusing your variables received more than once or twice you can save the casting call back to the original catch list, or to appropriate variables. such as
catch = [(float)llList2String( catch, 0 ), (vector)llList2String( catch, 1 ), (vector)llList2String( catch, 2 )];
or
hollow = (float)llList2String( catch, 0 );
etc...
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
07-22-2009 09:53
I agree with Void, for this case.

Innula's method is very handy, though -- a nice bit of code.
Ceera Murakami
Texture Artist / Builder
Join date: 9 Sep 2005
Posts: 7,750
07-22-2009 10:40
I'll probably switch to Void's method for this particular case, since the exact items and types for the list are definitely known, and it's less complex code. The first solution is gret for a general case, however.

Now I have another odd twist, but I think I'll make that a seperate thread... The difference in llGetPos and llSetPos between root prims and child prims...
_____________________
Sorry, LL won't let me tell you where I sell my textures and where I offer my services as a sim builder. Ask me in-world.
Ceera Murakami
Texture Artist / Builder
Join date: 9 Sep 2005
Posts: 7,750
Revised, simplified, and HUH?
07-25-2009 11:55
OK, I streamlined the code, and passing the values generally works now, with a few odd glitches. In this example, I want to pass the frame prim's position, the root prim's position, and a flag for weather or not the frame IS the root, to a child prim. (In a later step, I'll need to be able to change how I handle moving the child prim, based on weather or not the frame prim is the root.)

In the frame prim, I have:

vector g_LocalPos; //pos of frame prim
vector g_RootPos; //pos of Root prim
list CollectedData; //List to send
string DataString; //List parsed into string form
integer FrameRoot; //True if frame is root prim, else false

default
{
touch_start(integer total_number)
{
g_LocalPos = llGetLocalPos(); //get pos of frame prim
g_RootPos = llGetRootPosition(); //get pos of Root prim
if(g_LocalPos == g_RootPos){
llSay(0, "The Frame is the root";);
FrameRoot = TRUE;
} else {
llSay(0, "The Frame is NOT the root";);
FrameRoot = FALSE;
}
CollectedData = [g_LocalPos, g_RootPos, FrameRoot];
DataString = llDumpList2String(CollectedData,";";);
// Show what was collected
llSay(0, "Local Position of Frame Prim is " + (string)g_LocalPos);
llSay(0, "Region Position of Root Prim is " + (string)g_RootPos);
llSay(0, "FrameRoot is " + (string)FrameRoot);
llSay(0, "Sending : " + DataString);
// send

llMessageLinked(LINK_SET, 3, DataString, NULL_KEY); // send prim info
}
}


In the Child prim, I have:

vector g_LocalPos; //pos of frame prim
vector g_RootPos; //pos of Root prim
list CollectedData; //List received
string DataString; //Incoming message, that was parsed into string form
integer FrameRoot; //True if frame is root prim, else false

default
{
link_message(integer sender_number,integer number,string DataString,key id)
{
if(number == 3) { // get root prim info
vector params_a = llGetLocalPos(); //get info on this prim
llSay(0, "Local Position of A Prim is " + (string)params_a);
// Display and compare Frame prim invo vs Root
llSay(0, "Incoming DataString is " + DataString);
CollectedData = llParseString2List( DataString, [";"], [] );
g_LocalPos = llList2Vector( CollectedData, 0 );
g_RootPos = llList2Vector( CollectedData, 1 );
FrameRoot = llList2Integer( CollectedData, 2 );
llSay(0, "g_LocalPos is " + llList2String( CollectedData, 0 ));
llSay(0, "g_RootPos is " + llList2String( CollectedData, 1 ));
llSay(0, "FrameRoot is " + llList2String( CollectedData, 2 ));
if(FrameRoot == TRUE){
llSay(0, "The Frame is the root";);
} else {
llSay(0, "The Frame is NOT the root";);
}
}
}
}


Which gets this result:

[11:53] Reporter Prim (frame): The Frame is NOT the root
[11:53] Reporter Prim (frame): Local Position of Frame Prim is < 0.00000, 0.00000, -0.50000 >
[11:53] Reporter Prim (frame): Region Position of Root Prim is < 104.76440, 21.04572, 701.00000 >
[11:53] Reporter Prim (frame): FrameRoot is 0
[11:53] Reporter Prim (frame): Sending : < 0.000000, 0.000000, -0.500000 >;< 104.764397, 21.045717, 701.000000 >;0
[11:53] Child A: Local Position of A Prim is < -0.50000, 0.00000, -0.50000 >
[11:53] Child A: Incoming DataString is < 0.000000, 0.000000, -0.500000 >;< 104.764397, 21.045717, 701.000000 >;0
[11:53] Child A: g_LocalPos is < 0.000000, 0.000000, -0.500000 >
[11:53] Child A: g_RootPos is < 104.764397, 21.045717, 701.000000 >
[11:53] Child A: FrameRoot is 0
[11:53] Child A: The Frame is NOT the root


The "HUH?" here is, why is the vector that has been parsed to string form different than the original? It has 6 digits after the decimal, instead of 5? Is there any way to pass the data so the same values are had on both sides?

There also seems to be a rounding error. I've seen values that should be something like 120.42200 get translated as 120.422032 .
_____________________
Sorry, LL won't let me tell you where I sell my textures and where I offer my services as a sim builder. Ask me in-world.
Viktoria Dovgal
Join date: 29 Jul 2007
Posts: 3,593
07-25-2009 12:32
From: Ceera Murakami
The "HUH?" here is, why is the vector that has been parsed to string form different than the original? It has 6 digits after the decimal, instead of 5? Is there any way to pass the data so the same values are had on both sides?


It's just the way vector casting was implemented in LSL. It gives each of the vector's floats a "%.5f" formatting instead of the default "%f".

From: someone
There also seems to be a rounding error. I've seen values that should be something like 120.42200 get translated as 120.422032 .

That representation a side effect of running a float through the printf family of functions too.

If you want the extra decimal place, you can expand it out the hard way, something like

"<"+ (string)v.x + "," + (string)v.y + "," + (string)v.z + ">"

ETA again: llList2CSV([v]) and llDumpList2String([v], "";) will also give more precision than (string)v

(ETA: you can also take away from this that string representations of floats are always going to be approximations.)
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
07-25-2009 13:51
ETA: plus what Viktoria said.

From: Ceera Murakami
The "HUH?" here is, why is the vector that has been parsed to string form different than the original? It has 6 digits after the decimal, instead of 5? Is there any way to pass the data so the same values are had on both sides?

There also seems to be a rounding error. I've seen values that should be something like 120.42200 get translated as 120.422032 .

scripts use 32bit floats, which IIRC have 7.6 digits of accuracy (regardless of leading/trailing zeros, or decimal placement... so really only 7 for highest accuracy before rounding might kick in... which will affect the 7th digit so really only 6 digits of precision, if you have more than 7 digits (eg 1.2345678 * 1.0 = 1.234568).

then you have the other problem that some math operations will have long precisions in binary which will round (eg 2.0/3.0 = 0.666667)

and lastly I *think* but am not sure, that the server uses 64bit floats for calculating position, which would mean more small roundings errors when converted to 32bitfloats by the script (at least when it's got an extended number of digits)....

the good news is that anything over 5 digits of precision is not going to be visibly different on the viewer.
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Ceera Murakami
Texture Artist / Builder
Join date: 9 Sep 2005
Posts: 7,750
07-26-2009 19:48
By the way, I finally did get my door script to work the way I wanted it to. A huge thank you to all who helped here with some of the difficult (for me) communications issues between prims! I now have a nifty self-configuring sliding door script, that reconfigures for changed frame sizes, with no script edits or notecards needed. The scripts can be no-mod, and the owner cam still resize the door frame, change its hollow % value, link it to a build, or change the door control channel.

Found one interesting glitch. The "changed" event apparently is only triggered if the script is in the root prim. I had to add a manual reset button to issue a reset command, instead of using the "changed" state to automatically reset the script if the frame size or hollow were changed. Otherwise, the door broke when linked to a build.
_____________________
Sorry, LL won't let me tell you where I sell my textures and where I offer my services as a sim builder. Ask me in-world.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
07-26-2009 23:58
From: Ceera Murakami
Found one interesting glitch. The "changed" event apparently is only triggered if the script is in the root prim.

would you believe I never noticed that!? (and now I know why, change in behavior as of 5/6/08, for scale, stupid stupid stupid, who thought of that!?)

quick testing revealed

fires in all linked prims:
CHANGED_LINK
CHANGED_OWNER (tested with group deed)

fires in the root only:
CHANGED_TELEPORT
CHANGED_REGION
CHANGED_SCALE <--- wtf?

fires in the specific prim only:
CHANGED_INVENTORY
CHANGED_COLOR
CHANGED_SHAPE
CHANGED_TEXTURE

unable to properly test:
CHANGED_ALLOWED_DROP (presumably fires in specific prim only)
CHANGED_REGION_START (presumably root only)

ETA:
found the jira on it, added my vote
https://jira.secondlife.com/browse/SVC-2549
_____________________
|
| . "Cat-Like Typing Detected"
| . This post may contain errors in logic, spelling, and
| . grammar known to the SL populace to cause confusion
|
| - Please Use PHP tags when posting scripts/code, Thanks.
| - Can't See PHP or URL Tags Correctly? Check Out This Link...
| -
Ceera Murakami
Texture Artist / Builder
Join date: 9 Sep 2005
Posts: 7,750
07-27-2009 05:39
From: Void Singer
would you believe I never noticed that!? (and now I know why, change in behavior as of 5/6/08, for scale, stupid stupid stupid, who thought of that!?)

quick testing revealed...
fires in the root only:
...
CHANGED_SCALE <--- wtf?
...
ETA:
found the jira on it, added my vote
https://jira.secondlife.com/browse/SVC-2549
Thanks for that testing. Added my vote and comments.
_____________________
Sorry, LL won't let me tell you where I sell my textures and where I offer my services as a sim builder. Ask me in-world.