Jump to content

Converting a UUID into a channel number, and back again


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

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

Recommended Posts

So, for some time, ive been doing the following, to generate a random channel number.

integer pchan = (integer)("0x" + (string)llGetKey()) * -1;

but now i'm wondering, if i pass that value to another script,

Is there a way of working backwards, and calculating the original uuid, from that integer?

Link to comment
Share on other sites

1 minute ago, Biran Gould said:

Is there a way of working backwards, and calculating the original uuid, from that integer?

No, It's an information theory impossibility. A UUID contains 4 integers worth of information (actually slightly less, because one hex of a UUID is almost always 4, but that's an irrelevant fiddly detail) , so to put a UUID into an integer you have to throw 3 integers of information away, you can't get them back.

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

7 minutes ago, Biran Gould said:

if i pass that value to another script,

Thinking about actual use cases a bit more, in pretty much any scenario where one object talks to another, it's pretty easy to get the sender's key, either directly as one of the parameters in the listen event, or with llDetectedKey().

Link to comment
Share on other sites

4 minutes ago, Tuu Munz said:

Could maybe llRezObjectWithParams with REZ_PARAM_STRING help? (https://wiki.secondlife.com/wiki/LlRezObjectWithParams)

It will, once it's live. But for the OP's immediate purposes, I'd suppose the follower would have a pretty small set of potential targets readily populated by sensor or llGetAgentList, with a negligible likelihood of hash collision just matching all the agents with that integer "channel number". Can't reconstruct the agent UUID but can almost always uniquely match it in a region full of agents.

Link to comment
Share on other sites

2 hours ago, Biran Gould said:

I wanted to rez a prim, and instruct it to follow a specific avatar, without having to call the object that rezzed this prim, for the uuid to follow.

My usual 'hack' would be to temporarily change the description of the rezzer to the key of the target, but REZ_PARAM_STRING would indeed be better when it goes live.

  • Like 1
Link to comment
Share on other sites

Posted (edited)

i've tried REZ_PARAM_STRING on aditi, and it sends the key just fine , ( tho you have to use the bitflag ... 13 ):)

Edited by Xiija
Link to comment
Share on other sites

Posted (edited)

For rezzing an object, and then targeting a specific avatar, I would populate a dialog menu with detected residents, and have it so that when I selected a resident the object rezzed and listened on a secret channel, as the rezzer called out the target key on that channel (llRegionSay works best, IMO) after a .2-second pause. The rezzed object hears the key and targets the avatar as the one it's to follow.

It's just the easiest way I could work it out at the time (about 8 years ago).

To have it triggered by something other than a menu, such as targeting someone who bumped into the rezzer or something, just get their key on collision (or using llVolumeDetect or similar), and pass it the same way as above once the follower-object has rezzed.

Maybe not the simplest and most elegant, but it's always worked perfectly for me.

Edited by PheebyKatz
Link to comment
Share on other sites

  • 2 weeks later...
  • Lindens
On 6/6/2024 at 10:46 AM, Biran Gould said:

So, for some time, ive been doing the following, to generate a random channel number.

integer pchan = (integer)("0x" + (string)llGetKey()) * -1;

but now i'm wondering, if i pass that value to another script,

Is there a way of working backwards, and calculating the original uuid, from that integer?

My strategy for coming up with a unique chat channel for an object, that works quite well, is to use llHash.  The function takes a string and returns a hashed integer. 

So, if I want a unique chat channel for a given object I can write:
 

integer channel = llHash((string)llGetKey() + "A short name for the app");

Or if I want a group of related objects to all talk on the same channel I can write something along the lines of:

integer channel = llHash(llGetOwner() + "My app's name");

You don't have the ability to work backwards from this method though since llHash is one way. 

  • Thanks 2
Link to comment
Share on other sites

On 6/7/2024 at 10:42 AM, Xiija said:

i've tried REZ_PARAM_STRING on aditi, and it sends the key just fine , ( tho you have to use the bitflag ... 13 ):)

I hate that bitflag.

On 6/6/2024 at 2:03 PM, Biran Gould said:

I wanted to rez a prim, and instruct it to follow a specific avatar, without having to call the object that rezzed this prim, for the uuid to follow.

If I wanted to do that, I would convert each character to it's ordinal value, using llOrd.  That would give a long list of numbers.  Then, I would concatenate them into one long integer.  After that, I would do some reversible operations.  I would probably do something simple like divide that by 2.  Then, if I cared about security, I would append or add a password number at the end.  The password would be a magic number I came up with.  Then, to get the UUID, I would subtract or un-append my magic number and multiply by 2.  Because of the way division works in LSL, you could be one off, if the starting number is odd.  You could speak on the resulting channel, one channel below it, or one channel above it.  If you want to get rid of ambiguity, though, you could run a simple check in a sensor event.  Hypothetically, I guess there is a mathematical chance that you could have two things in a sensor result that have UUIDs exactly one digit apart, but that's statistically rare enough that it can't realistically happen.

That is a bit kludgy.  It's just a quick, off the top of my head concept.  I'm sure the other coders here could come up with something that is more reversible than what I just described, without checking UUIDs on a sensor, or things like that.

Link to comment
Share on other sites

On 6/21/2024 at 2:04 AM, Bubblesort Triskaidekaphobia said:

I hate that bitflag.

If I wanted to do that, I would convert each character to it's ordinal value, using llOrd.  That would give a long list of numbers.  Then, I would concatenate them into one long integer.  After that, I would do some reversible operations.  I would probably do something simple like divide that by 2.  Then, if I cared about security, I would append or add a password number at the end.  The password would be a magic number I came up with.  Then, to get the UUID, I would subtract or un-append my magic number and multiply by 2.  Because of the way division works in LSL, you could be one off, if the starting number is odd.  You could speak on the resulting channel, one channel below it, or one channel above it.  If you want to get rid of ambiguity, though, you could run a simple check in a sensor event.  Hypothetically, I guess there is a mathematical chance that you could have two things in a sensor result that have UUIDs exactly one digit apart, but that's statistically rare enough that it can't realistically happen.

That is a bit kludgy.  It's just a quick, off the top of my head concept.  I'm sure the other coders here could come up with something that is more reversible than what I just described, without checking UUIDs on a sensor, or things like that.

not to be picky but as Tessa said there is an information theory problem (sometimes known as the pigeonhole problem) No matter the algorithm we can't stuff 2 pigeons at the same time into a hole sized for 1 pigeon and get 2 pigeons back out, we can only get 1 pigeon out

for example:

in a 1 bit hole the pigeons in the hole can only be 0 or 1.  The hole can't contain both at the same time

a 128 bit pigeon can't be stuffed into a 32 bit hole and get back the original 128 bits.  At best we can only get back 32 bits of the 128 bits we stuffed into the hole. For us to get back the remainder 96 bits these (96 bits of information) have to be stuffed into another hole

 

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

2 hours ago, elleevelyn said:

not to be picky but as Tessa said there is an information theory problem (sometimes known as the pigeonhole problem) No matter the algorithm we can't stuff 2 pigeons at the same time into a hole sized for 1 pigeon and get 2 pigeons back out, we can only get 1 pigeon out

for example:

in a 1 bit hole the pigeons in the hole can only be 0 or 1.  The hole can't contain both at the same time

a 128 bit pigeon can't be stuffed into a 32 bit hole and get back the original 128 bits.  At best we can only get back 32 bits of the 128 bits we stuffed into the hole. For us to get back the remainder 96 bits these (96 bits of information) have to be stuffed into another hole

 

Oh yeah, there is definitely an information theory issue with the original post!  What I'm talking about is a way to avoid that.  There are way better programmers than me in this thread, so I may be wrong about all of this, but my understanding is that a UUID is something like 3.5 integers long, in memory space.  I think (integer)("0x" + (string)llGetKey()) truncates everything behind the first integer of memory space.  Once that truncates, the data is gone.  I'm not proposing anything gets truncated, though.  I'm trying to think up a way to preserve all the data, and maybe transform it a bit.

What I propose is that you translate each character into it's ASCII equivalent integer, then put them together into one big integer.  That might cause an overrun, because integers are only so large.  There is probably a way to avoid that.  Maybe chunk the list of integers into two or three integers, then subtract them, and use absolute value, instead of adding them?  That doesn't seem reversible.  Maybe use a reversible operation to make the integers smaller?  I don't know.  It's a thought experiment.

In the end, there are a million different ways to do what OP needs, without deriving a channel from a UUID in a reversible function.  So what I'm proposing is more theoretical than practical.

Link to comment
Share on other sites

27 minutes ago, Bubblesort Triskaidekaphobia said:

There is probably a way to avoid that.  Maybe chunk the list of integers into two or three integers, then subtract them, and use absolute value, instead of adding them?  That doesn't seem reversible.  Maybe use a reversible operation to make the integers smaller?  I don't know.  It's a thought experiment.

There is no difference between truncation and addition/subtraction to build some kind of a hash when it comes to data loss. You are using a non-bijective function that maps 2*32 bits into 32 bits when you do that to an integer (and if you were using absolute values at some point you'd be discarding an extra bit).

  • Thanks 1
Link to comment
Share on other sites

If you restrict the scope down to just ~avatars rather than arbitrary keys, it's at least theoretically possible to assign every avatar to an integer based on the order accounts were created (assign the first ever account to 1, 2nd to 2 etc.) but there's no way to access that information from just the UUID, and that would obviously break down if/when there were more than 2^32 (about 4 billion) accounts.

  • Like 1
Link to comment
Share on other sites

I just wanted something to fiddle with today, so I figured that I can chop a UUID into 5 integers, that fit in LSL integers, without data loss, or getting overruns.

How is this useful?  I don't know.  Maybe you can use these 5 numbers to channel hop?

Either way, it's a thing I wrote for fun.  It's ugly, but maybe somebody will find it useful, someday:

default{
    state_entry(){
        key My_Key = llGetKey();
        string KeyString = (string)My_Key;
        list ChoppedKey = llParseString2List(KeyString, ["-"], [""]);
        string LongHexKeyStr;
        
        integer count = 0;
        for(count; count<=4; count++){
            LongHexKeyStr += llList2String(ChoppedKey,count);
        }
        
        string IntegerOneStr = llGetSubString(LongHexKeyStr,0,6);
        string IntegerTwoStr = llGetSubString(LongHexKeyStr,7,13);
        string IntegerThreeStr = llGetSubString(LongHexKeyStr,14,20);
        string IntegerFourStr = llGetSubString(LongHexKeyStr,21,27);
        string IntegerFiveStr = llGetSubString(LongHexKeyStr,28,31);
        
        integer intOne = (integer)("0x" + IntegerOneStr);
        integer intTwo = (integer)("0x" + IntegerTwoStr);
        integer intThree = (integer)("0x" + IntegerThreeStr);
        integer intFour = (integer)("0x" + IntegerFourStr);
        integer intFive = (integer)("0x" + IntegerFiveStr);
        
        llOwnerSay("\n"+(string)intOne+"\n"+(string)intTwo+"\n"+(string)intThree+"\n"+(string)intFour+"\n"+(string)intFive+"\n");
    }
}

 

Link to comment
Share on other sites

On 6/7/2024 at 8:24 AM, Quistess Alpha said:

My usual 'hack' would be to temporarily change the description of the rezzer to the key of the target, but REZ_PARAM_STRING would indeed be better when it goes live.

this is the most efficient way to do this without using a listen, in the absence of REZ_PARAM_STRING

  • Like 1
Link to comment
Share on other sites

 I haven't tested it with a rezzer, but you could use this hash/unhash thing to

send an int to a rez param mebbe? ( this is just a touch box for demo )

* kudos to whoever wrote this :)



list codes = [

"031", "",      "032", " ",     "033", "!",     "034", "\"",    "035", "#",    
"036", "$",     "037", "%",     "038", "&",     "039", "'",     "040", "(",    
"041", ")",     "042", "*",     "043", "+",     "044", ",",     "045", "-",    
"046", ".",     "047", "/",     "048", "0",     "049", "1",     "050", "2",    
"051", "3",     "052", "4",     "053", "5",     "054", "6",     "055", "7",    
"056", "8",     "057", "9",     "058", ",",     "059", ";",     "060", "<",    
"061", "=",     "062", ">",     "063", "?",     "064", "@",     "065", "A",    
"066", "B",     "067", "C",     "068", "D",     "069", "E",     "070", "F",    
"071", "G",     "072", "H",     "073", "I",     "074", "J",     "075", "K",    
"076", "L",     "077", "M",     "078", "N",     "079", "O",     "080", "P",    
"081", "Q",     "082", "R",     "083", "S",     "084", "T",     "085", "U",    
"086", "V",     "087", "W",     "088", "X",     "089", "Y",     "090", "Z",    
"091", "[",     "092", "\\",    "093", "]",     "094", "^",     "095", "_",    
"096", "`",     "097", "a",     "098", "b",     "099", "c",     "100", "d",    
"101", "e",     "102", "f",     "103", "g",     "104", "h",     "105", "i",    
"106", "j",     "107", "k",     "108", "l",     "109", "m",     "110", "n",    
"111", "o",     "112", "p",     "113", "q",     "114", "r",     "115", "s",    
"116", "t",     "117", "u",     "118", "v",     "119", "w",     "120", "x",    
"121", "y",     "122", "z",     "123", "{",     "124", "|",     "125", "}",    
"126", "~",     "127", ""

];

hasher( string txt )
{  llOwnerSay("Owner Key: \n" + txt ); 
   string hashed;
   integer x;
   integer len =  llStringLength( txt );
   for( x = 0; x < len; ++x )
   {
      string letter =  llGetSubString( txt , x, x);
      integer idx = llListFindList( codes, [letter] );
      if ( idx )
      {  string tmp = llList2String( codes, idx-1 );
         hashed += tmp ;
      }
   }
   llOwnerSay("Key hashed: \n" + hashed + "\n" ); 
   llSleep(2.0);
   unhasher( hashed  );
}

unhasher( string txt )
{  string output;
   list unhash = [];
   integer len = llStringLength( txt );
   integer n;
   for( n = 0; n < len; n = n+3 )
   { string tmp = llGetSubString( txt, n, n+2 );
     unhash += tmp; // list of 3 digit codes
   }               
   integer x;
   integer len2 = llGetListLength( unhash );            
   for( x = 0; x < len2; ++x )
   {    
      string tmp  = llList2String( unhash, x );
      integer idx = llListFindList( codes, [tmp] );
      if ( idx )
      {  string cv = llList2String( codes, idx + 1 );
         output += cv;
      }           
   }
   llOwnerSay("Key unhashed: \n" + output );   
}

default
{
    state_entry()
    {      
    }   
    touch_start(integer num)
    { hasher( (string)llGetOwner() );
    }
}

....

Quote

[06:58] Hash Unhash  2 script 1.1: Owner Key:
06451596-818c-4975-8bc0-058ab2fe8274
[06:58] Hash Unhash  2 script 1.1: Key hashed:
048054052053049053057054045056049056099045052057055053045056098099048045048053056097098050102101056050055052

[06:58] Hash Unhash  2 script 1.1: Key unhashed:
06451596-818c-4975-8bc0-058ab2fe8274

 

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

2 hours ago, Xiija said:
list codes = [

looks like that was written before llChar and llOrd, could probably make some simple adjustments to improve efficiency and scope by replacing list lookup with llOrd and llChar calls.

Edited by Quistess Alpha
  • Like 2
Link to comment
Share on other sites

@Quistess Alpha

Good idea Quistess, seems to work :)

( this is in a touch box for demo, split into 2 scripts for rezzer/rezzed )

// ===== this func in rezzed obj too !
string unhasher( string txt )
{  string output;
   list unhash = [];
   integer len = llStringLength( txt );
   integer n;
   for( n = 0; n < len; n = n+3 )
   { string tmp = llGetSubString( txt, n, n+2 );
     unhash += tmp; // list of 3 digit codes
   }      
   output =  llDumpList2String( unhash , "");
   return output;  
}


default
{    // ============= in rezzer =======================
    touch_start(integer total_number)
    {  
      
        string avi = (string)llDetectedKey(0);      
        list test_list = [];
        string test_string2 = "";
    
        string sOrd;
        integer index;
        integer ord;
        for (index = 0; index < llStringLength( avi ); ++index)
        {  ord = llOrd( avi, index );
           sOrd = (string)ord;
           if( ord < 100 )
           { sOrd = "0" + sOrd; // pad 2 digit ords to make all 3 digit entries
           }
           test_list = test_list + [ sOrd ];
        }
        
      
        
        string t1 = llDumpList2String( test_list , "");  
        llOwnerSay("T1: " + t1);
        // integer send = (integer)t1;  // ===== THIS IS THE INTEGER TO USE IN REZ PARAM
        
        
       // ================  in rezzed obj   ========================================= 
       //  get t1 from rez param 
        string char = unhasher( t1 ) ; // use func from above
        for (index = 0; index < llGetListLength(test_list); ++index)
        { 
           ord = llList2Integer(test_list, index);
           char = llChar(ord);
           test_string2 = test_string2 + char;
        }
        
        llOwnerSay( "\nAvi Key: " + avi + 
                    "\n\nOrd: " + llDumpList2String(test_list, ", ")  +
                    "\n\nAvi Key Unhashed: " + test_string2 );
    }
}

result:

Quote

[06:04] Object: T1: 048054052053049053057054045056049056099045052057055053045056098099048045048053056097098050102101056050055052
[06:04] Object:
Avi Key: 06451596-818c-4975-8bc0-058ab2fe8274

Ord: 048, 054, 052, 053, 049, 053, 057, 054, 045, 056, 049, 056, 099, 045, 052, 057, 055, 053, 045, 056, 098, 099, 048, 045, 048, 053, 056, 097, 098, 050, 102, 101, 056, 050, 055, 052

Avi Key Unhashed: 06451596-818c-4975-8bc0-058ab2fe8274

 

Edited by Xiija
Link to comment
Share on other sites

On 6/27/2024 at 1:06 AM, Xiija said:

string t1 = llDumpList2String( test_list , "");  
integer send = (integer)t1;

string char = unhasher(ti);

this introduces the (typically inadvertent) hidden information channel problem which can sometimes lead us to an erroneous conclusion

consider:

string t1 = llDumpList2String( test_list , "");  
integer send = (integer)t1;

string received = (string)send;
string char = unhasher(received);

explaiin

the channel is:  send -> received. send being an integer type. received being a string cast from an integer type (channel width is 32 bits)

this channel is hidden as the original code is using the channel: string ti -> string ti (channel width in this case being all available string memory)

 

Edited by elleevelyn
()
Link to comment
Share on other sites

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