Jump to content

How could we store two different pieces of information about a single llDialog() button?


Wampster
 Share

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

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

Recommended Posts

I am redesigning an old system that I made a year or two ago. A "food can" and "food bowl". But, the same principle could be applied to many different things. The goal being to move data from one object to another. Now, on the surface, moving a typed integer ( (string)myInteger ) to denote a quantity from one object to another is straight forward. In this example, moving a quantity of 'food' (integer) from a 'food can' to a 'food bowl'; By simply parsing the typed string through a chat channel and listening on the receiver.

What if there's multiple receivers? Ahh, now, we'll need to build a list of objects that responded to the first "call" and our sender "listening" to each object that responds. That's fine. Each returning message contains the UUID of the object that sent a response. So, we can build the list of button names from those. Let's say that we build the button list by returning the object name of each respondant, such as "Food Bowl". The owner, for whatever reason, may have rezzed two or more bowls. Fine. The list contains two (or more) buttons labeled "Food Bowl". This is where we're presented with the dilemna.

When the user clicks on either button, the only thing that we know is that it was a "Food Bowl". Sure, instead of the object name, we could parse the UUID to the button list. But, then, it wouldn't give a meaningful label to the owner. Ideally, when we build the button list to parse through the llDialog(), we want to track which "label" (the string parsed to each button and presented to the user) has which UUID associated to it. I know of no way that we can know two things about any message returned by the llDialog(). Am I simply missing something obvious? Or is this truly a bottleneck in the system. In which case, how can we overcome it? I know that others have overcome this issue. But, I draw a blank on how they may have handled it.

key userKey;
integer menuChannel = -654321;
integer menuListener;
integer bowlListener;
list mainMenuButton = ["Find bowl:"];
string mainMenuMessage = "";

default
{
    touch_start(integer num_detected)
    {
        userKey = llDetectedKey(0);
        menuListener = llListen(menuChannel, "", userKey, "");
        llDialog(userKey, mainMenuMessage, mainMenuButton, menuChannel);
    }

    listen(integer menuChannel, string name, key id, string message)
    {    
        if(message == "Find Bowl")
        {
            llListenRemove(menuListener);
            bowlListener = llListen(menuChannel, "", NULL_KEY, "");
            llSay(menuChannel, "Food bowl");
        }

        if(message == "Food Can")
        {
            llListenRemove(bowlListener);
            //list buttonList = []; Would reset the list after each message received (one bowl listed).
            buttonlist = llGetObjectDetails(id, [OBJECT_OWNER]);
            list textMenuButtons += [llKey2Name(llList2Key(buttonList, 0))];
            string textMenuMessage = "Please select a bowl:";
            llDialog(userKey, textMenuMessage, textMenuButtons, menuChannel);
            // Can we somehow send more than one piece of information in the button?
            // As far as I can tell, we can 'only' send a label (string to be displayed on each button).
            // How then can we distinguish between multiple, identicle objects (multiple food bowls)?
        }

        if(message != "Find Bowl" & message != "Food Can")
        {
            llListenRemove(bowlListener);
            llSay(menuChannel, message);
            //We can't exactly concatonate UUID + message, since the button label would
            // be illegible to the owner.
            // Therefor, we can't have a string to attempt to split at the other end.
            // It would seem that the llDialog() button label is a bottleneck.
        }
    }
}

Screen capture of Visual Studio Code with LSL Intellisense.

How, in the listen() event, can we recieve both the UUID and the button label?

integer myChannel = -654321;
integer foodListener;

default
{
    state_entry()
    {
        foodListener = llListen(menuChannel, "", NULL_KEY, "");
    }

    listen(integer channel, string name, key id, string message)
    {
        string myUUID = (string)llGetKey();
        
        if(channel == myChannel)
        {
            if (message == "Food Bowl")
            {
                if (name == myUUID)
                {
                    // Can we parse the UUID of the label's originating object through here, as name?
                    // If so, how do we 'send' it?
                }
            }
        }
    }
}

Screen capture of Visual Studio Code with LSL Intellisense.

The previous code snippets are not compiled, tested code. I literally wrote it just now as examples to attempt to clarify where my mind is at and attempt to pinpoint the bottleneck that I see (rightfully or wrongfully) regarding llDialog(). My apologies for any errors that may be associated with it which may make it more difficult to understand the overall dilemna posed. Any insight and discussion is welcomed.

Link to comment
Share on other sites

Maybe I'm missing something in the question.  The message that's received in the listen event was generated by clicking on one of your own dialog buttons.  You know where it came from because you are the one who clicked the button and you know which button you clicked.  If you have more than one bowl, give them different names and add those names to your button list.

Link to comment
Share on other sites

I don't like to use buttons as a transmitter to another script. Much prefer to have the script that issues the dialog listen to the results, and, if needed, tell the remote listener what to do. Since the source script has all the info, it can decide what needs to be sent to the remote object using a different channel.

 

  • Like 5
Link to comment
Share on other sites

34 minutes ago, Madelaine McMasters said:

It's been ages since I've scripted a dialog, but aren't the button labels 24 chars long, only the first 12 of which are displayed? Pad the button name out to 12 characters with spaces, then tack on something unique after that to differentiate the returned message text.

It can be done, but dialog buttons don't display 12 characters every time. It depends on the individual character width. For Example, you might only be able to show 10 characters of "WWWWWWWWWWWW" but many more in a string full of "i" characters. Padding with a bunch of spaces, followed by a hidden message also forces the text in the buttons to be left justified instead of centered.

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

The simple solution is to A) append 1, 2, 3, etc. to the end of the button name as suggested above (fiddle with leading spaces to keep centered), or B) only use one button and apply the result to all receivers.

As you stated, using UUIDs as button labels is meaningless to the user... well, two buttons that do two different actions but use the exact same label can also be meaningless.  How would the user know which button goes to which item?  Adding a number (or letter A, B, or anything) would help the user to differentiate the two buttons and it makes the scripting problem go away.

In your unique scenario, if you really really needed to use multiple buttons with the 'same' label name... you could possibly substitute the "O" character with "0". For instance; "FOOD BOWL", "FOOD B0WL", "FO0D BOWL".  But your initial thought is correct, there is no way to get two different actions from two identical messages.

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

For me, the simplest solution would be to simply index the labels themselves.

 1. Food Bowl |  2. Food Bowl |  3. Food Bowl
 4. Food Bowl |  5. Food Bowl |  6. Food Bowl
 7. Food Bowl |  8. Food Bowl |  9. Food Bowl
10. Food Bowl | 11. Food Bowl | 12. Food Bowl

It's not obnoxious or stand out. The user might not intuitively know which bowl is which, but you could include in the dialog text: "The bowls are sorted by distance."

This even has the additional benefit that you can typecast the name of the label into an integer, and get the correct number.

(integer)"1. Food bowl" // 1
(integer)"12. Food bowl" // 12

(integer)"   -892. food bowl" // Still correct: -892

Edit: Right, as Mollymews says below, the "outsider" listening still wouldn't know what the index means. So you'd have to inevitably construct the "real" message beyond the dialog choice. Having the object name and key in the same list (or two adjacent lists) would be a good solution.

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

within the same script

a way is to use a list to simulate an array

example pcode using 3 columns and 3 rows. Where the first row contains the button labels

caveat: ensure that button labels are unique

list buttons [
  "button1", "button2", "button3", //  button labels
  "data1",   "data2",   "data3",   //  data
  "more1",   "more2",   "more3"    //  more data
];

llDialog( ..., llList2List(buttons, 0, 2), ...);

listen(..., string text)
{
   // llList2List ensures that we only look in the labels row
   integer i = ~ListFindList(llList2List(buttons, 0, 2), [text]);

   string data = llList2String(buttons, i * 3 + 3);  // where 3 is the number of columns
   string more = llList2String(buttons, i * 3 + 6);  // where 6 is the number of columns * 2
}

 

listening from a script in an independent device

with dialog listen (... string name, key id, string text).

'string name' is the User Name of the person who pressed the dialog button  
'key id' is the UUID of that person

there is no way from within an independent script listener to know anything about the device that the person has dialog pressed, other than what can be contained in 'string text'

so what we typically do, as Phate mentions, is listen within the script that contains the dialog. On getting the button, we construct a message from this to send on another channel to the independent device

 

  • Like 3
Link to comment
Share on other sites

2 hours ago, Mollymews said:

there is no way from within an independent script listener to know anything about the device that the person has dialog pressed, other than what can be contained in 'string text'

suppose we did want to do this. I don't recommend doing this, but suppose we did anyway. A way to do this example:
 

string RightPad(string source, integer length)
{
    string spacer = "________________________"; // 24 chars of same kind
    // a caution with using space chars is that they are narrow font width so more than
    // 12 chars can be displayed on the button. See Yellow Apple. 'l' and " " are
    // narrow font width and because so then 13 chars are displayed on the button
    // This said if we can ensure that our button labels don't contain more than
    // 4 narrow width font chars then we can use spaces to right pad  
    return llGetSubString(source + spacer, 0, length-1);                  
}

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

    touch_start(integer total_number)
    {
        string info = "DeviceA"; // info is device identifier for example
        // info is 7 chars long
        // max. length of button label is 24 chars
        // right pad the labels with spacer so length of our button is 24
        list buttons = [
           RightPad("Red Apple", 17) + info,
           RightPad("Yellow Apple", 17) + info,
           RightPad("Green Apple", 17) + info
        ];
        
        llDialog(llDetectedKey(0), "Pick an apple!", buttons, 1);
    }
    
    listen(integer channel, string name, key id, string text)
    {
        llSay(0, text);
        // parse
        list message = llParseString2List(text, ["_"], []); // removes the spacer chars
        string apple = llList2String(message, 0);
        string info = llList2String(message, 1);
        llSay(0, apple + ", " + info);
    }
}

 

 

 

  • Like 1
Link to comment
Share on other sites

9 minutes ago, Mollymews said:

suppose we did want to do this. I don't recommend doing this, but suppose we did anyway. A way to do this example:
 


string RightPad(string source, integer length)
{
    string spacer = "________________________"; // 24 chars of same kind
    // a caution with using space chars is that they are narrow font width so more than
    // 12 chars can be displayed on the button. See Yellow Apple. 'l' and " " are
    // narrow font width and because so then 13 chars are displayed on the button
    // This said if we can ensure that our button labels don't contain more than
    // 4 narrow width font chars then we can use spaces to right pad  
    return llGetSubString(source + spacer, 0, length-1);                  
}

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

    touch_start(integer total_number)
    {
        string info = "DeviceA"; // info is device identifier for example
        // info is 7 chars long
        // max. length of button label is 24 chars
        // right pad the labels with spacer so length of our button is 24
        list buttons = [
           RightPad("Red Apple", 17) + info,
           RightPad("Yellow Apple", 17) + info,
           RightPad("Green Apple", 17) + info
        ];
        
        llDialog(llDetectedKey(0), "Pick an apple!", buttons, 1);
    }
    
    listen(integer channel, string name, key id, string text)
    {
        llSay(0, text);
        // parse
        list message = llParseString2List(text, ["_"], []); // removes the spacer chars
        string apple = llList2String(message, 0);
        string info = llList2String(message, 1);
        llSay(0, apple + ", " + info);
    }
}

 

edit add: I might not have explained the narrow font issue very well

a way to better see what I mean is to change the labels to: Red", "Yellow", "Green"  and spacer to 24 space chars

and if we do use space as the spacer then if we also include a space in our button text then it introduces a parsing issue

 

 

 

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

First of all, thank you all for your kind, thoughtful responses. You've all given me a lot of ideas and insight in to how others see the problem. I truly appreciate the time and effort. 

 

19 hours ago, Rolig Loon said:

Maybe I'm missing something in the question.  The message that's received in the listen event was generated by clicking on one of your own dialog buttons.  You know where it came from because you are the one who clicked the button and you know which button you clicked.  If you have more than one bowl, give them different names and add those names to your button list.

In the example, we're supposing that the object is a pet food bowl. Changing the name (whilst not impossible) is not ideal. Since, we would be expecting that a vendor would be selling duplicates of a "prefab" object. Thus every instance of the object are identical, including the name. The only thing unique to each instance would be the UUID, which itself changes each time that the object rezzes. Of course, we can cache that UUID in the on_rez() callback. The second thing that we're supposing is that the owner intentionally or unintentionally rezzes more than one bowl. Thus we want to handle not just the 'first' bowl to respond but multiple bowls which may respond.

 

19 hours ago, Phate Shepherd said:

I don't like to use buttons as a transmitter to another script. Much prefer to have the script that issues the dialog listen to the results, and, if needed, tell the remote listener what to do. Since the source script has all the info, it can decide what needs to be sent to the remote object using a different channel.

 

I hadn't thought about this angle, honestly. I think that you're right. It is better to do any processing within the same script and only parse results to the bowl which leads us to the next ideas.

 

18 hours ago, DoteDote Edison said:

The simple solution is to A) append 1, 2, 3, etc. to the end of the button name as suggested above (fiddle with leading spaces to keep centered), or B) only use one button and apply the result to all receivers.

As you stated, using UUIDs as button labels is meaningless to the user... well, two buttons that do two different actions but use the exact same label can also be meaningless.  How would the user know which button goes to which item?  Adding a number (or letter A, B, or anything) would help the user to differentiate the two buttons and it makes the scripting problem go away.

In your unique scenario, if you really really needed to use multiple buttons with the 'same' label name... you could possibly substitute the "O" character with "0". For instance; "FOOD BOWL", "FOOD B0WL", "FO0D BOWL".  But your initial thought is correct, there is no way to get two different actions from two identical messages.

I agree that appending a pre-fix integer to the button name is a great solution. It looks clean and presentable to the user, having the buttons numbered, but, also, gives us a unique string to deal with. Two birds with one stone almost.

 

17 hours ago, Wulfie Reanimator said:

For me, the simplest solution would be to simply index the labels themselves.


 1. Food Bowl |  2. Food Bowl |  3. Food Bowl
 4. Food Bowl |  5. Food Bowl |  6. Food Bowl
 7. Food Bowl |  8. Food Bowl |  9. Food Bowl
10. Food Bowl | 11. Food Bowl | 12. Food Bowl

It's not obnoxious or stand out. The user might not intuitively know which bowl is which, but you could include in the dialog text: "The bowls are sorted by distance."

This even has the additional benefit that you can typecast the name of the label into an integer, and get the correct number.


(integer)"1. Food bowl" // 1
(integer)"12. Food bowl" // 12

(integer)"   -892. food bowl" // Still correct: -892

Edit: Right, as Mollymews says below, the "outsider" listening still wouldn't know what the index means. So you'd have to inevitably construct the "real" message beyond the dialog choice. Having the object name and key in the same list (or two adjacent lists) would be a good solution.

This is what popped in my mind, from the previous comment. Well, for the most part. I love the simplicity of pre-fixed numerical labels. Perfect to build a second list containing UUID's with matching index to the pre-fixed numerical labels in the first list.

 

17 hours ago, Mollymews said:

within the same script

a way is to use a list to simulate an array

example pcode using 3 columns and 3 rows. Where the first row contains the button labels

caveat: ensure that button labels are unique


list buttons [
  "button1", "button2", "button3", //  button labels
  "data1",   "data2",   "data3",   //  data
  "more1",   "more2",   "more3"    //  more data
];

llDialog( ..., llList2List(buttons, 0, 2), ...);

listen(..., string text)
{
   // llList2List ensures that we only look in the labels row
   integer i = ~ListFindList(llList2List(buttons, 0, 2), [text]);

   string data = llList2String(buttons, i * 3 + 3);  // where 3 is the number of columns
   string more = llList2String(buttons, i * 3 + 6);  // where 6 is the number of columns * 2
}

 

listening from a script in an independent device

with dialog listen (... string name, key id, string text).

'string name' is the User Name of the person who pressed the dialog button  
'key id' is the UUID of that person

there is no way from within an independent script listener to know anything about the device that the person has dialog pressed, other than what can be contained in 'string text'

so what we typically do, as Phate mentions, is listen within the script that contains the dialog. On getting the button, we construct a message from this to send on another channel to the independent device

 

This is very insightful. I literally did not know what the "string name" held, nor if we could use it. An example of why I pondered if we could parse our own data through this argument can be found in llMessageLinked(). In llMessageLinked(), I parse my own "integer num" through, along with "message". This allows me to control two different levels of data tranmission within the same object. One level being the integer, which that value might be used for pathfinding only. And then the next level, which pathfinding function to access.

 

I did try to quote Molly, here, regarding the very interesting and expanded substring approach to padded labels. I guess I hit my quote limit or something. The idea is very interesting but I am not particularly confident with substrings (in any language). More of a failing on my behalf than anything to do with substrings. I can usually fumble through such cases but feel lost & confused the entire time and out of my depth. About the only time that I've ever felt cornered in to using substrings with delimiters was when I was working on a login manager for a game and wanted the character and account stats sent to the game manager from SQL. Otherwise, I typically find ways to avoid substrings! Again, nothing against substrings, just my inability to process them in my head!

 

I think for my skill level and comprehension the best suggestions that would work for me are pre-fixed numerical button names to clearly distinguish one from another with matching indexes built in a second list to reference UUIDs. As each message comes in, you can add each to both lists ensuring matching indexes. The llDialog() response can therefor match a UUID.

 

Thank you all so much for taking the time and effort to give me your thoughts and opinions.

Link to comment
Share on other sites

19 hours ago, DoteDote Edison said:

How would the user know which button goes to which item?

18 hours ago, Wulfie Reanimator said:

The user might not intuitively know which bowl is which, but you could include in the dialog text: "The bowls are sorted by distance."

Unless I'm misunderstanding, this seems to be the crux of the problem: we're expecting the user to choose among functionally identical objects, giving them artificially unique labels. How could they know if the script even followed their choice? The "sorted by distance" is kind of a hint, but probably not the most intuitive way for them to discriminate among the options. It's almost as if the script should just pick one at random, or always pick the nearest one, or abandon the dialog altogether and instead put all the bowls into a mode of "touch your selection."

  • Like 2
Link to comment
Share on other sites

To thank everyone for their input, I thought that I would show you the resultant, working code. With all of the input that I received in this thread, I was able to look at the problem from a new perspective which allowed me to acheive the desired goal. We can't parse more than one thing through a llDialog() but we sure can use a conglomeration of ideas from several of you.

  1. Pre-fix button labels with an identifying numeral.
  2. Build a concurrent list for storing UUID's whilst building the button list.
  3. Use an integer to index both lists.

The Food can:

listen(integer channel, string name, key id, string message)
    {
        if(channel == menuChannel)
        {
            if(message == "Find a bowl")
            {
                llListenRemove(menuListener);
                bowlListener = llListen(bowlChannel, "", NULL_KEY, "");
                llSay(bowlChannel, "BowlPing");
            }
        }

        if(channel == bowlChannel)
        {
            if(message == "BowlPong")
            {
                integer bowlNumber = bowlCount + 1; // Because the first bowl would be zero'th index.
                list listObjectNames = llGetObjectDetails(id, ([OBJECT_NAME]));
                string objectName = llList2String(listObjectNames, 0);
                string objectUUID = (string)id;
                bowlLabels += (string)bowlNumber + ". " + objectName;
                bowlUUIDs += objectUUID;
                bowlCount += 1;
                SelectBowlMenu();
            }
        }

        if(channel == selectedBowlChannel)
        {
            llListenRemove(selectedBowlListener);
            integer selectedBowlNumber = (integer)message;
            selectedBowlNumber -= 1; // Because we adjusted for the zero'th index.
            string sendFood = llList2String(bowlUUIDs, selectedBowlNumber) + "|" + (string)foodContent;
            llSay(sendFoodChannel, sendFood);
        }
    }

The bowl selection dialog.

 

The food bowl:

listen(integer channel, string name, key id, string message)
    {
        if(channel == listenChannel)
        {
            llListenRemove(canListener);

            if(message == "BowlPing") // Food can
            {
                foodListener = llListen(receiveChannel, "", NULL_KEY, "");
                llSay(listenChannel, "BowlPong");
            }

            if(message == "Bowl Pos") // Hungry cat
            {
                if(foodContent > 0)
                {
                    llSay(listenChannel, (string)llGetPos());
                    canListener = llListen(listenChannel, "", NULL_KEY, "");
                }
            }
        }

        if(channel == receiveChannel) // Bowl selected and food sent
        {
            llListenRemove(foodListener);
            list data = llParseString2List(message, ["|"], [" "]);
            string newUUID = llList2String(data, 0);
            string foodAmount = llList2String(data, 1);

            if( newUUID == myUUID)
            {
                foodContent += (integer)foodAmount;
                UpdateFoodContent();
            }
            
            canListener = llListen(listenChannel, "", NULL_KEY, "");
        }
    }

Food can, bowls and cat.

 

I couldn't completely dodge substrings but certainly kept it to a minimum. And, of course, the "messages" used to transmit information will be changed to more suitably discreet codes later. These are merely preliminary ones to get the overall systems working. As it stands now, I can click on the food can, tell it to find bowls, select one of the discovered bowls and then send the food to that selected bowl. Granted, I don't 'intend' for someone to have a bunch of bowls laying around. Nonetheless, I felt it necessary to handle the istuation which is probably inevitable. I still have a lot more work to do but the purpose of my original question is solved. Thank you to everyone who responded.

  • Like 1
Link to comment
Share on other sites

14 hours ago, Qie Niangao said:

Unless I'm misunderstanding, this seems to be the crux of the problem: we're expecting the user to choose among functionally identical objects, giving them artificially unique labels. How could they know if the script even followed their choice? The "sorted by distance" is kind of a hint, but probably not the most intuitive way for them to discriminate among the options. It's almost as if the script should just pick one at random, or always pick the nearest one, or abandon the dialog altogether and instead put all the bowls into a mode of "touch your selection."

I completely agree with you. And, I might try to find a more meaningful way to distinguish the bowls to the user at a later date. For now, I'll consider what I have as "working" and get other systems working around it. Of course, everything will need "polish" once the initial systems are built.

  • Like 1
Link to comment
Share on other sites

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