Jump to content

elleevelyn

Resident
  • Posts

    592
  • Joined

  • Last visited

Everything posted by elleevelyn

  1. yes that's a issue with brokers a broker-less multi-vendors - multi-scripts - multi-script-internal processes scheme which produces unique codes less in length than (string)key from the rule that scripts in object Contents must be uniquely named. A unique code can be obtained from linknumber + scriptname + counter example: Say script has same inventory name "A", then when dropped into the object with 3 links and 3 scripts in each link for 9 independent processes then prefixes: "0A", "0A 1", "0A 2", "1A", "1A 1", "1A 2", "2A", "2A 1", "2A 2" naming convention is scripts to be named with an alphabet char in the 1st position. The underscore char "_" reserved and must be coded internally as the prefix delimiter. Creating the prefix code, a one time occurrence in state_entry, edit [dynamically in changed event] each script can then manage its own sub-code scheme as it prefers. Examples: "0A 1_" + "P1" + (string)counter "0A 1_ + "P2" + (string)counter "1A 2_ + "0" + (string)counter "1A 2_ + "1" + (string)counter "2A_" + (string)counter where "PI", "P2", "0", "1" is a sub-process code internal to the script. In the "2A_" case there is no sub-process code (script only having 1 process) bit size of counter is up to the scripter, beyond 32 bits then a multiple integer type counter
  2. a way to do the multiple processes case is with a broker script that manages the allocation of unique key/codes. Each client process requests a block of codes for its own use. When the block is exhausted then either roll over the block or request a new block from the broker the number of processes and size of the assigned block can be quite large for example: integer 32 bits: 1st 8 bits is the unique process prefix: 256 processes and 2*24 unique codes for each process. Key = (string)Prefix + (string)blockcounter the bit split can be other. Like 7 bits for 128 processes. 9 bits for 512 processes with the block size adjusting accordingly scaling up to a 2 integer broker then it can be 2^32 processes each with a 2^32 block. Or 2^16 processes, 2^48 block. etc edit add. The 2 integer method is the simplest as the broker need only assign the process a unique integer identifier. The process managing its own 32 bit counter
  3. i have been following along your posts about your app is unclear to me why you are not using ordinal order to construct unique identifiers. eg. getNext(0) getNext(1) ... getNext(MAX) , getNext(0), ... MAX = 2^32 is a lot of unique keys until the counter rolls over. If need more that this, the getNext counter can use 2 or more integer types we can use a feistel network algo to change the ordinal order arrangement so getNext returns a random-looking unigue value but I don't see any gain for the performance hit
  4. you might be right about this. If I get a bit of time I might have a go at measuring the offerings to date
  5. i am not giving enough explanation, so I do so now in the context of packed keys. The purpose of packed keys storage in a greeter is so that during a period we will only greet a person one time. Person comes to our location, we greet them (typically the greet is some combination of greet message, give LM, invite to group, external website link, etc) the number of keys we can store determines the upper limit of the period Sam visits first time. Greeter: if (key not found in storage) {Welcome Sam! Save key to storage} Sam changes name to Sammy. Greeter: if (key found in storage) {Do nothing as Sammy has the same key as Sam and the key is already in storage} the next time Sam/Sammy is greeted as Sammy is when the next/new period begins. At start of new period Sam/Sammy is not in storage leveling this up. to what you have mentioned send a greetings message every time (Welcome! Sam then Welcome back! Sammy (as you mention), then only drop LMs, group invites, etc one time during the period is the dropping of LMs, etc that greeters typically also do that I didn't explain well when we are ok with dropping on our visitor every time they visit then is no need to pack and store keys
  6. just on this. We can't know that the username has changed when we have only saved the key on the general matter while I am into things like compression algorithms generally, is not always appropriate. Like it can be inappropriate to spend additional server cycles because of a desire to pack as much information as we can into a space one of the issues that comes with LSD is that Linden choose to go with UTF-8 in the LSD space, while the LSL space is UTF-16. A thing about this is that every LSL<->LSD transfer has to convert from UTF-16 to UTF-8 and vice versa. And while each instance is time trivial I am not sure how that is going to scale as scripters get more and more into using LSD, but I leave it to Linden to worry about that consider tho the impact of this on the specific issue being discussed a UTF-8 encoded key (optimally 19 bytes in the LSD space) consumes 38 bytes in the LSL space. Which is 20 more bytes than Key2Packed (optimally 18 bytes in the LSL UTF-16 space ). Without having done any LSD testing at all, the arithmetic tho suggests/shows that a string of UTF-8 encoded keys will not contain the max. number of keys that we might be hoping. For the reason the string of keys has to fit in the LSL space so we might have to rethink about how to write/design the key packing algorithm for LSD, The optimal (optimal in this case meaning most useful) would that the length of the encoded key in bytes is not greater in the LSL space than it is in the LSD space. And I think that a method that uses 2 UTF-8 byte chars is maybe the way to do this, if we want the byte length to be the same in both spaces. And here is a thing. Your packer algorithm in the Scripting Library touches on doing this and I think with some little more work we may be able to get a key packer which is optimal. (optimal meaning most useful in terms of how space is used) i haven't done any work on this myself. Naively tho, in the 2 byte case there are 11 bits available. 128 bits / 11 * 2 = 23.73 (24) bytes in both LSL and LSD space
  7. with shortened username then we probably better off using the middle X chars of the firstname rather than the first X chars. Without looking it up I am fairly confident that in the set of all name choices that people make with alphanumeric chars, then the set of the middle X chars is greater than the set of the first X chars or the last X chars example. pick 3 chars sam, sam123, sammy123 middle are: sam, am1, mmy as an aside. With the usecase of a visitor greeter then a changed username can be beneficial for both the location owner and the visitor. Example: My name is Sam. Greeter: Welcome Sam!.On my next visit in the same interval my name is now Sammy. Greeter: Welcome Sammy!. If the greeter is using my key then it won't greet me as Sammy (my new name which I paid $50 for)
  8. ^^ this in the OP case they are doing the photography on their own sky platform
  9. yes we have to guard against this. In the must always be valid case then all usernames (current and historical) for the same account, resolve to the same key edit ps. I just clarify this. In the imprecise case where we shorten the key then we can get a collision (meaning that 2 or more people can be seen as the same person). In the change username case then it can result in a duplicate of the same person the imprecise method we use depends on the use case for example: In a one gift per visitor giveaway store promotion then shortened key can result in a store visitor not receiving the gift. In the username case then everybody receives the gift, and a person can receive more than one copy of the gift should they change their name during the life of the promotion. As the store owner we have to weigh the pros and cons of this. Everybody gets the gift and some people may get more than one copy, or some people may not get a gift at all edit more: In the case where we are monitoring unique visitors to our store then the shorten key method means that we may slightly under count the number. And the username method means that we may slightly over count. In this case as the store owner we may decide that slightly under counting is preferable so we go with the shorten key method
  10. is kinda interesting when we start thinking about usernames in the LSD space LSD UTF-8 starts making it viable to use the username as the unique identifier. Max. username length is 32 chars (including the dot). As usernames are variable length then we may (most likely) find that over the set of all usernames we use less chars in the LSD space than the set of all packed keys for example your username is 20 chars. Mine is 11 with dot as the last char "elleevelyn." Combined 31 chars as opposed to packing both our keys for 38 chars total as a exercise, when we do the arithmetic then packing usernames gives a max. length of 25 chars and a min length of 2 chars. 26 and 3 if we include a separator char. Base37 to base2^32 to base121 to base128
  11. alternatively the region server could set the LI available to 0 when an owner has less than Xm on the region. Xm could be say 256. Given that to own mainland the min. sqm tier is 1024 then 256 is probably not unreasonable. 4 x 256 parcels on 4 different regions
  12. is not possible to encode the set of all keys into a 16 byte length string and have the full set decodable in either UTF-8 or UTF-16. LSL follows the C convention for handling strings. Char(0) denotes end of string and on its own in LSL returns an empty string (length 0) so the best we could ever do (assume all UTF-8 8-bit chars other than 0 could be decoded which is currently not the case) is 19 bytes. 128 bits / log2(127) = roundup(18.32 bytes) = 19 bytes the (recent-ish) examples in the Scripting Library are more about showing different ways to encode/pack keys in the LSD space. But there is a hard lower limit to this as shown above. So any further work (should it happen) would be to make more efficient methods. Efficient in this case meaning faster
  13. Prokofy Neva touched on this in another thread. At the rate Linden have set, it costs about $US10 more to pay in L$. Prokofy also touched on that an audience/market for this are those who earn L$ inworld and are unable to have a cashout account (Paypal/Skrill) due to their countries RL banking/credit systems restrictions (non-acceptance by Paypal/Sof of that person's RL currency etc) Global South countries for example another suggested audience are those who never cash out even if they could. And another person commented that they have a VAT compliant SL business presence already and will take advantage of this to more simply separate out their SL leisure use from their SL business use from an accounting pov. A simplification they suggested that is leaning toward getting another region for purely leisure reasons
  14. this makes a lot of sense to me. As you say it works for SL creatives from countries where there are obstacles to obtaining access to USD. Global South countries for instance also. So I think people in this circumstance will be happy with this Linden move this said though the affected people will still have the difficulty of obtaining USD for the Premium Plus payment, mitigated I suppose by paying premium annually (once a year rather than monthly)
  15. Linden would have done their due diligence on the VAT component I would think. What we don't know is the outcome of Linden's due diligence vis a vis VAT. It may be that Linden still does pay VAT even though they receive payment from Customer B in L$. Customer B paying tier inclusive of VAT. Might not be either. Customer B can determine this by requesting a VAT receipt for the L$ tier payment If a correlated argument is that Linden is discriminating against region owners who are not also Premium Plus members (i.e pay tier in L$ reserved to Premium Plus members) then the argument is seemingly valid in a universal context-free sense, but not necessarily so in the directed sense (contextual) of structured paid membership benefits for example a structured benefit of Premium Plus is that one can tier a homestead without also tiering a full region. A argument can be made that this is also unfair in the universal sense (i.e discriminates against non Premium Plus members). Yet this is not unfair in a structured system of paid memberships as Customer A can also obtain Premium Plus the same as Customer B. As Customer A is not denied the ability to do this then Customer A is not discriminated against
  16. examples showing the principle of using indexing as "pointers" in a list are here: and here
  17. we have to manage multiple users ourselves is a number of ways to do this. I wrote this example off the top of my head, so not tested. But I think can help to explain/show the basics of managing multiple users and listeners list users; // [userid, listener, timestamp] integer channelrange; default { touch_start(integer num) { // get user id (key) key thisuser = llDetectedKey(0); // remove thisuser from users list if exists, ignoring previous touches/actions from thisuser // that have not yet been submitted integer i = llListFindList(users, [thisuser]); if (~i) // found so remove { // remove previous assigned listener to thisuser llListenRemove(llList2Integer(users, i + 1)); // remove thisuser from users list users = llDeleteSubList(users, i, i + 2); } // generate a unique channel within a range. As wrote range is 1000 unique channel numbers // in range of the base channel -1234567 // this is not absolutely necessary for this script as wrote, but it does show a way to // create unique channel numbers within a range should we also need/want to track these in users list channelrange = ++channelrange % 1000; integer thischannel = -1234567 - channelrange; // generate a unique listener, Listen only for thisuser on thischannel using thislistener integer thislistener = llListen(thischannel, "", thisuser, ""); // append thisuser to our current list of users users += [thisuser, thislistener, llGetUnixTime()]; // display textbox to user llTextBox(thisuser, "", thischannel); } listen(integer channel, string name, key id, string text) { // find thisuser (key id) in users list integer i = llListFindList(users, [id]); if (~i) // found { // remove thislistener for thisuser's textbox llListenRemove(llList2Integer(users, i + 1)); // remove thisuser from users list users = llDeleteSubList(users, i, i + 2); // ...here we process text from thisuser (key id, string text)... } // ... here we might want to remove (age out) users/listeners that exceed some time // ... so our users list won't potentially blow up due to incomplete submissions for (i = -llGetListLength(users); i > 1; i += 3) { if (llGetUnixTime() > 300 + llList2Integer(users, i)) // 300 is 5 minutes. Change to suit { llListenRemove(llList2Integer(users, i + 1)); users = llDeleteSubList(users, i + 2, i); } } } }
  18. i had a go at doing this optimally. Optimal meaning within the log2 space of decodeable LSD chars, which at this time (January 2023) is 19 chars this version is not compatible with the above 20 char version /* pack/unpack a key into 19 7-bit symbols which should convert to 19 LSD-compatible UTF-8 bytes borrows the carry method found in Key2Packed from the forum Scripting Library as wrote it allows a TAB char to be optionally appended, for 20 symbols appending TAB prevents cross-boundary collisions when searching a string of encoded keys i havent tested this with LSD, but should be ok according to the LSD documentation to date (January 2023) public domain */ string Key2LSD(string k, integer delimit) { // encode high to low string result; integer carry; integer sign; // strip dash chars k = llDumpList2String(llParseString2List(k, ["-"], []), ""); // encode symbols. loop 4 times: 128 bits / 32 bits = 4 integer i; for (i; i < 25; i += 8) { // read 8 hex symbols into 32-bit buffer // strip off the sign bit and save integer buffer = (integer)("0x" + llGetSubString(k, i, i+7)); sign = (sign << 1) | ((buffer >> 31) & 1); buffer = buffer & 0x7FFFFFFF; // encode remainder 31 bits integer j; for (j; j < 4; ++j) { // undecodable values 00,01,08,0C,0E,1F // allow 09 (TAB) as a optional delimiter // leaves 121 decodable values // poke base121 into base128 symbol integer ord = buffer % 121; if (ord == 0x0) ord = 121; else if (ord == 0x1) ord = 122; else if (ord == 0x8) ord = 123; else if (ord == 0x9) ord = 124; else if (ord == 0xC) ord = 125; else if (ord == 0xE) ord = 126; else if (ord == 0x1F) ord = 127; result += llChar(ord); buffer /= 121; } // carry is base11: log2(0x80000000) - (4 * log2(121)) = log2(11) carry = carry * 11 + buffer; } // encode carry // 4 * log2(11) + 4 (high bits) = 17.84 bits // encode as base96 = 3 symbols // note that encoding carry in base121 will also use 3 symbols // so go with the more simple base96 method carry = (carry << 4) | sign; for (i = 0; i < 3; ++i) { result += llChar(carry % 96 + 32); carry /= 96; } // append delimiter when requested return result += llChar(delimit * 0x9); } key LSD2Key(string s) { // decode low to high // note: decoder ignores the 20th symbol (TAB) when appended integer dash = 0x3C; list hex = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]; string result; integer carry; integer sign; // decode carry and sign integer i; for (i = 18; i > 15; --i) carry = carry * 96 + (llOrd(s, i) - 32); sign = carry & 0x1F; carry = carry >> 4; // decode symbols for (i = 12; i >= 0; i -= 4) { integer buffer = carry % 11; carry /= 11; integer j; for (j = i + 3; j >= i; --j) { // depoke base128 to base121 integer ord = llOrd(s, j); if (ord > 120) { if (ord == 121) ord = 0x0; else if (ord == 122) ord = 0x1; else if (ord == 123) ord = 0x8; else if (ord == 124) ord = 0x9; else if (ord == 125) ord = 0xC; else if (ord == 126) ord = 0xE; else if (ord == 127) ord = 0x1F; } buffer = buffer * 121 + ord; } // restore sign bit to buffer buffer = buffer | ((sign & 1) << 31); sign = sign >> 1; // buffer to hex symbols string h; if (dash & 1) h = "-"; dash = dash >> 1; h += llList2String(hex, (buffer >> 28) & 0xF) + llList2String(hex, (buffer >> 24) & 0xF) + llList2String(hex, (buffer >> 20) & 0xF) + llList2String(hex, (buffer >> 16) & 0xF); if (dash & 1) h += "-"; dash = dash >> 1; h += llList2String(hex, (buffer >> 12) & 0xF) + llList2String(hex, (buffer >> 8) & 0xF) + llList2String(hex, (buffer >> 4) & 0xF) + llList2String(hex, buffer & 0xF); result = h + result; } return (key)result; } default { state_entry() { key k = llGenerateKey(); string e = Key2LSD(k, FALSE); // no delimiter string t = Key2LSD(k, TRUE); // delimited with TAB string d = LSD2Key(t); llOwnerSay("\nk: " + (string)k + "\nd: " + (string)d + "\ne: " + e + " len: " + (string)llStringLength(e) + "\nt: " + t + " len: " + (string)llStringLength(t)); } }
  19. i had some time so had a play. Another 20 symbol key packer /* pack/unpack a key into 20 7-bit symbols which should convert to 20 LSD-compatible UTF-8 bytes borrows the carry method found in Key2Packed from the forum Scripting Library i havent tested this at all with LSD, but should be ok according to the LSD docs to date (January 2023) public domain */ string Key2Lsd(string k) { // encode high to low string result; integer carry; integer i; k = llDumpList2String(llParseString2List(k, ["-"], []), ""); for (i; i < 31; i += 2) { // read in 2 hex symbols, add upto 2 high bits as needed to clear low control chars, encode low 7 bits into a 1 byte symbol integer ord = (integer)("0x" + llGetSubString(k, i, i+1)); if (ord < 32) ord += 288; else if ((ord > 127) & (ord <160)) ord += 192; result += llChar(ord & 127); // 2 high bits of ord are in 00,01,10. So encode these into the carry as log2(3) bits carry = carry * 3 + (ord >> 7); } // carry is 16 * log2(3) = 25.36 (26) bits. So encode carry (signals) in 4 symbols. Modulo 96 + 32 to clear low control chars for (i = 0; i < 4; ++i) { result += llChar(carry % 96 + 32); carry /= 96; } return result; } key Lsd2Key(string s) { // decode low to high integer dash = 0x550; list hex = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"]; string result; integer carry; integer i; // decode carry for(i = 19; i > 15; --i) carry = (carry * 96) + (llOrd(s, i) - 32); // decode symbols, add/sub bits as signalled by carry for (i = 15; ~i; --i) { integer ord = llOrd(s, i); integer car = carry % 3; if (car == 1) ord += 128; else if (car == 2) { if (ord < 64) ord -= 32; else ord += 64; } carry /= 3; result = (llList2String(hex, (ord >> 4) & 15) + llList2String(hex, ord & 15)) + result; if (dash & (1 << i)) result = "-" + result; } return (key)result; } default { state_entry() { key k = llGenerateKey(); string e = Key2Lsd(k); string d = Lsd2Key(e); llOwnerSay("\nk: " + (string)k + "\nd: " + d + "\ne: " + e + " len: " + (string)llStringLength(e)); } }
  20. We at Rothbard Manor are a discreet adult community. Our Mozart residential parcel has become available. We live on the ground at Rothbard Manor, we are not a skybox community Should you be a similarly-minded discreet person then Mozart, Rothbard Manor may very well suit you as it does us http://maps.secondlife.com/secondlife/Rothbard/162/186/25 To obtain: Buy the parcel for L$10. Pay the Mozart rent box. And welcome! to Rothbard Manor
×
×
  • Create New...