Difference between revisions of "Category talk:LSL Key"

From Second Life Wiki
Jump to navigation Jump to search
Line 243: Line 243:
}
}
</source>
</source>
: I just checked the following
<source lang="lsl2">
default
{
    state_entry()
    {
        key uuid = "#my&_kit7y<3-b4rfed^ hairba11s!0n*thi5@L1ne !";
        if(uuid){
            llOwnerSay("Yup");
        }
        else {
            llOwnerSay("Nope");
        }
    }
}
</source>
: and under both LSL2 and Mono the script prints "Nope". If I remove the else, it prints nothing as expected. Do you have an example script that demonstrates the problem that all this is supposed to solve? Is it happening within Second Life? Also, what server version? --[[User:ObviousAltIsObvious Resident|ObviousAltIsObvious Resident]] ([[User talk:ObviousAltIsObvious Resident|talk]]) 14:31, 6 March 2015 (PST)

Revision as of 14:31, 6 March 2015

Characteristics

I just got the info that it is easily spottable if a key is from an agent or not.
My key is 42bae93c-7fbd-4e02-a34e-6ea982e9f92a.
Any agent key seems to have the marked 4 at this place in the key. I'm sure that there are other hints for "object", "notecard", "texture"... Does anyone know? If so, plz add it to the article =)
Greetz, Zai Lynch(talk|contribs) 22:24, 28 June 2008 (PDT)

Appendix: I just created multiple objects to find similarities. One of these objects had a 4 in the same spot. So I tested multiple agents in the w-hat database but couldn't find any with a different value then 4 in this spot so far. Seems to be: Any agent must has this value, other keys can have this value too. --Zai Lynch(talk|contribs) 22:51, 28 June 2008 (PDT)
You will find that the "4" in that position indicates that they are using Version 4 of the UUID format as specified in RFC-4122. -- Strife Onizuka 03:27, 29 June 2008 (PDT)
Kinda late, but not all avatars have a 4 there. Some old stats: http://w-hat.com/keydistribution.txt (notice also the 20th character). -- Masakazu Kojima 23:13, 17 May 2009 (UTC)
If object UUID's used a different range I would say it was intentional but they don't, I wonder if assets use a different range? Someone should tell LL their UUID generator is buggy (it has two dead bits, one stuck on (bit 67), the other stuck off (bit 66)). -- Strife (talk|contribs) 08:50, 18 May 2009 (UTC)
It's the 'variant' field, see 4.1.1/4.4 of the RFC. Masakazu Kojima 14:10, 24 May 2009 (UTC)
omg the RFC does a bad job of describing that. Good thing the wikipedia article has been updated since I commented last June (The python documentation is pretty good). I'd love to see the names (and ages) of those accounts which don't fit the v4 of RFC-4122. -- Strife (talk|contribs) 18:56, 24 May 2009 (UTC)

Comparison of Keys not working right

I just did this:

//snip
    state_entry(){
        key request_key =llGetNotecardLine(llGetInventoryName(INVENTORY_NOTECARD,0),gLine);
        llOwnerSay("Request sent, request_key is "+(string)request_key);
    }
    dataserver(key query_id, string data){
        if(query_id == request_key){
            llOwnerSay("received reply, request_key matches: "+(string)query_id);
            // Do something
        }else{
            llOwnerSay("received reply, but it's not ours: "+(string)query_id);
        }
    }
//snip

Now here's the reply I got:

[4:37]  Object: Request sent, request_key is 305a0774-0e24-4037-884b-f3052ed337e9
[4:37]  Object: received reply, but it's not ours: 305a0774-0e24-4037-884b-f3052ed337e9

How am I supposed to work around that? Feyn Graves 11:51, 25 August 2009 (UTC)

You are going to kick yourself. You are redeclaring 'request_key' in the state_entry. You aren't storing the value to the global variable, but to the local variable. Remove 'key' from in front of 'request_key' on the first line of your state entry. No worries, happens to the best of us. -- Strife (talk|contribs) 13:18, 25 August 2009 (UTC)
D'OH! And I even was stupid enough to sign this... File this under "Lessons learned" and "Temporary brain malfunction"
//snip
    state_entry(){
        request_key = llGetNotecardLine(llGetInventoryName(INVENTORY_NOTECARD,0),gLine);
        llOwnerSay("Request sent, request_key is "+(string)request_key);
    }
    dataserver(key query_id, string data){
        if(query_id == request_key){
            llOwnerSay("received reply, request_key matches: "+(string)query_id);
            // Do something
        }else{
            llOwnerSay("received reply, but it's not ours: "+(string)query_id);
        }
    }
//snip


Actual key Validation

I worked out a fairly different key Validity check.

Normally you can pass Garbage to a key and it will pass as valid.

Consider this:

key uuid = "#my&_kit7y<3-b4rfed^ hairba11s!0n*thi5@L1ne !"

with this as quoted from LSL_Key

if(uuid){
     //do something
}

The above will process the "//do something" line even with that horribly malformed key. While this is useful for some hacks commonly seen in llLinkedMessage()'s it's not very useful when you are looking to assure a key received from user-input is not malformed. Examples include, Reading a key from a notecard line, or chat input on a listen channel.


In my opinion keys should go through more scrutiny, and "GarbageData" should result in a completely invalid key or when passed to functions, at the very least they should report an error. In light of this, I've written my own key Validate'r, (Not to be confused with a "Verifier" which would be for confirming occupied keys, Then again I could have my definitions confused between Verify and Validate, maybe someone could verify my semantics? or invalidate my attempt? -1)

Here is the ready-to-use version of aKey() by ThumpieBunnyEve.Hax, no bells, whistles, comments, instructions, nor examples.

integer aKey(string s){
if((key)s==NULL_KEY)return 0;
if(llKey2Name((key)s)!="")return 1;
integer i;
if(llStringLength(s)==36){string c; integer h;i=35;
do{c=llGetSubString(s,i,i);h=(integer)("0x"+c);
if(i==23||i==18||i==13||i==8){if(c!="-")i=-1;}
else if(!h&&c!="0"){i=-1;}
}while(--i>-1);
}
return -1+((!~i)*2);
}


And here is a extremely long winded verbose version, with run-on comments, examples, instructions, usage suggestions, extensive testing, and so on. I think it speaks for itself really.

integer aKey(string s){
if((key)s==NULL_KEY)return 0;
if(llKey2Name((key)s)!="")return 1;
integer i;
if(llStringLength(s)==36){string c; integer h;i=35;
do{c=llGetSubString(s,i,i);h=(integer)("0x"+c);
if(i==23||i==18||i==13||i==8){if(c!="-")i=-1;}
else if(!h&&c!="0"){i=-1;}
}while(--i>-1);
}
return -1+((!~i)*2);
}

string sA(string k,string s,string d){ float T; string t="\t\t";
T=llGetTime();return (string)aKey(k)+t+(string)(llGetTime()-T)+t+k+t+s+t+d+"\n";
} // this function is just for use in the default { example } below.

default { // this is just a default example,
          // "integer aKey()" above, is the function this example tests.
    state_entry() {
    llOwnerSay(" aKey() by ThumpieBunnyEve.Hax 2015.03.05:\n"+
    "\taKey() Verbose demo with examples.\t (Strech your Local Chat log-window wide for proper viewing.)\n"+
    "\nSum\tTime\t\t   String\t\t\t\t\t\t\t\t//Expected\t\t\tDescription"+
    "\n=\t\t   Used\t\t   Key \t\t\t\t\t\t\t\t\t//Result\t\t\t\t\tof input.");
    
    string o="\n"+
    sA((key)"fad14487-e769-4d33-ba6a-968546fb7e3d","//1","Valid: Occupied")+
    sA("1F31F7F2-8D75-4CEB-9581-B186C71D7C03","//1","Valid")+
     sA((key)"00000000-0000-0000-0000-000000000000","//0","NULL_KEY")+
    "\n"+
    sA("",                                    "//-1","Malformed \"\"")+
    sA((key)"                                    ","//-1","Malformed")+
    sA("        -    -    -    -            ", "//-1","Malformed")+
    sA((key)"------------------------------------","//-1","Malformed")+
    "\n"+
    sA((key)"ac97b7e8-bbe1-840344f4e86e",          "//-1","Malformed")+
    sA("7868811ea99a7baf14cd105d69ca8af61961","//-1","Malformed")+
    sA((key)"7868811e-99a7-af14-d105-69ca8aZ1961","//-1","Malformed: Out of Bounds.")+
    sA("g868811e-99a7-af14-d105-69ca8ab61961","//-1","Malformed: Out of Bounds.");
    llOwnerSay(o);

    string k1U=llToUpper(llGetKey());
    string k1L=llToLower(llGetKey());
    string k2U=llToUpper(llGetOwner());
    string k2L=llToLower(llGetOwner());
    llOwnerSay( "\n Note: The next 4 lines when run in the external program \"LSLEditor.exe\" will likely fail to display Names."+
"\n\t\t\t\t This is expected behavior exclusive to LSLEditor due to it's internal option settings.\n\n"+
    llKey2Name(k1U)+"'s key("+k1U+") is detected in UPPERCASE,\t\taKey returns: "+(string)aKey(k1U)+"\n"+
    llKey2Name(k1L)+"'s key("+k1L+") is detected in lowercase.\t\t\t\taKey returns: "+(string)aKey(k1L)+"\n"+
    "\n"+
    llKey2Name(k2U)+"'s key("+k2U+") is detected in UPPERCASE,\t\taKey returns: "+(string)aKey(k2U)+"\n"+
    llKey2Name(k2L)+"'s key("+k2L+") is detected in lowercase.\t\t\t\taKey returns: "+(string)aKey(k2L)+"\n"
    );
    }//end state entry
}// end default

/*
In LSLEditor 2.55, a valid key took 5 to 7 milliseconds (.07s) to validate.
In SL Beta grid, using MONO: Detecting NULL_KEY took 1ms (.01s) all other operations took <1ms.
In SL Beta grid, using LSL:  All operations took <1ms.

A Valid key is a key that is structured properly. Technically, NULL_KEY is a valid key.
An Occupied key, is a key that is used by an object or avatar on a sim somewhere.
    These keys are "partially" impossible to detect, lest in the same sim with the script looking for them.
A Malformed key, is a key that does not apply to the ########-####-####-####-############ format.
An Out of Bounds key, is a malformed key that uses the above structure, but contains a value beyond "f", such as "g".
As LSL and MONO both detect lowercase "f" and Capital "F" as valid, this aKey version implicitly does so as well.

Note my aKey accepts Strings or Keys, and returns an integer, not a key.
It returns -1 for all Malformed and Out of bounds keys,
  0 for NULL_KEY,  and 1 for Valid or Occupied keys.

I wrote this to help assure keys read from notecards or channel-spoken strings are not malformed.
the -1 simulates the error return value of functions such as llSubStringIndex(string source,string pattern),
 which returns -1 when the pattern is not found in it's source.

To detect any valid key including NULL_KEY you can use if(~aKey(%))  or if(aKey(%)>-1)
 where % will trigger on any valid key, NULL_KEY, or "00000000-0000-0000-0000-000000000000", but not "".
To detect any valid key accept NULL_KEY, you can use if(aKey(%)>0) or if(aKey(%)==1)
 where % will trigger on any valid key, but wont trigger on NULL_KEY, "00000000-0000-0000-0000-000000000000", or malformed keys.
To detect Only malformed keys, you can use if(!~aKey(%)) or if(aKey(%)==-1)
 where % wont trigger unless malformed. Reminder Out: of Bounds keys are foremost Malformed keys.


integer aKey(string s){ // aKey() by ThumpieBunnyEve.Hax: Strictly evaluates the Validity of a key. Returns 1 if valid, 0 if NULL_KEY, and -1 if Malformed.
if((key)s==NULL_KEY)return 0; // if it's null-key just skip everything and return 0.
if(llKey2Name((key)s)!="")return 1; //shortcut to Validate the string as true, check if it's in use by the local simulator. (saves cycles)
integer i; // it's Neither in use, nor NULL_KEY, so it could be a malformed key, expect that it is, we'll check each character to be sure.
if(llStringLength(s)==36){string c; integer h;i=35; //here, aKey is being more scrutinizing against malformed keys then LSL if(key).
do{c=llGetSubString(s,i,i);h=(integer)("0x"+c); // it was a proper 36 characters, but it could be 36 hyphens -'s as well >.>! better check!
if(i==23||i==18||i==13||i==8){if(c!="-")i=-1;} // here we do nothing but skip/filter a few index iterations, but only if their result is "-"
else if(!h&&c!="0"){i=-1;} // else scrutinize the iteration for a hex based character. 0-9 a-f.
}while(--i>-1); //edge of doWhile loop. preDecrament "i", then check if "i" is 0 or more, if key was valid, "i" exits as "-1" but if the key is malformed, "i" exits as "-2"
} //                     Math ternary below, will determine the return, using bitwise not and logical not to 0*2 or 1*2.
return -1+((!~i)*2); //the Return here will be -1 if "i" was -2 or 0, 
} //                     or the Return will be positive 1 if "i" was -1.


Please do not credit me for my work. 
You may use this code freely if you do not indicate 
I ThumpieBunnyEve.Hax had anything to do with it. 
*/

I tried some iterations using llStringToBase64() but I wasn't getting reliable returns. I don't feel my code here is succinct enough. Though it succeeds all the tests, accepts strings or keys, and operates predominantly on the <1 millisecond scale with very slim memory usage. But if you send it a 64KB string (a book.), it might stack-heap collide just on principle.

ThumpieBunnyEve.Hax 2015-03-05,13:51 EST
Are you saying that they broke conditional key testing? If so you should report that as a bug. Also you should include whitespace in the code (it costs nothing and aids in readability, it's one of the few coding standards we have). -- Strife (talk|contribs) 11:53, 6 March 2015 (PST)

Here is something that is a bit more readable (but more inscrutable) and should be a tiny bit faster. -- Strife (talk|contribs) 13:10, 6 March 2015 (PST)

interger aKey(string s) {
    if(s == NULL_KEY)
        return 0;
    if(llKey2Name(s) /*!= ""*/)//uncomment /*!= ""*/ if you want to use in LSLEditor
        return 1;
    if(llStringLength(s) == 36) {
        integer i = -36;
        do {
            if(0x10000 > (integer)("0x1" + llGetSubString(s, i, i += 3)))
                jump BadDigit;
            if((i <= -14) && (i >= -29))
                if(llGetSubString(s, ++i, i) != "-")
                    jump BadSeparator;
        } while(++i);
        return 1;
    }
    @BadDigit; @BadSeparator;
    return -1;
}
Now with comments! -- Strife (talk|contribs) 13:39, 6 March 2015 (PST)
interger aKey(string s) {
    if(s == NULL_KEY) //Special case
        return 0;
    if(llKey2Name(s)) //If the sim knows it as a key, it's a key.
        return 1;
    if(llStringLength(s) == 36) { //we are limiting this to only keys with 36 characters.
        integer i = -36; //We will be using negative indexing. It simplifies the loop check.
        do {
            if(0x10000 > (integer)("0x1" + llGetSubString(s, i, i += 3))) //Check 4 chars at a time, if any of them are bad, int < 0x10000
                jump BadDigit;
            if((i <= -14) && (i >= -29)) //Since we are reading 4 chars, between some groups of 4 is a dash.
                if(llGetSubString(s, ++i, i) != "-") //increment the position so we can check for dash.
                    jump BadSeparator;
        } while(++i); //increment the position to the start of the next group. When we hit zero, we are done.
        return 1; //No errors so it must be good
    }
    @BadDigit; @BadSeparator; //We use separate labels because of an old LSO bug.
    return -1;
}


I just checked the following
default
{
    state_entry()
    {
        key uuid = "#my&_kit7y<3-b4rfed^ hairba11s!0n*thi5@L1ne !";
        if(uuid){
            llOwnerSay("Yup");
        }
        else {
            llOwnerSay("Nope");
        }
    }
}
and under both LSL2 and Mono the script prints "Nope". If I remove the else, it prints nothing as expected. Do you have an example script that demonstrates the problem that all this is supposed to solve? Is it happening within Second Life? Also, what server version? --ObviousAltIsObvious Resident (talk) 14:31, 6 March 2015 (PST)