Jump to content
livb89

Saving information

Recommended Posts

I am thinking of writing a script that records who visits our parcel then we know who has been there. Possibly then the device could speak to them (in nearby) if they are not on a whitelist. But also we don't want it to repeatedly say the same thing as that would be annoying.

I got as far as getting the list and counting it using this:

list avatarsInRegion = llGetAgentList(AGENT_LIST_PARCEL, []);
integer numOfAvatars = llGetListLength(avatarsInRegion);

Now I have done that I hoped to go through the avatars and record them. But I have just discovered it is not possible to write to a notecard, only read.

Is there any SL way of recording information like this?

The only other way I can think is with an external web service / API but then that all gets a lot more complicated to set up.

Share this post


Link to post
Share on other sites

You can safely keep at least a couple hundred avatar keys in your script's memory.

Is it necessary for you to keep a lifetime history of every avatar that has ever visited the sim/parcel? Would you be okay with only X most recent avatars?

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)

Without an external API, as far as I know, you can only keep residents in memory and do some pruning on a daily / weekly basis. Sure, you can try to keep data in (limited) invisible hover text, child prim names, slave scripts via linked messages, do kvp etc. but at the end, you'll make it even more complicated. Still, if you make your code small and robust (like, keeping only avatar keys on that list), you should have around ~30-40 KBs of free space to spare.

Can be useful:

 

Edited by panterapolnocy
  • Thanks 1

Share this post


Link to post
Share on other sites

You have few options.  If you have an Experience to work with, you can save information in KVP -- essentially dumping it into an LL server.  If not, you'll have to store your list in your own script, shove it off to another utility script, or send it to an off-world server.  If you'd like to save it in your own script or a helper script, you might want to look at  http://wiki.secondlife.com/wiki/User:Rolig_Loon/High-Capacity_Greeter-Counter .  That's a somewhat old script now, but it still works quite well and can save an amazing amount of information in compressed form before it hits a limit.

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites
9 minutes ago, Wulfie Reanimator said:

You can safely keep at least a couple hundred avatar keys in your script's memory.

During one of my tests, I've found that at least 600 keys will fit into a list, but it will cause a stack heap collision before it reached 650 IIRC.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
Posted (edited)

How long do you want to hold on to the data? If it's for a short period, you could do as Wulfie suggests and keep the list in the script's memory, clearing it out periodically or as it gets too close capacity. The LSL wiki contains several example scripts for what you describe, known as "greeters". Some of them even employ compression to store lots and lots of avatar keys at once. Rolig's high capacity greeter is a great example.

Another option would be to use the Key-Value-Pair database that comes with each Experience as a way to store data within Second Life. Though that requires some extra setup and it very likely overkill for the application of a humble greeter.

Edited by Fenix Eldritch
typos
  • Thanks 1

Share this post


Link to post
Share on other sites

Thanks everyone - super helpful and so quickly!

I won't go through and quote reply everyone but I have taken all that in. I think keeping them in the script memory is fine for our purposes.

I kind of forgot that even when we log out the scripts keep running. I assume region restarts would cause them to be reset though?

Maybe I will look into storing KVPs with an experience as an upgraded version once I get the basic thing running. And I will certainly look into greeters! I know this thing probably already exists but I want to write my own so I can learn better.

  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)
2 minutes ago, livb89 said:

I kind of forgot that even when we log out the scripts keep running. I assume region restarts would cause them to be reset though?

You would think that, but no. A region restart does not reset any script, unless it is specifically designed to do so.
Look into the changed() event if you want to write a script that resets upon region restart.


 

Edited by Fritigern Gothly
Improved my answer
  • Thanks 1

Share this post


Link to post
Share on other sites
Just now, Fritigern Gothly said:

You would think that, but no.

Oh awesome!

  • Like 1

Share this post


Link to post
Share on other sites
4 minutes ago, livb89 said:

And I will certainly look into greeters! I know this thing probably already exists but I want to write my own so I can learn better.

By all means, use it as an example and tweak/rewrite to suite your specific needs. It's one of the best ways to lean. :)

  • Like 1

Share this post


Link to post
Share on other sites
7 minutes ago, Fritigern Gothly said:

During one of my tests, I've found that at least 600 keys will fit into a list, but it will cause a stack heap collision before it reached 650 IIRC.

Note that I said "safely." The absolute maximum amount of keys you can put into a list doesn't tell you what's practical. How big is the rest of the script? What else does it need to keep in memory? What kinds of operations does it need do on that list?

100 keys is about 9KB in Mono, which isn't very much. 100-300 keys is probably doable in most cases, but we need to know more before making any assumptions.

  • Like 1

Share this post


Link to post
Share on other sites

The script that I referenced uses compression techniques to jam a lot more unique partial keys into the script.  I don't recall how many I have been able to fit in it, but it was something like a couple of thousand.  And when it nears a limit, it starts deleting he oldest ones.  Even if you're writing your own script, it's worth looking to see how it works.

  • Like 4
  • Thanks 1

Share this post


Link to post
Share on other sites
3 minutes ago, Wulfie Reanimator said:

What kinds of operations does it need do on that list?

I am thinking it will record which avatars are present (checking say every 60 seconds) then it will record when they were there and possibly how long for. So a list of keys and maybe a parallel list of timestamps.

The owner will be able to click to see the list. Basically a security system of sorts with a greeter built in. A more polite system than a security orb, kinda. Anyone on our whitelist gets a more welcoming message :)

Share this post


Link to post
Share on other sites

Try storing part of the UUID and an abbreviated timestamp as a single list element  ( time + "~" + UUID, maybe). Then periodically -- every few hours, every week, whatever ... -- delete list elements with the oldest timestamps.  Those will always be the ones at the start of the list.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites
16 minutes ago, Wulfie Reanimator said:

The absolute maximum amount of keys you can put into a list doesn't tell you what's practical.

I never said it was practical, but I have noticed that many people are afraid to put more than just a handful of values in a list. My intention was only was to point out that a list can hold more keys than some people may think.

  • Like 2

Share this post


Link to post
Share on other sites

When the script is finished I'd do some profiling to see how many entries the script savely holds.

Once the script reaches that limit I add new entries at the beginning of the list and remove one entry from the end of the list.

The list will keep it's size then and I don't need a timestamp since the oldest entry is always at the end of the list.

So if I hold 250 entries I always have the newest 250 in the list. No need to think about a time for purging. Happens all automatic.

For higher capacity storage you should look into Rolig's script and if you need many many 1000 entries you can combine that with additional storage scripts but that's a point where the complexity reaches the level of external server usage.

  • Like 3

Share this post


Link to post
Share on other sites
4 hours ago, Rolig Loon said:

Try storing part of the UUID and an abbreviated timestamp as a single list element  ( time + "~" + UUID, maybe). Then periodically -- every few hours, every week, whatever ... -- delete list elements with the oldest timestamps.  Those will always be the ones at the start of the list.

It's a great idea, thanks. I will figure out some sort of string-based storage format like this, given that we can't make lists of complex objects like we can with arrays in javascript (I am a web designer in RL so know my way around that pretty well). 

Share this post


Link to post
Share on other sites
3 hours ago, Nova Convair said:

and I don't need a timestamp

The timestamp in my concept is more for literally recording the time of entry of the user in order to inform the owner. Not for list management purposes.

  • Like 1

Share this post


Link to post
Share on other sites
Posted (edited)
15 minutes ago, livb89 said:

It's a great idea, thanks. I will figure out some sort of string-based storage format like this, given that we can't make lists of complex objects like we can with arrays in javascript (I am a web designer in RL so know my way around that pretty well). 

You could think of a strided list as a multi-dimensional array.

I have no Java experience, but I used arrays in BASIC (yes, I am old 🙂 )

From the example at http://wiki.secondlife.com/wiki/Category:LSL_List#Strided_lists :

list demographics = ["John Adams", "male", "2007-06-22", 
                     "Shirley Bassey", "female", "2005-11-02", 
                     "Matt Damon", "male", "2008-05-19"];

(I added newlines for readability)

Alternatively, you could store each triplet as a single string, along these lines:

list demographics = ["John Adams|male|2007-06-22", 
                     "Shirley Bassey|female|2005-11-02", 
                     "Matt Damon|male|2008-05-19"];

and then split the lines with llGetSubString();

As for memory use, I did not test one method against the other, but it should not make a world of difference considering it's all strings.

 

 

Edited by Fritigern Gothly
Missed a word or two

Share this post


Link to post
Share on other sites

@Fritigern Gothly

I'd not heard of strided lists, that's good to know. and I have sen there is llList2ListStrided() for getting data out too.

Or yes... some known string separator that won't appear in the data itself. Lots of ways of doing this, thanks for the suggestions :)

 

 

  • Like 1

Share this post


Link to post
Share on other sites

Since it was not mentioned, you can also use llSetLinkMedia() to store up to 27KB of data in 1KB blocks per legacy prim using the current, home and whitelist params.

Caveats:

  • URL schema enforcement exists for current and home params. ("https://" added at the beginning of data if "http://" or "https://" is not present)
  • When using the whitelist param, there is a 64 count limit if data is in CSV format. (attempting to exceed it yields a script error)
  • Reducing face count results in data loss on faces that no longer exist.

Benefits:

  • Synchronous access to data. (No leaving/entering script events)
  • Data survives taking into inventory and rerezzing.
  • Data survives shift-copying in-world.
  • Error control/reporting with r/w failure.

Detriments:

  • The presence of data yields media beacons.
  • Data is not secure.

Share this post


Link to post
Share on other sites
16 minutes ago, Lucia Nightfire said:

Since it was not mentioned, you can also use llSetLinkMedia() to store up to 27KB of data in 1KB blocks per legacy prim using the current, home and whitelist params.

Interesting, I did not know of this trick yet. 
Could you post a code sample?

Share this post


Link to post
Share on other sites
Posted (edited)
default
{
    touch_end(integer i)
    {
        if (llDetectedKey(0) == llGetOwner())
        {
            string data = llGetObjectDesc();
            integer link = llGetLinkNumber();
            integer face;
            if (i = llClearLinkMedia(link,face))
            {
                llOwnerSay("Media failed to be cleared on link " + (string)link + ", face " + (string)face + " due to error " + (string)i);
                return;
            }
            if (i = llSetLinkMedia(link,face,[PRIM_MEDIA_WHITELIST,data,PRIM_MEDIA_PERMS_INTERACT,0x0,PRIM_MEDIA_PERMS_CONTROL,0x0]))
            {
                llOwnerSay("Media failed to be set on link " + (string)link + ", face " + (string)face + " due to error " + (string)i);
                return;
            }
            llOwnerSay("Data set on link " + (string)link + ", face " + (string)face + ": '" + llList2String(llGetLinkMedia(link,face,[PRIM_MEDIA_WHITELIST]),0) + "'");
        }
    }
}

I included example usage of llClearLinkMedia() even though it may or may not be necessary for any specific application.

I used None for interact and controls perms so a media interface isn't generated when anyone hovers their mouse over the prim, but this doesn't prevent the stored data from leaking.

I used the whitelist param above. Remember to parse out the url schema if using the current url or home url param.

Also, another caveat is that touch won't work on a face with media, so if you want to use a touch script in the same prim, do so on a face that doesn't have media.

Edited by Lucia Nightfire
  • Like 2

Share this post


Link to post
Share on other sites
5 hours ago, Lucia Nightfire said:

Since it was not mentioned, you can also use llSetLinkMedia() to store up to 27KB of data in 1KB blocks per legacy prim using the current, home and whitelist params.

Caveats:

  • URL schema enforcement exists for current and home params. ("https://" added at the beginning of data if "http://" or "https://" is not present)
  • When using the whitelist param, there is a 64 count limit if data is in CSV format. (attempting to exceed it yields a script error)
  • Reducing face count results in data loss on faces that no longer exist.

So if I understand this correctly this is storage is *designed* store media URLs but here we are doing a sort of a hack where we are using those fields to store other data as a string? Basically taking advantage of the fact that LL allow up to 1KB for each field (about 1024 characters? allowing for 'https://')

Is that right?

Share this post


Link to post
Share on other sites

I don't consider using string literals to hold data outside of valid url format a hack.

LSL functions have always allowed literal type inputs.

The execution of incorrect formatted data should have error control/handling tied to it and it usually does with LSL or the viewer.

You can store data in many unconventional/unintended ways with other functions. As long as you don't plan on actually attempting to execute those functions you're still using literal type inputs.

A very basic example, an object's description field is commonly used to hold operation data with many applications instead of an actual "description".

  • Like 1

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...