Difference between revisions of "Category talk:LSL LinksetData"

From Second Life Wiki
Jump to navigation Jump to search
(3 Best practices for Linkset Data in shared scripting environments)
Line 16: Line 16:


— [[User:Gwyneth Llewelyn|Gwyneth Llewelyn]] ([[User talk:Gwyneth Llewelyn|talk]]) 04:23, 20 October 2022 (PDT)
— [[User:Gwyneth Llewelyn|Gwyneth Llewelyn]] ([[User talk:Gwyneth Llewelyn|talk]]) 04:23, 20 October 2022 (PDT)
== Best practices ==
In discussions with other scripters, I noticed not everyone is aware that linkset data (LSD) is '''shared''' memory: Every script inside a linkset, be it your own or 3rd party scripts, share that memory. Unless you can be 100% sure that you are the only one being able to add scripts to a linkset, you can't be sure of the integrity of the LSD. Considering this, I have three best-practice suggestions for scripters using linkset data.
=== 1. Prefix keys ===
Prefixing all your keys avoids namespace collisions. An obvious key name like 'license' could be easily overwritten by another script in the linkset. If you preset the key with e.g. 'PSTO1-license', an accidental overwrite is far less likely. The prefixes I use in my scripts consist of:
* Two letters for the author name: PS = Peter Stindberg
* Two letters for the object/product: TS = Test Script
* One number as a versioning hint: 1 = first version of the prefix
Along with a dash, this takes up 6 characters, which is a good balance between being sufficiently unique and not consuming too much storage.
P.S. Protecting keys with a password is '''not''' the option to rely on, as it might prevent a 3rd party script from using their own LSD-keys. Prefixing is the way better approach, and of course you can password protect your prefixed keys as well.
=== 2. Check storage before writing ===
With increasing popularity of LSD, the typical progression of scripter capabilities will start, beginning with "Hooray, memory galore!". Especially in shared scripting environments, be prepared that some 3rd party scripts will manage to fill up those juicy 64 kbytes soon enough. So before you store a key, check if there is actually room for it:
<pre>
integer lsd_write(string config_key, string config_value) {
    if (llLinksetDataAvailable() > llStringLength(prefix_str + config_key + config_value)) {
        return llLinksetDataWrite(config_key,config_value); 
    } else {
        return FALSE;
    }   
}
</pre>
=== 3. Prepare for the nuclear option ===
While you can protect individual keys against overwriting by using a password, [[llLinksetDataReset]] is the nuclear option and wipes '''all''' the keys, wether protected or not. It's a good idea to be prepared for that option by using the [[linkset_data]] event:
<pre>
    linkset_data( integer action, string name, string value )
    {
        if (action == LINKSETDATA_RESET)
        {
            llOwnerSay(identifier + ": A 3rd-party script has deleted my data storage, which is rude and speaks of the creator's poor development skills. Please remove the offending script (most likely the one you added last) as it will affect proper operation of this program.");
        }
    }
</pre>
In order to avoid deleting other scripter's keys yourself, a alternative to [[llLinksetDataReset]] is needed. That - unfortunately - will tale some execution time, especially with many keys, but it is the honorable and considerate option to do:
<pre>
lsd_reset(string prefix_str) {
    list prefixed_keys = llLinksetDataFindKeys( "^" + prefix_str + "*", 0, llLinksetDataCountKeys());
    integer j = llGetListLength(prefixed_keys);
    integer k;
    integer i = 0;
    while (i < j) {
        k = llLinksetDataDelete(llList2String(prefixed_keys, i));
        ++i;
    }
    llOwnerSay("lsd_reset deleted " + (string) j + " key prefixed with " +  prefix_str);
}
</pre>
----
I hope those 3 tips will help other scripters out there to write better code, and to be a good enighbour in shared script spaces.
— [[User:Peter Stindberg|Peter Stindberg]] ([[User talk:Peter Stindberg|talk]]) 11:50, 15 January 2023 (PDT)

Revision as of 12:51, 15 January 2023

Ok, so now (as of Oct 2022) we have two ways to address a key-value-pair datastore...

... one is for Experiences, the other for everything else.

Since the actual interface to each is so different, I wonder if it makes sense to keep both around? Wouldn't it be simpler to keep the Linkset Data functions (they have a nicer API, IMHO), as well as its own event (instead of the 'slow' dataserver event)? Scripts are already assignable to Experiences, so I guess this would allow all Experiences to 'share' the same KVP, using a common interface (instead of duplicating code).

The purpose would be having the exact same script relying on persistent storage that could work either assigned to an experience or as a stand-alone. Consider a simple snowball launcher script, which counts the number of snowballs that have hit a target (an avatar) and shows the total on hovertext (granted, this would not require, strictly speaking, any KVP storage and still be persistent, but it's just an example). You can use it to play with friends, and the counters hovering over each launcher would display the correct number of targets hit, so that you can 'prove' that you 'won' the ad-hoc game. But if you participate on the annual Linden Snowball Throwing Winter Event, and accept to participate in an Experience, then your snowball launcher would register the count globally on the Experience's own KVP storage instead — possibly to show a leaderboard and award a prize to the avatar that hit more Lindens during the event :)

I'm quite aware that this is a quite forced example, which could be done quite differently and still work, but my point is that you could have certain 'Experience-aware' items that would work outside the Experience as well, without the need of duplicating all the KVP storage code.

This would even allow for a simple way of reusing the same item across different Experiences (obviously assuming these would be 'compatible', of course) and still maintain data persistent — without needing to use HTTP requests to an off-world KVP. This would allow for, say, different hosts to create their own (independent) Experiences, but allow certain items across Experiences — think about role-playing games designed by different people, but where you can acquire a sword on one, acquire XPs or Skill points or whatever, then go to a similar RPG but hosted by someone else, and reuse your battle-tested sword (as well as your XPs!) on that Experience instead.

Again, you can do all the above by using an off-world server and a previously defined API, but how cool would it to do it without any off-world storage at all? :-)

Just my L$0.02...

Gwyneth Llewelyn (talk) 04:23, 20 October 2022 (PDT)

Best practices

In discussions with other scripters, I noticed not everyone is aware that linkset data (LSD) is shared memory: Every script inside a linkset, be it your own or 3rd party scripts, share that memory. Unless you can be 100% sure that you are the only one being able to add scripts to a linkset, you can't be sure of the integrity of the LSD. Considering this, I have three best-practice suggestions for scripters using linkset data.

1. Prefix keys

Prefixing all your keys avoids namespace collisions. An obvious key name like 'license' could be easily overwritten by another script in the linkset. If you preset the key with e.g. 'PSTO1-license', an accidental overwrite is far less likely. The prefixes I use in my scripts consist of:

  • Two letters for the author name: PS = Peter Stindberg
  • Two letters for the object/product: TS = Test Script
  • One number as a versioning hint: 1 = first version of the prefix

Along with a dash, this takes up 6 characters, which is a good balance between being sufficiently unique and not consuming too much storage.

P.S. Protecting keys with a password is not the option to rely on, as it might prevent a 3rd party script from using their own LSD-keys. Prefixing is the way better approach, and of course you can password protect your prefixed keys as well.

2. Check storage before writing

With increasing popularity of LSD, the typical progression of scripter capabilities will start, beginning with "Hooray, memory galore!". Especially in shared scripting environments, be prepared that some 3rd party scripts will manage to fill up those juicy 64 kbytes soon enough. So before you store a key, check if there is actually room for it:

integer lsd_write(string config_key, string config_value) {
    if (llLinksetDataAvailable() > llStringLength(prefix_str + config_key + config_value)) {
        return llLinksetDataWrite(config_key,config_value);  
    } else {
        return FALSE;
    }    
}

3. Prepare for the nuclear option

While you can protect individual keys against overwriting by using a password, llLinksetDataReset is the nuclear option and wipes all the keys, wether protected or not. It's a good idea to be prepared for that option by using the linkset_data event:

    linkset_data( integer action, string name, string value )
    {
        if (action == LINKSETDATA_RESET)
        {
            llOwnerSay(identifier + ": A 3rd-party script has deleted my data storage, which is rude and speaks of the creator's poor development skills. Please remove the offending script (most likely the one you added last) as it will affect proper operation of this program.");
        }
    }

In order to avoid deleting other scripter's keys yourself, a alternative to llLinksetDataReset is needed. That - unfortunately - will tale some execution time, especially with many keys, but it is the honorable and considerate option to do:

lsd_reset(string prefix_str) {
    list prefixed_keys = llLinksetDataFindKeys( "^" + prefix_str + "*", 0, llLinksetDataCountKeys());
    integer j = llGetListLength(prefixed_keys);
    integer k;
    integer i = 0;
    while (i < j) {
        k = llLinksetDataDelete(llList2String(prefixed_keys, i));
        ++i;
    }
    llOwnerSay("lsd_reset deleted " + (string) j + " key prefixed with " +  prefix_str);
}

I hope those 3 tips will help other scripters out there to write better code, and to be a good enighbour in shared script spaces.

Peter Stindberg (talk) 11:50, 15 January 2023 (PDT)