Jump to content

Timing Issues on Open Source Rental Script


Prokofy Neva
 Share

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

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

Recommended Posts

Here's a free open-source rental script originally created by Hank Ramos and modified by Gabriele Graves. Neither of them seem to come inworld much so I thought I'd ask here.

I have it placed at various infohubs and stores and it is quite popular with newbies in particular. It works pretty well (Gabriele fixed it so that it can offer 4-week discount payments and take payments ahead) but now it has an issue that I understand other rental scripts also suffer from -- timing glitches. (There may be a JIRA but I can't locate it).

The issue is the "timer" -- that is, the paid state freezes and it never elapses, it just stays as is in some (but not all) cases; it also doesn't survive sim resets and can revert to a previous state (which it didn't used to do). However, it survives returns to inventory and keeps on ticking, which is a great feature. (There's one other odd thing -- griefers are able to resize it even when it is not in share).


Any ideas why this is happening (it's some recent Linden patch) and/or the JIRA related to it?

 

//Rental Script v1.5.3
//by Hank Ramos

//Options
vector  rentalOffset   = <0,0,10>;
float   updateInterval = 60.0; //seconds
string  infoNotecard   = "Rent This Space Info";

//Variables
string  tierName;
float   rentalCost;
float discountTime;
integer primCount;
integer rentalVolume;
float   refundFee;
key     renterID;
string  renterName;
float   rentalTime;
integer listenQueryID;
vector  initPos;
vector  initScale;
integer count;
integer lineCount;
key     readKey;
string  rentalGrade;
integer primAllotment;

//Constants
float ONE_WEEK = 604800.0;
float ONE_DAY  = 86400.0;
float ONE_HOUR = 3600.0;
float DISCOUNT_PERCENT = 10.0;

dispString(string value)
{
    llSetText(value, <1,1,1>, 1);
}
sendReminder(string message)
{
    llInstantMessage(renterID, "Your lease located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") will expire " + message);
}
saveData()
{
    list saveData;
    vector storageVector;
    
    saveData += renterID;
    saveData += renterName;
    saveData += llRound(rentalTime);
    storageVector = initPos * 1000;
    saveData += "<" + (string)llRound(storageVector.x) + "," + (string)llRound(storageVector.y) + "," + (string)llRound(storageVector.z) + ">";
    storageVector = initScale * 1000;
    saveData += "<" + (string)llRound(storageVector.x) + "," + (string)llRound(storageVector.y) + "," + (string)llRound(storageVector.z) + ">";
    saveData += llRound(discountTime);
    
    llSetObjectDesc(llDumpList2String(saveData, "|"));
}
string getTimeString(integer time)
{
    integer days;
    integer hours;
    integer minutes;
    integer seconds;
    
    days = llRound(time / 86400);
    time = time % 86400;
    
    hours = (time / 3600);
    time  = time % 3600;

    minutes = time / 60;
    time    = time % 60;

    seconds = time;
    
    return (string)days + " days, " + (string)hours + " hours, " + (string)minutes + " minutes"; // + ":" + (string)seconds;
}

integer setupDialogListen()
{
    integer chatChannel = (integer)llFrand(2000000);
    llListenRemove(listenQueryID);
    listenQueryID = llListen(chatChannel, "", NULL_KEY, "");
    return chatChannel;
}

updateTimeDisp()
{
    dispString("Leased by: " + renterName + "\nTime Remaining: " + getTimeString(llRound(rentalTime)));   
}

dispData()
{
    llSay(0, "========================");
    llSay(0, "Rental Space Information");
    llSay(0, "========================");
    llSay(0, "This space is currently leased by " + renterName);
    llSay(0, "The current rental price is L$" + (string)((integer)rentalCost) + " per week.");
    llSay(0, "This space will be open for lease in " + getTimeString(llRound(rentalTime)) + ".");
    llSay(0, "Memory Free: " + (string)llGetFreeMemory());
}
default
{
    state_entry()
    {
        state initialize;
    }
}

state initialize
{
    state_entry()
    {
        llSetTimerEvent(300);
        llOwnerSay("Waiting to obtain Debit Permissions.");
        llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
    }
    run_time_permissions(integer permissions)
    {
        //Only wait for payment if the owner agreed to pay out money
        if (permissions & PERMISSION_DEBIT)
        {
            state loadSettings;
        }
    }    
    on_rez(integer start_param)
    {
        llResetScript();
    }
    timer()
    {
        llRequestPermissions(llGetOwner(), PERMISSION_DEBIT);
    }
    touch_start(integer total_number)
    {
        integer x;
        for (x = 0; x < total_number; x += 1)
        {
            if (llDetectedKey(x) == llGetOwner())
            {
                llResetScript();
            }
        }
        llSay(0, "Waiting to obtain Debit Permissions from Owner.");
    }
    state_exit()
    {
        llSetTimerEvent(0);
        llSay(0, "Initialized.");
    }
}

state loadSettings
{
    state_entry()
    {
        integer found = FALSE;
        integer x;
        
        count = 0;
        lineCount = 0;
        
        list savedList = llCSV2List(llGetObjectDesc());
        
        if (llGetListLength(savedList) == 4)
        {
            rentalGrade = llList2String(savedList, 0);
        }
        else
        {
            rentalGrade = llGetObjectDesc();
        }
        for (x = 0; x < llGetInventoryNumber(INVENTORY_NOTECARD); x += 1)
        {
            if (llGetInventoryName(INVENTORY_NOTECARD, x) == "Settings")
            {
                found = TRUE;
            }
        }
        if (found)
        {
            llOwnerSay("Reading Settings Notecard...");
            readKey = llGetNotecardLine("Settings", lineCount);
        }
        else
        {
            llOwnerSay("Settings Notecard Not Found.");
            llResetScript();
        }
    }
    dataserver(key requested, string data)
    {
        integer integerData;
        float   floatData;
        
        if (requested == readKey)
        {
            if (data != EOF)
            {
                if ((llSubStringIndex(data, "#") != 0) && (data != "") && (data != " "))
                {
                    integerData = (integer)data;
                    floatData   = (float)data;
                    
                    if (count == 0)
                    {
                        tierName = data;
                    }
                    else if (count == 1)
                    {
                        if (integerData >= 0)
                        {
                            rentalCost = integerData;
                        }
                        else
                        {
                            rentalCost = 0;
                        }
                    }
                    else if (count == 2)
                    {
                        if (integerData >= 1)
                        {
                            primCount = integerData;
                        }
                        else
                        {
                            primCount = 1;
                        }
                    }
                    else if (count == 3)
                    {
                        if (integerData >= 16)
                        {
                            rentalVolume = integerData;
                        }
                        else
                        {
                            rentalVolume = 16;
                        }
                    }
                    else if (count == 4)
                    {
                        if (integerData >= 0)
                        {
                            refundFee = integerData;
                        }
                        else
                        {
                            refundFee = 0;
                        }
                    }
                    else if (count == 5)
                    {
                        rentalOffset = (vector)data;
                    }
                    else if (count == 6)
                    {
                        infoNotecard = data;
                    }
                    count += 1;
                }
                lineCount += 1;
                readKey = llGetNotecardLine("Settings", lineCount);
            }
            else
            {
                llOwnerSay("===============");
                llOwnerSay("Settings Loaded");
                llOwnerSay("===============");
                llOwnerSay("Space Name: " + tierName);
                llOwnerSay("Rental Cost: L$" + (string)llRound(rentalCost));
                llOwnerSay("Prim Count: " + (string)primCount);
                llOwnerSay("Space Volume: " + (string)rentalVolume + " sqm");
                llOwnerSay("Refund Fee: L$" + (string)refundFee);
                llOwnerSay("===============");
                llOwnerSay("Ready for Service!");

                list savedList = llParseString2List(llGetObjectDesc(), ["|"], []);

                if (llGetListLength(savedList) == 6)
                {
                    renterID    = llList2Key(savedList, 01);
                    renterName  = llList2String(savedList, 1);
                    rentalTime  = llList2Integer(savedList, 2);
                    initPos     = (vector)llList2String(savedList, 3) / 1000;
                    initScale   = (vector)llList2String(savedList, 4) / 1000;
                    discountTime = llList2Integer(savedList, 5);
                    state rented;
                }
                else
                {
                    renterID   = NULL_KEY;
                    renterName = "Nobody";
                    rentalTime = 0;
                    initPos    = llGetPos();
                    initScale  = llGetScale();
                    discountTime = 0;
                    state idle;
                }
            }
        }
    }
}
state idle
{
    state_entry()
    {        
        llSetObjectDesc("");
        llSetTexture("rentthisspace", ALL_SIDES);
        llSetScale(initScale);
        llSetPos(initPos);
        integer fourweekdiscount = (integer)(((float)rentalCost * 4.0) * (DISCOUNT_PERCENT / 100.0));
        integer fourweekrentalprice = (integer)(((float)rentalCost * 4.0) - fourweekdiscount);
        llSetPayPrice((integer)rentalCost, [ (integer)(rentalCost * 1.0), (integer)(rentalCost * 2.0), (integer)(rentalCost * 3.0), (integer)fourweekrentalprice]);
        llSetTimerEvent(updateInterval);

        dispString(tierName + "\nLease this space for L$" + (string)llRound(rentalCost) + " per week.\n" + (string)rentalVolume + " sq meters\n" + (string)primCount + " prims\nPay this Sign to begin your lease.");
    }
    moving_end()
    {
        initPos = llGetPos();
    }
    changed(integer change)
    {
        if (change & CHANGED_SCALE)
        {
            initScale = llGetScale();
        }
    }
    touch_start(integer num_detected)
    {
        integer x;
        integer chatChannel;
        
        for (x = 0; x < num_detected; x += 1)
        {
            if (llDetectedKey(x) == llGetOwner())
            {
                llDialog(llGetOwner(), "Owner Options.  Select one of the options below...", ["Info", "Reset"], setupDialogListen());
                return;
            }
        }
               
        llSay(0, "Lease this space for L$" + (string)llRound(rentalCost) + " per week. " + (string)rentalVolume + " sq meters. " + (string)primCount + " prims. Pay this Sign to begin your lease.");
        
        for (x = 0; x < num_detected; x += 1)
        {
            llGiveInventory(llDetectedKey(x), infoNotecard);
        }
    }
    listen(integer channel, string name, key id, string message)
    {
        if (message == "Reset")
        {
            llResetScript();
        }
        else if (message == "Info")
        {
            llListenRemove(listenQueryID);
            dispData();
            llSay(0, "Lease this space for L$" + (string)llRound(rentalCost) + " per week. " + (string)rentalVolume + " sq meters. " + (string)primCount + " prims. Pay this Sign to begin your lease.");
            llGiveInventory(id, infoNotecard);
        }
    }    
    money(key id, integer amount)
    {
        if (amount >= rentalCost)
        {
            renterID   = id;
            renterName = llKey2Name(renterID);
            integer fourweekdiscount = (integer)(((float)rentalCost * 4.0) * (DISCOUNT_PERCENT / 100.0));
            integer fourweekrentalprice = (integer)(((float)rentalCost * 4.0) - fourweekdiscount);
            if (fourweekrentalprice == amount)
            {
                rentalTime = ONE_WEEK * (rentalCost * 4) / rentalCost;
                discountTime = rentalTime;
            }
            else
            {
                rentalTime = ONE_WEEK * amount / rentalCost;
                discountTime = 0;
            }

            saveData();
            
            llSay(0, "Thank you " + renterName + " for leasing!  Your lease will expire in " + getTimeString((integer)rentalTime) + ".");
            
            state rented;
        }
        else
        {
            llSay(0, "This space costs L$" + (string)rentalCost + " to rent. Refunding paid balance.");
            llGiveMoney(id, amount);
        }
    }
}

state rented
{
    state_entry()
    {
        llSetTexture("infosign", ALL_SIDES);
        llSetScale(<0.5, 0.5, 0.5>);
        llSetPos(initPos + rentalOffset);
        
        updateTimeDisp();
        llResetTime();
        llSetTimerEvent(updateInterval);
    }
    touch_start(integer num_detected)
    {
        integer x;
        key     detectedKey;
        
        for (x = 0; x < num_detected; x += 1)
        {
            detectedKey = llDetectedKey(x);
            if (detectedKey == llGetOwner())
            {
                llDialog(detectedKey, "Lease Options. Select one of the options below...", ["Refund Time", "Info", "Release", "Reset"], setupDialogListen());
            }
            else if (detectedKey == renterID)
            {
                llDialog(detectedKey, "Lease Options. Select one of the options below...", ["Refund Time", "Info"], setupDialogListen());
            }
            else
            {
                dispData();
                llGiveInventory(detectedKey, infoNotecard);
            }
        }
    }
    money(key id, integer amount)
    {
        if ((id == renterID)||(id == llGetOwner()))
        {
            float addTime;
            
            integer fourweekdiscount = (integer)(((float)rentalCost * 4.0) * (DISCOUNT_PERCENT / 100.0));
            integer fourweekrentalprice = (integer)(((float)rentalCost * 4.0) - fourweekdiscount);
            if (fourweekrentalprice == amount)
            {
                addTime = ONE_WEEK * (rentalCost * 4) / rentalCost;
                discountTime += addTime;
            }
            else
                addTime = ONE_WEEK * amount / rentalCost;

            rentalTime += addTime;
            
            llInstantMessage(id, "Adding " + getTimeString(llRound(addTime)) + " to your lease. Lease Time is Now: " + getTimeString(llRound(rentalTime)) + ".");
            saveData();
            updateTimeDisp();
        }
        else
        {
            llInstantMessage(id, "Refunding Money...");
            llGiveMoney(id, amount);
            llInstantMessage(id, "This space is currently leased by " + renterName + ". This space will be open for lease in " + getTimeString(llRound(rentalTime)) + ".");
        }
    }
    listen(integer channel, string name, key id, string message)
    {
        integer refundAmount;
        
        llListenRemove(listenQueryID);

        if (message == "Info")
        {
            dispData();
            llGiveInventory(id, infoNotecard);
        }
        else if (message == "Refund Time")
        {
            llDialog(id, "Are you sure you want to TERMINATE your lease and refund your money, minus a L$" + (string)refundFee + " fee?", ["YES", "NO"], setupDialogListen());
        }
        else if (message == "YES")
        {
            float discount = (float)rentalCost * (DISCOUNT_PERCENT / 100.0);
            integer discountCost = (integer)((float)rentalCost - discount);
            refundAmount = llRound((((rentalTime - discountTime)/ ONE_WEEK) * rentalCost) + ((discountTime / ONE_WEEK) * discountCost) - refundFee);
            llInstantMessage(renterID, "Refunding L$" + (string)refundAmount + ", which includes a L$" + (string)refundFee + " termination fee.");
            llGiveMoney(renterID, refundAmount);
            llInstantMessage(llGetOwner(), "LEASE REFUNDED: leased by " + renterName + " located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") has ended. Refunded L$" + (string)refundAmount + ".");
            state idle;
        }
        else if (message == "Release")
        {
            llDialog(id, "Are you sure you want to TERMINATE this lease with NO REFUND?", ["Yes", "No"], setupDialogListen());
        }
        else if (message == "Yes")
        {
            llInstantMessage(llGetOwner(), "LEASE TERMINATED: leased by " + renterName + " located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") has ended. Refunded L$0.");
            state idle;            
        }
        else if (message == "Reset")
        {
            llResetScript();
        }
    }
    timer()
    {
        float timeElapsed = llGetAndResetTime();
        if (timeElapsed > (updateInterval * 4))
        {
            timeElapsed = updateInterval;
        }
        rentalTime -= timeElapsed;
        
        saveData();
 
        updateTimeDisp();
        
        //Process Reminders
        if (rentalTime <= 0)
        {
            llInstantMessage(llGetOwner(), "LEASE EXPIRED: leased by " + renterName + " located in " + llGetRegionName() + " (" + (string)initPos.x + "," + (string)initPos.y + "," + (string)initPos.z + ") has expired.");

            state idle;
        }
        if ((rentalTime <= ONE_DAY)&&(rentalTime >= ONE_DAY - (updateInterval*2)))
        {
            sendReminder("in one day.");
        }              
        else if ((rentalTime <= ONE_HOUR*12)&&(rentalTime >= ONE_HOUR*12 - (updateInterval*2)))
        {
            sendReminder("in 12 hours.");
        }        
        else if ((rentalTime <= ONE_HOUR)&&(rentalTime >= ONE_HOUR - (updateInterval*2)))
        {
            sendReminder("in one hour.");
        }        
    }
}



Link to comment
Share on other sites

Not sure it's related, but in the past week it seems someone else experienced (SVC-8165) what turns out to be a very old jira (SVC-387) that happens very rarely during some server crashes: Timers simply stop altogether, even though the rest of the script continues to function. As I mention in a comment to the older jira, I also encountered the problem last week. This thing is so rare, however, that I wouldn't know how to give a bug-hunter a fighting chance of finding and fixing it.

If the problem with this rental script arises more often, then it's likely something other than those jiras.

Link to comment
Share on other sites


... the cause of the problem was ll rolling out the new path finding tools...

Well... probably not in my case, because it happened to me last weekend (Saturday night or early Sunday, PDT), which doesn't really coincide with the Pathfinding rollout. Also, it only happened on one of the dozen or so* sims where one of the affected scripts is running, despite all sims getting Pathfinding at more or less the same time. 

As I recall, however, the Pathfinding rollout had some pretty rocky restarts, so it might not be surprising that this bug got triggered a few places on the grid.

________

*Unfortunately, that's not definitive because that script could be in a state where new timer events are triggered outside the timing loop itself.

Link to comment
Share on other sites

I'm pretty sure conrad means resetting the script itself, not the sim. At least in my experience with the frozen timer bug, restarting the sim has no effect on the problem.

To try to get some handle on whether it's really that bug (in which case there's pretty much nothing you can do about it), it would help to understand the distribution of the problems you've encountered.  If it happened a few times between, say, three weeks ago and last Wednesday, but very rarely if ever before or since, then it might be this very obscure bug we've been discussing.  If, on the other hand, it's been an ongoing occurrence for months across multiple sims, then it's something else, and we should take a much closer look at the script.

I did take an initial look at the script for things that have changed in SL, and there is one thing that might possibly be a problem: it uses the object description to store data about the rental and the position and size of the object itself. The length of this description field was dramatically reduced a few years ago (causing great anguish for users of the old Timeless Prototype linked door script), so now it's documented to be 127 bytes UTF-8. The script is trying to stuff the character representation of the renter's key (36 characters), the renter's name (up to 63 characters), a representation of position (a la "<255000,255000,4096000>", so up to 23 chars) and scale (another 23), plus integers for rentalTime and discountTime which should be maybe 7 digits each, depending on the actual duration of the rental... which might total 159 characters, for very long-named renters, assuming my arithmetic is correct.

One interesting thing is that the scale of the object is stored near the end of that string of data -- before the discountTime, but still potentially beyond the 127 char limit. So that just might explain the problem with the boxes mysteriously resizing themselves, too.

So, whether the boxes are really ever bumping up against that limit or not, I think they could, given a renter with an absurdly long name (and we all know who those might be).  So... we could look into tidying up this script (truncating the name string, being less precise about position and scale, etc), unless you were thinking of replacing it for other reasons.

Link to comment
Share on other sites

But the rental box isn't using the avatar's actual name. It's merel capturing his UUID and using that. That even appears on the box description. So that would be a standard size and it wouldn't matter what the length of his name is. To be sure, the avatar's name shows up if you click on it, but that's probably because it's fetched from the UUID, no?

Link to comment
Share on other sites

It's saving considerably more than just the avatar's uuid.   The userfunction that collects the data and then saves it is:

saveData(){	list saveData;	vector storageVector;	saveData += renterID;	saveData += renterName;	saveData += llRound(rentalTime);	storageVector = initPos * 1000;	saveData += "<" + (string)llRound(storageVector.x) + "," + (string)llRound(storageVector.y) + "," + (string)llRound(storageVector.z) + ">";	storageVector = initScale * 1000;	saveData += "<" + (string)llRound(storageVector.x) + "," + (string)llRound(storageVector.y) + "," + (string)llRound(storageVector.z) + ">";	saveData += llRound(discountTime);	llSetObjectDesc(llDumpList2String(saveData, "|"));}

 

So it's building a list of the uuid, the name, the rental time, the position of the box, its size and the discount time, and saving (or trying to) that.

Link to comment
Share on other sites

I should clarify that I don't think the (potential?) problem with using the object description field as persistent storage is the source of the main issue here, that of the timer getting stuck. As far as I can tell, the timer is always set to fire either every minute or (when first getting debit permission) every 5 minutes, and off while reading the settings notecard--nothing depending on the values stored in that description. So the description length problem is only interesting as a possible explanation of unexpected resizing.

The "idle" state sets the timer without actually having a timer() handler, which is slightly weird but shouldn't cause any problems. Anyway, the problem must be arising in the "rented" state, and there's nothing wrong there that's immediately obvious to me.

There may be other sim bugs that freeze timers, I suppose; the one conrad and I discussed would have frozen all the rented boxes in a sim at the same time, but not occur again--maybe ever. It's not clear whether that's anything like the distribution of frozen timers you've experienced with this script.

Link to comment
Share on other sites

I don't know if this is what's causing it, but I've just noticed in the Caveats to llSetObjectDesc that

  • The pipe character '|' and the newline character '\n' are not legal in a prim's description, they will be replaced by with '?'

and the pipe character is precisely what saveData() uses as a separator when it saves all the data as a list in the prim's description, so I don't think that list is ever going to be read properly.

To add to the potential confusion, in state loadSettings, in state_entry it reads

        list savedList = llCSV2List(llGetObjectDesc());


which I think means it's going to ignore the actual separators, be they pipes or question marks, and, instead, try to use the commas it finds in the stored vectors.    That just puzzles me;  the storedList it envisages finding there doesn't seem to me much like the storedList it's building elsewhere, since it says (lines 163 and following) 

        list savedList = llCSV2List(llGetObjectDesc());                if (llGetListLength(savedList) == 4)        {            rentalGrade = llList2String(savedList, 0);        }        else        {            rentalGrade = llGetObjectDesc();        }        for (x = 0; x < llGetInventoryNumber(INVENTORY_NOTECARD); x += 1)        {            if (llGetInventoryName(INVENTORY_NOTECARD, x) == "Settings")            {                found = TRUE;            }        }

 

Then , as soon as it does that, if it can find the "Settings" notecard, it starts to read it, and, having read it, reads savedList again at line 279, only this time using a pipe character as the separator.

 Normally, I'd suggest using something other than the pipe character as the separator, which I think could be achieved simply by changing two lines:

line 56 llSetObjectDesc(llDumpList2String(saveData, "@"));
line 279 list savedList = llParseString2List(llGetObjectDesc(), ["@"], []);

But I'm really puzzled by the comma separated list it expects, at line 163,  to find, because I can't see where that ever gets set.  I think that whatever is happening, it's never going to find a valid value for rentalGrade, but I'm not sure what problems that would cause.

I would try, though, making those two changes, since it removes something that's going to be a problem, I think, though maybe not the problem we're looking for.

Certainly, I suspect the underlying issue is that it's not getting the information from the object's description field that it's supposed to.   

Link to comment
Share on other sites

  • 2 weeks later...

As requested in a post on another subform, I'll resurrect this thread and try to make some progress here. I've loaded the original script and replacements for required notecards and textures, and will investigate further. I've noticed some basic excursions of its behavior from the script's apparent design, and it's not clear whether those are best left "as implemented" or "as designed," so I'll ask about that after I've gotten further into it and have a more complete list of specification questions.

Meanwhile, to respond to some topics in that post:

The problem is in the sim, sure, aren't they all? Oh, except when they're in my "outdated consumer router"! But there's something then *interacting* with that problem in the rental script, a timer, a something, that might be nudged to accommodate the new server bug.

That's the problem with this particular sim bug: Scripts waiting on frozen timers have to be "nudged" by some other event. The good part is that such a thing is possible* because it seems only the timer() event gets frozen. The bad part is that it's not easy to generate that other, non-timer event for many objects scattered around multiple sims. If one could run around and touch them all, or say something on chat, or collide with them, etc., etc., those events could trigger a check for the frozen timer and unfreeze with a reset. To do that remotely for all the boxes adds a lot of complexity to the scripting, and requires redundant "server" objects somewhere that know about all the rental boxes everywhere.

All that is doable, but it's a big deal. It's not just that it's outside the scope of what most scripters will do for free, but worse, it would add to the script weight on the sims. And both of those are okay, if that's what's needed--but it's not yet clear that this is actually the problem you've encountered. It's so rare that it literally may never happen again, no matter how many tests we run. It may even get "fixed" by some change in the sim software, without itself ever being diagnosed, and we'd never know it was fixed: it would just never happen again.

That's why I've been fussing about the "distribution" of freezes: if the underlying cause is not this sim bug, it's probably better not to address it in this script--even though, if this sim bug ever strikes anywhere these scripts are running, they will definitely freeze with the exact behavior described.

So, how can we know? Well, for the nonce, I'll assume it's not that bug and study the script for another explanation. It may indeed be something else (which I may or may not find myself), based on this:

It happens enough times to be of concern. Others have found it too. I will try to get them to post.

That doesn't really sound like the sim bug we're discussing, if it's as rare as I think it is. If anybody can confirm that, when it happens, all timer-waiting scripts on the sim are frozen--or that they're not--that would be a huge help.

 


* Possible, that is, even without using a separate script to call llResetOtherScript() on the frozen-timer script. Unfortunately, using a separate "watchdog" script doesn't solve the problem here because it, too, would have its timer frozen by the bug, and so it, too, would need to be triggered by some other event.

Link to comment
Share on other sites

I set up a running copy -- using the alterations I suggested, though I'm not sure they fix the problem -- a few days ago.   So far it seems to be working normally, with the hovertext display counting down, though I'm yet to enter the reminder period.

I'm still not completely sure what I'm looking for -- is the timer likely to stop at some point (and, if so, at which) or does the timer continue but  the reminders don't get sent?   Or does the fact that the timer started running at all after my initial payment mean that I've avoided the bug, whatever it is, at least this time round?

Link to comment
Share on other sites

Reading the OP, I'm of the impression that the timer, while ticking down, will freeze at some point--but only sometimes.

So far, I don't see anything about the process of sending reminders that should affect the timer at all, so I'm guessing that it could happen either before or after the reminders start getting sent -- but if there's a pattern to that, it would be another huge clue.

I'm also a bit concerned whether this is really the version that's in use out in the field. The reason I say that (and also the main way in which the implemented behavior varies from the apparent design) is that this script without the alterations you suggested, when reset, will always revert to having no memory of the rental that's taken place. Maybe that's what the OP means by saying that it "can revert to a previous state (which it didn't used to do)" but I'm not sure.

Incidentally, that CSV thing is a puzzle, but it seems vestigial: it only serves to set rentalGrade, which isn't actually used anywhere in this version of the script.

Link to comment
Share on other sites

The problem with the script as I see it, is that it's using llResetTime() and llGetAndResetTime().  Using script time to keep track of elapsed time is dicy at best. As the wiki states:

 

  • Script time resets when...
  • Script time doesn't measure real world time, it is affected by time dilation.

So that has the potential to introduce a lot of error into the timekeeping function. A better method would be to use llGetUnixTime() or some other real-world time to ensure accurate timekeeping.

 

It could be what is interpreted as freezing is in fact that enough error has been introduced that it's days or even weeks off in it's timekeeping.

Link to comment
Share on other sites

Oh, jeez. The wiki is inconsistent on whether script time is or isn't affected by sim time dilation. According to llGetTIme() and llGetAndResetTime() it is unaffected -- apparently stemming from a 23 November 2009 edit to llGetTime() by one "Chance Zeta" (complete with a link to a picture of a wristwatch in the change log!), which (mis-?)information was apparently copied over to llGetAndResetTime() by Gaius Goodliffe as part of a larger edit on 14 April 2010. But your cited text showing it to be affected by dilation is still present in llResetTime().

FWIW, my own homegrown rental script uses unix time, and I don't think I've ever used these other functions for anything significant or long term.

I don't know for certain that they're actually affected by dilation. We should test that and update the wiki accordingly. But my sense is that the timer freeze we seek is more all-or-nothing than the kind of drift we'd get from cumulative dilation.

Link to comment
Share on other sites


Qie Niangao wrote:

Oh, jeez. The wiki is inconsistent on whether script time is or isn't affected by sim time dilation. According to llGetTIme() and llGetAndResetTime() it is
un
affected -- apparently stemming from
(complete with a link to a picture of a wristwatch in the change log!), which (mis-?)information was apparently
. But your cited text showing it to be affected by dilation is still present in llResetTime().

FWIW, my own homegrown rental script uses unix time, and 
I don't think I've ever used these other functions for anything significant or long term.

I don't know for certain that they're actually affected by dilation. We should test that and update the wiki accordingly. But my sense is that the timer freeze we seek is more all-or-nothing than the kind of drift we'd get from cumulative dilation.

Yes I noticed that inconsistency after posting. I started a thread at SLU to see what people's experiences are. I'd try testing it myself, but so far have been unable to find a consistently dilated sim. (if anyone knows of one, let me know.)

I know as long as I can remember, time measured that way was considered innacurate due to dilation. But much has changed since then.

Link to comment
Share on other sites

On one sim where I have land, the neighbours have helpfully :smileywink: generated a pretty consistent time dilation around 0.95, so about one part in twenty. I set out the script below, and so far I've not seen any drift.

float SAMPLING_INTERVAL = 10.0;integer startUnixTime;default{    state_entry()    {        startUnixTime = llGetUnixTime();        llResetTime();        llSetTimerEvent(SAMPLING_INTERVAL);    }    timer()    {        integer secondsRunning = llGetUnixTime() - startUnixTime;        integer drift = secondsRunning - llRound(llGetTime());        llSetText("Drift: "+(string)drift            +"\nafter "+(string)secondsRunning+" seconds"            +"\nCurrent dilation: "+(string)llGetRegionTimeDilation()            , <1.0, 1.0, 1.0>, 1.0            );    }}

 I think this is measuring what I should be measuring, but please check to make sure I didn't do something stupid.

Link to comment
Share on other sites

Thanks, Dari.  I posted some follow-up on the SLU thread.

Returning to the original frozen timer problem, nothing in the rental box script is sticking out to me as a likely cause. This most-recent main channel server roll coincided with misbehaviors in some of my sometimes timer-bound scripts. I'd seen those problems before and attributed them to something else, but now I'm suspicious that SVC-387 may be more common than I'd appreciated.

And so, for my own sanity, too, I'm pondering ways to reasonably detect and "nudge" those scripts remotely.

As far as I can tell, the "nudge" needs to be a script reset, so Innula's fix needs to be in place for the rental box script, lest a reset lose all information about the ongoing rental.

This would all be much simpler were it not for SVC-23, which is closed as "Won't Finish" but still very definitely happens. (It's the fun one where you wear the email-frozen object, TP to another region then back again, and then all the queued-up emails come flooding in.) It means the frozen-timer solution has to also detect email non-delivery, if it uses email... which seems pretty unavoidable, if it sticks to in-world services only.

Link to comment
Share on other sites

Couple of updates. First, not surprisingly, my copy of the rental script merrily did its thing, warning and "evicting" me, so it doesn't fail every time... which we knew already. Second, the two (Mono- and LSL-compiled) test scripts both show about 4000 seconds of "drift" after yesterday's (rather ungraceful) sim update and restart... so whatever llGetTime is getting, it's not simply wall clock, but it seems to be the same for both LSL and Mono compilation. (Well, one's drift  is 4015 seconds and the other 4016, but close enough.)

Link to comment
Share on other sites

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