Jump to content

dataserver event being interrupted stops


Gregory McLeod
 Share

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

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

Recommended Posts

I am struggling with a HUD that refuses to work correctly.

One script handles the ATTACHing, another script hansles the ANIMATIONs and CONTROLS and the thirs handles the user ci=ommunication. It is this script that is causing me problems.

It is structured so that in the defaults state_entry event it calls a function to load messages from a NOTECARD into memory and only when complete notify the other scripts it is ready to accept messages intended for the User.

These messages are coming in the form of link_message events. They seem to interruot the dataserver event and prevent it from receiviing other lines which are requested.

Is there a problem here or am I coding it incorrectly.

<code>

integer    S5ChecksComplete    = FALSE;
integer    AllChecksComplete    = FALSE;
key        gkPlayer            = NULL_KEY;
float    gfTimeOut            = 60.0;
integer    giTimerMsgs            = FALSE;
integer    giTimerChecks        = FALSE;
list    gaMessageIDs        = [];
list    gaMessages            = [];
string    gsMessageNotecard    = "";
integer    giLine                = 0;
key        gkRequestID            = "";
string    gsDefaultMessageNC    = "@Messages";
string   gsEscape           = "~";
integer    MSG_OWNERSAYMSG        = 0x0210;
integer    MSG_WHISPERMSG        = 0x0211;
integer    MSG_SAYMSG            = 0x0212;
integer    MSG_SHOUTMSG        = 0x0213;
string   gsHexDigits        = "0123456789ABCDEF";
integer MessagesLoaded = FALSE;

LoadMessages() {
    if (llGetInventoryType(gsMessageNotecard) == INVENTORY_NOTECARD) {
        gaMessageIDs = [];
        gaMessages = [];
        giLine = 0;
        gkRequestID = llGetNotecardLine(gsMessageNotecard,giLine);
    }
    else
        llOwnerSay((string)llGetUnixTime() + " UI: Messages notecard '"
                + gsMessageNotecard + "' not found!");
}
string GetMessage(integer piMessageID, list paMessageData) {
    integer  liIndex;
    integer  liLast;
    integer  liEnd;
    string   lsRawMessage;
    string   lsMessage;
    string   lsData;
    string   lsCode;
    list     laData;
    liIndex = llListFindList(gaMessageIDs,[piMessageID]);
    if (liIndex >= 0) {
        lsRawMessage = llList2String(gaMessages,liIndex);
        lsMessage = "";
        while (lsRawMessage != "")
        {
            liLast = llStringLength(lsRawMessage) - 1;
            liIndex = llSubStringIndex(lsRawMessage,gsEscape);
            lsData = "";
            if (liIndex >= 0) {
                liEnd = liIndex - 1;
                if (liIndex < liLast) {
                    ++liIndex;
                    lsCode = llGetSubString(lsRawMessage,liIndex,liIndex);
                    if (lsCode == gsEscape)
                        lsData = gsEscape;
                    else if (lsCode == "n")
                        lsData = "\n";
                    else if (lsCode == "t") 
                        lsData = "\t";
                    else if ((string)((integer)lsCode) == lsCode)
                        lsData = llList2String(paMessageData,(integer)lsCode);
                }
            }
            else liEnd = liLast;
            if (liEnd >= 0) lsMessage += llGetSubString(lsRawMessage,0,liEnd);
            lsMessage += lsData;
            lsRawMessage = llDeleteSubString(lsRawMessage,0,liIndex);
        }
    }
    else lsMessage = "[Unknown Message ID: "+BinaryToHex(piMessageID,4)+"; Params='"+llDumpList2String(llDeleteSubList(laData,0,0),"','")+"']";
    return lsMessage;
}
string BinaryToHex(integer piData, integer piDigits) {
    string  lsData;
    lsData = "";
    do
    {
        lsData = llGetSubString(gsHexDigits,piData & 0x0F,piData & 0x0F) + lsData;
        piData = piData >> 4;
        piDigits -= 1;
    } while (piDigits > 0);
    return lsData;
}

default {
    state_entry() {
        llOwnerSay((string)llGetUnixTime() + " U5:default state_entry event");
        llOwnerSay((string)llGetUnixTime() + " U5:default "+(string)llGetFreeMemory());
        gkPlayer = llGetOwner();
        llSetTimerEvent(gfTimeOut);
        giTimerMsgs = TRUE;
		giTimerChecks = TRUE;
        gsMessageNotecard = gsDefaultMessageNC;
        LoadMessages();
    }
    dataserver(key pkRequestID, string psData) {
        integer  liIndex;
        if (pkRequestID == gkRequestID) {
            if (psData != EOF) {
                liIndex = llSubStringIndex(psData,"=");
                if (liIndex > 0) {
                    gaMessageIDs += [(integer)("0x"+llGetSubString(psData,0,liIndex-1))];
                    gaMessages   += [llDeleteSubString(psData,0,liIndex)];
                    ++giLine;
                    llOwnerSay((string)llGetUnixTime() + " dataserver line: " + (string)giLine);
                    gkRequestID = llGetNotecardLine(gsMessageNotecard,giLine);
                }
                else llOwnerSay((string)llGetUnixTime() + " dataserver Bad Message line:\n"+psData);
            }
            else {
				llOwnerSay((string)llGetUnixTime() + " dataserver message lines loaded: " 
					+ (string)giLine);
				MessagesLoaded = TRUE;
				llSetTimerEvent(0.0);
				giTimerMsgs = FALSE;
			}
        }
		else llOwnerSay((string)llGetUnixTime() + " dataserver error");
    }
    on_rez(integer piParam) {
            llResetScript();
    }
    attach(key pkOwner) {
        llOwnerSay((string)llGetUnixTime() + " U5:default attach event");
        if (pkOwner != NULL_KEY) {
            llOwnerSay((string)llGetUnixTime() + " U5:default Game data set up!");
            llSetTimerEvent(gfTimeOut);
            giTimerChecks = TRUE;
        }
        else {
            llOwnerSay((string)llGetUnixTime() + " U5:default Invalid game data!");
            llSleep(10.0);
            llDie();
        }
    }
    link_message(integer piSender, integer piData, string psData, key pkID) {
        llOwnerSay((string)llGetUnixTime() + " U5:default link_message-event");
        llOwnerSay((string)llGetUnixTime() + " U5:default link_message data " + psData);
        if (psData == "S5ChecksComplete") {
            llOwnerSay((string)llGetUnixTime() + " U5:default S5ChecksComplete message received");
            S5ChecksComplete = TRUE;
            giTimerChecks = FALSE;
            llSetTimerEvent(0.0);
			if (S5ChecksComplete & MessagesLoaded) {
				state Initialize;
			}
			else llOwnerSay((string)llGetUnixTime() + " U5:default waiting for MessagesLoaded");
        }
        else llOwnerSay((string)llGetUnixTime() + " U5:default waiting for S5ChecksComplete message");
    }
    timer() {
        if (giTimerChecks) {
            llOwnerSay((string)llGetUnixTime() + " U5:default timed out waiting on " 
                + "S5CheckComplete message");
            llSleep(10.0);
            llDie();
        }
        if (giTimerMsgs) {
            llOwnerSay((string)llGetUnixTime() + " U5:default timed out waiting on " 
                + "message loading");
            llSleep(10.0);
            llDie();
        }
    }
}
state Initialize {
    state_entry() {
        llOwnerSay((string)llGetUnixTime() + " U5:Initialize state_entry event");
        llOwnerSay((string)llGetUnixTime() + " U5:Initialize doing other things");
        llMessageLinked(LINK_THIS,0,"U5ChecksComplete",gkPlayer);
    }
    on_rez(integer piParam) {
            llResetScript();
    }
    link_message(integer piSender, integer piData, string psData, key pkID) {
        llOwnerSay((string)llGetUnixTime() + " U5:Initialize link_message-event");
        llOwnerSay((string)llGetUnixTime() + " U5:Initialize link_message data " + psData);
        if (psData == "AllChecksComplete") {
            llOwnerSay((string)llGetUnixTime() + " U5:Initialize AllChecksComplete message received");
            AllChecksComplete = TRUE;
            giTimerChecks = FALSE;
            llSetTimerEvent(0.0);
            state Runtime;
        }
        else llOwnerSay((string)llGetUnixTime() + " U5:Initialize waiting for AllChecksComplete message");
    }
    timer() {
        if (giTimerChecks) {
            llOwnerSay((string)llGetUnixTime() + " U5:Initialize timed out waiting on " 
                + "AllCheckComplete message");
            llSleep(10.0);
            llDie();
        }
    }
}
state Runtime {
    state_entry() {
        llOwnerSay((string)llGetUnixTime() + " U5:Runtime state_entry event");
        llOwnerSay((string)llGetUnixTime() + " U5:Runtime doing other things");
    }
    attach(key pkPlayer) {
        llOwnerSay((string)llGetUnixTime() + " U5:Runtime attach event");
        if (pkPlayer == NULL_KEY) {
            llOwnerSay((string)llGetUnixTime() + " U5:Runtime attach null-key");
        }
        else {
            gkPlayer = pkPlayer;
            llOwnerSay((string)llGetUnixTime() + " U5:Runtime waiting");
        }
    }
    link_message(integer piSender, integer piData, string psData, key pkID) {
        list laData;
        if ((piData == MSG_OWNERSAYMSG) || (piData == MSG_WHISPERMSG)
                || (piData == MSG_SAYMSG) || (piData == MSG_SHOUTMSG)) {
            laData = llParseString2List(psData,["\n"],[]);
            psData = GetMessage((integer)llList2String(laData,0),llDeleteSubList(laData,0,0));
            if (piData == MSG_OWNERSAYMSG) llOwnerSay(psData);
            else {
                psData = "[" + llKey2Name(llGetOwner()) + "] " + psData;
                if (piData == MSG_WHISPERMSG) llWhisper(0,psData);
                else if (piData == MSG_SAYMSG) llSay(0,psData);
                else if (piData == MSG_SHOUTMSG) llShout(0,psData);
            }
        }
    }
    on_rez(integer piParam) {
            llResetScript();
    }
    touch_start(integer number) {
        llOwnerSay((string)llGetUnixTime() + " U5:Touched in Runtime state");
        llOwnerSay((string)llGetUnixTime() + " U5:Messages loaded: " 
            + (string)llGetListLength(gaMessages));
    }
}

 </code>

Any help cheerfully acknowledged.

Link to comment
Share on other sites


dataserver(key pkRequestID, string psData) { integer liIndex; if (pkRequestID == gkRequestID) {

 You're using this code (bolded, above) to ensure that responses match up with requests.

This is OK if your scripts do not set gkRequestID (i.e., they send another llHTTPRequest) before the first response is returned. If you are sending several llHTTPRequests very quickly, this will not work as you intend.

You may be expecting http_response in serial (one after the other), but receiving in parallel (still processing a request as a new one is sent), and this conditional will discard any that are have arrived before the most recent llHTTPRequest (since it only contains the most recent gkRequestID).

Suggest you remove this if it is likely to be a hinderance to your application, or if you still need send/response tracking you move to a list.

Link to comment
Share on other sites

Thanks for the input. I think I am sending a request for a line one at a time.

The default state/state_entry  calls LoadMessages()

LoadMessages issues request for first line (giLine = 0) setting gkRequestID

The Dataserver event checks the requestID received matches the gkRequestID and proceeds to process the line from the data supplied and then calls for a second line setting the gkRequestID again. Repeating this until EOF received.

If I understand you correctly you are saying that the response may be arriving before the request is sent???

Link to comment
Share on other sites

I wonder if we're all looking at the same script because I don't see  HTTP comms here.

I also don't see where this script, upon EOF reading notecard contents, sends an llMessageLinked() to any other scripts, so I'm not sure I understand how they're supposed to know when it's time to send messages.

If they do send messages while the notecard is being read by this script, yeah, they'll certainly interrupt notecard processing but until the MessagesLoaded flag is set, it shouldn't force this script into the Initialize state where it no longer processes notecard lines. At first glance I'm not what's going wrong here, and can't readily test it, so I wonder what of the various llOwnerSay() debug outputs it's generating. (E.g., does it say it's getting into that Initialize state somehow?)

Link to comment
Share on other sites

I don't think it's the link messages that are causing the problems.   I suspect it's the way you've structured things in your dataserver event.   I've tweaked it a bit to how I think it should be put together -- in particular, I ask for the next line whether or not the previous line was valid (as far as I can see, one invalid line in the notecard makes the script fall over).

This is untested.

	dataserver(key pkRequestID, string psData) {		integer  liIndex;		if (pkRequestID == gkRequestID) {			if (psData != EOF) {				psData = llStringTrim(psData,STRING_TRIM);//chop off leading and trailing spaces				if (psData){//ignore lines with no printing characters					liIndex = llSubStringIndex(psData,"=");					if (liIndex > 0) { //if it's a valid message, process it						gaMessageIDs += [(integer)("0x"+llGetSubString(psData,0,liIndex-1))];						gaMessages   += [llDeleteSubString(psData,0,liIndex)];					}					else{ //if it's not a valid message, complain						llOwnerSay((string)llGetUnixTime() + " dataserver Bad Message line:\n"+psData);					}				}				llSetTimerEvent(gfTimeOut);//?? not really sure why you're bothering with the time-out thingy but I think I would reset it here each time				++giLine; // advance the counter				llOwnerSay((string)llGetUnixTime() + " dataserver line: " + (string)giLine);				gkRequestID = llGetNotecardLine(gsMessageNotecard,giLine); //ask for the next line			}			else { //reached the end of the card				llOwnerSay((string)llGetUnixTime() + " dataserver message lines loaded: "					+ (string)giLine);				MessagesLoaded = TRUE;				llSetTimerEvent(0.0);				giTimerMsgs = FALSE;			}		}		else llOwnerSay((string)llGetUnixTime() + " dataserver error");	}

 

Link to comment
Share on other sites

Thanks Innula I will try your version as soon as I can.

The script was meant to fall over and report the line that caused the failure if it was not correctly formatted but I changed that to simplify the testing.

The verbose version gets as far as reading ALL the lines in the notecard but it is waiting for a message from the first script, the one that attaches the prim as a hud to complete the attaching, whereupon it sends a message to all the other scripts via link_message. When this script gets all the notecard lines it is supposed to go to the Initialize state and send the fact that it is finished. But the trace from the llOwnerSay tracking seems to indic ate that the message is never received.

Thanks to all who took the time and trouble to help me out.

Link to comment
Share on other sites

Not that it's the cause of the problem, but why does this script need a link message to tell it that that the prim containing it is attached?    What's wrong with if (llGetAttached()) ?

Have you checked the other script to make sure it's sending you the message you're expecting?

 

ETA -- since we can only see one of the scripts, and I've got no clear picture of what's supposed to be happening and why, it's a bit of a guessing game, but why not restructure things a bit, thus.

In state default, just read the notecard.   once that's read, go into state next, and , if you must, ask the other script to tell you if it's finished whatever it does when the object is attached.   That should give it plenty of time to do whatever it is it needs to, and you don't have to worry about the message getting lost because the script's busy reading the notecard (though I doubt that's the problem, myself).

Link to comment
Share on other sites

It should be pointed out, with no disrespect to either the OP nor any of the people that have attempted to address this issue, this is query does not address any element of LSL; it goes beyond that, through the realm of program design and architecture and into that of networked, parallel programming.

 

When one decides there is a need for multiple scripts to accomplish the task at hand, whether to circumvent the restrictive memory requirements we must work within, a (possibly unjustified) need for modularization of components or whatever; they will be faced with a number of obstacles. The primary obstacle is thatat it's most basic levelLSL is NOT a synchronous paradigm.

 

That is to say, any LSL script is driven solely by its own unique, server-set event queue for its specific logical execution. Attempting to get (in this case) three different scripts into "step" with each other by using 3 different states (a default, initialization state and, finally, a "OK, everybody else is up and running" state" for each script actually boils down to 1 possible configuration out of a total of 27 (3^3) resulting in a "stable state", where the three programs can continue forward.

 

That's all well and good, but this has to do with design and architecture. If the overall thought behind the structure as a whole is beyond the capabilities of the platform one is trying to implement it on, one needs to re-address either the need for that capability (Do these programs actually need to synch up in this way?) or rethink the basic premise of the requirements (Are multiple programs actually required to solve the problem at hand?).

Link to comment
Share on other sites

That is a very thoughtful posting LepreKhaun.

To address some of the points.

<quote>When one decides there is a need for multiple scripts to accomplish the task at hand, whether to circumvent the restrictive memory requirements we must work within, a (possibly unjustified) need for modularization of components or whatever; they will be faced with a number of obstacles. The primary obstacle is thatat it's most basic levelLSL is NOT a synchronous paradigm.</quote>

I would refer to a previous post of mine on the subject , if I could find it, in which one of the respondents advised that the function of the HUD scripts needed to be segregated. Animation and control separate from UI.

This was the original  designers approach but I am being tasked with modifying it to accomodate additional functions. These functions are to offload a central prim which is overloaded by multiple users requesting service and honor the requests from the individual's HUD.

My initial attempt at this kept the original designer's two scripts but ran into the inevitable stack/heap collision. This led to the three script solution.

<quote>That's all well and good, but this has to do with design and architecture. If the overall thought behind the structure as a whole is beyond the capabilities of the platform one is trying to implement it on, one needs to re-address either the need for that capability (Do these programs actually need to synch up in this way?) or rethink the basic premise of the requirements (Are multiple programs actually required to solve the problem at hand?).</quote>

The third script, the one that is the subject of this post, is not the complete script it has been reduced to the minimum to enable the testing of the whole.

You make mention of the capabilities of the platform. What do you suggest I do about that? I submit that I am working within the capabilies provided by LL I cannot change that, what I can do is get the best available brains to help me resolve the problems which is what I am doing.

Thanks for your input.

Link to comment
Share on other sites


Gregory McLeod wrote:

The verbose version gets as far as reading ALL the lines in the notecard but it is waiting for a message from the first script, the one that attaches the prim as a hud to complete the attaching, whereupon it sends a message to all the other scripts via link_message. When this script gets all the notecard lines it is supposed to go to the Initialize state and send the fact that it is finished. But the trace from the llOwnerSay tracking seems to indic ate that the message is never received.

As Innula said, it's very difficult to determine what's going on in a system by examining just one component, so this is shooting in the dark:

Are you sure that this script gets that "message from the first script" sometime after it's read all the notecard lines, not while it's still in the process of reading them? I ask because the Initialize state can only be reached from the link_message event, and then only if the notecard has already been read. If those events happen in the other order, it will be stuck in default state, having no way to get to Initialize from dataserver() when it reaches the notecard's EOF after it already got that link_message.

Thing is, if that's all that's wrong, the llOwnerSay debug trail should include

<unixtime> U5:default S5ChecksComplete message received

<unixtime> U5:default waiting for MessagesLoaded

sometime before it reports how many notecard lines it read. So if you're not seeing that, then I'd wonder if that "message from the first script" ever gets sent at all, or if it does, whether this script is recognizing it when it arrives. 

Link to comment
Share on other sites

OK, disregard everything else I may have said except for the key to having multiple scripts working together - LSL is NOT a synchronous paradigm.

 

Examine what that means.

 

Say you have 3 scripts within your object- "A", "B" and "C". Each of these scripts have specific jobs to do. All you, as the programmer, have to do is to co-ordinate these three scripts (it doesn't matter if you're using chat or link_message for this btw, just some way of communication between the scripts). OK?

 

Your first consideration is the constraints of the platform these 3 scripts operate on, which is the LSL VM (Virtual Machine). What, exactly, does that mean?

 

A Virtual Machine is a sand box, where untrusted code (your program) is allowed to execute within strictly defined limits. The limit you have to consider in this case is what is known as a "time slice".

 

Fortyfive times a second the server completes what is called a "frame". During that frame, it updates everything that server is responsible for. That means logging all the chat, keeping track of all the avatars within the sim, all of the physical interactions, updating individual agents' interest lists (oh, need a new texture for that mesh object you're nearing? NP, here it is from my asset server), updating each and every movement, scripted or physical (bouncing ball into the sea perhaps) and, with what is left over, servicing your 3 scripts.

 

And that's what a "time slice" is about. 45 times a second, every script in a region gets a chance to "do something". Notice the quotes, most of the time our scripts simply "pass" because there is no event message in their queue at the moment. Otherwise they have an event message that triggers an event handler (the only time a script is actually "doing" something) or the script resumes execution where they were interrupted. And, if the event handler isn't quick enough to finish out what you've assigned it to do in its "time slice", it will be interrupted- its local variables stored away along with the address of the next instruction until the next game cycle, one fortyfifth (1/45) of a second later.

 

No problem, really, so far. Our scripts get to "do their thing" in little time slices but those slices happen so quickly and so often we barely notice it (just as a cartoon is done with cels!). Program "A" looks just fine!

 

The problem arises when "B" and "C" also enter the picture. And the problem is with failing to understand what asynchronicity fully means: Not only will scripts "A", "B" and "C" not occur at the same time, you have no control over the order of their occurance. In one server time frame, it may be ABC, the next may be BCA, then CAB etc. The only guarantee you have is that your script will have a chance to "do something" each frame/cycle. But the server doesn't know that you may be expecting "B" before "C" and there is no way to make it even consider servicing those two scripts in that order, much less three of such.

Link to comment
Share on other sites

To Innula,

 

I have tried the modified script and it worked intermittently.

If the hud was rezzed on the ground it worked if it was the detached and worn it did not work.

I have made on addition to the scripts in the first I have put a sleep for 5 seconds in at the point where it is about to send the S5ChecksComplete message. This solved the problem.

I now have a HUD which will if rezzed on the ground ask for permission to attach, take controls and animate. It will also if worn in the correct place do the same correct actions. It will also survive logging off while it is still ayyached and correct;y operate when the avatar logs in again.

All the llOwnerSay message have been recorded and could be shown as proof but I do not want to clutter thias thread with them when the problem is solved. Incidentally a sleep of 2 seconds was not enough.

Thanks to all who helped, read but did not contribute. These forums are a major reason why I keep doing this..

Link to comment
Share on other sites

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