Jump to content
Sylvia Wasp

HUD coding w/two sets of buttons

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

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

Recommended Posts

OK, I'm not a great scripter, so I'm probably going to phrase this incorrectly ... please have patience! 

I'm a clothing designer and I make & use HUDs to change the textures on the clothes.  Up until now this has been just a simple array of 4, 6, 9 or 12 buttons, (one for each texture).  Clicking the button on the HUD makes it shout it's name ("button_01", "button_02" etc.).  The script in the clothing listens for the HUD.  It has a list of keys (UUIDs) at the top and an if, else if structure that uses llSetLinkPrimitiveParamsFast to set the textures on the correct faces and so on depending on which button it heard.

This works fine when there's one simple array of 12 or less buttons, but I'm now trying to make a HUD with two sets of buttons, one for choosing the pattern and one for setting the level of sheerness of the pattern.  To be clear, I'm not talking about setting the Alpha of a texture, (I know how to do that) these are all still just textures but some of the textures have sheer areas.  

There are 6 fabric patterns and 3 levels of sheerness, so ... 18 textures in all.

For clarity, I've labelled the keys to the textures things like,  "texture_01_sheer_90", "texture_01_sheer_80", "texture_01_sheer_60", "texture_02_sheer_90" ... and so on

But if I want the level of sheerness and the selected texture to "stick," then every time I press one of the six texture buttons, I have to test for three possible levels of sheerness' and every time I press a sheerness button I have to test for the six different patterns.  This gives me (at minimum) a super unwieldy if-else tree that has 36 levels!  It just doesn't seem like the right way to go.  

Even if I save the pattern choice (1 of 6) and the sheerness level (1 of 3) in variables, I still have to test for that variable and I'm still stuck with the super long unwieldy if-else structure. 

I'm sure this problem must have been solved before, can anyone point me in the right direction?  

Assuming I've made any goddamned sense at all, lol. :) 

Sylvia  

Share this post


Link to post
Share on other sites

Just to recap, to make sure I've understood it correctly:

6 textures. 3 variations of each texture.

You want the "variant" to stay when the texture is changed. (Or just know which one to apply?)

Either way, you want to get rid of the big if-else tree.

 

I guess my first question is why would you need 36 if-else checks if you only have 18 textures / buttons with unique names? You should only need 18 at most.

if (message == "texture_01_sheer_90")
{
    // Do stuff;
}
else if (message == "texture_01_sheer_80")
{
    // Do stuff;
}
else if (message == "texture_01_sheer_60")
{
    // Do stuff;
}
else if (message == "texture_02_sheer_90")
{
    // Do stuff;
}

Right?

Alternatively, you could name your buttons something like "texture1", "texture2", "texture3" and so on. Then you have an ordered list in the clothing item, which uses the number as an index of that list. Then you won't need any if-checks (besides making sure you're getting correct input).

list textures = ["UUID", "UUID","UUID", "UUID"];

listen(integer channel, string name, key id, string message)
{
    // 7 is the length of "texture", -1 means "end of string"
    integer index = (integer)llGetSubString(message, 7, -1);
    
    string uuid = llList2String(textures, index);
    llSetLinkTexture(LINK_SET, uuid, ALL_SIDES);
}

 

Edited by Wulfie Reanimator

Share this post


Link to post
Share on other sites

here's a snippet that should work, only two if statements

list button_list = ["sheer-1","sheer-2","sheer-3","texture-4","texture-5","texture-6","texture-1","texture-2","texture-3"];
string sheer = "sheer-1";
string texturep = "texture-1"; //part of the texture name
string texture;
integer lchan = -12345; //listenchannel

default
{
    state_entry()
    {
    }

    touch_start(integer total_number)
    {
        llListen(lchan,"","","");
        llDialog(llDetectedKey(0),"select texture or sheer \ncurrent texture: "+texturep+"\ncurrent sheer: "+sheer,button_list,lchan);
    }
    listen(integer chan, string name, key id, string text)
    {
        if (llGetSubString(text,0,4) == "sheer")
        {
            sheer = text;
            texture = texturep+"-"+sheer;
            llOwnerSay("set texture to : "+texture);
        }
        else if (llGetSubString(text,0,6) == "texture")
        {
            texturep = text;
            texture = texturep+"-"+sheer;
            llOwnerSay("set texture to : "+texture);
        }
    }         
}

regs,

Dargo

Share this post


Link to post
Share on other sites
3 hours ago, Wulfie Reanimator said:

Just to recap, to make sure I've understood it correctly:

6 textures. 3 variations of each texture.

You want the "variant" to stay when the texture is changed. (Or just know which one to apply?)

Either way, you want to get rid of the big if-else tree.

 

I guess my first question is why would you need 36 if-else checks if you only have 18 textures / buttons with unique names? You should only need 18 at most.


if (message == "texture_01_sheer_90")
{
    // Do stuff;
}
else if (message == "texture_01_sheer_80")
{
    // Do stuff;
}
else if (message == "texture_01_sheer_60")
{
    // Do stuff;
}
else if (message == "texture_02_sheer_90")
{
    // Do stuff;
}

Right?

Alternatively, you could name your buttons something like "texture1", "texture2", "texture3" and so on. Then you have an ordered list in the clothing item, which uses the number as an index of that list. Then you won't need any if-checks (besides making sure you're getting correct input).


list textures = ["UUID", "UUID","UUID", "UUID"];

listen(integer channel, string name, key id, string message)
{
    // 7 is the length of "texture", -1 means "end of string"
    integer index = (integer)llGetSubString(message, 7, -1);
    
    string uuid = llList2String(textures, index);
    llSetLinkTexture(LINK_SET, uuid, ALL_SIDES);
}

 

Well, because there are two sets of buttons and I want the choice in each set to "stick" as I said, one possible solution is to have six if-else checks for the six texture buttons with three if-else checks for the sheer buttons nested inside each one, so ... 18.  Then the three if-else checks for the sheer buttons would each have six if-else checks nested inside them for the texture buttons, which is another 18. i.e - 36.  Doing it that way, I'd have to probably add some other checks to account for conditions that are outside of what's expected too, so probably *more* than 36.  

The thing is, if you select a texture button, the HUD has to save the level of transparency/sheer selected (one of three options) to know which level of sheer texture to apply.  The only way I can think of doing that is to have a variable that saves the level of transparency and another variable to save which of the six textures is currently selected.  Then checking for those in the giant if else structure described.  

I think that would actually work, but it's long, kind of crazy, and I just think there has to be a better way.  

It's a pictorial HUD, so the user selects a fabric texture based on the six pictures and then they can fool around with the level or transparency/sheer.  Because it's clothing however, I can't just adjust the alpha of the texture, I have to pre-prepare 18 detailed dress textures (3 levels of sheer for each pattern) and then pick one based on the user input from the buttons.  

Like, if the user selects "texture_02" and then selects a different sheer factor, the HUD needs to know that "we are on texture 2 right now" and select the texture_02 version of that sheer factor.  If they then select "texture_03" instead, I need the HUD to remember that the sheer factor was 90 or 60 or whatever it happened to be and select the texture_03 version of that sheer level.  

I'm attaching a crude mockup of the HUD (below) to illustrate.  

Sylvia

 

Demo_HUD.jpg

  • Thanks 1

Share this post


Link to post
Share on other sites
2 hours ago, Kardargo Adamczyk said:

here's a snippet that should work, only two if statements


list button_list = ["sheer-1","sheer-2","sheer-3","texture-4","texture-5","texture-6","texture-1","texture-2","texture-3"];
string sheer = "sheer-1";
string texturep = "texture-1"; //part of the texture name
string texture;
integer lchan = -12345; //listenchannel

default
{
    state_entry()
    {
    }

    touch_start(integer total_number)
    {
        llListen(lchan,"","","");
        llDialog(llDetectedKey(0),"select texture or sheer \ncurrent texture: "+texturep+"\ncurrent sheer: "+sheer,button_list,lchan);
    }
    listen(integer chan, string name, key id, string text)
    {
        if (llGetSubString(text,0,4) == "sheer")
        {
            sheer = text;
            texture = texturep+"-"+sheer;
            llOwnerSay("set texture to : "+texture);
        }
        else if (llGetSubString(text,0,6) == "texture")
        {
            texturep = text;
            texture = texturep+"-"+sheer;
            llOwnerSay("set texture to : "+texture);
        }
    }         
}

regs,

Dargo

Thanks Drago, but it's a picture HUD for changing dress textures.  llDialog is not a good fit for that situation.  

Sylvia

Share this post


Link to post
Share on other sites

ahhh, yup in that case you need 9 if / else if statements....

thats okay though, as long as you put the actual texture changer in a subroutine so you don't have to write the texture change command itself 9 times.

and yeah, you can put the sheer and current texture in a global var

 

regs,

Dargo

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, Sylvia Wasp said:

I'm attaching a crude mockup of the HUD (below) to illustrate.

OH. It's super clear now, thanks for that.

If you rename your buttons a little, you could get rid of all the if-checks and use my list method. For example:

  • Let's assign your sheer options some simple values: 0, 1, and 2.
  • Let's do the same thing for the textures: 0, 1, 2, 3, 4, and 5.
  • So the first button would be called "texture_0_" and the last one is "texture_5_"

Based on that, you can write your HUD like this:

integer sheer; // 0, 1, 2

default
{
    touch_start(integer n)
    {
        string link = llGetLinkName(llDetectedLinkNumber(0));

        if (link == "sheer 90")
        {
            sheer = 0;
        }

        // ...

        else // Not a sheer option
        {
            // In this case it sends the message "texture_0_0"
            llRegionSayTo(llGetOwner(), CHANNEL, link + (string)sheer);
        }
    }
}

The reason why we're counting from zero is because that's how list indexes work...

// "texture_01_sheer_90", "texture_01_sheer_80", "texture_01_sheer_60"
list textures = ["UUID", "UUID", "UUID"];

default
{
    listen(integer channel, string name, key id, string message)
    {
        // In this case, parameters = ["texture", "0", "0"];
        list parameters = llParseString2List(message, ["_"], []);

        // Multiply the first index by 3 because we have 3 variants for each texture.
        integer index = (llList2Integer(parameters, 1) * 3) + llList2Integer(parameters, 2);
        string uuid = llList2String(textures, index);
    }
}

(0 * 3) + 0 = index 0; First texture with 90 sheer.

(5 * 3) + 2 = index 17; Last texture with 60 sheer. That covers all 18 textures.

Edited by Wulfie Reanimator
  • Like 1

Share this post


Link to post
Share on other sites

using the same calculation method Wulfie's shows.  Calculating a list index from some input ordered differently to the list

when our HUD is a single surface then we can use llDetectedTouchST

http://wiki.secondlife.com/wiki/LlDetectedTouchST


for a HUD using this function, then in the OP minimalist case there is a 3 x 3 grid and we need a way to convert the grid reference to a list index


in this example the texture uuids are in the HUD and we are sending the uuid to the garment/object. In another setup the UUIDs list can be in the garment/object and we send the index in the message

 

// there is a list of textures. Typically UUIDs but for this example
// we use names to help with clarity

// a unique identifier of some kind for this HUD and its garment/object
string myid = "GarmentID_3193";


list textures =
[
  texture1_60, texture1_80, texture1_90,
  texture2_60, texture2_80, texture2_90,
  texture3_60, texture3_80, texture3_90,
  texture4_60, texture4_80, texture4_90,
  texture5_60, texture5_80, texture5_90,
  texture6_60, texture6_80, texture6_90
];

// global registers
integer tex;
integer sheer;

touch_start( ... )
{
   integer row;
   integer column;

   ... here we get the row and column of a 3 x 3 grid
   ... using the code in the wiki example. Resolving this to:

   // a 3 x 3 grid row:column reference
   // 2:0 2:1 2:2
   // 1:0 1:1 1:2
   // 0:0 0:1 0:2     
   //
   // where row 0 is the Sheer buttons as per OP design

   if (row == 0)
   {
      sheer = column; // in set [0..2]
   }
   else // row is 1 or 2. is a tex button
   {
      tex = 3 * (row - 1) + column;  // in set [0..5]
   }

   integer index = 3 * tex + sheer;  // in set [0..17]

   string texture = llList2String(textures, index);      
   string message = myid + "," + texture;

      ... send message to garment/object  ...
}

 

 

  • Like 1

Share this post


Link to post
Share on other sites
13 hours ago, Wulfie Reanimator said:

OH. It's super clear now, thanks for that.

If you rename your buttons a little, you could get rid of all the if-checks and use my list method. For example:

  • Let's assign your sheer options some simple values: 0, 1, and 2.
  • Let's do the same thing for the textures: 0, 1, 2, 3, 4, and 5.
  • So the first button would be called "texture_0_" and the last one is "texture_5_"

Based on that, you can write your HUD like this:

The reason why we're counting from zero is because that's how list indexes work...

(0 * 3) + 0 = index 0; First texture with 90 sheer.

(5 * 3) + 2 = index 17; Last texture with 60 sheer. That covers all 18 textures.

Thanks Wulfie, 

I have to admit I don't quite get this, (because math), but if I understand generally ... you're saying that I should keep the texture keys in a list and use the information sent by the HUD to generate indexes and thus pick the correct texture from the list?  

I usually just list the texture keys at the top of the script like this: 

key         texture_02_80 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";    
key         texture_02_60 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
key         texture_03_90 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";    
key         texture_03_80 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
key         texture_03_60 = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";    

but putting them in an actual list makes a lot of sense.  

Re: the HUD,

This is the entirety of my HUD code.  It's very short and simple and allows me to re-use it over and over again.  I just change the HUD_name variable and make sure the buttons are labelled correctly.  It relies on a scheme wherein the button simply says it's name when clicked, like so: 

//===== Texture Changer (HUD) - by Sylvia Wasp ====
//      installed in the root prim of a linkset
//      where the other prims are buttons labelled
//      "button_01", "button_02", etc.
//=================================================

integer cmdChannel;
string  HUD_Name = "some HUD";

integer Key2Chan(key ID) 
{
    return 0x80000000 | (integer)("0x"+(string)ID);
}

default
{
    state_entry ()
    {
        llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_NAME,HUD_Name]);
        cmdChannel = Key2Chan(llGetOwner());
    }

    touch_start(integer n)
    {
        string button = llGetLinkName(llDetectedLinkNumber(0));
        llRegionSayTo(llGetOwner(),cmdChannel, button);
    }                
}

For me, this reusability is crucial as I don't have the skill to work out a new HUD for every piece of clothing.  

So you're suggesting (for the HUD) that instead of this bit: 

    touch_start(integer n)
    {
        string button = llGetLinkName(llDetectedLinkNumber(0));
        llRegionSayTo(llGetOwner(),cmdChannel, button);
    }                

That I detect the link number instead of the link name and send that to the clothing item instead? 

And then use that number to find things in the list of keys based on indexes?  

This makes a lot of sense also, but I'm not sure why I couldn't continue to use the button names, since they are logical and mostly numbers themselves (despite being technically strings of course).  

Also, is there not a way to pick a texture (a key) based on the naming convention I've used in my list of keys?  

I mean I can easily shorten the if-else trees that I normally use, if, instead of picking the texture key by it's full name, I can pick it though a concatenated string.  Like instead of saying (essentially) "texture_01_90" if I could say "(Text_var + Alph_variable)" instead?  

Can I call a key just by using it's name (the name that I give it at the top of the file)?  It seems to fail when I try.   

Sylvia

 

Edited by Sylvia Wasp

Share this post


Link to post
Share on other sites
9 hours ago, Mollymews said:

using the same calculation method Wulfie's shows.  Calculating a list index from some input ordered differently to the list

when our HUD is a single surface then we can use llDetectedTouchST

http://wiki.secondlife.com/wiki/LlDetectedTouchST


for a HUD using this function, then in the OP minimalist case there is a 3 x 3 grid and we need a way to convert the grid reference to a list index


in this example the texture uuids are in the HUD and we are sending the uuid to the garment/object. In another setup the UUIDs list can be in the garment/object and we send the index in the message

Hey Molly, 🙂 

This is pretty brilliant, but I honestly, think it's beyond me.  

It involves math, which for me is a no-no (I'm not dumb but I'm a very visually oriented person).  It also requires me to figure out each individual HUD differently as I create them (although I guess I could create a series of HUDs with different button arrangements as templates?)  As I said to Wulfie, re-usability and solving the problem 'once and for good' is key for me.  

TBH, I'm still trying to figure out that great solution you gave me for the listens in my avatar detector thingie, lol.  

I keep putting the code in the wrong place which stops the whole function of the scanner.  

Sylvia

Share this post


Link to post
Share on other sites
30 minutes ago, Sylvia Wasp said:

As I said to Wulfie, re-usability and solving the problem 'once and for good' is key for me.  

go with Wulfie's method

making a HUD where each button is its own linked prim can be a lot simpler to visualise and get up and working

 

edit add

linked buttons HUD

10 prims. 1 x backboard. 6 x texture buttons. 3 x sheer buttons

name the texture buttons 0, 1, 2, 3, 4, 5

name the sheer buttons 6, 7, 8

// global
integer tex;
integer sheer;


touch_start(...)
{
   integer button = (integer)llGetLinkName(llDetectedLinkNumber(0));
   
   if (button >= 6) // is 6, 7 or 8
   {
      sheer = button - 6;   // is in [0, 1, 2]
   }   
   else
   {
      tex = button;  // is in [0, 1, 2, 3, 4, 5]
   } 

   integer index = 3 * tex + sheer;

   ... send index as message ...
}

 

Edited by Mollymews
code err
  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Sylvia Wasp said:

I have to admit I don't quite get this, (because math), but if I understand generally ... you're saying that I should keep the texture keys in a list and use the information sent by the HUD to generate indexes and thus pick the correct texture from the list?  

Yes. You can imagine a list as an "array of boxes" or slots or whatever. You would have all 18 textures in a single list, all in the same order. Here's a visual (pardon my poor digital handwriting):

529f3dba76.png

Index 0-2 are for the first texture, each with the different kinds of sheer. Index 3-5 are for the second texture, and so on.

1 hour ago, Sylvia Wasp said:

So you're suggesting (for the HUD) that instead of this bit: 


    touch_start(integer n)
    {
        string button = llGetLinkName(llDetectedLinkNumber(0));
        llRegionSayTo(llGetOwner(),cmdChannel, button);
    }                

That I detect the link number instead of the link name and send that to the clothing item instead?

No. You should add the "level of sheer" to the end of the name of the button you're sending. Change the following:

llRegionSayTo(llGetOwner(), cmdChannel, button);
llRegionSayTo(llGetOwner(), cmdChannel, button + "_" + (string)sheer);

This way, instead of "button_01" you send "button_01_0". Now you've sent the two numbers needed for the clothing to figure out exactly which texture to apply.

You would of course need to add just 3 if-checks into the HUD so that you can change the "sheer" value, based on which of the three options you've clicked on.

I believe you would also need to remove the "0" from "01" (and any other numbers that begin with 0) because it won't convert to an integer correctly. I might be wrong, but anyway.

Then, when you have the two numbers, you know which index to start from (0 to 5, multiplied by 3 because of the amount of variants, giving you a starting index of 0, 3, 6, 9, 12 or 15), and how many indexes to move forward from that (0, 1, or 2) to get the correct sheer texture.

This is definitely a reusable technique that you'll be able to use whenever you have variants for each texture.

1 hour ago, Sylvia Wasp said:

Also, is there not a way to pick a texture (a key) based on the naming convention I've used in my list of keys?  

I mean I can easily shorten the if-else trees that I normally use, if, instead of picking the texture key by it's full name, I can pick it though a concatenated string.  Like instead of saying (essentially) "texture_01_90" if I could say "(Text_var + Alph_variable)" instead?  

Not the way your current convention works, I think. What I'm suggesting is going to address exactly that.

Edited by Wulfie Reanimator
  • Like 1

Share this post


Link to post
Share on other sites

i just add to the conversation as something to think about

yes this is a texture applier and the outcome is to apply a selected texture to a surface

when we are newish to a particular type of app then we tend to think in outcomes. In these newish situations we can keep thinking about textures and surfaces as things, and our coding thoughts/efforts can sometimes be more directed to the things than is necessary

our effort should be toward developing/coding a mapping model. Applying a texture is an outcome of the model

in this app, what we want is a model that takes inputs from 2 independent sets of ordered indice values (tex and sheer) and map the inputs to a different single set of ordered indice values (the list of textures)

Wulfie's diagram above shows the ordered indice of the list of textures

the mapping model should not care what the textures themselves are. As the HUD creator can order the list of textures manually to fit the model. Changing out the textures in the list also as they please, without breaking the model/app

  • Like 1

Share this post


Link to post
Share on other sites
9 hours ago, Mollymews said:

go with Wulfie's method

making a HUD where each button is its own linked prim can be a lot simpler to visualise and get up and working

 

edit add

linked buttons HUD

10 prims. 1 x backboard. 6 x texture buttons. 3 x sheer buttons

name the texture buttons 0, 1, 2, 3, 4, 5

name the sheer buttons 6, 7, 8

 

Okay, thanks for this.  Here is my HUD code altered to match the idea, (I changed the variable names a bit and I had to cast the index as a string to send it)

//===== Texture Changer (HUD) - by Sylvia Wasp ====

string  HUD_Name = "some HUD";

integer cmdChannel;
integer Text_var;
integer Alph_var;

integer Key2Chan(key ID) 
{
    return 0x80000000 | (integer)("0x"+(string)ID);
}

default
{
    state_entry ()
    {
        llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_NAME,HUD_Name]);
        cmdChannel = Key2Chan(llGetOwner());
    }

    touch_start(integer n)
    {
        integer button = (integer)llGetLinkName(llDetectedLinkNumber(0));

        if (button >= 6) {
            Alph_var = button - 6;
            }   
        else {
            Text_var = button;
            } 

        integer index = 3 * Text_var + Alph_var;
        llRegionSayTo(llGetOwner(),cmdChannel, (string)index);
    }                
}

But this is what I mean about math, lol. 

I know this bit:  integer index = 3 * Text_var + Alph_var; ... is *why* it works.  But I don't exactly understand why it works.

 I will have to think about it for a (long) while. 

Sylvia

Edited by Sylvia Wasp

Share this post


Link to post
Share on other sites
9 hours ago, Wulfie Reanimator said:

Yes. You can imagine a list as an "array of boxes" or slots or whatever. You would have all 18 textures in a single list, all in the same order. Here's a visual (pardon my poor digital handwriting):

529f3dba76.png

Index 0-2 are for the first texture, each with the different kinds of sheer. Index 3-5 are for the second texture, and so on.

Cool pic, excellent illustration of what's happening.  :)

OK, so I took Molly's advice and put the calculations for the index in the HUD itself, which makes the code in the mesh even simpler.  Here is my finished version of both the HUD code and the Mesh code.  

 

The only thing I can think of that may be wrong here is that I'm not "handling" the listen I guess.  

//===== Texture Changer (HUD code) - by Sylvia Wasp ====

string  HUD_Name = "some HUD";

integer cmdChannel;
integer Text_var;
integer Alph_var;

integer Key2Chan(key ID) 
{
    return 0x80000000 | (integer)("0x"+(string)ID);
}

default
{
    state_entry ()
    {
        llSetLinkPrimitiveParamsFast(LINK_THIS,[PRIM_NAME,HUD_Name]);
        cmdChannel = Key2Chan(llGetOwner());
    }

    touch_start(integer n)
    {
        integer button = (integer)llGetLinkName(llDetectedLinkNumber(0));

        if (button >= 6) {
            Alph_var = button - 6;
            }   
        else {
            Text_var = button;
            } 

        integer index = 3 * Text_var + Alph_var;
        llRegionSayTo(llGetOwner(),cmdChannel, (string)index);
    }                
}

//===== Texture Changer (Mesh code) - by Sylvia Wasp ======

string  HUD_Name = "some HUD";
integer cmdChannel;

integer Key2Chan(key ID) 
{
    return 0x80000000 | (integer)("0x"+(string)ID);
}

key     label_tex = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";
key     trans_tex = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx";

list    textures =
    [
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_01_90
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_01_80
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_01_60
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_02_90
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_02_80
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_02_60
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_03_90
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_03_80
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_03_60
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_04_90
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_04_80
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_04_60
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_05_90
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_05_80
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_05_60
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_06_90
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",    //texture_06_80
        "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"     //texture_06_60 
    ];

default
{
    state_entry ()
    {      
        cmdChannel = Key2Chan(llGetOwner());
        llListen(cmdChannel, HUD_Name, NULL_KEY, "");
        llSetLinkPrimitiveParamsFast(LINK_THIS,[ PRIM_TEXTURE, ALL_SIDES, trans_tex, <1.0, 1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
        llSetLinkPrimitiveParamsFast(LINK_THIS,[ PRIM_TEXTURE, 0, label_tex, <-1.0, -1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
        llSetLinkPrimitiveParamsFast(LINK_THIS,[ PRIM_TEXTURE, 5, label_tex, <1.0, 1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
    }

    listen(integer channel, string name, key id, string message) 
    {
        integer index = (integer) message;
        string  texture_key = llList2String(textures, index);
        llSetLinkPrimitiveParamsFast(2,[ PRIM_TEXTURE, ALL_SIDES, texture_key, <1.0, 1.0, 1.0>,ZERO_VECTOR, 0.0 ]);
    }
}

This seems to work although it's late and I haven't tested it much.  

Sokay? :) 

Sylvia

  • Like 2

Share this post


Link to post
Share on other sites
57 minutes ago, Sylvia Wasp said:

 

But this is what I mean about math, lol. 

I know this bit:  integer index = 3 * Text_var + Alph_var; ... is *why* it works.  But I don't exactly understand why it works.

 

 

i try explain this in long hand

here is the list of textures, but with index values rather than a name

list textures =
[
  0, 1, 2
  3, 4, 5
  6, 7, 8
  9,10,11
 12,13,14
 15,16,17  
];

6 rows of Text_var. 3 columns of Alpha_var

the row (Text_var) buttons are numbered 0 to 5

Text_var = button;

Text_var is a number >= 0 and <= 5

we mulitply Text_var by 3

3 * Text_var

3 * 0 = 0
3 * 1 = 3
3 * 2 = 6
3 * 3 = 9
3 * 4 = 12
3 * 5 = 15

this gives us the value in each row of the first (Oth) column of list textures


the column (Alpha_var) buttons are numbered 6 to 8 - so to distinguish them from the row (Text_var) buttons when we touch

Alpha_var = button;

we subtract 6 reducing Alpha_var to a number >= 0 and <= 2. To get the column number

Alpha_var = Alpha_var - 6;

we add this to the sum of 3 * Text_var

3 * Text_var + Alpha_var

3 * 0 + 0 = 0
3 * 0 + 1 = 1
3 * 0 + 2 = 2
3 * 1 + 0 = 3
3 * 1 + 1 = 4
3 * 1 + 2 = 5
3 * 2 + 0 = 6
3 * 2 + 1 = 7
3 * 2 + 2 = 8
3 * 3 + 0 = 9
3 * 3 + 1 = 10
3 * 3 + 2 = 11
3 * 4 + 0 = 12
3 * 4 + 1 = 13
3 * 4 + 2 = 14
3 * 5 + 0 = 15
3 * 5 + 1 = 16
3 * 5 + 2 = 17

integer index = 3 * Text_var + Alpha_var;

because of the way our brains can work sometimes then we can change the order. Instead of 3 * Text_var

integer index = ( Text_var * 3 ) + Alpha_var;

 

if this is still all a bit muddy don't worry overly much about it

the more prim texture HUDs you make the more clear this is going to become


 

  • Like 2

Share this post


Link to post
Share on other sites

Thanks again, 🙂 

For a visually oriented person the way in which it's displayed makes all the difference.  

For instance I looked at the key section for a long time and always saw it as:

3 * (Text_var + Alpha_var);

not 

( Text_var * 3) + Alpha_var;

which would give: 

3 * 0 + 0 = 0
3 * 0 + 1 = 3
3 * 0 + 2 = 6
3 * 1 + 0 = 3
3 * 1 + 1 = 6
3 * 1 + 2 = 9
3 * 2 + 0 = 6
3 * 2 + 1 = 9
3 * 2 + 2 = 12
3 * 3 + 0 = 9
3 * 3 + 1 = 12
3 * 3 + 2 = 15
3 * 4 + 0 = 12
3 * 4 + 1 = 15
3 * 4 + 2 = 18
3 * 5 + 0 = 15
3 * 5 + 1 = 18
3 * 5 + 2 = 7

Anyway, problem solved.  :) 

The product will be finished in a day or two but anyone who contributed to this thread is welcome to a gift of anything in my store, although they're not likely to be of interest to all you math geeks, lol.  Check it out if you need female avatar clothing for Tonic (objectively, the BEST mesh body) or Maitreya (the most POPULAR mesh body)

https://marketplace.secondlife.com/stores/184651 

Sylvia

Share this post


Link to post
Share on other sites
On 1/31/2020 at 7:03 PM, Sylvia Wasp said:

Sokay?

i just pick up a little thing

in the object script there is a listener

llListen(cmdChannel, HUD_Name, NULL_KEY, "");

narrow the listener down to the named HUD on the channel owned by the Owner

llListen(cmdChannel, HUD_Name, llGetOwner(), "");

this will prevent the object being changed by a device owned by somebody else which just happens to have the same name and is operating on the same channel

 

i have had this happen to me. I was wearing a top, and it keep ( what seemed like randomly ) changing textures. I was like whaaaa!  After a while I worked out that it only happened when a neighbour was on their parcel.  So cam them wearing my top.  When they changed the curtains in their house, it changed my top 

 

edit more:

another little thing in the HUD script

because we have a button named '0' then we have a issue. Any string that does not start with a number when cast to an integer will be 0

button = (integer)"some string";
button = (integer)"Backboard";
button = (integer)"My_HUD_Name";

these will all be 0

there are number of ways to resolve this. A simple way is name the buttons from 1 to 9, instead of 0 to 8

then deduct 1 and test for it

touch_start(...)
{
   integer button = (integer)llGetLinkName(llDetectedLinkNumber(0)) - 1;
   
   // any prim name that is not a number >= 1 will result in button being -1

   if (~button) // >= 0
   {
       ... code here same as ...
       if (button >= 6)
        
       ...
 
       llRegionSayTo ....
   }
}

 

Edited by Mollymews
  • Like 1

Share this post


Link to post
Share on other sites
6 hours ago, Mollymews said:

i just pick up a little thing in the object script there is a listener


llListen(cmdChannel, HUD_Name, NULL_KEY, "");

narrow the listener down to the named HUD on the channel owned by the Owner


llListen(cmdChannel, HUD_Name, llGetOwner(), "");

this will prevent the object being changed by a device owned by somebody else which just happens to have the same name and is operating on the same channel

Done

Quote

another little thing in the HUD script

because we have a button named '0' then we have a issue. Any string that does not start with a number when cast to an integer will be 0 there are number of ways to resolve this. A simple way is name the buttons from 1 to 9, instead of 0 to 8, then deduct 1 and test for it


touch_start(...)
{
   integer button = (integer)llGetLinkName(llDetectedLinkNumber(0)) - 1;
   
   // any prim name that is not a number >= 1 will result in button being -1

   if (~button) // >= 0
   {
       ... code here same as ...
       if (button >= 6)
        
       ...
 
       llRegionSayTo ....
   }
}

 

and Done.  Both excellent ideas.  

One last thing that always secretly bothered me though ... I don't "handle" the listen in the mesh object.  

Is it really necessary for a simple thing like this?  Should I be using a timer and deleting the listen after a set time?  It seems like extra complication and also something that will get in the way of utility of the product itself.  If the HUD stops working after x seconds or whatever, the user will be left thinking it's broken.  Won't the listen just disappear by itself? 

Sylvia

Share this post


Link to post
Share on other sites
30 minutes ago, Sylvia Wasp said:

One last thing that always secretly bothered me though ... I don't "handle" the listen in the mesh object.  

Is it really necessary for a simple thing like this?  Should I be using a timer and deleting the listen after a set time?  It seems like extra complication and also something that will get in the way of utility of the product itself.  If the HUD stops working after x seconds or whatever, the user will be left thinking it's broken.  Won't the listen just disappear by itself?

our object script can't know when the user will want to use the HUD. And we can't detect/read from another object what huds a person is wearing. Inworld attachments yes. Huds no

so the listen needs to remain open for the reasons you mention

Share this post


Link to post
Share on other sites
9 hours ago, Mollymews said:

narrow the listener down to the named HUD on the channel owned by the Owner


llListen(cmdChannel, HUD_Name, llGetOwner(), "");

this will prevent the object being changed by a device owned by somebody else which just happens to have the same name and is operating on the same channel

This does not limit the listener to objects owned by the owner. In that line of code you've created a listener that will never hear anything, because it's listening for a specific object name and a specific avatar key. By definition that's impossible to satisfy, you have to leave out the llGetOwner part and check for llGetOwnerKey separately. See:

default
{
    state_entry()
    {
        llListen(0, "Object", llGetOwner(), "");
    }

    listen(integer channel, string name, key id, string message)
    {
        llOwnerSay("Heard!"); // Will never happen.
    }
}

Instead, you need:

default
{
    state_entry()
    {
        llListen(0, "Object", "", "");
    }

    listen(integer channel, string name, key id, string message)
    {
        if (llGetOwnerKey(id) != llGetOwner()) return;

        // Heard a message from something owned by owner.
    }
}
2 hours ago, Sylvia Wasp said:

One last thing that always secretly bothered me though ... I don't "handle" the listen in the mesh object.

Is it really necessary for a simple thing like this?  Should I be using a timer and deleting the listen after a set time?  It seems like extra complication and also something that will get in the way of utility of the product itself.  If the HUD stops working after x seconds or whatever, the user will be left thinking it's broken.  Won't the listen just disappear by itself? 

Listens won't "just disappear by itself." It's good practice to close listens when they aren't needed.

However in the case of scripted communication, there's no real way to "re-open" a listen without the user directly interacting with the clothing itself. I would just let the listen live forever with the channel and object-name filters.

Edited by Wulfie Reanimator
  • Like 1

Share this post


Link to post
Share on other sites
1 hour ago, Wulfie Reanimator said:

This does not limit the listener to objects owned by the owner. In that line of code you've created a listener that will never hear anything, because it's listening for a specific object name and a specific avatar key. By definition that's impossible to satisfy, you have to leave out the llGetOwner part and check for llGetOwnerKey separately. See:


default
{
    state_entry()
    {
        llListen(0, "Object", llGetOwner(), "");
    }

    listen(integer channel, string name, key id, string message)
    {
        llOwnerSay("Heard!"); // Will never happen.
    }
}

 

Yes, I discovered this in testing today.  

The whole thing stopped working and since the only thing that was changed is the NULL_KEY, I went back to the old version and it worked again.  

I think I'm just going to leave it the way it is (was) because I think having this at the top of the HUD code ...

string  HUD_Name = "some HUD";
integer cmdChannel;
...

integer Key2Chan(key ID) 
{
    return 0x80000000 | (integer)("0x"+(string)ID);
}

and this in the Mesh code ...

string  HUD_Name = "some HUD";
integer cmdChannel;

integer Key2Chan(key ID) 
{
    return 0x80000000 | (integer)("0x"+(string)ID);
}

...

default
{
    state_entry ()
    {      
        cmdChannel = Key2Chan(llGetOwner());
        llListen(cmdChannel, HUD_Name, NULL_KEY, "");

...
	}

... does essentially the same thing.  

I'm already communicating between a named object on a secure channel that's specific to the user/owner of both objects.  The HUD even names itself on startup so it can never be called anything else.  The mesh can't listen for any other HUDs other than the named HUD, and the user would have to own both objects anyway.  No other users should even be able to listen in on the same channel.  

I think that covers everything except ... "I've stupidly rezzed multiple copies of my own HUD all over my property and am just randomly clicking them now."  Even in that case, the result would either be the very thing the user wanted, or it just wouldn't work at all AFAICS. 

Sylvia

Share this post


Link to post
Share on other sites
7 minutes ago, Sylvia Wasp said:

I'm already communicating between a named object on a secure channel that's specific to the user/owner of both objects.  The HUD even names itself on startup so it can never be called anything else.  The mesh can't listen for any other HUDs other than the named HUD, and the user would have to own both objects anyway.  No other users should even be able to listen in on the same channel.  

I think that covers everything except ... "I've stupidly rezzed multiple copies of my own HUD all over my property and am just randomly clicking them now."  Even in that case, the result would either be the very thing the user wanted, or it just wouldn't work at all AFAICS. 

Not quite.

I assume the HUD name is always the same, regardless of owner? Meaning if I had the HUD, it would be called "Some HUD" and if someone else had the same HUD, it would also be called "Some HUD". If your listener is acting on all messages heard from an object called "Some HUD", it's easy for me to break all of your products.

A channel generated directly from the owner's key is not unique nor secure.

"0x"+(string)ID creates  a hexadecimal number with 4 bytes (8 characters, like 0x12345678).

default
{
    state_entry()
    {
        string avatar = llGetOwner();
        llOwnerSay(avatar);
        // output: "779e1d56-5500-4e22-940a-cd7b5adddbe0"

        string hexadecimal = "0x" + avatar;
        llOwnerSay(hexadecimal);
        // output: "0x779e1d56-5500-4e22-940a-cd7b5adddbe0"

        integer number = (integer)hexadecimal;
        llOwnerSay((string)number);
        // output: "2006850902" (= 0x779E1D56)
    }
}

Note that the first part of a UUID is 8 characters long. It's the same number.

The first problem is that there may exist other avatars who have a key that is identical no matter which 8 characters you pick. You can't pick more characters because they'll be ignored when typecast to an integer.

Besides that, if I create a new object and rename it to "Some HUD", I can just put my own script in there so it starts sending random messages on channels based on the avatars around me. Even if I don't know which part of the key you're using, the options are pretty limited and I can just send messages within ~200 channel range of different parts of the key.

For these reasons, you absolutely should add an additional check to make sure that "Some HUD" is owned by the same owner.

Share this post


Link to post
Share on other sites
49 minutes ago, Wulfie Reanimator said:

Not quite.

I assume the HUD name is always the same, regardless of owner? Meaning if I had the HUD, it would be called "Some HUD" and if someone else had the same HUD, it would also be called "Some HUD". If your listener is acting on all messages heard from an object called "Some HUD", it's easy for me to break all of your products.

A channel generated directly from the owner's key is not unique nor secure.

"0x"+(string)ID creates  a hexadecimal number with 4 bytes (8 characters, like 0x12345678).


default
{
    state_entry()
    {
        string avatar = llGetOwner();
        llOwnerSay(avatar);
        // output: "779e1d56-5500-4e22-940a-cd7b5adddbe0"

        string hexadecimal = "0x" + avatar;
        llOwnerSay(hexadecimal);
        // output: "0x779e1d56-5500-4e22-940a-cd7b5adddbe0"

        integer number = (integer)hexadecimal;
        llOwnerSay((string)number);
        // output: "2006850902" (= 0x779E1D56)
    }
}

Note that the first part of a UUID is 8 characters long. It's the same number.

The first problem is that there may exist other avatars who have a key that is identical no matter which 8 characters you pick. You can't pick more characters because they'll be ignored when typecast to an integer.

Besides that, if I create a new object and rename it to "Some HUD", I can just put my own script in there so it starts sending random messages on channels based on the avatars around me. Even if I don't know which part of the key you're using, the options are pretty limited and I can just send messages within ~200 channel range of different parts of the key.

For these reasons, you absolutely should add an additional check to make sure that "Some HUD" is owned by the same owner.

Hmmm ... 

It seems like a very very very very very unlikely situation that you're talking about IMO.  

I mean I'm going to get a completely unique number almost every time despite the outside chance that two avatars *may* generate the same number.  

Also, we're already talking about active griefing rather than just covering off stupid mistakes, two avatars in the same room with the same product, or sloppy work on my part.  Someone would have to do a lot of work to construct something just to screw around with another avatars clothing texture HUD, and why would they?  

Sylvia

Share this post


Link to post
Share on other sites
6 minutes ago, Sylvia Wasp said:

Hmmm ... 

It seems like a very very very very very unlikely situation that you're talking about IMO.  

I mean I'm going to get a completely unique number almost every time despite the outside chance that two avatars *may* generate the same number.  

Also, we're already talking about active griefing rather than just covering off stupid mistakes, two avatars in the same room with the same product, or sloppy work on my part.  Someone would have to do a lot of work to construct something just to screw around with another avatars clothing texture HUD, and why would they?  

Sylvia

I forgot you're already using RegionSayTo, so the "duplicate channel" issue isn't really a problem after all.

The effort it takes to totally prevent any kind of exploit is literally one line of code that I already included in this thread. If you think it's not worth it, that's your choice. A choice I can't understand, but it's not my product so whatever.

Edited by Wulfie Reanimator
  • Like 1

Share this post


Link to post
Share on other sites
You are about to reply to a thread that has been inactive for 148 days.

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

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • Create New...