Jump to content

Scripts clashing


drewbrown
 Share

You are about to reply to a thread that has been inactive for 3655 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

Hi, I'm new to scripting here but a long time programmer.

I have a script from the script library for a dance prim. I have multiple instances of the prim containing the script and am finding that when you click on one to get the dance menu, i also get menus from the other ones as well. 

I tried calling each object a different name but i get a menu for each of the scripted objects.

Is there a way in script to only get the menu from the object you selected? 

Here is the code i'm using:

[php]

// Add this script into a prim with dances and touch the prim to start.
//
// You can put as many dances as will fit into memory. If you put in too many,
// you will get a nice friendly stack/heap collision error. If that happens, take
// out some dances and reset the script. Isn't that fun?

list dances;
list danceButtons;
integer danceNumber;

// Stuff for the menu
integer dlgSlots = 9; // available buttons in a multipage dialog
integer dlgMax = 12; // number of buttons in a plain old dialog
integer dlgLength; // size of the button list
integer dlgLastPage; // how many pages do we have?
integer dlgChannel = -468;


// Stop dances and play the next, if any.
PlayDance() {
integer totalDances = llGetInventoryNumber(INVENTORY_ANIMATION);
integer i = 0;
while(i < totalDances)
llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, i++));

if(danceNumber > -1) {
llStartAnimation(llGetInventoryName(INVENTORY_ANIMATION, danceNumber));
}
}


// Hunt through inventory and load any animiations found.
LoadDances() {
integer totalDances = llGetInventoryNumber(INVENTORY_ANIMATION);
dances = [];
danceButtons = [];
integer i = 0;
while(i < totalDances){
dances = (dances = []) + dances
+ [(string)(++i) + " " + llGetInventoryName(INVENTORY_ANIMATION, i)];
danceButtons = (danceButtons = []) + danceButtons + (string)i;
}

dlgLength = llGetListLength(danceButtons);

// Add a stop button if we won't need a paged menu.
if (dlgLength > 0 && dlgLength < dlgMax) {
danceButtons = (danceButtons = []) + "[STOP]" + danceButtons;
dlgLength++;
}

dlgLastPage = (dlgLength - 1) / dlgSlots;
}

Dialog(key id, integer page) {
if (danceButtons == []) {
llInstantMessage(id, "No dances to play, feed me!");
return;
}

if (llList2String(danceButtons, 0) == "[STOP]") {
// plain old dialog
llDialog(id, llDumpList2String(dances, "\n"), danceButtons, dlgChannel);
}
else {
// paged dialog
integer firstButton = page * dlgSlots;
list allButtons = llList2List(danceButtons, firstButton, firstButton + dlgSlots - 1);

// figure out what the next and previous page are.
integer nextPage = page + 1;
if (nextPage > dlgLastPage)
nextPage = 0;
integer prevPage = page - 1;
if (prevPage < 0)
prevPage = dlgLastPage;

allButtons = (allButtons=[])
+ ["<< " + (string) prevPage, "[STOP]", ">> " + (string) nextPage] + allButtons;
llDialog(
id,
llDumpList2String(llList2List(dances, firstButton, firstButton + dlgSlots - 1), "\n"),
allButtons,
dlgChannel
);
}
}

default {
state_entry() {
LoadDances();
llListen(dlgChannel, "", NULL_KEY, "");
}

changed (integer change) {
if (change & CHANGED_INVENTORY)
LoadDances();
}

touch_start(integer total_number) {
Dialog(llDetectedKey(0), 0);
}

listen(integer channel, string name, key id, string message) {
string pageCheck = llGetSubString(message, 0, 2);
if (pageCheck == "<< " || pageCheck == ">> ") {
if (message != pageCheck) // actually more than the arrow markers?
Dialog(id, (integer) llGetSubString(message, 3, -1));
return; // no need to mess with animations this time
}
else if (message == "[STOP]") {
danceNumber = -1;
}
else {
danceNumber = (integer)message - 1;
}

// If we still hold animation permission for this avatar, we don't
// need to ask again.
if ((id == llGetPermissionsKey()) && (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION))
PlayDance();
else
llRequestPermissions(id, PERMISSION_TRIGGER_ANIMATION);
}

run_time_permissions(integer perms) {
if(perms & PERMISSION_TRIGGER_ANIMATION)
PlayDance();
}
}

[/php]

Link to comment
Share on other sites


Dora Gustafson wrote:

When all instances listen on the same channel (dlgChannel) they will all act on what you choose in one dialog

I don't think you are having dialog boxes from all of them

:smileysurprised:
:)
:smileyvery-happy:

I'm not certain but perhaps dialog boxes do come from all of them eventually.  One of the reactions to a dialog response is to issue another dialog.  Which might be avoided by moving the llListen from state_entry to the touch event where it can be limited to the detected key.

Link to comment
Share on other sites

You may be right about that but what you suggest is far more complicated than it need to be

I suggest that you assign a big negative random number to dlgChannel in order to give every instance a unique channel number
This is a very common way to assign "unique" communication channels to a script

Tell me, your pads are not linked together??
If they are we may be looking at something different

:smileysurprised::):smileyvery-happy:

Link to comment
Share on other sites

Thanks all for the suggestions. i tried changing the listen to touch and thought that worked for a second but no.

it seems to only happen when there is a next/previous button and you hit next if you move the dialog you see another one underneith. i changed the names on the 2 objects to test and i can see both objects dialogs from just clicking on the one.

 I'm not sure what is meant my pads being joined together or assign a dialog channel number.

Once again i appreciate your suggestions.

Link to comment
Share on other sites

Interesting. changeing the value of dlgChannel for each seems to work.

i've never seen this before so does this mean i have to change that value on each one i rezz now?

i'm ok with that i only have about 20 of them but it seems a bit odd i think.

Link to comment
Share on other sites

Please link to scripts found elsewhere from now on.

 

Your problem is that the listen event handler is being mishandled, common to many early scripts such as this one. Please study http://wiki.secondlife.com/wiki/Dialog_Menus to understand what a listen handler is and how to open and close them correctly.

 [Edited to remove unneeded key id references in script. Edited again to remove error introduced by the first edit.]

// Original script by Viktoria Dovgal 8/4/2008// found at http://forums-archive.secondlife.com/54/8b/274487/1.html// Script modified by LepreKhaun 4/8/2014 to handle listen properly// Add this script into a prim with dances and touch the prim to start.//// You can put as many dances as will fit into memory. If you put in too many,// you will get a nice friendly stack/heap collision error. If that happens, take// out some dances and reset the script. Isn't that fun?list dances;list danceButtons;integer danceNumber;// Stuff for the menuinteger dlgSlots = 9;       // available buttons in a multipage dialoginteger dlgMax = 12;        // number of buttons in a plain old dialoginteger dlgLength;          // size of the button listinteger dlgLastPage;        // how many pages do we have?integer dlgChannel;// See http://wiki.secondlife.com/wiki/Dialog_Menusinteger listenHandle;       // Required for correct listen operation!!!key toucherKey;             // Keep track of who touched us// Stop dances and play the next, if any.PlayDance() {    integer totalDances = llGetInventoryNumber(INVENTORY_ANIMATION);    integer i = 0;    while(i < totalDances)        llStopAnimation(llGetInventoryName(INVENTORY_ANIMATION, i++));    if(danceNumber > -1) {        llStartAnimation(llGetInventoryName(INVENTORY_ANIMATION, danceNumber));    }}// Hunt through inventory and load any animiations found.LoadDances() {        integer totalDances = llGetInventoryNumber(INVENTORY_ANIMATION);        dances = [];        danceButtons = [];        integer i = 0;        while(i < totalDances){            dances = (dances = []) + dances                        + [(string)(++i) + " " + llGetInventoryName(INVENTORY_ANIMATION, i)];            danceButtons = (danceButtons = []) + danceButtons + (string)i;        }        dlgLength = llGetListLength(danceButtons);        // Add a stop button if we won't need a paged menu.        if (dlgLength > 0 && dlgLength < dlgMax) {            danceButtons = (danceButtons = []) + "[sTOP]" + danceButtons;            dlgLength++;        }        dlgLastPage = (dlgLength - 1) / dlgSlots;}Dialog(integer page) {    if (danceButtons == []) {        llInstantMessage(toucherKey, "No dances to play, feed me!");        return;    }    if (llList2String(danceButtons, 0) == "[sTOP]") {        // plain old dialog        llSetTimerEvent(20.0);        listenHandle = llListen(dlgChannel, "", toucherKey, "");        llDialog(toucherKey, llDumpList2String(dances, "\n"), danceButtons, dlgChannel);    }    else {        // paged dialog        integer firstButton =  page * dlgSlots;        list allButtons = llList2List(danceButtons, firstButton, firstButton + dlgSlots - 1);            // figure out what the next and previous page are.        integer nextPage = page + 1;        if (nextPage > dlgLastPage)            nextPage = 0;        integer prevPage = page - 1;        if (prevPage < 0)            prevPage = dlgLastPage;        allButtons = (allButtons=[])            + ["<< " + (string) prevPage, "[sTOP]", ">> " + (string) nextPage] + allButtons;        llSetTimerEvent(20.0);        listenHandle = llListen(dlgChannel, "", toucherKey, "");        llDialog(            toucherKey,            llDumpList2String(llList2List(dances, firstButton, firstButton + dlgSlots - 1), "\n"),            allButtons,            dlgChannel        );    }}default {    state_entry() {        LoadDances();    }    changed (integer change) {        if (change & CHANGED_INVENTORY)            LoadDances();    }        touch_start(integer total_number) {        toucherKey = llDetectedKey(0);        dlgChannel = 0x80000000 | (integer)("0x"+(string)toucherKey);        Dialog(0);    }        listen(integer channel, string name, key toucherKey, string message) {        llListenRemove(listenHandle);        string pageCheck = llGetSubString(message, 0, 2);        if (pageCheck == "<< " || pageCheck == ">> ") {            if (message != pageCheck) // actually more than the arrow markers?                Dialog((integer) llGetSubString(message, 3, -1));            return; // no need to mess with animations this time        }        else if (message == "[sTOP]") {            danceNumber = -1;        }                else {            danceNumber = (integer)message - 1;        }        // If we still hold animation permission for this avatar, we don't        // need to ask again.        if ((toucherKey == llGetPermissionsKey()) && (llGetPermissions() & PERMISSION_TRIGGER_ANIMATION))                PlayDance();        else            llRequestPermissions(toucherKey, PERMISSION_TRIGGER_ANIMATION);    }    run_time_permissions(integer perms) {        if(perms & PERMISSION_TRIGGER_ANIMATION)            PlayDance();    }        timer()    {        llSetTimerEvent(0.0);        llListenRemove(listenHandle);        llRegionSayTo(toucherKey, 0, "Sorry. You snooze; you lose.");    }    }

 

 

Link to comment
Share on other sites

If you take a close look at the script mr. Khaun has been kind enough to post, you will see how you assign a channel number to 'dlgChannel' in a proper way:

dlgChannel = 0x80000000 | (integer)("0x"+(string)toucherKey);

 and not the way it is in the copy you found:

integer dlgChannel = -468;

drewbrown wrote:

i've never seen this before so does this mean i have to change that value on each one i rezz now?

There is no need for that, the script can do it for you the way it is done by Mr. Khaun

:smileysurprised::):smileyvery-happy:

Link to comment
Share on other sites

When I come to think of it, a random channel would be better:

dlgChannel = (integer)llFrand( 8388608.0 ) | 0x80000000;

 Because by the other approach the number is always the same for the same person clicking
So if one person clicks more prims with the script you end up in the same situation as with a constant number

:smileysurprised::):smileyvery-happy:

Link to comment
Share on other sites


Dora Gustafson wrote:

When I come to think of it, a random channel would be better:
dlgChannel = (integer)llFrand( 8388608.0 ) | 0x80000000;

 Because by the other approach the number is always the same for the same person clicking

So if one person clicks more prims with the script you end up in the same situation as with a constant number

:smileysurprised:
:)
:smileyvery-happy:

Ahhh, but the listen is opened and closed only as needed. The main problem with the original script was having the listen always open, never a good idea.

Link to comment
Share on other sites


Dora Gustafson wrote:

You are partly right but It is not closed until the menu is actually used, so it is possible for one person to open more menus using the same channel at the same time

In everyday use it probably is not a big problem

:smileysurprised:
:)
:smileyvery-happy:

Incorrect.

 

When listen handles are correctly opened only when needed and closed properly, any one script (as I solved it) can only be listening for no more than 20 seconds (which could even be shortened further!). Unless there is an active listen handler on a specific channel opened at any given moment, it doesn't matter what is being chatted on ANY channel by ANYONE.

 

As I said in the beginning, "the listen event handler is being mishandled, common to many early scripts such as this one." The solution I gave is entirely correct, though other aspects of the script could be improved.

Link to comment
Share on other sites

I am talking about two ways to compute a suited communications channel number

(1)  0x80000000 | (integer)("0x"+(string)toucherKey)(2)  (integer)llFrand( 8388608.0 ) | 0x80000000

In the example at hand (1) expose a risk immensely reduced by (2)

Try to figure out why

:smileysurprised::):smileyvery-happy:

Link to comment
Share on other sites


Dora Gustafson wrote:

I am talking about two ways to compute a suited communications channel number
(1)  0x80000000 | (integer)("0x"+(string)toucherKey)(2)  (integer)llFrand( 8388608.0 ) | 0x80000000

In the example at hand (1) expose a risk immensely reduced by (2)

Try to figure out why

:smileysurprised:
:)
:smileyvery-happy:

In (1) there is a hexadecimal number of length 8 being used. When one reduces a number pool by a factor of 32 (which is what you're doing here) the risk increases by a factor of 32, not the other way around.

Link to comment
Share on other sites


LepreKhaun wrote:

Dora Gustafson wrote:

I am talking about two ways to compute a suited communications channel number
(1)  0x80000000 | (integer)("0x"+(string)toucherKey)(2)  (integer)llFrand( 8388608.0 ) | 0x80000000

In the example at hand (1) expose a risk immensely reduced by (2)

Try to figure out why

:smileysurprised:
:)
:smileyvery-happy:

In (1) there is
being used. When one reduces a number pool by a factor of 32 (which is what you're doing here) the risk
increases
by a factor of 32, not the other way around.

Wrong:

(1) returns the same number for the same key and for all keys having 8 identical first digits in it

The probability that the same toucher generates the same number in any number of tries is 1

 

(2) returns a random number each time it is called

The probability that the same toucher generates the same number two times in a row is 1/8388608

:smileysurprised::):smileyvery-happy:

Link to comment
Share on other sites


Dora Gustafson wrote:

LepreKhaun wrote:

Dora Gustafson wrote:

I am talking about two ways to compute a suited communications channel number
(1)  0x80000000 | (integer)("0x"+(string)toucherKey)(2)  (integer)llFrand( 8388608.0 ) | 0x80000000

In the example at hand (1) expose a risk immensely reduced by (2)

Try to figure out why

:smileysurprised:
:)
:smileyvery-happy:

In (1) there is
being used. When one reduces a number pool by a factor of 32 (which is what you're doing here) the risk
increases
by a factor of 32, not the other way around.

Wrong:

(1) returns the
same number
for the
same key
and for
all keys having 8 identical first digits
in it

The probability that the same toucher generates the same number in any number of tries is 1

 

(2) returns a
random number each time
it is called

The probability that the same toucher generates the same number two times in a row is 1/8388608

:smileysurprised:
:)
:smileyvery-happy:

Unsure what your point is. It doesn't matter if the same number is used each time for the same avatar. If the listen handler is being closed properly each time it's used (which it is in the script I gave), chance of collision is 0 for that avatar.

 

1/8388608(Yours) > 1/268435458(Mine) > 0(Reality within the script)

Link to comment
Share on other sites


LepreKhaun wrote:

Unsure what your point is. It doesn't matter if the same number is used each time for the same avatar.
If the listen handler is being closed properly each time it's used
(which it is in the script I gave), chance of collision is 0 for that avatar.

  • Your assumption is not met
  • The handler is not closed until you have a listen event on the channel (or a time out)

    One person can open several handlers before that happens

  • More scripts using same communications channel pose a risk for cross talk

:smileysurprised::):smileyvery-happy:

 

 

Link to comment
Share on other sites


Dora Gustafson wrote:

...One person can open several handlers before that happens ...

 

Dora, if someone is wanting to click on "several" dance balls within 20 seconds while ignoring the dialog menus popping up on their screen, then they don't need a dance animation anyway-

They need to learn how to interact with objects in Second Life. :smileytongue:

 

I do apologize to the others for allowing allowing myself to be pulled into a mean-spirited game. I should've realised from the outset what was happening and just ignored it.

 

Getting back on topic, many of the older scripts do not handle listeners properly. The original script example given by the OP and the problem it presented is typical of the time period before scripters found ways to open listen handles when needed and close them when they were no longer required. That practice is to be recommended and I hope my rewrite shows how an older script can be upgraded to follow it. 

Link to comment
Share on other sites

Sorry have been away for a bit. i didn't realise this resulted in 3 pages :)

thanks everyone i have learnt some more :)

changing the channel number actually worked for me but i'll go though these and try some.

i think they may be better solutions. thanks again everyone. appreciated.

Link to comment
Share on other sites

as a general safe coding practice then i agree

should build a stack/list of listenhandles then clear them all. for example:

 

list handles;int handle;...// addhandle = llListen( ... );handles += [handle];...// remove allinteger n = llGetListLength(handles);integer i;for (i = 0; i < n; i++)  llListenRemove(llList2Integer(handles, i));handles = [];

 +

this might actual not be necessary in LSL. As i dont know what happens LSL serverside if create a new global handle and not release the previous before overwriting. (users going click click click click click bc in a club and laaagggggg !!! which is what people actual do in laggy clubs to danceballs)

maybe the way LL have implemented LSL runtime is smart enough to garbage collect this. I do know tho that in most other compilers/runtimes then not releasing globals specifically yourself can lead to memory leak

would be good if someone reading could confirm this either way

Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 3655 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...