Jump to content

Notify when visitor arrives and leaves area


GloriaGlitter
 Share

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

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

Recommended Posts

@Mak7ka Your script works correctly, once you fix the compiler errors. I tested it without the HTTP requests, and it's detecting people entering and leaving.

As a side-note, since your script is using llGetDisplayName, there's a significant chance it'll send an empty string as the display name, since that can only be retrieved for a short while after an avatar leaves.

Edited by Wulfie Reanimator
Link to comment
Share on other sites

  • 1 year later...

@Mak7ka Your script almost worked. I think this is a working version detecting correctly people leaving as well.

float timer_interval = 10.0; // how often to scan the area
list im_recipients = []; // a list of the keys of the people who will get IMed, if blank owner is set
list current_visitors; // keeps a list of current and previous people who were in the area, global values to preserve them between scans
list previous_visitors;
string region_name;

default
{
    on_rez ()
    {
        llResetScript();
    }

    changed (integer changed)
    {
        if (changed & CHANGED_OWNER)
        {
            llResetScript();
        }
    }

    state_entry ()
    {
        if (im_recipients == [])
        {
            im_recipients = llGetOwner();
        }
        region_name = llGetRegionName();
        llSetTimerEvent (timer_interval); // start the timer (and wait for it to trigger)
    }

    timer ()
    {
        list arrivals;
        list departures;
        integer count;

		current_visitors = llGetAgentList (AGENT_LIST_PARCEL, []); // get the list of who's here now

        count = llGetListLength (current_visitors);
        while (count) // for each current visitor (if any), working backwards through the list (beacuse it's easier)
        {
            key visitor = llList2Key (current_visitors, --count); // decrement the counter (to convert it to a list index, and also set it up for the while loop test next iteration) and get the visitor's key
            if (llListFindList (previous_visitors, [visitor]) == -1) // are they in the list of people who were here last time?
            {
                arrivals += llGetDisplayName (visitor); // if not, add their name to the list of new arrivals
                llInstantMessage (im_recipients, "Arrival of " + visitor + " at " + region_name);
            }
        }

        count = llGetListLength (previous_visitors);
        while (count) // for each visitor who was here last time
        {
            key visitor = llList2Key (previous_visitors, --count);
            if (llGetAgentSize (visitor) == ZERO_VECTOR)
            {
                departures += llGetDisplayName (visitor); // if not, add their name to the list of departures
                llInstantMessage (im_recipients, "Departure of " + visitor + " from " + region_name); // Note that avatar names not present can not be resolved
            }
        }

        previous_visitors = current_visitors; // save the list of current visitors for next time;
    }
}

 

Link to comment
Share on other sites

1 hour ago, primerib1 said:

Using llListFindList() might be a bit slow-ish. To check existence.

Leverage llLinksetDataRead() instead.

Out of curiosity: the break-even point (very lazy test, looking for the last float in a list of floats) is around list length 20. Actually finding the entry without scanning the whole list makes it considerably faster, of course.

LinksetDataRead is not quite constant time (it finds entry with the key that was written first, (string)0.0, faster than it finds the nonexistence of a key) but close enough for practical purposes.

  • Like 2
Link to comment
Share on other sites

Okay so for the version using LSD:

// Released to the Public Domain by Claire Morgenthau
// If Public Domain is not applicable in your jurisdiction,
// you can use one of the following licenses:
// WTFPL, CC0, Unlicense, 0BSD, or MIT-0

list EMPL;

default {
    on_rez(integer n) {
        llLinksetDataDeleteFound("^v:.*", "");
        llResetScript();
    }
    state_entry() {
        llSetTimerEvent(5);
    }
    timer() {
        list visitors = llGetAgentList(AGENT_LIST_PARCEL_OWNER, EMPL);
        integer count = llGetListLength(visitors);
        key v;
        string nm;
        string k;
        while (count) {
            v = llList2Key(visitors, --count);
            if (llLinksetDataRead(k = ("v:" + (string)v)) == "") {
                llOwnerSay(">>> " + (nm = llGetDisplayName(v)) + " entered.");
                llLinksetDataWrite(k, nm);
            }
        }
        visitors = llLinksetDataFindKeys("^v:", 0, 0);
        count = llGetListLength(visitors);
        while (count) {
            v = (key)(llGetSubString((k = llList2String(visitors, --count)), 2, -1));
            if (llGetAgentSize(v) == ZERO_VECTOR ) {
                llOwnerSay("<<< " + llLinksetDataRead(k) + " has left.");
                llLinksetDataDelete(k);
            }
        }
        // Trigger GC
        visitors = EMPL;
        llSleep(0.01);
    }
}

Adjust as necessary.

One change I suggest: If you plop this into an item you wear, detect if you teleported somewhere and suppress the "arrival" message; pre-populate the LSD with list of avatars of the place you teleported to, then business as usual.

Edited by primerib1
  • Thanks 1
Link to comment
Share on other sites

2 hours ago, primerib1 said:
"^v:.*"

FYI, ".*" is implied at the beginning and end of a regex unless "^" (beginning of key) or "$" (end of key) is specified. So, "^v:" should be equivalent.

*This was not the case in early implementations of LSD functions, but has been fixed for some time.

Link to comment
Share on other sites

Eh, stupid me. Why need to record the name? We can use the "secondlife:///app/agent/UUID/about" URL instead.

So here's a much improved version:

  • Uses the secondlife URL
  • Excludes self
  • Pre-populates on first rez / teleport
  • Tells how far the visitor is from us (e.g., when you're in a skybox and visitor arrives at ground level)

 

// Released to the Public Domain by Claire Morgenthau
// If Public Domain is not applicable in your jurisdiction,
// you can use one of the following licenses:
// WTFPL, CC0, Unlicense, 0BSD, or MIT-0

float EVERY = 2.5;

list EMPL;
list GETPOS = [OBJECT_POS];
key ME;

GatherExisting() {
    llSetTimerEvent(0);
    llLinksetDataDeleteFound("^v:", "");
    list avs = llGetAgentList(AGENT_LIST_PARCEL_OWNER, EMPL);
    integer count = llGetListLength(avs);
    llOwnerSay("||| Detected " + (string)(count - 1) + " OTHER avatars on current parcelset.");
    vector mypos = llGetPos();
    key v;
    string nm;
    vector pos;
    list nms;
    while (count) {
        v = llList2Key(avs, --count);
        if (ME == v) jump skip; // Skip recording ourselves
        llLinksetDataWrite("v:" + (string)v, (nm = llGetDisplayName(v)));
        pos = llList2Vector(llGetObjectDetails(v, GETPOS), 0);
        nms += nm + "(" + (string)((integer)(llVecDist(pos, mypos))) + "m)";
        @skip;
    }
    llOwnerSay("||| " + llDumpList2String(nms, ", "));
    // Trigger GC
    nms = avs = EMPL;
    llSleep(0.01);
    llSetTimerEvent(EVERY);
    return;            
}

default {
    on_rez(integer n) {
        llResetScript();
    }
    state_entry() {
        ME = llGetOwner();
        GatherExisting();
    }
    timer() {
        vector mypos = llGetPos();
        list visitors = llGetAgentList(AGENT_LIST_PARCEL_OWNER, EMPL);
        integer count = llGetListLength(visitors);
        key v;
        string k;
        vector pos;
        while (count) {
            v = llList2Key(visitors, --count);
            if (ME == v) jump skip1; // Skip recording ourselves
            if (llLinksetDataRead(k = ("v:" + (string)v)) == "") {
                pos = llList2Vector(llGetObjectDetails(v, GETPOS), 0);
                llOwnerSay(
                    ">>> secondlife:///app/agent/" + (string)v + "/about entered, "
                    + (string)((integer)(llVecDist(pos, mypos))) + "m"
                );
                llLinksetDataWrite(k, "1");
            }
            @skip1;
        }
        visitors = llLinksetDataFindKeys("^v:", 0, 0);
        count = llGetListLength(visitors);
        while (count) {
            v = (key)(llGetSubString((k = llList2String(visitors, --count)), 2, -1));
            if (ME == v) jump skip2; // This should never happen, but we just do a defensive one here
            if (llGetAgentSize(v) == ZERO_VECTOR ) {
                llOwnerSay("<<< secondlife:///app/agent/" + (string)v + "/about has left.");
                llLinksetDataDelete(k);
            }
            @skip2;
        }
        visitors = EMPL;
        llSleep(0.01);
    }
    changed(integer change) {
        if (change & CHANGED_TELEPORT) {
            llResetScript();
        }
    }
}

 

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

Here, if anyone is interested, is the code my NPCs use for that: https://github.com/John-Nagle/l*****ils/blob/master/npc/pathavatartrack.lsl

The basic search for agents is the usual

agents = llGetAgentList(AGENT_LIST_PARCEL_OWNER,[]);

Lists of avatars are checked with llListFindList, which is linear search but at the level below LSL, so it's not too slow. All lists are by avatar UUID, not name.

What all that is doing is finding new avatars on the parcels to greet. There's much filtering. Avatars at huge elevation differences (skyboxes) are ignored, because the NPCs can't get there. Avatars are only to be greeted once per visit, so there's a list of avatars greeted and a list of those who left within the last five minutes, so stepping off the parcel doesn't cause an immediate re-greet. There's a list of avatars where greeting failed (the NPCs couldn't get close to them), and those will be re-tried, at increasing time intervals.  Of the eligible avatars, the one closest to the NPC is greeted first. This keeps the NPCs from being too annoying.

There's also a check for running out of script memory, to avoid script crashes if there are too many visitors.

Notecard and landmark distributors could benefit from some of those checks. There are places which keep hitting on you to accept their notecard. Tracking who's been seen recently and who's been processed (given a notecard, greeted, etc.) makes such tools much more tolerable.

Oh, and don't send popup messages to off-parcel avatars. That's a TOS violation and really annoying when you're driving.

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...
On 12/3/2023 at 4:42 AM, animats said:

Notecard and landmark distributors could benefit from some of those checks. There are places which keep hitting on you to accept their notecard. Tracking who's been seen recently and who's been processed (given a notecard, greeted, etc.) makes such tools much more tolerable.

In the mainbase of my group, I created a script that does just that.

Scan for people in the parcel, if the people is not in an exclusion list, check when they last seen (Unix Epoch). If last seen is > 1 month ago, or has never been seen, send the visitor a package containing (current) rules of the parcel, what's new, newsletter, etc.

Link to comment
Share on other sites

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