recorder.lsl
///////////////////////////////////////////////////////////////////////////
//  Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3      //
//  Please see: http://www.gnu.org/licenses/gpl.html for legal details,  //
//  rights of fair usage, the disclaimer and warranty conditions.        //
///////////////////////////////////////////////////////////////////////////
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
///////////////////////////////////////////////////////////////////////////
float wasFmod(float a, float p) {
    if(p == 0) return (float)"nan";
    return a - ((integer)(a/p) * p);
}
 
///////////////////////////////////////////////////////////////////////////
//    Copyright (C) 2014 Wizardry and Steamworks - License: GNU GPLv3    //
//     Original: Clive Page, Leicester University, UK.   1995-MAY-2      //
///////////////////////////////////////////////////////////////////////////
list wasUnixTimeToDateTime(integer seconds) {
    integer mjday = (integer)(seconds/86400 + 40587);
    integer dateYear = 1858 + (integer)( (mjday + 321.51) / 365.25);
    float day = (integer)( wasFmod(mjday + 262.25, 365.25) ) + 0.5;
    integer dateMonth = 1 + (integer)(wasFmod(day / 30.6 + 2.0, 12.0) );
    integer dateDay = 1 + (integer)(wasFmod(day,30.6));
    float nsecs = wasFmod(seconds, 86400);
    integer dateSeconds = (integer)wasFmod(nsecs, 60);
    nsecs = nsecs / 60;
    integer dateMinutes = (integer)wasFmod(nsecs, 60);
    integer dateHour = (integer)(nsecs / 60);
    return [ dateYear, 
        dateMonth, dateDay, dateHour, dateMinutes, dateSeconds ];
}
 
// helper function, return the properly formatted elapsed time from seconds
string wasSecondsToTime(integer seconds) {
    list remaining = wasUnixTimeToDateTime(seconds);
    remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 0)-1970 ], 0, 0);
    remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 1)-1 ], 1, 1);
    remaining = llListReplaceList(remaining, [ llList2Integer(remaining, 2)-1 ], 2, 2);
 
    // properly format plurals
    list count = [];
    integer Y = llList2Integer(remaining, 0);
    if(Y == 0) jump MO;
    count += (string)Y;
    if(Y > 1) {
        count += "years";
        jump MO;
    }
    count += "year";
@MO;
    integer M = llList2Integer(remaining, 1);
    if(M == 0) jump DA;
    count += (string)M;
    if(M > 1) {
        count += "months";
        jump DA;
    }
    count += "month";
@DA;
    integer D = llList2Integer(remaining, 2);
    if(D == 0) jump HO;
    count += (string)D;
    if(D > 1) {
        count += "days";
        jump HO;
    }
    count += "day";
@HO;
    integer H = llList2Integer(remaining, 3);
    if(H == 0) jump MI;
    count += (string)H;
    if(H > 1) {
        count += "hours";
        jump MI;
    }
    count += "hour";
@MI;
    integer I = llList2Integer(remaining, 4);
    if(I == 0) jump SE;
    count += (string)I;
    if(I > 1) {
        count += "minutes";
        jump done;
    }
    count += "minute";  
@SE;
    integer S = llList2Integer(remaining, 5);
    if(S == 0) jump done;
    count += (string)S;
    if(S > 1) {
        count += "seconds";
        jump done;
    }
    count += "second";
@done;
    return llDumpList2String(count, " ");
}
 
// purges the lists if the memory is under a treshold
purgeMem(integer mem) {
    if (llGetFreeMemory() > mem) return;
    uuids = llDeleteSubList(uuids, 0, 0);
    times = llDeleteSubList(times, 0, 0);
    reqes = llDeleteSubList(reqes, 0, 0);
}
 
list uuids = [];
list times = [];
list reqes = [];
 
default {
    state_entry() {
        llSetTimerEvent(1);
    }
    timer() {
        list agents = llGetAgentList(AGENT_LIST_REGION, []);
        if(llGetListLength(agents) == 0) jump display;
        do {
            key agent = llList2Key(agents, 0);
            if(agent == NULL_KEY) jump continue;
            integer i = llListFindList(uuids, [agent]);
            if(i == -1) {
                purgeMem(1024);
                uuids += [agent];
                times += 1;
                jump continue;
            }
            times = llListReplaceList(times, [ llList2Integer(times, i) + 1 ], i, i);
@continue;
            agents = llDeleteSubList(agents, 0, 0);
        } while(llGetListLength(agents) != 0);
 
@display;
        llMessageLinked(LINK_ROOT, 204000, "V:" + (string)llGetListLength(uuids), "0");
    }
    link_message(integer sender_num, integer num, string message, key id) {
        if(message == "reset") {
            llResetScript();
        }
        if(message != "display") return;
        integer total = llGetListLength(uuids);
        integer i = 0;
        do {
            purgeMem(1024);
            llOwnerSay((string)llList2Key(uuids, i));
            reqes = llListReplaceList(reqes, [ llRequestAgentData(llList2Key(uuids, i), DATA_NAME) ], i, i);
        } while(++i<total);
 
    }
    dataserver(key queryid, string name) {
        integer i = llListFindList(reqes, [queryid]);
        llOwnerSay(name + " ▼ " + wasSecondsToTime(llList2Integer(times, i)));
        reqes = llListReplaceList(reqes, [NULL_KEY], i, i);
    }
    on_rez(integer num) {
        llResetScript();
    }
}