Jump to content

Best way to InstantMessage several text strings of info.?


Raena Parx
 Share

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

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

Recommended Posts

Hello. I have several text strings (basically data fields), each with it's own string name. The strings range from short or long depending on the data needed.

 I'm trying to send these strings privately to the individual who requests them on touch, so I'm using llInstantMessage, but am running into a few issues.

1. If I send them one string at a time it's    s-l-o-w       a-s       m-o-l-a-s-s-e-s.

2. There's no way to combine them into one long string before sending with llInstantMessage to speed things up since a string field cannot be more than 1024 characters.

Can someone suggest the best way to send several text strings privately on-touch?

Thank you in advance. :)

Edited by Raena Parx
Link to comment
Share on other sites

  • Raena Parx changed the title to Best way to InstantMessage several text strings of info.?
9 minutes ago, Raena Parx said:

BTW, I'm looking at the command - it goes to a channel (rather than an avatar key) - so how would I direct the output to the avatar that's inquiring?  Thank you agin.

 

llRegionSayTo goes to a target key, using a specific channel. Both are required, hence "say to."

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

2 hours ago, Raena Parx said:

BTW, I'm looking at the command - it goes to a channel (rather than an avatar key) - so how would I direct the output to the avatar that's inquiring?  Thank you agin.

llRegionSayTo(IdOfAvatar,0,"My message"); 0 is the channel that people see in local chat, all the other channels are for script stuff.

Link to comment
Share on other sites

Two things you want to do for the sake of efficiency.

1.) Swap to llRegionSayTo()
2.) Combine as much data into one string as possible.

for point 1. you can use llRegionSayTo() to send the data to them, i'd recommend checking if they're in the same region if this is going to be somewhere that has neighboring regions, otherwise the region say will fail. this will allow you to fall back to llInstantMessage() in such cases.

for point 2. strings can be a max of 1024, you can utilize for loops and do the math to combine data into a single string by comparing length left vs length of string to add. and if string to add is too long, send the data and start a new string. you can utilize for loops and "\n" for a newline where applicable. This will allow you to send more data in less strings, which is overall better for sending data to an agent as even llRegionSayTo() gets throttled if too many messages are sent on channel 0, but also data isn't always received in the order it was sent to the agent, so if you require message integrity you're better off formatting where you can. This approach also allows you to fall back to llInstantMessage() more reliably.

Link to comment
Share on other sites

23 hours ago, Quistess Alpha said:

llRegionSayTo(IdOfAvatar,0,"My message"); 0 is the channel that people see in local chat, all the other channels are for script stuff.

Thank you. So just to confirm the IdOfAvatar is the Avatar Key, correct?

Link to comment
Share on other sites

6 hours ago, alexx Ohmai said:

Two things you want to do for the sake of efficiency.

1.) Swap to llRegionSayTo()
2.) Combine as much data into one string as possible.

for point 1. you can use llRegionSayTo() to send the data to them, i'd recommend checking if they're in the same region if this is going to be somewhere that has neighboring regions, otherwise the region say will fail. this will allow you to fall back to llInstantMessage() in such cases.

for point 2. strings can be a max of 1024, you can utilize for loops and do the math to combine data into a single string by comparing length left vs length of string to add. and if string to add is too long, send the data and start a new string. you can utilize for loops and "\n" for a newline where applicable. This will allow you to send more data in less strings, which is overall better for sending data to an agent as even llRegionSayTo() gets throttled if too many messages are sent on channel 0, but also data isn't always received in the order it was sent to the agent, so if you require message integrity you're better off formatting where you can. This approach also allows you to fall back to llInstantMessage() more reliably.

Thank you for the suggestion.  The receiving avatar would be on the same region because it's activated on-touch.  The strings are already formatted with "\n'"'s, but vary a lot in size.

I'm a bit lost on how the "For" operators works in .lsl. 

For example, i found this on the lsl wiki:

/block statement
integer a = 0;
integer b = 10;
for(; a < b; ++a)
{
    llOwnerSay((string)a);
    llOwnerSay((string)b);
}

why does it start with a semicolon? does that mean there's no initializer? - i'm just guessing.

and the ++a....is that a counter?  meaning "a" will increment plus one each loop?

trying to figure out how I would write a loop that reads all the data, one string at at time (so using lists for the string names, i'm guessing), and arrays?...(that's where I really get confused), then join the strings together into strings no longer than 1024 characters.  Not sure I'm that program-savvy.  But if I understand the concept, I might be able to pull it off.

Thank you for your help :)

 

 

 

Link to comment
Share on other sites

41 minutes ago, Raena Parx said:

why does it start with a semicolon? does that mean there's no initializer? - i'm just guessing.

and the ++a....is that a counter?  meaning "a" will increment plus one each loop?

Correct on both!

An alternative method is to use a while-loop, which is similar / maybe simpler:

integer a = 0;
integer b = 10;
while (a < b)
{
  llOwnerSay((string)a);
  ++a;
}

...just don't forget to increment, otherwise you end up with a very spammy infinite loop. 🙂 The for-loop may be better for your own memory!

 

41 minutes ago, Raena Parx said:

trying to figure out how I would write a loop that reads all the data, one string at at time (so using lists for the string names, i'm guessing), and arrays?...(that's where I really get confused), then join the strings together into strings no longer than 1024 characters.  Not sure I'm that program-savvy.  But if I understand the concept, I might be able to pull it off.

You don't have to worry about arrays - LSL doesn't have any.

How are you storing your strings currently? It could be very simple if it's all in one list, maybe you don't even need a loop if each line is its own variable (or there are only a few lines).

If all your strings are in single list already, you could do something like...

list lines; // Your strings are here.

integer max = llGetListLength(lines);
integer i = 0; // "i" is commonly used to "iterate" through a loop.

while (i < max)
{
    string text = llList2String(lines, i); // Pick one line from the list.
    llRegionSayTo(avatar, PUBLIC_CHANNEL, text); // Send it to an avatar.
    ++i; // Increment the variable by one, so the next line will be picked on the next iteration.
}

...and just for completeness:

// Set i to 0 before the loop starts. (This is kinda redundant in this case and could be left out.)
// Before each iteration of the loop, check that i is less than list length.
// After each iteration of the loop, increment i by 1.
for (i = 0; i < max; ++i)
{
    string text = llList2String(lines, i);
    llRegionSayTo(avatar, PUBLIC_CHANNEL, text);
}

 

Edited by Wulfie Reanimator
Link to comment
Share on other sites

Thank you Wulfie. The code is awesome, and will simplify sending all the strings. 

What I was trying to figure out tho, in the prior messages, is how to group the strings into larger strings that are as close to 1024 characters, so bulk-sending in a way. The code would need to calculate the length of each current string ( no problem there), plus the string length of next string, then decide if it can be added to a groupstring that's  <1024 characters, and if so, how much space is left , then repeat til all strings are sent. I can probably figure it out, but I guarantee it'll be like 20 lines of code more than is needed lol.  I'll let you know what I come up with so you can have a good laugh lol. 🙃

Thank you for your help :)

 

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

9 hours ago, Raena Parx said:

why does it start with a semicolon? does that mean there's no initializer?

indeed, in LSL declaring a variable in the for statement is forbidden, so using the initializer of the for loop is somewhat rare.

a LSL idiom I use often is to iterate through a list backwards with a while loop:

list myList = [1,2,3,4,5,6,7,8,9,10];

integer index = llGetListLength(myList);
string ret;
while(~--index) // for(;index!=-1;--index)
{   integer val = llList2Integer(myList,index);
    if(val&1) myList = llDeleteSubList(myList,index,index); // remove from list if odd.
    ret += (string)index + ": "+llList2CSV(myList)+"\n";
}
llOwnerSay(ret);

for example to (very verbosely) remove odd numbers from a list.

  • Like 1
Link to comment
Share on other sites

2 hours ago, Quistess Alpha said:

using the initializer of the for loop is somewhat rare

I almost always have an initializer even if it's reduntant, just for legibility. As much as I love crafting mystical runes of code in the name of efficiency, it's worth it to be certain of the initial value on a glance, to me.

 

  • Like 2
Link to comment
Share on other sites

Don't underestimate iterating lists backwards.  We tend to think forwards, with increasing numbers.  But sooo very often it makes no actual difference, and counting backwards often makes the task fiendishly simpler.

An example being if you're adding to removing items from a list; going forwards, you have to account for the added or removed items in your loop counter.  Going backwards you don't.

Another, is 0 is much easier to detect.  Going forwards, you need to compare against some arbitrary stopping point, which you'll either need to keep handy in another variable, or keep referring back to the source (especially if it changes because you're adding or removing items).  Going backwards, 0 is 0 (even -1, still simple, and it stays put).

  • Like 1
Link to comment
Share on other sites

I've been writing lots of 6502 assembly for a SL project and yes, going backwards tends to be simpler. "loop: LDX #endvalue, do_stuff_with_x, DEX, BNE loop" is easier for a 1-N loop than "loop: LDX #1, do_stuff_with_x, INX, CPX #endvalue+1, BCC loop", no comparison required as the CPU is already aware it reached 0.

Makes me all the more thankful I don't *have* to do that at the LSL side and can just for/while away. As much as I enjoy working with the restrictions of lowest level code, it makes my head hurt.

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

Should have also mentioned in my prior post, LSL often lets you go backwards backwards.  This can be a solution to the tiny little second issue I mentioned, for times when you really do actually want to go forwards.

If you start your loop at -length, and progress "forwards" to 0, LSL's negative indexing will turn that into regular forward looping without having to explicitly remember the end point.

  • Like 1
Link to comment
Share on other sites

11 hours ago, Bleuhazenfurfle said:

If you start your loop at -length, and progress "forwards" to 0, LSL's negative indexing will turn that into regular forward looping without having to explicitly remember the end point.

You can, but there are some subtle gotchas. Unless you're absolutely sure your list is non-empty, it's a bit safer to start at -length-1 and immediately increment back in the while check, or use an explicit for loop.

// method A:
list l;
integer index = -llGetListLength(l) -1;
while(++index) { /*...*/ }

// method B:
for(index= -llGetListLength(l); index; ++index) { /*...*/ }

 

Link to comment
Share on other sites

Just an update. The llRegionSayTo worked perfectly.  Turns out it's so fast, it doesn't need text-line grouping like I thought it would from when I was using llInstantMessage. It displayed the text in order, privately and fast...awesome!  Now if I can figure out how to avoid the timestamp when copy/pasting it from chat it'll be perfect lol. 

Thank  you for the code Wulfie. Yours is the code I used mainly because I could understand it, and it was short and sweet and functional.

I wasn't sure I understood the advantage of counting backward, so I used what I was more comfortable with. Though that may be a great option too if I understood it better lol. 

THANK YOU TO EVERYONE for being so helpful!!!.   I really appreciate it!!!

On 5/7/2023 at 9:41 PM, Wulfie Reanimator said:
list lines; // Your strings are here.

integer max = llGetListLength(lines);
integer i = 0; // "i" is commonly used to "iterate" through a loop.

while (i < max)
{
    string text = llList2String(lines, i); // Pick one line from the list.
    llRegionSayTo(avatar, PUBLIC_CHANNEL, text); // Send it to an avatar.
    ++i; // Increment the variable by one, so the next line will be picked on the next iteration.
}

 

Edited by Raena Parx
Link to comment
Share on other sites

37 minutes ago, Raena Parx said:

It displayed the text in order, privately and fast...awesome!  Now if I can figure out how to avoid the timestamp when copy/pasting it from chat it'll be perfect lol. 

I don't think you can rely on the text staying in order if there's no delay between sending the separate messages; most of the time it'll work, but I'm pretty sure ordered delivery is not guaranteed, so it's worth keeping an eye out for.

As for removing the timestamp, no can do on the script side, but you could change the object name before sending messages and apply a newline to reduce the need for post-processing, like, say:

string original_name = llGetObjectName(); // store object's real name
llSetObjectName("info dump"); // set the name to something descriptive for sending
while (i < max)
{
    string text = "\n"+llList2String(lines, i); // prepend a newline
    llRegionSayTo(avatar, PUBLIC_CHANNEL, text);
    ++i;
}
llSetObjectName(original_name); // restore original name

 

Link to comment
Share on other sites

51 minutes ago, Raena Parx said:

Now if I can figure out how to avoid the timestamp when copy/pasting it from chat it'll be perfect lol. 

If it's for personal use, try using the debug channel; I thing that doesn't add timestamps.

Link to comment
Share on other sites

Well...I rescind what I thought was the solution. lol  So it worked perfectly in test environment where the strings were pre-filled when creating the stringnames. But when I got it into my build, it didnt recognized the strings as they had been updated throughout the code.   Not sure why it would take the updated string values.

So, I ended up having to just using the llOwnerSayTo command 7 times to get all the text to display.  But it was MUCH faster then using InstantMessage!

I never knew that command existed. So thank you!

 

  • Like 1
Link to comment
Share on other sites

1 hour ago, Raena Parx said:

I've never used the debug channel, how is it used?

llSay(DEBUG_CHANNEL, "some text"); or similarly for llWhisper llShout or llRegionSay llRegionSayTo (it doesn't seem work with llRegionSayTo(); (no viewer support?) but does work with llRegionSay(); and all the others). it makes the text show up in a special kind of window in the same way as a stack-heap overflow, or llSetLinkPrimitiveParamsFast usage error.

People who aren't the owner of the object can also see the message, so llWhisper might be the "most polite" version.

random note: Cool VL viewer shows debug channel messages in red in the local chat window (with timestamps) and also does not echo llRegionSayTo DEBUG_CHANNEL to the user.

Edited by Quistess Alpha
Link to comment
Share on other sites

3 hours ago, Quistess Alpha said:

llSay(DEBUG_CHANNEL, "some text"); or similarly for llWhisper llShout or llRegionSay llRegionSayTo (it doesn't seem work with llRegionSayTo(); (no viewer support?) but does work with llRegionSay(); and all the others). it makes the text show up in a special kind of window in the same way as a stack-heap overflow, or llSetLinkPrimitiveParamsFast usage error.

Oh cool!  Thank you Quistess!  I'll definately play with it. Thank you for explaining how it works ☺️

Link to comment
Share on other sites

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