Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

Discussion: How to make buttons without a script in each button.

Obsidian Stormwind
Second Life Resident
Join date: 13 Nov 2004
Posts: 16
11-02-2005 05:44
Hello,

I've noticed that many machines in Second Life use a separate script for each prim that acts as a button in an object, usually using a MessageLinked() function to send a message to the script in the root prim. However, seeing how this adds to the total script count in a SIM, I thought I'd post a way to reduce the need for these types of scripts.

Take a look at the code snippet below:


CODE
default
{
touch_start(integer total_number)
{
if(llDetectedLinkName(llDetectedLinkNumber(0)) == "Button1")
{
// Whatever Button1 does...
}
else if(llDetectedLinkName(llDetectedLinkNumber(0)) == "Button2")
{
// Whatever Button2 does...
}
// And so on...
}
}


This would be code stored in the root prim. As long as the individual prims for the buttons are uniquely named ("Button1" and "Button2" in this example), then one can contain all the scripting needed in the primary script and not need to have a child script for each button.

Seeing how most simple vendors scripts use 3 scripts, one for the primary script in the root prim and one for each button ("Next" and "Previous";), this technique would reduce the number of scripts by 1/3rd. And for vendors with a whole lot of buttons? I've seen one vendor with at least 36 scrtipts. If one takes out the scripts for buttons one would reduce the number of scripts by at least 26.

Let me know if you can see any improvements, or better way to do this code. :) I'm always looking for more efficient ways to write LSL scripts.

Obsidian Stormwind
Nada Epoch
The Librarian
Join date: 4 Nov 2002
Posts: 1,423
original thread
11-02-2005 06:54
/15/df/69401/1.html
_____________________
i've got nothing. ;)
Christopher Omega
Oxymoron
Join date: 28 Mar 2003
Posts: 1,828
11-02-2005 08:00
From: Obsidian Stormwind

CODE
default
{
touch_start(integer total_number)
{
if(llDetectedLinkName(llDetectedLinkNumber(0)) == "Button1")
{
// Whatever Button1 does...
}
else if(llDetectedLinkName(llDetectedLinkNumber(0)) == "Button2")
{
// Whatever Button2 does...
}
// And so on...
}
}


Im pretty sure you're referring to llGetLinkName instead of llDetectedLinkName there... but you present a very good idea.
==Chris
_____________________
October 3rd is the Day Against DRM (Digital Restrictions Management), learn more at http://www.defectivebydesign.org/what_is_drm
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
11-02-2005 08:34
I'm not sure that the "active script count" is all that meaningful a thing to worry about. A script that's just sitting there waiting for a touch isn't using up CPU time (or if it is, that's a bug in SL).
Kenn Nilsson
AeonVox
Join date: 24 May 2005
Posts: 897
11-02-2005 08:39
The script count isn't as big of a deal as the time spent processing scripts (which often depends on the number of currently active functions)...

Good idea though.
_____________________
--AeonVox--

Computer games don't affect kids; I mean if Pac-Man affected us as kids, we'd all be running around in darkened rooms chasing ghosts, eating magic pills, and listening to repetitive, addictive, electronic music.
Aliasi Stonebender
Return of Catbread
Join date: 30 Jan 2005
Posts: 1,858
11-02-2005 09:32
It does, however, have a very real usability improvement, in that havign it all in one script and just worrying about naming the buttons is easier than remembering to drop a scrip in each and every one.
_____________________
Red Mary says, softly, “How a man grows aggressive when his enemy displays propriety. He thinks: I will use this good behavior to enforce my advantage over her. Is it any wonder people hold good behavior in such disregard?”
Anything Surplus Home to the "Nuke the Crap Out of..." series of games and other stuff
October Brotherhood
Registered User
Join date: 24 Jun 2005
Posts: 70
11-02-2005 09:38
i believe this will simplify my life tonight.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
11-02-2005 13:15
Point.

For other "drop a script in every prim" situations, I've got a script I use called "Obedient Child" that automatically updates itself in all your scripted prims when you drop it in the root (it also does version checking, so you don't accidentally clobber a newer one with an older). It's pretty fugly at the moment, but when I get a Tuit I'll clean it up and post.
Iron Perth
Registered User
Join date: 9 Mar 2005
Posts: 802
11-02-2005 15:05
From: Argent Stonecutter
I'm not sure that the "active script count" is all that meaningful a thing to worry about. A script that's just sitting there waiting for a touch isn't using up CPU time (or if it is, that's a bug in SL).


I'd actually like confirmation on this. Every script consumes 16K, as far as I can tell.

Do those scripts remain in memory? Or are they swapped to and fro from disk?

Either way, I think reducing the amount of 16K chunks is probably wise, and so any design pattern that can cut down on the active script number is likely to be a good thing.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
11-02-2005 15:29
every script gets 16k of memory to exist in. When a script is compiled the bytecode is 16k. when the bitecode is run script, the sim writes to different areas of that memory space. It's a slick evil solution :D

I've done this sort of thing before and it is very slow for complex controls as it has to evaluate very long if statements. I use a similar solution for coloring prims.

CODE

default
{
touch_start(integer total_number)
{
string name = llGetLinkName(llDetectedLinkNumber(0));
if(name == "Button1")
{
// Whatever Button1 does...
}
else if(name == "Button2")
{
// Whatever Button2 does...
}
// And so on...
}
}
_____________________
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
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
11-02-2005 15:32
We need arrays of function pointers!
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
11-02-2005 15:34
From: Iron Perth
I'd actually like confirmation on this. Every script consumes 16K, as far as I can tell.
The maximum amount of memory a script can use is 16k, but that memory is persistent... it stays intact when the script is moved from the world back into your inventory, so it damn well better be a quota and not a fixed block...
From: someone
Do those scripts remain in memory? Or are they swapped to and fro from disk?
...and it better be swappable.

They're running Linux, so the data lives in VM and if it's not actually referenced should end up sitting in a swapfile or partition most of the time.
Fairge Kinsella
Gravity isn't so serious!
Join date: 23 Oct 2004
Posts: 158
11-02-2005 17:02
From: Strife Onizuka
I've done this sort of thing before and it is very slow for complex controls as it has to evaluate very long if statements.


I tried to tighten up in this situation by using a button naming convention to split the if's, although the slowness of llSubStringIndex() may negate any speed benefit benefit?
CODE

// Structure of button names
// OptionLeftColourColourName
// OptionRightColourColourName
// OptionLeftSizeSizeName
// OptionRightSizeSizeName

string strButtonName = llGetLinkName(iTouchedLinkNo);

if (llSubStringIndex(strButtonName, "Left") >= 0) // It's a button in the left side controls
{
if(llSubStringIndex(strButtonName, "Colour") >= 0) // It's a colour button
{
if(llSubStringIndex(strButtonName, "Red") >= 0)
{
// do stuff
}
else if (llSubStringIndex(strButtonName, "Stone") >= 0)
{
// do stuff
}

// etc.
_____________________

Gravity Works - Drop balls with physics on - Dowden (25, 88)
---
Gravitas - Available in green - Dowden (18, 206), Skyline Mall at Cass (80, 64), SL Boutique, SL Exchange

Beatfox Xevious
is THOUSANDS OF PEOPLE
Join date: 1 Jun 2004
Posts: 879
11-02-2005 20:44
From: Strife Onizuka
I've done this sort of thing before and it is very slow for complex controls as it has to evaluate very long if statements.

Maybe I'm missing something here, but how is evaluating a long set of if-statements on names of unscripted buttons different from evaluating a long set of if-statements on link-messages coming from scripted buttons? Is there a more efficient method that can be used for the latter setup, yet can't be used for the former?
_____________________
My Beatworks: Zephyr Chimes wind chimes, the KanaMaster Japanese kana tutor, and the FREE Invisibility Prim Public. Look for them at the Luskwood General Store in Lusk (144, 165).

"You have been frozen. You cannot move or chat. A pony will contact you via instant message (IM)."
- mysterious system message I received after making off with Pony Linden
Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809
11-03-2005 00:52
General guideline - comparing very short strings is slower than comparing numbers - comparing longer strings is lots slower than comparing numbers. (and I *think* substrings are worse by far)

For your llMessageLinked prefer to use the integer num parameter, and for touch_start prefer llDetectedLinkNumber, when you can (just be consisitent in how you order your linkset)

Also. if your if-else-if chains are getting so long that you start considering binary-tree approaches :D consider changing the architecture of your code. Do you, for example, have one state listeneing for 25 different things, when you should really have 5 states, each responding to 5 different things.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
The next level...
11-03-2005 06:48
From: Ben Bacon
General guideline - comparing very short strings is slower than comparing numbers - comparing longer strings is lots slower than comparing numbers. (and I *think* substrings are worse by far)

I'm working on some code that's going to be sending a bunch of commands between prims, and I've been thinking about how to do this efficiently too. I want to minimise the number of llMessageLinked calls because I want to delay the control script as little as possible, so I want to send to a group of prims with one call so they get the message and work in sync. That means I want to batch commands and I want to use the "num" parameter to get the prims not in the group in and out as quickly as possible.

Since I have to use strings, because I've got a lot of different things to check on, I want to get LSL to do as much heavy lifting as possible. llParseString2List or llCSV2List will do a lot of string manipulation for me in one call, and use numeric sub-codes instead of names... think of how llParticleSystem and llSetPrimitiveParams work...

In the control script:
CODE

llMessageLinked(LINK_SET,group_id,llList2CSV([CODE_SET_ALPHA,1.0,CODE_SET_TEXT,"Boo!"]),NULL_KEY);

Then in the child prims:
CODE

link_message(integer from, integer num, string csv, key id)
{
if(group_id == 0 || group_id == my_group_id) {
list cmds = llCSV2List(csv);
integer n = llGetListLength(l);
integer i;
for(i = 0; i < n; i+=2) {
integer cmd = (integer)llList2String(l,i);
if(cmd == CODE_SET_ALPHA)
llSetAlpha((float)llList2STring(l,i+1),ALL_SIDES);
else if(cmd==CODE_SET_TEXT)
llSetText(llList2STring(l,i+1),my_text_color,my_text_alpha);
...
else if(cmd==CODE_GET_NAME)
llMessageLinked(from,GROUP_RESPONSE_NAME,llGetObjectName(),NULL_KEY);
else if(cmd==CODE_SET_GROUP)
my_group_id = (integer)llList2String(l,i+1);
}
}
}

Messages to single prims will still be sent direct to the prim, and responses use the num as a command...
Comments on the performance and impact of this approach?
Ben Bacon
Registered User
Join date: 14 Jul 2005
Posts: 809
11-03-2005 07:30
hmmmm - looks as though you're working on something quite complex. without knowing more I can't promise my advice is relevent - but here's me thoughts neway.

as complexity increases - legibility may suffer. with complex code, run infrequently, don't worry about performance too much. the code you presented should be perfect for the job.

if it's gonna run every second or so... focus on performance at the expense of legibility and intuitive understanding. as an arb (over the top) example - use just the integer in the link message - the top 4 bits of the number are the group number, the next 3 give 8 levels of alpha (because for this example I need to change alpha on almost every call) - the next 5 identify which of my 32 pre-chosen attributes i wanna change - and the remainder of the bits contain the new value for the attribute.

consider shifting comms to the "business level" - if you are making some ghosts, and find that you keep sending them SetAlpha(5) + SetText(Boo) + PlaySound(Eery) - could that be replaced by a single "activate" message. i.e have the control prim macro-manage rather than micro-manage.

if you do need to keep it complex, and yet it must be fast, then it's time to benchmark. rather than guessing about which of two techniques to use - try both and time them.

trying to strike the right balance between maintainability and efficiency is one of the thing that keeps programming fun!
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
11-04-2005 09:35
From: Ben Bacon
if it's gonna run every second or so... focus on performance at the expense of legibility and intuitive understanding. [...]

Let's pretend that I've been doing real-time control systems for 30 years and so I'm pretty faimiliar with the general principles of how you write tight code, and that I have a reason for making my script general-purpose rather than specific.

:)

Don't forget that one of the ways you improve the performance of scripting languages is to do less work in the scripting language itself. I've found cases where it's MUCH more efficient (for example) to call grep from Perl than to use the Perl string operations on the file... even though you're taking on a lot of overhead in forking and execcing grep.

So what I'm looking for are analogs of those kinds of tricks in LSL, to get LSL to do the heavy lifting in primitive operations, or pass data around in unexpected ways. Someone's already pointed me to the trick of passing a second string in the key, for example, so a long parameter can get shoved there instead of giving llCSV2List a big chunk of text to skip over.
JackBurton Faulkland
PorkChop Express
Join date: 3 Sep 2005
Posts: 478
11-04-2005 09:37
From: Ziggy Puff
We need arrays of function pointers!


Yes Indeed
_____________________
You know what Jack Burton always says... what the hell?
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
11-04-2005 09:38
Heck, we need arrays. Period.
Stephane Zugzwang
Brat
Join date: 26 Jun 2004
Posts: 192
11-13-2005 03:44
Very useful idea indeed, when the buton is only sending a message. How would you handle it if the button has to react, change texture, etc... can we still do it from the root prim?

I'm scripting things like chess boards and sudoku problems, so I have upwards of 70 individual squares/buttons/whatever to script. Eventhough the scripts are all the same (relying on the prim name to know what they have to do) I certainly would LOVE to reduce the clutter. One undetected bug or typo is 15mn of tedious work as I have to replace/change 70+ scripts.
_____________________
Stephane Zugzwang
--
To see a world in a grain of sand and heaven in a wild flower
Hold infinity in the palms of your hand and eternity in an hour
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
11-13-2005 06:53
From: Stephane Zugzwang
Very useful idea indeed, when the buton is only sending a message. How would you handle it if the button has to react, change texture, etc... can we still do it from the root prim?
For some things, yes. You can use llSetLinkAlpha and llSetLinkColor, for example.
From: someone
I'm scripting things like chess boards and sudoku problems, so I have upwards of 70 individual squares/buttons/whatever to script. Eventhough the scripts are all the same (relying on the prim name to know what they have to do) I certainly would LOVE to reduce the clutter. One undetected bug or typo is 15mn of tedious work as I have to replace/change 70+ scripts.
You can use the script itself to do that pushing, by having it use llRemoteLoadScriptPin to copy itself through the object.
Stephane Zugzwang
Brat
Join date: 26 Jun 2004
Posts: 192
11-13-2005 15:33
Thanks Argent !
_____________________
Stephane Zugzwang
--
To see a world in a grain of sand and heaven in a wild flower
Hold infinity in the palms of your hand and eternity in an hour
Denrael Leandros
90 Degrees From Everythin
Join date: 21 May 2005
Posts: 103
11-13-2005 15:53
Wow, very nice! I was just thinking I needed something like that after havign copied the same script over multiple updates into my 12 children.

From: Argent Stonecutter
Point.

For other "drop a script in every prim" situations, I've got a script I use called "Obedient Child" that automatically updates itself in all your scripted prims when you drop it in the root (it also does version checking, so you don't accidentally clobber a newer one with an older). It's pretty fugly at the moment, but when I get a Tuit I'll clean it up and post.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
11-13-2005 17:43
From: Argent Stonecutter
Don't forget that one of the ways you improve the performance of scripting languages is to do less work in the scripting language itself. I've found cases where it's MUCH more efficient (for example) to call grep from Perl than to use the Perl string operations on the file... even though you're taking on a lot of overhead in forking and execcing grep.


LSL is like Perl in that they are both interpreted scripting language that can't be compiled to machine code (when you compile LSL it just turns it into bytecode that is basicly the same as the text, LSL's compiler does not optimise your code, so in most cases it's a 1 to 1). Interpreted languages cannot match pace with programs compiled to machine code; which is why grep is faster then perl.

In LSL the internal functions will give you excelent speed (ignoring script delays) but with some size cost associated with calling them (calling a function User or LSL is expensive compaired to inlining a single instantce). Some delays can be sidesteped by offloading expensive functions into other scripts or by distributing the code across multiple scripts.

In LSL you can't turn Lead into Gold.

If you want your program to go faster reduce the number of commands excecuted.

When we get mono maybe things will be faster :P
_____________________
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
1 2