Jump to content

scanning for avatar names script has bug(s)


Sylvia Wasp
 Share

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

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

Recommended Posts

Okay, so I wrote this script (with help from this very forum!) that lists out all the avatars in a sim and puts their names on a button dialogue.  

That may not seem useful but it's just a part of a bigger project.  :) 

There are two problems with the script (if anyone wants to help out).  

1) I clearly don't know what I'm doing with the listens, lol.  every time I try to fix them so they are both created and deleted properly, I get "doubles" on the buttons, (every avatar name appears twice) so I've had to comment out most of it which is kind of bad practice (listens must be dealt with).


2) If there is some douchebag avatar in the sim with some fancy ass name with symbols and stuff in it, I get a script error something like, "buttons should only have so many characters dumbass," even though I've got code in here to stop that exact thing (see below).  

            for (i = 0; i < number_of_keys; ++i) {
                thisKey = llList2Key(keys,i);
                if (thisKey != llGetOwner()) {
                    detected_list += [
                        llRound(llVecDist(currentPos, llList2Vector(llGetObjectDetails(thisKey,[OBJECT_POS]),0))), 
                        llGetSubString(llGetDisplayName(thisKey), 0, 11),
                        thisKey 
                        ];
                    }

 

Only the names with the weird symbols in them sets off the error, so I'm thinking that somehow the Trademark symbol or the Copyright symbol or whatever stupid emoji it is, is functioning like an escape character or something?  

It rarely happens, and it only happens when some 8 foot tall toxic male with a ridiculous long name is in the sim, so it's probably just a single individual character that's doing it.  

Maybe it's being done on purpose?  

In any case, if anyone know if there is a way to limit the Unicode set, or identify the errant character and then check for it, or take it out before this happens I'd appreciate it.    

the entire script (so far) follows:

integer cmdChannel;
integer gMenuPos;
integer gListen;

list    detected_list;
list    avatar_list;

integer Key2Chan(key ID) 
{
    return 0x80000000 | (integer)("0x"+(string)ID);
}

multipage_dialogue()    // replaces regular dialogue, deals with more pages
{
    integer lastPage;
    list    buttons;
    integer listLength = llGetListLength(avatar_list);
    
    if(gMenuPos >= 9) {
        buttons += "    <----";
            if((listLength - gMenuPos) > 11) {
                buttons += "    ---->";
                }
            else {
                lastPage = TRUE;
                }            
        }    
    else if (listLength > gMenuPos+9) {
            if((listLength - gMenuPos) > 11) {
                buttons += "    ---->";
                }
            else {
                lastPage = TRUE;
                }            
        }
    else {
        lastPage = TRUE;
        }

    if (listLength > 0) {
        integer b_count;
        integer num_of_buttons = llGetListLength(buttons);

        for(b_count = gMenuPos + num_of_buttons + lastPage - 1 ; (num_of_buttons < 12) && (b_count < listLength); ++b_count) {
            buttons = buttons + [llList2String(avatar_list,b_count)];
            num_of_buttons = llGetListLength(buttons);
            }
        }
//    gListen = llListen(cmdChannel,"","","");    
    llSetTimerEvent(10.0);
    llDialog(llGetOwner()," \n",buttons,cmdChannel);
}

 
default
{
    state_entry()
    {
        cmdChannel = Key2Chan(llGetOwner());
        llListen(cmdChannel, "", llGetOwner(), "");
    }
                
    touch_start(integer total_number)
    {
        llListenRemove(gListen);
        llSetTimerEvent(0.0);
        gMenuPos = 0;
        avatar_list = ["SCAN", "Reset", "Done"];
        detected_list = [];
        multipage_dialogue();
    }
    
    listen( integer channel, string name, key id, string message )
    {                
        if( message == "SCAN" ){
            avatar_list = [];
            llOwnerSay( "scanning for avatars within 96m ..." );
            
            key     thisKey;
            vector  currentPos = llGetPos();
            list    keys = llGetAgentList(AGENT_LIST_REGION, []); 
            integer number_of_keys = llGetListLength(keys);
     
            integer i;
            for (i = 0; i < number_of_keys; ++i) {
                thisKey = llList2Key(keys,i);
                if (thisKey != llGetOwner()) {
                    detected_list += [
                        llRound(llVecDist(currentPos, llList2Vector(llGetObjectDetails(thisKey,[OBJECT_POS]),0))), 
                        llGetSubString(llGetDisplayName(thisKey), 0, 11),
                        thisKey 
                        ];
                    }
                }
            detected_list = llListSort(detected_list, 3, FALSE);
            avatar_list = llList2ListStrided(llDeleteSubList(detected_list, 0, 0), 0, -1, 3);
                if (llGetListLength(avatar_list) < 1) {
                    llSleep(1.0);
                    llOwnerSay( "Sorry ... no other avatars found in this simulator");
                    return;
                    }
                else
                    multipage_dialogue();
            }
        else if ( message == "Reset") {
            llResetScript();
            }
        else if ( message == "Done") {
//            llListenRemove(gListen);
            }
        else if (~llSubStringIndex(message,"---->")) {
            gMenuPos += 10;
            multipage_dialogue();
            }
        else if (~llSubStringIndex(message,"<----")) {
            gMenuPos -= 10;
            multipage_dialogue ();
            }
        else {
            llOwnerSay(message);    // the (named) button you pressed
            llOwnerSay((string) llList2String(detected_list, (llListFindList(detected_list,[message]) + 1)));   // that avatar's key
            multipage_dialogue ();
            }
    }        

    timer()
    {
        llSetTimerEvent(0.0);
//        llListenRemove(gListen);
    }
}

thanks, 

Sylvia Wasp

Link to comment
Share on other sites

1 hour ago, Sylvia Wasp said:

Only the names with the weird symbols in them sets off the error, so I'm thinking that somehow the Trademark symbol or the Copyright symbol or whatever stupid emoji it is, is functioning like an escape character or something?  

The explanation is a little technical, but in short: Almost any "exotic" (non-ASCII) character runs the risk of causing this error.

Dialog buttons aren't limited to 12 characters, but 24 bytes of memory.

Any ASCII character will take up one byte. (Look at the three columns on the right side of that table.)

Any non-ASCII character will take between 2 and 4 bytes.

This means that, if someone has just 7 high characters from the UTF-8 character format (which is what dialog buttons use), your script will crash.

This means that if you want to be absolutely safe, you should not use more than 6 characters from display names.

 

1 hour ago, Sylvia Wasp said:

If there is some douchebag avatar in the sim

It rarely happens, and it only happens when some 8 foot tall toxic male with a ridiculous long name is in the sim

Check yourself.

Link to comment
Share on other sites

A thought here, in state_entry a listen is opened without the handle being assigned, so that listen is always going to be open?

 

In the touch event, I can't understand why the first thing done is to remove a listen, the best times to do this are in a timeout (as done but commented out) , and also when a "done" command is processed, again, as happens.

My suggestion here is remove the listen in state_entry, open a listen in the touch event before calling the multi-dialog, and just keep using that handle until either it gets timed-out or the Done button is pressed.

Inside the touch event, I would therefore suggest you have a global variable set whenever processing has started, so a touch only starts a new set of menu dialogs and listens off if there isn't already one in progress.

I would also suggest cancelling the timer at the very start of the listen event to avoid it triggering should some of the list processing drag on a bit.

Edited by Profaitchikenz Haiku
  • Like 1
Link to comment
Share on other sites

to not create and destroy listeners unnecessarily  then we can use llListenControl() to listen only for the time needed

http://wiki.secondlife.com/wiki/LlListenControl

pcode dialog menu example with timeout

//global listener handle
integer handle;

state_entry()
{
    handle = llListen( ... );
    llListenControl(handle, FALSE);  // stop listening until touch
}

touch_start(...)
{
    llListenControl(handle, TRUE); // begin listening
    llSetTimerEvent(30.0);  // timeout the listener after 30 seconds 
    llDialog(...);          // present the dialog menu
}

listen( ..., string text, ...)
{
    llListenControl(handle, FALSE); // stop listening until next touch
    if (text == ...)   // process dialog menu button texts
    {
        ...
    }
}

timer()
{
    llSetTimerEvent(0.0);   // stop the timer
    llListenControl(handle, FALSE); // timeout, so stop listening until next touch
}

 

  • Like 1
Link to comment
Share on other sites

3 hours ago, Wulfie Reanimator said:

The explanation is a little technical, but in short: Almost any "exotic" (non-ASCII) character runs the risk of causing this error.

Dialog buttons aren't limited to 12 characters, but 24 bytes of memory.

Any ASCII character will take up one byte. (Look at the three columns on the right side of that table.)

Any non-ASCII character will take between 2 and 4 bytes.

This means that, if someone has just 7 high characters from the UTF-8 character format (which is what dialog buttons use), your script will crash.

This means that if you want to be absolutely safe, you should not use more than 6 characters from display names.

 

Check yourself.

There's a nice little code snippet in the llDialog caveats that shows how to truncate a string so that it'll work in a button, even when it contains UTF-8 characters that'd take it over the byte limit although it's 24 or fewer characters in length:

llBase64ToString(llGetSubString(llStringToBase64(theString), 0, 31))

So, @Sylvia Wasp, all you need to do is replace llGetSubString(llGetDisplayName(thisKey), 0, 11) in your button list building loop with llBase64ToString(llGetSubString(llStringToBase64(llGetDisplayName(thisKey)), 0, 31)).

  • Like 3
  • Thanks 1
Link to comment
Share on other sites

On 1/12/2020 at 5:28 PM, Mollymews said:

to not create and destroy listeners unnecessarily  then we can use llListenControl() to listen only for the time needed

http://wiki.secondlife.com/wiki/LlListenControl

pcode dialog menu example with timeout


//global listener handle
integer handle;

state_entry()
{
    handle = llListen( ... );
    llListenControl(handle, FALSE);  // stop listening until touch
}

touch_start(...)
{
    llListenControl(handle, TRUE); // begin listening
    llSetTimerEvent(30.0);  // timeout the listener after 30 seconds 
    llDialog(...);          // present the dialog menu
}

listen( ..., string text, ...)
{
    llListenControl(handle, FALSE); // stop listening until next touch
    if (text == ...)   // process dialog menu button texts
    {
        ...
    }
}

timer()
{
    llSetTimerEvent(0.0);   // stop the timer
    llListenControl(handle, FALSE); // timeout, so stop listening until next touch
}

 

Cool. :) Thanks a lot Molly.  This is exactly what I need.  

  • Like 1
Link to comment
Share on other sites

On 1/12/2020 at 7:33 PM, KT Kingsley said:

There's a nice little code snippet in the llDialog caveats that shows how to truncate a string so that it'll work in a button, even when it contains UTF-8 characters that'd take it over the byte limit although it's 24 or fewer characters in length:

llBase64ToString(llGetSubString(llStringToBase64(theString), 0, 31))

So, @Sylvia Wasp, all you need to do is replace llGetSubString(llGetDisplayName(thisKey), 0, 11) in your button list building loop with llBase64ToString(llGetSubString(llStringToBase64(llGetDisplayName(thisKey)), 0, 31)).

Thanks KT.  :) 

This is what I suspected (if I understand what you're saying here).  That it might be possible to screen out the errant characters before they cause problems and hopefully, (again if I understand what you're saying), without having to cut the names down to un-recogniseable stubs of only 6 characters in length.  

The whole deal in what I'm trying to achieve is a bank of buttons with avatars names on them.  If they aren't identifiable as the avatars standing around the user, then there isn't much point. 

Link to comment
Share on other sites

On 1/13/2020 at 5:33 AM, KT Kingsley said:

There's a nice little code snippet in the llDialog caveats that shows how to truncate a string so that it'll work in a button, even when it contains UTF-8 characters that'd take it over the byte limit although it's 24 or fewer characters in length:

llBase64ToString(llGetSubString(llStringToBase64(theString), 0, 31))

This has a minor problem at the cutoff point, where it will turn any multi-byte character into the either the "non-displaying" or "unrecognized" symbol.

string display_name = "I, the Alpha and Omega Ω"; // 23 + 3 bytes
llOwnerSay(display_name);

string truncated = llBase64ToString(llGetSubString(llStringToBase64(display_name), 0, 31));
llOwnerSay(truncated);
Object: I, the Alpha and Omega Ω
Object: I, the Alpha and Omega ?

The least significant byte of Ω (which is left after truncating) is 166, which is the extended-ASCII character ¦, which isn't in my viewer font's character map so I get an error symbol (could be other symbols like ☐, ☒, �). Other characters might get cut off in a way that displays normal-looking characters that weren't actually there.

This really only matters if you're comparing the button name directly with something, or trying to use it to find information about the avatar, because the two strings no longer match.

That's a neat way of getting around the problem though.

Edited by Wulfie Reanimator
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

Thanks, Wulfie. I did have a passing wonder about that, but dismissed it until such time I might need to address it directly.

I think in a situation like this I'd be building parallel lists, one for the names truncated to button labels and the other for the keys of the avatars. And maybe a third list of the full names were these needed for display purposes, though there I've taken to using the viewer URI thingy for that.

Link to comment
Share on other sites

On 1/14/2020 at 9:44 AM, Wulfie Reanimator said:

This has a minor problem at the cutoff point, where it will turn any multi-byte character into the either the "non-displaying" or "unrecognized" symbol.


string display_name = "I, the Alpha and Omega Ω"; // 23 + 3 bytes
llOwnerSay(display_name);

string truncated = llBase64ToString(llGetSubString(llStringToBase64(display_name), 0, 31));
llOwnerSay(truncated);

Object: I, the Alpha and Omega Ω
Object: I, the Alpha and Omega ?

The least significant byte of Ω (which is left after truncating) is 166, which is the extended-ASCII character ¦, which isn't in my viewer font's character map so I get an error symbol (could be other symbols like ☐, ☒, �). Other characters might get cut off in a way that displays normal-looking characters that weren't actually there.

This really only matters if you're comparing the button name directly with something, or trying to use it to find information about the avatar, because the two strings no longer match.

That's a neat way of getting around the problem though.

thanks for this detail Wulfie. 🙂 

Overall, though I don't see this as a problem, especially when the alternative is severely truncated names.  The scanner is mostly for use at dances, shopping malls, etc. and let's face it, most avatars won't be using characters that cause any problems. 

The majority of avatars just use regular names in the language symbols of their home country in standard fonts available on most modern computers.  Only a small sub-set of avatars will use "weird/interesting" characters from other languages and font sets (for the "cool" factor 🙂 ), and an even smaller subset of those will use characters that might cause a problem with the scanner.  

In testing, it seems that llBase64ToString deals with those rare cases very well.  

All the names I've seen on the buttons are clear and easily recognisable as the folks standing around me when I'm scanning. 

Link to comment
Share on other sites

  

On 1/12/2020 at 10:01 PM, Sylvia Wasp said:

 every time I try to fix them so they are both created and deleted properly, I get "doubles" on the buttons, (every avatar name appears twice)

I think you can remedy that thus:

            integer i;
            for (i = 0; i < number_of_keys; ++i) {
                thisKey = llList2Key(keys,i);
                if (thisKey != llGetOwner() && !~llListFindList(detected_list,[thisKey])) {//if thisKey is not the uuid of the owner and is not already in the list
                    detected_list += [
                        llRound(llVecDist(currentPos, llList2Vector(llGetObjectDetails(thisKey,[OBJECT_POS]),0))), 
                        llGetSubString(llGetDisplayName(thisKey), 0, 11),
                        thisKey 
                        ];
                    }
                }

 

Link to comment
Share on other sites

8 hours ago, hectic1 said:

Wouldn't be much simpler to just use llKey2Name() instead of llGetDsiplayName()? It returns the legacy name of the avatar which I think it's guaranteed to consist of ascii letters only (double check on that please).

 

from a ease of scripting pov then yes this would be simpler

a display name, when different from a username, is what the person prefers to be known as. OP script takes this preference into account 

Link to comment
Share on other sites

12 hours ago, Mollymews said:

from a ease of scripting pov then yes this would be simpler

a display name, when different from a username, is what the person prefers to be known as. OP script takes this preference into account 

Nods! However, ll2Key() besides making the script easier and lighter, it also guarantees the name returned is unique. Combined with the menu buttons limit would decrease even more the (thin) chances of names appearing as being the same inside the menu. Nipticking, but maybe worth noting.

Link to comment
Share on other sites

On 1/21/2020 at 2:05 AM, hectic1 said:

Wouldn't be much simpler to just use llKey2Name() instead of llGetDsiplayName()? It returns the legacy name of the avatar which I think it's guaranteed to consist of ascii letters only (double check on that please).

 

Yes, except for the fact that most people don't actually use their legacy names (sadly).  

The idea behind this scanner is that if you're in a room full of avatars and looking at the labels above their heads, what the scanner should return is what those labels say, so that the user can correlate the label on the button with the persons they see in front of them.   

Edited by Sylvia Wasp
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 1548 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...