Difference between revisions of "Category talk:LSL Key"
(38 intermediate revisions by 5 users not shown) | |||
Line 7: | Line 7: | ||
::Kinda late, but not all avatars have a 4 there. Some old stats: http://w-hat.com/keydistribution.txt (notice also the 20th character). -- [[User:Masakazu Kojima|Masakazu Kojima]] 23:13, 17 May 2009 (UTC) | ::Kinda late, but not all avatars have a 4 there. Some old stats: http://w-hat.com/keydistribution.txt (notice also the 20th character). -- [[User:Masakazu Kojima|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)). -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 08:50, 18 May 2009 (UTC) | |||
::::It's the 'variant' field, see 4.1.1/4.4 of the RFC. [[User:Masakazu Kojima|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}}. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 18:56, 24 May 2009 (UTC) | |||
== Comparison of Keys not working right == | |||
I just did this: | |||
<source lang="lsl2"> | |||
//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 | |||
</source> | |||
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? | |||
[[User:Feyn Graves|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. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 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" | |||
<source lang="lsl2"> | |||
//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 | |||
</source> | |||
== 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:<source lang="lsl2">key uuid = "#my&_kit7y<3-b4rfed^ hairba11s!0n*thi5@L1ne !"</source> | |||
with this as quoted from [https://wiki.secondlife.com/wiki/Category:LSL_Key LSL_Key] | |||
<source lang="lsl2">if(uuid){ | |||
//do something | |||
}</source>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. | |||
<source lang="lsl2">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;i=35; | |||
do{c=llGetSubString(s,i,i); | |||
if(i==23||i==18||i==13||i==8){if(c!="-")i=-1;} | |||
else if(!(integer)("0x"+c)&&c!="0"){i=-1;} | |||
}while(--i>-1); | |||
} | |||
return -1+((!~i)*2); | |||
}</source> | |||
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. | |||
<source lang="lsl2"> | |||
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); | |||
if(i==23||i==18||i==13||i==8){if(c!="-")i=-1;} | |||
else if(!(integer)("0x"+c)&&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. | |||
*/</source> | |||
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. | |||
<table width="100%"><tr><td align=right>''ThumpieBunnyEve.Hax'' 2015-03-05,13:51 EST</td></tr></table> | |||
: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). -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 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. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 13:10, 6 March 2015 (PST) | |||
<source lang="lsl2"> | |||
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; | |||
} | |||
</source> | |||
:Now with comments! -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 13:39, 6 March 2015 (PST) | |||
<source lang="lsl2"> | |||
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; | |||
} | |||
</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) | |||
::I'm guessing Hax is on some weird non LL server that has crappy LSL support. It's not OpenSim 0.8 because I just checked the opensim source. I'm just enjoying writing some LSL functions. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 18:30, 6 March 2015 (PST) | |||
==Who's Line is it Anyway?== | |||
Hay Strife, | |||
Please explain for everyone how your | |||
<source lang="lsl2">if(0x10000>(integer)("0x1"+llGetSubString(s,i,i+=3))){</source> | |||
Line checks each of the 4 character spaces at once to assure each character in the 4 is less then F and more then 0. | |||
Mine moved character by character, and just checked the decimal value of each, to assure it was < F and > 0, Unless it was at the position of a hyphen. | |||
After just a few tests, as fast as mine was, it's evident yours is faster. Though I've not wrapped my head around how your checking 4 characters at once assuring each is <F. | |||
I still use this function over if(Key?) because it can tell me if a key is Malformed, not just NULL_KEY. | |||
Also, I have 7 year old scripts, that used llMessageLinked() to transmit secondary strings in the Key-field(a known/documented hack). Back then using if(key) inside of link_message() would be TRUE, even for a "Key" such as "Gobelty-gook-sent through here!". I was not aware that the functionality had changed since then. But in my present script still choose to return a "-1" value for a malformed key, as apposed to 0 for both null and malformed keys. Simply so i can alert the user of my script that they have misrepresented a key entry in their user input. | |||
Erf... I just tested what ObviousAltIsObvious posted, in LSLEditor 2.55 | |||
<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 it Very clearly outputs: | |||
<nowiki>Object: Yup</nowiki> | |||
in the LSLEditor 2.55 simulator console. And not the indicated "Nope" ObviousAltIsObvious was reporting for Mono and LSL. | |||
This inconsistency has furthered my desire to byte by byte validate against malformed keys. | |||
So, as it stands, the fastest method that returns -1 for malformed keys, is based on ObviousAltIsObvious code, but behaves as expected ONLY in ''LSL'' and ''Mono''. '''Not LSLEditor'''. | |||
<source lang="lsl2">// This version based on ObviousAltIsObvious's code, | |||
//is faster then Strife's or mine but is not compatible with LSLEditor 2.55 | |||
integer aKey(string s){ | |||
if((key)s) return 1; | |||
else if(s=="00000000-0000-0000-0000-000000000000")return 0; | |||
return -1; | |||
} | |||
//here's a demo script to illustrate it: | |||
default{state_entry(){ | |||
llOwnerSay((string)aKey("a444ef2d-b718-edce-fd1a-c5c09f658a8f")); //Valid key Expected 1 Got 1 | |||
llOwnerSay((string)aKey(NULL_KEY)); //Valid Key with Null value: Expecting 0 Got 0 | |||
llOwnerSay((string)aKey("#my&_kit7y<3-b4rfed^ hairba11s!0n*thi5@L1ne !")); //Malformed: Expecting -1 Got -1 | |||
llOwnerSay((string)aKey("")); //Malformed: Expecting -1 Got -1 | |||
llOwnerSay((string)aKey((key)"")); //Malformed typecast to a actual key: Input still is "", so Expecting -1 Got -1 | |||
}}</source> | |||
The above Results in the following expected output in ''LSL'' and ''Mono'': | |||
<nowiki>Object: 1 | |||
Object: 0 | |||
Object: -1 | |||
Object: -1 | |||
Object: -1</nowiki> | |||
However in '''LSLEditor 2.55''' the exact same script outputs the following results. | |||
<nowiki>Object: 1 | |||
Object: 0 | |||
Object: 1 ← there's the change. | |||
Object: -1 | |||
Object: -1</nowiki> | |||
Strife's seems to be the best choice for compatibility between LSLEditor and LSL/Mono. | |||
So apparently LSLEditor 2.55 needs an update in order to mirror LSL/Mono simulation properly. At present LSLEditor assumes garbage data is a valid key. *grunts* | |||
[[User:ThumpieBunnyEve Hax|ThumpieBunnyEve Hax]] ([[User talk:ThumpieBunnyEve Hax|talk]]) 09:14, 13 March 2015 (PDT) | |||
---- | |||
: I suspected that was the problem. LSL Editor has never had a faithful implementation of LSL. It really can't be trusted as an error checking tool. --[[User:ObviousAltIsObvious Resident|ObviousAltIsObvious Resident]] ([[User talk:ObviousAltIsObvious Resident|talk]]) 11:28, 13 March 2015 (PDT) | |||
---- | |||
Ok, so here is now it works: | |||
In the best case scenario: You get 4 hex characters. We prefix it with a 0x and parse it as an integer. The value is between 0x0 and 0xFFFF. Now 0x0 is not expressed as 0x0, it's really 0x0000. We always get 4 characters. Now by attaching a 1 to the front and the range is now 0x10000 to 0x1FFFF. However lets say you have an non-hex character. Our string becomes something like "0x1000w" or "0x1wwww". When these bad strings get parsed the values are in the range of 0x1 to 0x1FFF. By adding the 1 to the front we can tell a number that is all zeros from one that is full of bad characters. I did this so I could drop the zero-char check. I made it work 4 chars at a time so that it would do fewer checks for "-". My incrementing of the position is kinda ugly but I don't think you can reduce the LSO byte count. I'm not keen on the llKey2Name(s) test but it makes sense. | |||
It would be better to fix LSLEditor than to use this code. -- '''[[User:Strife_Onizuka|Strife]]''' <sup><small>([[User talk:Strife_Onizuka|talk]]|[[Special:Contributions/Strife_Onizuka|contribs]])</small></sup> 17:35, 13 March 2015 (PDT) |
Latest revision as of 16:35, 13 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)
- It's the 'variant' field, see 4.1.1/4.4 of the RFC. Masakazu Kojima 14:10, 24 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)
- 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)
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;i=35;
do{c=llGetSubString(s,i,i);
if(i==23||i==18||i==13||i==8){if(c!="-")i=-1;}
else if(!(integer)("0x"+c)&&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);
if(i==23||i==18||i==13||i==8){if(c!="-")i=-1;}
else if(!(integer)("0x"+c)&&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;
}
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)
Who's Line is it Anyway?
Hay Strife, Please explain for everyone how your
if(0x10000>(integer)("0x1"+llGetSubString(s,i,i+=3))){
Line checks each of the 4 character spaces at once to assure each character in the 4 is less then F and more then 0.
Mine moved character by character, and just checked the decimal value of each, to assure it was < F and > 0, Unless it was at the position of a hyphen.
After just a few tests, as fast as mine was, it's evident yours is faster. Though I've not wrapped my head around how your checking 4 characters at once assuring each is <F.
I still use this function over if(Key?) because it can tell me if a key is Malformed, not just NULL_KEY.
Also, I have 7 year old scripts, that used llMessageLinked() to transmit secondary strings in the Key-field(a known/documented hack). Back then using if(key) inside of link_message() would be TRUE, even for a "Key" such as "Gobelty-gook-sent through here!". I was not aware that the functionality had changed since then. But in my present script still choose to return a "-1" value for a malformed key, as apposed to 0 for both null and malformed keys. Simply so i can alert the user of my script that they have misrepresented a key entry in their user input.
Erf... I just tested what ObviousAltIsObvious posted, in LSLEditor 2.55
default
{
state_entry()
{
key uuid = "#my&_kit7y<3-b4rfed^ hairba11s!0n*thi5@L1ne !";
if(uuid){
llOwnerSay("Yup");
}
else {
llOwnerSay("Nope");
}
}
}
and it Very clearly outputs:
Object: Yup
in the LSLEditor 2.55 simulator console. And not the indicated "Nope" ObviousAltIsObvious was reporting for Mono and LSL.
This inconsistency has furthered my desire to byte by byte validate against malformed keys.
So, as it stands, the fastest method that returns -1 for malformed keys, is based on ObviousAltIsObvious code, but behaves as expected ONLY in LSL and Mono. Not LSLEditor.
// This version based on ObviousAltIsObvious's code,
//is faster then Strife's or mine but is not compatible with LSLEditor 2.55
integer aKey(string s){
if((key)s) return 1;
else if(s=="00000000-0000-0000-0000-000000000000")return 0;
return -1;
}
//here's a demo script to illustrate it:
default{state_entry(){
llOwnerSay((string)aKey("a444ef2d-b718-edce-fd1a-c5c09f658a8f")); //Valid key Expected 1 Got 1
llOwnerSay((string)aKey(NULL_KEY)); //Valid Key with Null value: Expecting 0 Got 0
llOwnerSay((string)aKey("#my&_kit7y<3-b4rfed^ hairba11s!0n*thi5@L1ne !")); //Malformed: Expecting -1 Got -1
llOwnerSay((string)aKey("")); //Malformed: Expecting -1 Got -1
llOwnerSay((string)aKey((key)"")); //Malformed typecast to a actual key: Input still is "", so Expecting -1 Got -1
}}
The above Results in the following expected output in LSL and Mono:
Object: 1 Object: 0 Object: -1 Object: -1 Object: -1
However in LSLEditor 2.55 the exact same script outputs the following results.
Object: 1 Object: 0 Object: 1 ← there's the change. Object: -1 Object: -1
Strife's seems to be the best choice for compatibility between LSLEditor and LSL/Mono.
So apparently LSLEditor 2.55 needs an update in order to mirror LSL/Mono simulation properly. At present LSLEditor assumes garbage data is a valid key. *grunts* ThumpieBunnyEve Hax (talk) 09:14, 13 March 2015 (PDT)
- I suspected that was the problem. LSL Editor has never had a faithful implementation of LSL. It really can't be trusted as an error checking tool. --ObviousAltIsObvious Resident (talk) 11:28, 13 March 2015 (PDT)
Ok, so here is now it works: In the best case scenario: You get 4 hex characters. We prefix it with a 0x and parse it as an integer. The value is between 0x0 and 0xFFFF. Now 0x0 is not expressed as 0x0, it's really 0x0000. We always get 4 characters. Now by attaching a 1 to the front and the range is now 0x10000 to 0x1FFFF. However lets say you have an non-hex character. Our string becomes something like "0x1000w" or "0x1wwww". When these bad strings get parsed the values are in the range of 0x1 to 0x1FFF. By adding the 1 to the front we can tell a number that is all zeros from one that is full of bad characters. I did this so I could drop the zero-char check. I made it work 4 chars at a time so that it would do fewer checks for "-". My incrementing of the position is kinda ugly but I don't think you can reduce the LSO byte count. I'm not keen on the llKey2Name(s) test but it makes sense.
It would be better to fix LSLEditor than to use this code. -- Strife (talk|contribs) 17:35, 13 March 2015 (PDT)