Jump to content

Reading a list from a notecard


MelodicRain
 Share

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

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

Recommended Posts

I was writing a simple "remote control" script, that basically captures your click on a HUD, then reads values from a list and sends some text in local. I was also testing how much data I can put into the list.

HUD script:

default
{
    touch_start(integer total_number)
    {
        string buttonClicked = llList2String(llGetLinkPrimitiveParams(llDetectedLinkNumber(0),[PRIM_NAME]),0);
        llSay(123456789, buttonClicked);
    }
}

Listener script:

list chatOptions = ["1","2","3","4",...,"200"];

default
{
    state_entry()
    {
        llListen(123456789, "", NULL_KEY, "");
    }
}

listen(integer chan, string name, key id, string msg)
{
    string buttonClicked = msg;
    integer index = (integer)buttonClicked-1;
    string whatToSay = llList2String(chatOptions, index); //if you clicked button 1 on the HUD it'll say the string in index 1 in the list
    llSay(0,whatToSay);
}

I'm getting a stack heap collision error after a while for the object container the "listener" script. I'm guessing it's due to the extremely long list? Maybe a better way to do this is to put the list in a notecard and read that? Can anyone give me some pointers on how to do this?

Edited by MelodicRain
Link to comment
Share on other sites

I realize that this is just a test, but you are doing a couple of things the hard way.  For one thing, your line

string buttonClicked = llList2String(llGetLinkPrimitiveParams(llDetectedLinkNumber(0),[PRIM_NAME]),0);

seems destined to just give you the link number ("1", "2", "3", ...), so why not just ask

string ButtonClicked = (string) llDetectedLinkNumber(0);  ?

For another thing, since ButtonClicked is the message that you are sending to the receiving script, you can just llSay(0, message); to hear it, at least during this testing phase while your list chatOptions is just a list of the same numbers.  Doing that will tell you whether the stack heap error has anything at all to do with the list.  I suspect that it doesn't.  You can put an incredibly long list into a script before you hit its 64K memory limit, and you are not doing anything in this script that requires a loop that might accidentally make the list blow up.

There's evidently something else going on.  For starters, I suggest using llOwnerSay in both scripts to check on what message is actually being sent and received.

Edited by Rolig Loon
typos. as always.
Link to comment
Share on other sites

22 minutes ago, Rolig Loon said:

I realize that this is just a test, but you are doing a couple of things the hard way.  For one thing, your line

string buttonClicked = llList2String(llGetLinkPrimitiveParams(llDetectedLinkNumber(0),[PRIM_NAME]),0);

seems destined to just give you the link number ("1", "2", "3", ...), so why not just ask

string ButtonClicked = (string) llDetectedLinkNumber(0);  ?

For another thing, since ButtonClicked is the message that you are sending to the receiving script, you can just llSay(0, message); to hear it, at least during this testing phase while your list chatOptions is just a list of the same numbers.  Doing that will tell you whether the stack heap error has anything at all to do with the list.  I suspect that it doesn't.  You can put an incredibly long list into a script before you hit its 64K memory limit, and you are not doing anything in this script that requires a loop that might accidentally make the list blow up.

There's evidently something else going on.  For starters, I suggest using llOwnerSay in both scripts to check on what message is actually being sent and received.

Yeah the values in the list right now are shortened for the forum so I don't clutter up the code block, they are actually pretty long random lorem ipsum strings for the purpose of testing how much I can cram into a list, so I can't use llDetectedLinkNumber for the actual message I want to send.

Edited by MelodicRain
Link to comment
Share on other sites

Getting stack heap collision most probably means that your script has run out of memory. You have probably put too many items in your list (or you have not shared the entire code with us).

 

1 hour ago, MelodicRain said:
	string buttonClicked = msg;
	integer index = (integer)buttonClicked-1;
	string whatToSay = llList2String(chatOptions, index); //if you clicked button 1 on the HUD it'll say the string in index 1 in the list

 

It is also usually a good practice to check your index before accessing a list with it. Although LSL is a pretty safe language and (usually) you will not shoot your own leg if you don't do it, adding such check really won't hurt and will make the code better. If buttonClicked was for example 0, then you will get a negative index and llList2String will not do what you wanted it to do.

Edited by tomm55
Link to comment
Share on other sites

Is this really the entire listener script that eventually gets the stack heap collision? (I mean "entire" besides the ellipsis for the list elements between 4 and 200.)

It's true that this error indicates the script has run out of memory, but if it's simply a question of list length, that should happen right away, or possibly the first time the list is used in a function call (call-by-value), but instead you're seeing it happen "after a while." That would seem to suggest there's more in that script than what we see here.

It's certainly possible to put the list into a notecard, one line per element, then read a single line to fetch the corresponding text, but that's slow and would make the script much more complex (because the value fetched from the notecard appears in a dataserver() event handler, not right there in the listen() handler), so it would be nice to avoid it.

Link to comment
Share on other sites

14 minutes ago, Qie Niangao said:

Is this really the entire listener script that eventually gets the stack heap collision? (I mean "entire" besides the ellipsis for the list elements between 4 and 200.)

It's true that this error indicates the script has run out of memory, but if it's simply a question of list length, that should happen right away, or possibly the first time the list is used in a function call (call-by-value), but instead you're seeing it happen "after a while." That would seem to suggest there's more in that script than what we see here.

It's certainly possible to put the list into a notecard, one line per element, then read a single line to fetch the corresponding text, but that's slow and would make the script much more complex (because the value fetched from the notecard appears in a dataserver() event handler, not right there in the listen() handler), so it would be nice to avoid it.

It is, except I shortened the strings in the list to avoid cluttering up the code block. There are also multiple lists, so it's something like this:

list1 = ["abcd","efgh",...,"z123"];
list2 = ["random string 1","random string 2",..."random string 200"];

...

listen(integer chan, string name, key id, string msg)
{
    string buttonClicked = msg;
    integer index = (integer)buttonClicked-1;
    string whatToSay1 = llList2String(list1, index); //if you clicked button 1 on the HUD it'll say the string in index 1 in the list
    string whatToSay2 = llList2String(list2, index);
    llSay(0,whatToSay1);
    llSay(0,whatToSay2);
}

 

Edited by MelodicRain
  • Like 1
Link to comment
Share on other sites

17 minutes ago, MelodicRain said:

It is, except I shortened the strings in the list to avoid cluttering up the code block. There are also multiple lists, so it's something like this:

list1 = ["abcd","efgh",...,"z123"];
list2 = ["random string 1","random string 2",..."random string 200"];

...

listen(integer chan, string name, key id, string msg)
{
    string buttonClicked = msg;
    integer index = (integer)buttonClicked-1;
    string whatToSay1 = llList2String(list1, index); //if you clicked button 1 on the HUD it'll say the string in index 1 in the list
    string whatToSay2 = llList2String(list2, index);
    llSay(0,whatToSay1);
    llSay(0,whatToSay2);
}

 

It's difficult to say if we don't know what is behind the three dots ("...") :) But if there is more code, it's really possible that you have just put too much into your LSL script. I recommend checking the used memory using e.g. http://wiki.secondlife.com/wiki/LlGetUsedMemory and llGetMemoryLimit to get available memory (maybe you are also not compiling to Mono?).

Edited by tomm55
typos
  • Like 1
Link to comment
Share on other sites

8 minutes ago, tomm55 said:

It's difficult to say if we don't know what is behind the three dots ("...") :) But if there is more code, it's really possible that you have just put too much into your LSL script. I recommend checking the used memory using e.g. http://wiki.secondlife.com/wiki/LlGetUsedMemory and llGetMemoryLimit to get available memory (maybe you are also not compiling to Mono?).

It's pretty much all random strings like "random string 158".

I get this when I click on a HUD button after adding the memory calculator:

76244 bytes of memory currently used.

Can you please also clarify how to compile to mono?

  • Like 1
Link to comment
Share on other sites

7 minutes ago, MelodicRain said:
76244 bytes of memory currently used.

If this is what LlGetUsedMemory returns, then that's way too much, even if you compile it to Mono. Surprises me that it even runs for a while :D

 

Edited by tomm55
Deleted part of my reply as Rolig already described it
  • Like 1
Link to comment
Share on other sites

I guess those strings add up. It might be possible to save a little space by combining the separate lists and their corresponding strings, but in this case it's very unlikely to be enough.

If the size is pretty representative, probably the easiest approach would be to use three scripts, one that stores the list1 strings, one for list2, and the third and main script that uses link messaging to combine a string from each into the output message. This means that the list-specific script would send their strings to the main script using llMessageLinked() and the main script would gather those strings in a link_message() event handler and only when it gets both can it generate the output.

If, on the other hand, the real strings could ever get even longer, you might be better off with your original suggestion: store them in notecards and fetch them in listen() when needed using llGetNotecardLine() and generating the output upon return of the notecard line value(s) in dataserver().

There are, of course, other options and other variants on these options, but they seem the simplest to start.

  • Like 1
Link to comment
Share on other sites

to the point of the OP, it's entirely possible to put long lists into notecards, although the obvious caveats are that you can't modify notecards, so you can't write data to the list while it's running, and fetching data happens asynchronysly, which can be a bit confusing.

key gHandleNotecardParse; // dataserver handle, for an initial pass through the script.
string gNotecardName = "New Notecard";
string gNotecardSepparator = "-"; // if this line is read from the notecard, it's the end of a list block.
list gNotecardListExtents = [0]; //notecard will hold multiple lists, delimited by these ranges. this will be filled in by searching the notecard for the seppparator line.
integer gNotecardCurrentLine = 0;

key gHandleNotecardSay; // dataserver handle, for saying a line from the notecard.

say_random_line(integer section) // sections start from 0.
{
  integer line_min = llList2Integer(gNotecardListExtents,section);
  integer line_max = llList2INteger(gNotecardListExtents,section+1)-1;
  integer line_rand = line_min+(integer)llFrand(line_max-line_min);
  gHandleNotecardSay=llGetNotecardLine(gNotecardName,line_rand);
}

default
{
  state_entry()
  {
    gHandleNotecardParse= llGetNotecardLine(gNotecardName,gNotecardCurrentLine);
  }
  dataserver(key ID, string data)
  {
    if(ID==gHandleNotecardParse)
    {
      if(data==gNotecardSepparator)
      {
        gNotecardListExtents+=[gNotecardCurrentLine+1];
      }else if(ID==EOF)
      {
        gNotecardListExtents+=[gNotecardCurrentLine+1];
        llOwnerSay("Read notecard.");
        // maybe go to a new state.
        return;
      }
      ++gNotecardCurrentLine;
      gHandleNotecardParse= llGetNotecardLine(gNotecardName,gNotecardCurrentLine);
    }else if(ID==gHandleNotecardSay)
    {
      llSay(0,data);
    }
  }
}

 

Link to comment
Share on other sites

Ok, I had a little bit of time, so inspired by @Qie Niangao a wrote a possible solution. It can really get as simple as this:

key notecard1Query;
key notecard2Query;

string NOTECARD1_NAME = "notecard1"; // edit this to match your notecard name
string NOTECARD2_NAME = "notecard2"; // edit this to match your notecard name
 
default
{
    state_entry()
    {
        if (llGetInventoryKey(NOTECARD1_NAME) == NULL_KEY 
            || llGetInventoryKey(NOTECARD2_NAME) == NULL_KEY)
        {
            llOwnerSay("Notecard missing");
            return;
        }
        llListen(123, "", NULL_KEY, "");
    }

    listen(integer chan, string name, key id, string msg)
    {
        integer index = (integer)msg;
        if (index >= 0)
        {
			// Line numbers are counted from zero, so 1st line is 0, 2nd line is 1, ...
            notecard1Query = llGetNotecardLine(NOTECARD1_NAME, index);
            notecard2Query = llGetNotecardLine(NOTECARD2_NAME, index);
        }
    }
 
    dataserver(key query_id, string data)
    {
        if (data == EOF)
            return;
        
        if (query_id == notecard1Query)
        {
            llSay(0, NOTECARD1_NAME + ": " + (string)data);
        }
        else if (query_id == notecard2Query)
        {
            llSay(0, NOTECARD2_NAME + ": " + (string)data);
        }
    }
}

In this code I have separate notecard for each list. I don't know what you want to do exactly, so I simply output the same line for both notecards. You can of course adjust it as needed. 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

On 1/19/2022 at 12:35 PM, tomm55 said:

Ok, I had a little bit of time, so inspired by @Qie Niangao a wrote a possible solution. It can really get as simple as this:

key notecard1Query;
key notecard2Query;

string NOTECARD1_NAME = "notecard1"; // edit this to match your notecard name
string NOTECARD2_NAME = "notecard2"; // edit this to match your notecard name
 
default
{
    state_entry()
    {
        if (llGetInventoryKey(NOTECARD1_NAME) == NULL_KEY 
            || llGetInventoryKey(NOTECARD2_NAME) == NULL_KEY)
        {
            llOwnerSay("Notecard missing");
            return;
        }
        llListen(123, "", NULL_KEY, "");
    }

    listen(integer chan, string name, key id, string msg)
    {
        integer index = (integer)msg;
        if (index >= 0)
        {
			// Line numbers are counted from zero, so 1st line is 0, 2nd line is 1, ...
            notecard1Query = llGetNotecardLine(NOTECARD1_NAME, index);
            notecard2Query = llGetNotecardLine(NOTECARD2_NAME, index);
        }
    }
 
    dataserver(key query_id, string data)
    {
        if (data == EOF)
            return;
        
        if (query_id == notecard1Query)
        {
            llSay(0, NOTECARD1_NAME + ": " + (string)data);
        }
        else if (query_id == notecard2Query)
        {
            llSay(0, NOTECARD2_NAME + ": " + (string)data);
        }
    }
}

In this code I have separate notecard for each list. I don't know what you want to do exactly, so I simply output the same line for both notecards. You can of course adjust it as needed. 

Thanks for the example.

I don't understand the dataserver portion. It seems it's only able to fetch data from one notecard at a time? How does it determine which notecard to fetch data from? Is it possible to fetch data from 2 or more notecards at once? For example if notecard 1 is:

one
two
three
four
five
six

And notecard 2 is:

a
b
c
d
e
f

If msg = 1, how do I retrieve line 1 from both notecards and have it print "one a"?

Edited by MelodicRain
Link to comment
Share on other sites

It's almost doing that now. The commands in the listen event trigger the dataserver event twice: once to read notecard1 and once to read notecard2.  It will llSay the result of each reading on a separate line.  If you'd like it to say both on the same line instead, you'll have to save the results from the first card and then read the second card and say both results together. So have your script do exactly that, saving two important bits of information as new global variables:

key notecard1Query;
key notecard2Query;
integer index;         // A new global integer variable to save the value received as a chat message in the listen event
string result_one;     // A new global string variable for saving the result of the query to notecard1
string NOTECARD1_NAME = "notecard1"; // edit this to match your notecard name
string NOTECARD2_NAME = "notecard2"; // edit this to match your notecard name
 
default
{
    state_entry()
    {
        if (llGetInventoryKey(NOTECARD1_NAME) == NULL_KEY 
            || llGetInventoryKey(NOTECARD2_NAME) == NULL_KEY)
        {
            llOwnerSay("Notecard missing");
            return;
        }
        llListen(123, "", NULL_KEY, "");
    }

    listen(integer chan, string name, key id, string msg)
    {
        //OK, here's where we use the first one of those two new global variables ....
        index = (integer)msg;  // We're going to need this value of index later, so save it as a global variable
        if (index >= 0)
        {
			// Line numbers are counted from zero, so 1st line is 0, 2nd line is 1, ...
            notecard1Query = llGetNotecardLine(NOTECARD1_NAME, index); // First read notecard1
        }
    }
 
    dataserver(key query_id, string data)
    {
        if (query_id == notecard1Query)
        {
            // And here's the second new global variable
            result_one = data;
            notecard2Query = llGetNotecardLine(NOTECARD2_NAME, index);  // This reads notecard2, using the saved value of index
        }
        else if (query_id == notecard2Query)
        {
            llSay(0, result_one + " "+ data);  // Now combine result_one with the value from notecard2
        }
    }
}

 

  • Like 1
Link to comment
Share on other sites

Be sure to read the documentation for the function over on the wiki: http://wiki.secondlife.com/wiki/LlGetNotecardLine

llGetNotecardLine takes two parameters: a string which is the name of the notecard you want to read from, and an integer which specifies what line you wish to read. So yes, it can only read from one notecard at a time. If you want to read the from two different notecards, then you need two different calls to llGetNotecardLine, each one specifying a single target notecard.

This also means if you're reading from two notecards to combine the data, that you will need to process two separate dataserver events. You'll need to structure your code to be able to handle that - and this is where the handle key returned by llGetNotecardLine becomes important. You will use that to determine which read has just happened. A very simple example, using your two notecards above (assume they are named "noteA" and "noteB" in the object's inventory:

//Reading from notecard demo.
//Owner will speak the line number they wish to have read.

key readLineA;	//handler to idenfify reads to noteA
key readLineB;	//handler to idenfify reads to noteB

string output;

default
{
    state_entry()
    {
        llListen(0,"",llGetOwner(),"");		//setup listen to drive demo. Say a number from 0 to 5
    }

    listen(integer chan, string name, key id, string msg)
    {	//when we hear a number 0-5, use that as a line number to read from both notecards
        readLineA = llGetNotecardLine("noteA",(integer)msg);
        readLineB = llGetNotecardLine("noteB",(integer)msg);
    }
    
    dataserver(key id, string data)
    {
        if(data == EOF)
        {
            llOwnerSay("no data, end of file");
            return;
        }
        
        if(id == readLineA) // process first read
        {
            output = data;	//initialize global variable
        }
        if(id == readLineB) // process second read and then output result
        {
            output += " " + data;	//append to global variable
            llOwnerSay(output);		//print combined output
            output = "";			//reset for next demo
        }
    }
}

 

In this example, we queue up two reads and then structure the dataserver event to be able to handle them both. In reality, the dataserver event will be invoked twice, so we use the handler keys to figure out what to do on each iteration.

Edited by Fenix Eldritch
Ah, Rolig beat me :)
  • Like 1
Link to comment
Share on other sites

1 hour ago, MelodicRain said:

Thanks for the example.I don't understand the dataserver portion.

It works like this:
1. You call llGetNotecardLine and it basically says "Ok, I will read a line from notecard, here you have an ID for this request and I will let you know when it's ready".
2. Execution of your code continues with the next line of code.
3. When your line has been read from notecard and is ready, then the dataserver event is fired. It basically says "Hey, here's the line that you requested, and gives you also the request ID."

The order in which the dataserver event will be fired is probably not guaranteed in my opinion (or at least it's not a good practice to rely on it), so you might get data for second request first. If that happens, then the solution proposed by @Fenix Eldritch will not work very well.

So if you need both lines at same time and order is important to you, then I propose a more robust way:

key notecard1Query;
key notecard2Query;

string NOTECARD1_NAME = "notecard1";
string NOTECARD2_NAME = "notecard2";

string resultOne;
string resultTwo;
 
default
{
    state_entry()
    {
        if (llGetInventoryKey(NOTECARD1_NAME) == NULL_KEY 
            || llGetInventoryKey(NOTECARD2_NAME) == NULL_KEY)
        {
            llOwnerSay("Notecard missing");
            return;
        }
        llListen(123, "", NULL_KEY, "");
    }

    listen(integer chan, string name, key id, string msg)
    {
        integer index = (integer)msg;
        if (index >= 0)
        {
            resultOne = "";
            resultTwo = "";
            notecard1Query = llGetNotecardLine(NOTECARD1_NAME, index);
            notecard2Query = llGetNotecardLine(NOTECARD2_NAME, index);
        }
    }
 
    dataserver(key query_id, string data)
    {
        if (data == EOF)
            return;
        
        if (query_id == notecard1Query)
        {
            resultOne = (string)data;
        }
        else if (query_id == notecard2Query)
        {
            resultTwo = (string)data;
        }
        
        if (resultOne != "" && resultTwo != "")
        {
            llSay(0, resultOne + " " + resultTwo);
        }
    }
}

 

Edited by tomm55
Link to comment
Share on other sites

1 minute ago, tomm55 said:

The order in which the dataserver event will be fired is probably not guaranteed in my opinion (or at least it's not a good practice to rely on it), so you might get data for second request first. If that happens, then the solution proposed by @Rolig Loon and @Fenix Eldritch will not work very well.

Wrong.  The request to read notecard2 will not even be sent until after the results from the query to notecard1 are received.  That's why you always daisy-chain dataserver requests in the dataserver event instead of issuing them as a lump in the listen event. 

Link to comment
Share on other sites

3 minutes ago, Rolig Loon said:

Wrong.  The request to read notecard2 will not even be sent until after the results from the query to notecard1 are received.

I don't agree @Rolig Loon:) llGetNotecardLine is an asynchronous function. The execution of next line of code follows immediatelly.

Try this example and see for yourself:

key notecard1Query;
key notecard2Query;

string NOTECARD1_NAME = "notecard1";
string NOTECARD2_NAME = "notecard2";

string resultOne;
string resultTwo;
 
default
{
    state_entry()
    {
        if (llGetInventoryKey(NOTECARD1_NAME) == NULL_KEY 
            || llGetInventoryKey(NOTECARD2_NAME) == NULL_KEY)
        {
            llOwnerSay("Notecard missing");
            return;
        }
        llListen(123, "", NULL_KEY, "");
    }

    listen(integer chan, string name, key id, string msg)
    {
        integer index = (integer)msg;
        if (index >= 0)
        {
            resultOne = "";
            resultTwo = "";
            notecard1Query = llGetNotecardLine(NOTECARD1_NAME, index);
            llSay(0, "request for 1st notecard sent");
            notecard2Query = llGetNotecardLine(NOTECARD2_NAME, index);
            llSay(0, "request for 2nd notecard sent");
        }
    }
 
    dataserver(key query_id, string data)
    {
        if (data == EOF)
            return;
        
        if (query_id == notecard1Query)
        {
            llSay(0, "result for 1st notecard received");
            resultOne = (string)data;
        }
        else if (query_id == notecard2Query)
        {
            llSay(0, "result for 2nd notecard received");
            resultTwo = (string)data;
        }
        
        if (resultOne != "" && resultTwo != "")
        {
            llSay(0, resultOne + " " + resultTwo);
        }
    }
}
            

The output will be:

request for 1st notecard sent
request for 2nd notecard sent
result for 1st notecard received
result for 2nd notecard received

 

Link to comment
Share on other sites

But that's not what I wrote.  Study the listen and dataserver events in the post I made above:

listen(integer chan, string name, key id, string msg)
    {
        //OK, here's where we use the first one of those two new global variables ....
        index = (integer)msg;  // We're going to need this value of index later, so save it as a global variable
        if (index >= 0)
        {
			// Line numbers are counted from zero, so 1st line is 0, 2nd line is 1, ...
            notecard1Query = llGetNotecardLine(NOTECARD1_NAME, index); // First read notecard1
        }
    }
 
    dataserver(key query_id, string data)
    {
        if (query_id == notecard1Query)
        {
            // And here's the second new global variable
            result_one = data;
            notecard2Query = llGetNotecardLine(NOTECARD2_NAME, index);  // This reads notecard2, using the saved value of index
        }
        else if (query_id == notecard2Query)
        {
            llSay(0, result_one + " "+ data);  // Now combine result_one with the value from notecard2
        }
    }

The listen event only makes one request to the dataserver.  The second request, to read notecard2, is issued in the dataserver event only if a result is received from the first request.  There is never a possibility of reversing the two.  That's the only responsible way to guarantee that the results will always be received in the proper order.

Link to comment
Share on other sites

21 minutes ago, tomm55 said:

I don't agree @Rolig Loon:) llGetNotecardLine is an asynchronous function. 

You're in violent agreement about that asynchrony, but talking about different scripts that handle it differently. If you look at Rolig's example, it issues the read request for that second notecard line from within the dataserver event generated by the result of reading the first notecard—as she described, a "daisy-chain" of requests.

It's possible to do it either way, but if both queries are issued together (in the listen handler here) then the dataserver handler must handle the possible—if unlikely—out-of-order condition, as you say.

 

  • Like 1
Link to comment
Share on other sites

7 minutes ago, Qie Niangao said:

You're in violent agreement about that asynchrony, but talking about different scripts that handle it differently. If you look at Rolig's example, it issues the read request for that second notecard line from within the dataserver event generated by the result of reading the first notecard—as she described, a "daisy-chain" of requests.

It's possible to do it either way, but if both queries are issued together (in the listen handler here) then the dataserver handler must handle the possible—if unlikely—out-of-order condition, as you say.

 

Aaaah, I'm sorry @Rolig Loon ! Now I can see it. I probably looked at @Fenix Eldritch's post instead of Rolig's.

So, @Rolig Loon's script will work fine :) Sorry for the confusion!

Edited by tomm55
  • Thanks 1
Link to comment
Share on other sites

29 minutes ago, Qie Niangao said:

if both queries are issued together (in the listen handler here) then the dataserver handler must handle the possible—if unlikely—out-of-order condition, as you say.

It's my nature to never trust asynchronous callbacks (after years of programming) :D

Anyway, @MelodicRain, you can use whatever solution was posted in this topic and you will be fine :)

And next time I will not write posts on a small display 🤪

Edited by tomm55
Link to comment
Share on other sites

24 minutes ago, tomm55 said:

It's my nature to never trust asynchronous callbacks (after years of programming) :D

Heh.  Mine too. 😉  So, @MelodicRain, there are two sorts of lessons to take away from this. First, the obvious one: There's more than one way to skin a cat (or write a script). It's usually hard to say which way is best, because scripters will come at a challenge from different directions, thinking of different potential problems and anticipating different possible enhancements down the line.  The second lesson is less obvious: Scripting is only slightly about the syntax and functions of whatever language we use. Those are important, but the heart of scripting is logic -- figuring out what you want to accomplish and creating an effective plan for getting there. It's like creating a map to describe how to drive between two distant points. There may be many possible routes, but your plan must account for all the choices you need to make along the way, all of the things that may go wrong, and how to make the route most interesting and least confusing.

  • Like 1
Link to comment
Share on other sites

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