secondlife / jira-archive

2 stars 0 forks source link

[BUG-234022] Teleport just before to change state will prevent the change state to occur #11025

Open sl-service-account opened 1 year ago

sl-service-account commented 1 year ago

This bug is hard to reproduce. It happens randomly in very lagy sims with a very low script run (like 5% or less). The script that i copy here is just the bare bone, but the problem occurs mainly on big heavy script that need more time to load on teleport or login. It has to be put in an attachment:


default
{
    state_entry()
    {
        llOwnerSay("default state_entry");
    }

    attach(key agent)
    {
        if (agent) {
            state clear;
        }
    }
}

state clear
{
    state_entry()
    {
        llOwnerSay("clear state_entry");
        llSetTimerEvent(1);
        state default;
        llOwnerSay("after trying to change state");
    }

    timer()
    {
        llOwnerSay("timer");
    }

    touch_start(integer num)
    {
        llOwnerSay("touch");
    }

    state_exit()
    {
        llOwnerSay("clear state_exit");
    }
}

Steps to reproduce:

1/ Wear the object as an attachment. 2/ Logout 3/ Login in a laggy sim with very low script run Here, usually, it takes a little while to load due to lag. The script will trigger attach and go to clear state. You will see "clear state_entry" in local chat. Here, sometimes, it takes 1 second or even more before you see "default state_entry" (so going back to default state) due to the natural lag of the sim. 4/ Just after seing "clear state_entry" in local chat, teleport to another sim (for example press CTRL SHIFT H to go home quickly) 5/ You have been teleported to another sim. But you still don't see "default state_entry" message. In other words: your script has stayed in the "clear" state. Note that you won't see neither "after trying to change state". You will not see neither "clear state_exit"

The rest of the code is for proving that the script is still in the clear state. The timer DOES trigger each second. And if you touch the object, you will see "touch".

=> The script stays in the clear state. the line "state default;" never executes.

Do you have any explanation to this?

Original Jira Fields | Field | Value | | ------------- | ------------- | | Issue | BUG-234022 | | Summary | Teleport just before to change state will prevent the change state to occur | | Type | Bug | | Priority | Unset | | Status | Accepted | | Resolution | Triaged | | Reporter | Aglaia (aglaia) | | Created at | 2023-06-21T03:00:54Z | | Updated at | 2023-06-22T17:02:44Z | ``` { 'Build Id': 'unset', 'Business Unit': ['Platform'], 'Date of First Response': '2023-06-21T11:25:05.737-0500', 'How would you like the feature to work?': 'This bug is hard to reproduce. It happens randomly in very lagy sims with a very low script run (like 5% or less). The script that i copy here is just the bare bone, but the problem occurs mainly on big heavy script that need more time to load on teleport or login, to be put in an attachment:\r\n\r\ndefault\r\n{\r\n state_entry()\r\n {\r\n llOwnerSay("default state_entry");\r\n }\r\n\r\n attach(key agent)\r\n {\r\n if (agent) {\r\n state clear;\r\n }\r\n }\r\n}\r\n\r\nstate clear\r\n{\r\n state_entry()\r\n {\r\n llOwnerSay("clear state_entry");\r\n llSetTimerEvent(1);\r\n state default;\r\n llOwnerSay("after trying to change state");\r\n }\r\n\r\n timer()\r\n {\r\n llOwnerSay("timer");\r\n }\r\n\r\n touch_start(integer num)\r\n {\r\n llOwnerSay("touch");\r\n }\r\n\r\n state_exit()\r\n {\r\n llOwnerSay("clear state_exit");\r\n }\r\n}\r\n\r\n\r\nSteps to reproduce:\r\n\r\n1/ Wear the object as an attachment.\r\n2/ Logout\r\n3/ Login in a laggy sim with very low script run \r\nHere, usually, it takes a little while to load due to lag. The script will trigger attach and go to clear state. You will see "clear entry" in local chat. Here, sometimes, it takes 1 second or even more before you see "clear state_entry" due to the natural lag of the sim.\r\n4/ Just after seing "clear state_entry" in local chat, teleport to another sim (for example press CTRL SHIFT H to go home quickly)\r\n5/ You have been teleported to another sim. But you still don\'t see "default state_entry" message. In other words: your script has stayed in the "clear" state. Note that you won\'t see neither "after trying to change state". You will not see neither "clear state_exit"\r\n\r\nThe rest of the code is for proving that the script is still in the clear state. The timer DOES trigger each second. And if you touch the object, you will see "touch".\r\n\r\n=> The script stays in the clear state. the line "state default;" never executes.\r\n\r\nDo you have any explanation to this?', 'ReOpened Count': 0.0, 'Severity': 'Unset', 'System': 'SL Simulator', 'Target Viewer Version': 'viewer-development', 'What just happened?': 'n/a', 'What were you doing when it happened?': 'n/a', 'What were you expecting to happen instead?': 'n/a', 'Why is this feature important to you? How would it benefit the community?': 'x', } ```
sl-service-account commented 1 year ago

Lucia Nightfire commented at 2023-06-21T16:25:06Z, updated at 2023-06-21T17:46:29Z

Since I couldn't find a laggy enough region to repro, I made a experience based teleport HUD test.

Here is the code:

string Trim_Vector(vector v, integer i)
{
    return "<" + Trim_Decimal_Zeros(v.x,i) + ", " + Trim_Decimal_Zeros(v.y,i) + ", " + Trim_Decimal_Zeros(v.z,i) + ">";
}
string Trim_Decimal_Zeros(float f, integer keep_tenths_zero)
{
    return llList2String(llParseString2List((string)f + "!",llDeleteSubList(["",".000000!","00000!","0000!","000!","00!","0!","!"],0,!!keep_tenths_zero),[]),0);
}
key owner = NULL_KEY;
key request = NULL_KEY;
integer req_exp_perms;
integer teleporting;
integer init = TRUE;
default
{
    state_entry()
    {
        if ((init = TRUE) != init)
        {
            llOwnerSay("default state_entry() " + llGetTimestamp());
        }
        owner = llGetOwner();
    }
    on_rez(integer i)
    {
        llResetScript();
    }
    touch_end(integer i)
    {
        if (llGetAttached() && (llDetectedKey(0) == owner))
        {
            if (req_exp_perms || (request != NULL_KEY) || teleporting)
            {
                llOwnerSay("default touch_end() Process interrupted.");
                req_exp_perms = FALSE;
                request = NULL_KEY;
                teleporting = FALSE;
                llSetTimerEvent(0.0);
                return;
            }
            if (llGetInventoryNumber(INVENTORY_LANDMARK))
            {
                req_exp_perms = TRUE;
                llOwnerSay("default touch_end() Requesting experience permissions... " + llGetTimestamp());
                llRequestExperiencePermissions(owner,"");
            }
            else
            {
                llOwnerSay("No landmarks in inventory...");
            }
        }
    }
    experience_permissions(key k)
    {
        if (llGetAttached() && req_exp_perms)
        {
            req_exp_perms = FALSE;
            if (llGetInventoryNumber(INVENTORY_LANDMARK))
            {
                if (request = llRequestInventoryData(llGetInventoryName(INVENTORY_LANDMARK,0)))
                {
                    llOwnerSay("default experience_permissions() reading landmark data... " + llGetTimestamp());
                    llSetTimerEvent(10.0);
                    return;
                }
                else
                {
                    llOwnerSay("llRequestInventoryData() returned " + (string)request + " when reading '" + llGetInventoryName(INVENTORY_LANDMARK,0) + "'...");
                }
            }
            else
            {
                llOwnerSay("no landmarks in inventory...");
            }
        }
    }
    experience_permissions_denied(key k, integer reason)
    {
        if (req_exp_perms)
        {
            req_exp_perms = FALSE;
            llOwnerSay("default experience_permissions_denied() Reason(" + (string)reason + "): " + llGetExperienceErrorMessage(reason));
        }
    }
    dataserver(key k, string data)
    {
        if (llGetAttached() && (k == request) && (llListFindList(["",NULL_KEY],[(string)k]) == -1))
        {
            llSetTimerEvent(0.0);
            request = NULL_KEY;
            if (data)
            {
                vector v = (vector)data;
                if ((v.x < 0.0) || (v.x >= 256.0) || (v.y < 0.0) || (v.y >= 256.0))
                {
                    vector look_at = llVecNorm((vector)llGetObjectDesc()); //local (vec norm)
                    vector global_pos = (llGetRegionCorner() + (vector)v); //world pos
                    global_pos = <global_pos.x - llFloor(global_pos.x / 256.0) * 256.0,global_pos.y - llFloor(global_pos.y / 256.0) * 256.0,global_pos.z>; //corrected to region pos
                    look_at = <global_pos.x + look_at.x,global_pos.y + look_at.y,0.0>; //corrected to region pos (because llTeleportAgent() look_at is a region pos)
                    teleporting = TRUE;
                    llOwnerSay("default dataserver() Teleporting you to " + Trim_Vector(global_pos,0) + " with look_at of " + Trim_Vector(look_at,0) + "... " + llGetTimestamp());
                    llSetTimerEvent(0.01);
                    llTeleportAgent(owner,llGetInventoryName(INVENTORY_LANDMARK,0),ZERO_VECTOR,look_at);
                    state new_state;
                    llOwnerSay("default dataserver() state change did not happen... " + llGetTimestamp());
                }
                else
                {
                    llOwnerSay("Landmark in inventory must be for a location in a different region.");
                }
            }
            else
            {
                llOwnerSay("dataserver returned empty data");
            }
        }
    }
    timer()
    {
        llSetTimerEvent(0.0);
        teleporting = FALSE;
        llOwnerSay("default timer() " + llGetTimestamp());
    }
    state_exit()
    {
        llOwnerSay("default state_exit() " + llGetTimestamp());
    }
}
state new_state
{
    state_entry()
    {
        llSetTimerEvent(0.0);
        teleporting = FALSE;
        llOwnerSay("new_state state_entry() " + llGetTimestamp());
    }
    on_rez(integer i)
    {
        llResetScript();
    }
    touch_end(integer i)
    {
        if (llDetectedKey(0) == llGetOwner())
        {
            llOwnerSay("new_state touch_end() " + llGetTimestamp());
            init = FALSE;
            state default;
        }
    }
    state_exit()
    {
        llOwnerSay("new_state state_exit() " + llGetTimestamp());
    }
}

Put the above code in an HUD and compile with an experience.

Allow the experience over the land you use it over.

Put a landmark to a different region in the HUD.

Allow the experience on your person.

Click the HUD and it will teleport you to another region.

state change is attempted, but does not occur as you will see that the timer event for default state triggers.

Touching the HUD again executes the touch_end() event in default state.

new_state is never reached.

 

[09:29:13] test: default touch_end() Requesting experience permissions... 2023-06-21T16:29:12.284205Z
[09:29:14] test: default experience_permissions() reading landmark data... 2023-06-21T16:29:13.306395Z
[09:29:14] test: default dataserver() Teleporting you to <128, 128, 22.96> with look_at of <128, 129, 0>... 2023-06-21T16:29:13.328703Z
[09:29:14] Second Life: Teleport completed from Main Channel Sandbox A (50,211,34)
You are now at Cleaned as needed and without warning, Testylvania Sandbox (128, 128, 23)
[09:29:15] test: default timer() 2023-06-21T16:29:14.147432Z 

 [Edit] I did get some attempts that did not fail, but not many.

I'm also seeing state change failures when detaching happens just before, but it is less consistent. I'll make a separate filing for that later.

sl-service-account commented 1 year ago

Aglaia commented at 2023-06-21T21:20:04Z

Thanks Lucy for your scenario, which is much more straightforward than mine. So the problem is actually that if a script tries to change state just after a teleport, the change state will not work. Be it a manual teleport or a scripted teleport.