User:Rolig Loon/High-Capacity Greeter-Counter

From Second Life Wiki
Jump to navigation Jump to search

Features

Uses a memory compression algorithm to store hashed visitor UUID's in a string instead of using a list, thus avoiding the extra memory overhead associated with lists as well as reducing the UUID to a 3-character element in that string. See notes in the script and the reference to Becky Pippen's User page for a description of the technique.

  • Records unique visitors only.
  • Offers a notecard to unique visitors, if a notecard has been placed in inventory.
  • Provides the current weekly count, if requested by chat command on channel 37.
  • Optionally, provides the owner with a week-end report of the total number of visitors that week.
  • Stores the weekly total each week in the object's Description field, which will survive if the script is reset for any reason or if the object is taken to the owner's inventory.

Update (4/13/2013): Modified by replacing the sensor sweep that was in the original version with llGetAgentList, which is much more sim-friendly.

Script

//High-Capacity greeter/counter -- Rolig Loon --  March 2010
//Incorporating a memory compression algorithm by Becky Pippen (https://wiki.secondlife.com/wiki/User:Becky_Pippen/Hashing)
//Please keep all notes intact, including this header.

//Revision (4/13/2013): Replaced llSensor with llGetAgentList. As written, this sensor now detects all traffic in the parcel.
 
// Records unique visitors only, not repeaters. Stores the weekly count in the object's Description field and (optionally) sends a report to the owner
 
// Commands on channel 37:   "list" gives the current traffic count since the previous Saturday midnight
//                           "reports" toggles the weekly report to the owner on or off
 
// If there is a notecard in inventory, the counter will offer it on a visitor's first encounter.
 
integer count;
integer restart = TRUE;
integer TellOwner = TRUE;
string hashedNames;
integer hashSize = 3;
 
string getDay()
{
    list weekdays = ["Thursday", "Friday", "Saturday", "Sunday", "Monday", "Tuesday", "Wednesday"];
    integer hours = llGetUnixTime()/3600;
    integer days = (hours -4)/24;
    integer day_of_week = days%7;
    return llList2String(weekdays, day_of_week);
}
 
string encode15BitsToChar(integer num)
{
    num = 0x1000 + (num & 0x00007fff);
    return llUnescapeURL(
        escapeHexChar(0xe0 + (num >> 12)) +
        escapeHexChar(0x80 + ((num >> 6) & 0x3f)) +
        escapeHexChar(0x80 + (num & 0x3f)));
}
 
string escapeHexChar(integer n)
{
    string hexChars = "0123456789abcdef";
    return "%" +
        llGetSubString(hexChars, n >> 4, n >> 4) +
        llGetSubString(hexChars, n & 0xf, n & 0xf);
}
 
default
{
    state_entry()
    {
        llSetTimerEvent( 30.0 );
        llListen(37,"","","");
    }
 
    listen(integer channel, string name, key id, string msg)
    {
        if (llToLower(msg) == "list")
        {
            llSay(0, "We have had " + (string)count + " unique visitors since midnight last Saturday.");
        }
        else if (llToLower(msg) == "reports") // Toggles weekly report to owner on /off
        {
            TellOwner = !TellOwner;
        }
    }
 
    timer()
    {
        if (getDay() == "Sunday" && restart == TRUE)
        {
            llSetObjectDesc(llGetObjectDesc() + ","+(string)count);
            if(TellOwner)
            {
                llInstantMessage(llGetOwner(),"The count for the week is " + (string)count);
            }
            count = 0;
            restart = FALSE;
        }
        else if (getDay() == "Monday")
        {
            restart = TRUE;
        }
        list Who = llGetAgentList(AGENT_LIST_PARCEL,[]); //Modify here to detect AGENT_LIST_REGION if desired
        integer i = llGetListLength(Who);
        while(i)
        {
            if ((llStringLength( hashedNames ) * 4.2) > llGetFreeMemory()) // In case memory gets tight
            {
                hashedNames = llGetSubString(hashedNames,3,-1); //Delete the oldest entry in hashedNames
            }
            string AvKey = llList2Key(Who,i);
            string md5 = llMD5String(AvKey, 0);
            integer n1 = (integer)("0x" + llGetSubString(md5, 0, 7));
            integer n2 = (integer)("0x" + llGetSubString(md5, 8, 11));
            string encoded3Chars =
                encode15BitsToChar(n1 >> 16) +
                encode15BitsToChar(n1) +
                encode15BitsToChar(n2);
            integer idx = llSubStringIndex(hashedNames, encoded3Chars);
            if (idx == ERR_GENERIC) 
            {
            // this is a new one, so save it and send a notecard, if there is one
                hashedNames += encoded3Chars;
                ++count;
                if(llGetInventoryNumber(INVENTORY_NOTECARD))
                {
                    llGiveInventory(AvKey,llGetInventoryName(INVENTORY_NOTECARD,0));
                }
            }
            --i;
        }
    }
}