lsl RLV Scripting

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
So, I'm playing around with making an RLV script, it's pretty basic, it's just a folder changer. the result will basically be a HUD with some interface for "up and down" to cycle through a set of prebuilt folders, attaching and detaching whatever is in them.

I think I've managed to wrap my head around how RLV works with the @ commands, but I'm having trouble getting started. Basically, there doesn't seem to be any sort of response. I built a listerner HUD for testing if commands are passing through and when I touch an object with

llRegionSayTo(targetUUID, -1812221819, (string)targetUUID+",@getinvworn:Folder=2225");

I see the command come in. But I don't see any response on 2225, or any channel. i have RLV enabled on my viewer but I suspect maybe I need to do something to establish permissions to use RLV? I tried setting the relay to Auto accept and still didn't see any response.

On a second question, I didn't see anything here LSL Protocol/RestrainedLoveAPI - Second Life Wiki about how to scan for avatars with RLV enabled to target a particular person. The ultimate idea is that Person A can use the HUD on Person B. Also, maybe I'm going about this wrong but with a relay would person B need to wear anything else to capture the commands given from Person A?
 

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
3,188
The API left out something really basic that caught me as well. I forgot what it was, but try looking for some basic rlv scripts online and compare. I think the missing magic is in the call to the rlv channel, a missing component. The people on the builder's brewery group caught it right away, it's really simple.

Also, try posting your code, or a simplified version if possible.
 

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
So first of all, it you're wanting to send commands to the wearer of the HUD, then you want to use llOwnerSay("@getinvworn:Folder=2225").

If on the other hand, you want to be sending commands to other Avatars, they need to be wearing a Relay, and you need to follow the instructions here: LSL Protocol/Restrained Love Relay/Specification - Second Life Wiki
Ultimately it needs to send to another avatar. I was sending to myself as a test, using the ID of whomever touched the test prim.
 

Innula Zenovka

Nasty Brit
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
20,558
SLU Posts
18459
Ultimately it needs to send to another avatar. I was sending to myself as a test, using the ID of whomever touched the test prim.

RLV viewers will accept RLV commands only if they come in the form llOwnerSay("@getinvworn=2225") or whatever. So you want to send a command to anyone other than yourself, you need to send it to that person's RLV relay, in the format the Relay expects, using channel -1812221819 . Their relay will parse the command and if it understand it, and if you're allowed to using that person's relay, then it will relay the command to the HUD's wearer in the form an llOwnerSay() message.

You can test RLV commands on yourself by touching the prim and having it call llOwnerSay, but I'd do all the developing using a relay.
 
  • 2Like
Reactions: Kalel and Clara D.

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
RLV viewers will accept RLV commands only if they come in the form llOwnerSay("@getinvworn=2225") or whatever. So you want to send a command to anyone other than yourself, you need to send it to that person's RLV relay, in the format the Relay expects, using channel -1812221819 . Their relay will parse the command and if it understand it, and if you're allowed to using that person's relay, then it will relay the command to the HUD's wearer in the form an llOwnerSay() message.

You can test RLV commands on yourself by touching the prim and having it call llOwnerSay, but I'd do all the developing using a relay.
Ok, so maybe this is something I'm not right on. Does OpenCollar have a relay built in? Or maybe it doesn't? Or maybe it's a different protocol.
 

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
Ok, so I found an free script in my inventory that just browsed and attached from the RLV folder. I played with it a bit and cut out a bunch of stuff and have a basic working script. Only the Owner can use it on themselves at the moment.

This isn't 100%, I need to add a few things before it's super usable, mostly, I need to parse through the number of folders available, since I want it to be open ended, and I need to parse if any folder is currently worn as a "starting point".

Step two after all that is done is to make it usable by someone else. Which means figuring out how to scan the area for available relays (targets) and choose a target, then make the commands work through a relay, which may be as easy as changign the llOwnerSay bits to llRegionSayTo(targetUUID, -1812221819, "whatever,"+(string)targetUUID+",RLVcommand|RLVcommand|...");

Anyway, here is the code I have right now. It will cycle up and down a list in the RLV folder "ShapeCycler" with sub folders numbered 1,2,3... etc It currently force wears the contents of folder 1, but I intend to change that. It will wear and detatch objects in theory, but the original idea was just for shapes which sort of just replace each other anyway. I haven't actually tested it with attachments yet.

Code:
integer channel;
key targetkey;
string targetname;
integer currentfolder=1;
integer maxfolder=10;


default
{
    on_rez(integer int)
    {
        llResetScript();
    }
    attach(key id)
    {
        llResetScript();
    }
    state_entry()
    {
        channel=-(integer)llFrand(100000)-1000000;
        llListen(channel,"",llGetOwner(),"");
        llListen(-channel,"",NULL_KEY,"");
    }   
    touch_start(integer total_number)
    {
        llOwnerSay("@getinvworn:ShapeCycler="+(string)(-channel));
    }



    listen(integer chan, string name, key id, string message)
    {
        //llOwnerSay(message); // For Debugging
        if(chan==(-channel)&&message!="")
        {
            llSay(0, message);
            //parse the message to find currentfolder if any
            // But for now just wear folder 1....
            if (currentfolder == 1) {  llOwnerSay("@attach:ShapeCycler/"+(string)(currentfolder)+"=force"); }
            
            if(currentfolder > 1 && currentfolder < maxfolder)
                    { llDialog(llGetOwner(),"Choose action",["NextShape","PrevShape"],channel); }
                else if(currentfolder == 1)
                    { llDialog(llGetOwner(),"Choose action",["NextShape"," - "],channel); }
                else if(currentfolder == maxfolder)
                    { llDialog(llGetOwner(),"Choose action",[" - ","PrevShape"],channel); }
                else
                    { llDialog(llGetOwner(),"Error",[" - "," - "],channel); }
        }

        else if(chan==channel)
        {
            if(message=="NextShape")
                {
                llOwnerSay("@attach:ShapeCycler/"+(string)(currentfolder+1)+"=force");
                llOwnerSay("@detach:ShapeCycler/"+(string)(currentfolder)+"=force");
                currentfolder=currentfolder+1;
                }
            else if(message=="PrevShape")
                {
                llOwnerSay("@attach:ShapeCycler/"+(string)(currentfolder-1)+"=force");
                llOwnerSay("@detach:ShapeCycler/"+(string)(currentfolder)+"=force");
                if (currentfolder > 1) { currentfolder=currentfolder-1; }
                }

        }
        else
        {
            // Do Nothing
        }
    }
}
 
  • 1Like
Reactions: Kalel

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
I figured it out. The base script was resetting after the button scripts, so it would change channels and the buttons were using the old channel. I've upsated the scripts below.

Folder Structure is

#RLV
|
---ShapeCycler
|
--- 1
|
--- 2
|
--- 3
|
--- 4
|
--- 5

It should, in theory go on indefinitely, but I have not tested any limit beyond 11. I will say it works with leading zeros, but it does some funky loop stuff for some reason, like it went down 04, 03, 02, 11, 10, 09, 08, in a loop. I may look into that, maybe.

My next concern is making a separate controller HUD that just issues commands, but I need to set up some menu based handshaking (Scan for targets, get responses, ask who to target, prompt user for permission, etc, pass the private channel back).

Also I may look into adding a few other features, locking or a timer based auto change.


Ok, so, I feel like this should be straight forward.

I have a decent working basic script, better than the last one, which I may delete just for the sake of less confusion.

Here is the basic script (below). It detects if the user is wearing something already and starts there, if not, it starts in slot 1. It wears the new folder and detatches the previous folder, leaving anything else alone. Put it in a prim and when you touch it, you get a menu to pick the next shape or the previous shape, except when at the min/max.

I want to turn this into a HUD, because menus are kind of ugly. Maybe it could be both. Whatever the case, the script drops the random channel into its description. I created two child prims, linked them all up and they will pull the proper channel from the root prim. It will llOwnerSay the correct channel both on rez and touch so I know that part is working. The buttons are SUPPOSED to say on the proper channel, "NextShape" and "PrevShape" which are the two commands to toggle. Except nothing happens. I suspect there is some sort of UUID issue here, maybe, like it sees the commands but it's not seeing a proper UUID on the listener. Except It's all still Owner touching Owned object, so I don't know what the issue would be.

I've put the script for the buttons below as well.


Code:
integer channel;
key targetkey;
string targetname;
integer currentfolder=1;
integer maxfolder=10;



default
{
    on_rez(integer int)
    {
        llResetScript();
    }
    attach(key id)
    {
        llResetScript();
    }
    state_entry()
    {
        channel=-(integer)llFrand(100000)-1000000;
        llListen(channel,"",llGetOwner(),"");
        llListen(-channel,"",NULL_KEY,"");
        llSetObjectDesc((string)channel);
    }  
    touch_start(integer total_number)
    {
        llOwnerSay("@getinvworn:ShapeCycler="+(string)(-channel));
    }



    listen(integer chan, string name, key id, string message)
    {
        //llOwnerSay(message); // For Debugging
        if(chan==(-channel)&&message!="")
        {
           
            //llSay(0, message);
            list folderlist = llParseString2List(message,[","],[""]);
            maxfolder = llGetListLength(folderlist) - 1;
           
            // See what if anything is work currently
            integer i;
            do {
                string tmp = llList2String(folderlist, i);
                if (llGetSubString(tmp, -2, -1) == "30")
                    {currentfolder = (integer) llGetSubString(tmp, 0, -4); }
                }
            while(++i <= maxfolder);
           
            //parse the message to find currentfolder if any
            // But for now just wear folder 1....
            if (currentfolder == 1) {  llOwnerSay("@attach:ShapeCycler/"+(string)(currentfolder)+"=force"); }
           
            if(currentfolder > 1 && currentfolder < maxfolder)
                    { llDialog(llGetOwner(),"Choose action",["NextShape","PrevShape"],channel); }
                else if(currentfolder == 1)
                    { llDialog(llGetOwner(),"Choose action",["NextShape"," - "],channel); }
                else if(currentfolder == maxfolder)
                    { llDialog(llGetOwner(),"Choose action",[" - ","PrevShape"],channel); }
                else
                    { llDialog(llGetOwner(),"Error",[" - "," - "],channel); }
        }

        else if(chan==channel)
        {
            if(message=="NextShape" && currentfolder < maxfolder)
                {
                llOwnerSay("@attach:ShapeCycler/"+(string)(currentfolder+1)+"=force");
                llOwnerSay("@detach:ShapeCycler/"+(string)(currentfolder)+"=force");
                currentfolder=currentfolder+1;
                }
            else if(message=="PrevShape" && currentfolder > 1)
                {
                    llOwnerSay("@attach:ShapeCycler/"+(string)(currentfolder-1)+"=force");
                    llOwnerSay("@detach:ShapeCycler/"+(string)(currentfolder)+"=force");
                    currentfolder=currentfolder-1;
                }

        }
        else
        {
            // Do Nothing
        }
    }
}
Button Code

Code:
integer channel=0;

default
{
    state_entry()
    {

    }

    touch_start(integer total_number)
    {
        channel=(integer)llList2String(llGetLinkPrimitiveParams(LINK_ROOT, [ PRIM_DESC ]), 0);
        llSay(channel,"NextShape");
    }
}
 
Last edited:

Free

The cool chick who doesn't know she's hot
VVO Supporter 🍦🎈👾❤
Joined
Sep 22, 2018
Messages
34,400
Location
Moonbase Caligula
SL Rez
2008
Joined SLU
2009
SLU Posts
55565
It's unusual in a scripting thread to be told how to buy the work you're trying yourself to code.
 
  • 1Like
Reactions: Noodles

Facts Not Feelings

New member
Joined
Dec 25, 2018
Messages
13
It's unusual in a scripting thread to be told how to buy the work you're trying yourself to code.
I didn't mean it like that. I am not saying "Stop doing things yourself and buy this", i meant it more as an hint to what other people, who did the exact same thing that you are wanting to do, already did.

If you want to do it yourself for your personal use, it might give you ideas about how it might work and how you could design it. If you are planing to sell it, you want to know your competition and what they do.
It might work different for you guys, but i personally always check if things that i want to make are already available in some form somewhere to get ideas.
 
  • 2Thanks
Reactions: Soen Eber and Free

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
I am not really trying to make an inventory management system. And I have not worked on it in a bit but I have a basic version going.

Basically I want something to cycle through a sequence of pre made shapes/outfits, in sequence, that can be controlled remotely by someone else. The idea would be for RP based on step by step transformation RP. Where instead of just going from A to B there would be stages that can be controlled by someone else.
 

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
3,188
Here's some code I wrote last spring/summer for adding/removing attachments from my teddy bear avatar. I'm afraid it's not that easy of a read, but it is short so you should be able to work through it. Instead of using the attach commands you can use attach folders. There's still some debug statements but you'll probably want to keep them while testing this out.

Give me an IM and I can give you a (mostly) full perm copy to mess around with. I can't give away some items made with someone else's mesh (bow, balloon wrap cage), but I can make the scripts full perm.

Code:
integer _attach_i;
integer _attach_n;
list _lAttachments = ["Bow","Bell"];

walk_attachments()
{
    string folder;
    string sRLVCmd;
    if (_attach_i < _attach_n) {
        folder = "/Teddy_Bear/Attach/"+llList2String(_lAttachments,_attach_i);
        sRLVCmd = "@getinvworn:"+folder+"=2222";
        llWhisper(0,"walk_attachments:Sending to RLV :"+sRLVCmd);
        llOwnerSay(sRLVCmd);
    }
}
default
{
    state_entry()
    {
        llSetTimerEvent(0);
        _attach_n = llGetListLength(_lAttachments);
        _attach_i = 0;
        llListen(2222, "", "", "");
    }
    listen(integer ch, string name, key id, string msg)
    {
        if (ch == 2222) {
            llOwnerSay("listen: msg="+msg);
            string item = llList2String(_lAttachments, _attach_i);
            if (llGetSubString(msg,0,0) == "|") {
                if (llGetSubString(msg,1,1) == "3") {
                    llOwnerSay(item+" is worn");
                }
                else if (llGetSubString(msg,1,1) == "1") {
                    llOwnerSay(item+" is not worn");
                }
            }
            _attach_i++;
            if (_attach_i < _attach_n) {
                walk_attachments();
            }
        }
    }
    timer()
    {
        llOwnerSay("in timer");
        _attach_i = 0;
        walk_attachments();
    }
}
Replace Bell and Bow with whatever attachments you have handy. You'll probably want to rename the directory as well.

Attachments are kept in an #RLV folder. Here is the relevant directory structure:
Code:
#RLV
    Teddy_Bear
        Attach
            Bell (neck)  // <- You need to include the attachment point in the folder name
                Bell (neck)  // <-- this is an actual object.  Not sure what perms are accepted, start with full perm and then see what is accepted.
            Bow (neck)
                Bow (neck)  / <-- again, an object
 
Last edited:

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
3,188
Missed a script, the interface. Touching my teddy bear avatar's nose launches this script. The fist script I posted is called by this. Ignore the cage menu, I don't think I finished that part of it.

Code:
// Some useful Unicode graphics for menu operations ⬢⬡☐☑☒🔘◯■□▶▼▲◀
// You may delete these first three lines, they're only used for copying and
// pasting into the source code while writing the script

// Populate the list _menu_all (below) with your attachments. 
// First column must always be "Attach"
// Second column is a short name for the attachment
//   The short name is the name which will appear in the "Attach" menu when the
//   attachment containing this menu script is clicked.
//   The short name is also the folder name for the attachment in the #RLV folder
//     the folder name will also have the attachment point in paranthesis
//       example: Bow (neck)
//     the list of attachment points can be found in the rlv api (google for it)
//     and are:
//       none,chest,skull,left shoulder,right shoulder,left hand,right hand,
//       left foot,right foot,spine, pelvis,mouth,chin,left ear,right ear,
//       left eyeball,right eyeball,nose,r upper arm,r forearm, l upper arm,
//       l forearm,right hip,r upper leg,r lower leg,left hip,l upper leg,
//       l lower leg,stomach,left pec,right pec,neck,root
//     HUD attachment points are:
//       center 2,top right,top,top left,center,bottom left,bottom,bottom right
//     the actual attachment will be in the folder; you do not have to change its name
//
// In your #RLV folder, create a subfolder for your avatar
//   example: #RLV/Teddy_Bear
//   in the Teddy_Bear folder, create a new folder "Attach"
//     example: #RLV/Teddy_Bear/Attach
//     in the Attach folder, create a folder for each attachment
//       Keep it SHORT! It will be used as a menu item
//       You will need to add the attachment point in parenthesis after the name
//       example: #RLV/Teddy_Bear/Attach/Bow (neck)
//         note the space before (neck)
//
// Replace "Teddy_Bear" in _Avatar with the name you will be using for your 
// avatar in the #RLV folder
string _Avatar = "Teddy_Bear";

// Replace the two "Attach" items in the _menu_all list with your attachment info
//
list _menu_all = [
    "Attach","Bell",
    "This shiny bell swings back and forth with a pleasant jingling noise when flicked!",
    "Attach","Bow",
    "This color changing bow makes your bear look ever so stylish!",
    "Cage","Hook",
    "Keeps teddy from getting lost or underfoot by hanging them out of the way!",
    "Cage","BalloonWrap",
    "Keep your bear all wrapped up and shiny and ready to bounce in place!",
    "Cage","Box",
    "Lost bear?  Keep it un a box until someone comes along to collect them!"
];
list _menu_cages = [
    "Hook", "Bear Hook",
    "BalloonWrap", "Updated Teddy Bear Avatar Balloon Wrap 1.04",
    "Box", "Lost & Found Box"
];
string _me = "bear nose";

key    _id_agent;

integer _dlg_ch;
integer _dlg_hdl;
integer _dlg_wait = 10;

integer _owner_ch;
integer _viewer_ch;

list    _menu_main = [];
list    _menu_sub = [];
string  _menu_context = "main";
string  _selected_type;
string  _selected_item;
integer _attach_i;
integer _attach_n;
key     _kBearHook;
key     _kBubble;

say(string s)
{
    string  saved_obj_name;
    
    saved_obj_name=llGetObjectName();
    llSetObjectName(":");
    llSay(0,s);
    llSetObjectName(saved_obj_name);
}
setMainMenu()
{
    integer i = 0;
    integer n = llGetListLength(_menu_all);
    string sType;
    for (i=0; i< n; i+=3) {
        sType = llList2String(_menu_all,i);
        if (llListFindList(_menu_main, [sType]) == -1) {
            _menu_main += [sType];
        }
    }
}
subMenuInit(string searchType)
{
    _menu_sub = []; // clear previous contents
    string sType;
    integer n = llGetListLength(_menu_all);
    integer i = 0;
    _attach_i = 0;
    _attach_n = 0;
    _selected_item = "";
    
    for (i=0; i<n; i+=3) {
        sType = llList2String(_menu_all,i);
        if (searchType == sType) {
            _attach_n++;
        }
    }
}
setSubMenu(string searchType)
{
    _menu_sub = []; // clear previous contents
    string sType;
    string sItem;
    integer n = llGetListLength(_menu_all);
    integer i = 0;
    
    for (i=0; i<n; i+=3) {
        sType = llList2String(_menu_all,i);
        if (searchType == sType) {
            sItem = llList2String(_menu_all,i+1);
            _menu_sub += [sItem];
        }
    }
}
buildSubMenu(string searchType)
{
    string sType;
    string sItem;
    string sRLVCmd;
    integer n = llGetListLength(_menu_all);
    integer i = 0;
    
    for (i = _attach_i*3; i<n; i+=3) {
        sType = llList2String(_menu_all,i);
        sItem = llList2String(_menu_all,i+1);
        if (searchType == sType) {
            if (searchType == "Attach") {
                string folder = "/"+_Avatar+"/"+sType+"/"+sItem;
                sRLVCmd = "@getinvworn:"+folder+"="+(string)_viewer_ch;
                llOwnerSay(sRLVCmd);
                jump break;
            }
        }
    }
    call_menu();
    @break;
}
call_menu()
{
    llSetTimerEvent(_dlg_wait);
    llListenControl(_dlg_hdl, TRUE);
    if (_menu_context == "main") {
        llDialog(_id_agent, "Main", _menu_main, _dlg_ch);
    }
    else if (_menu_context = "sub") {
        llDialog(_id_agent, _selected_type, _menu_sub, _dlg_ch);
    }
}
default
{
    state_entry()
    {
        _dlg_ch = (integer)("0x"+llGetSubString((string)llGetKey(),-8,-1))+10;
        _dlg_hdl = llListen(_dlg_ch,"","","");
        _owner_ch = (integer)("0x"+llGetSubString((string)llGetOwner(),-8,-1));
        _viewer_ch = llAbs((integer)("0x"+llGetSubString((string)llGetKey(),-8,-1)));
        llListen(_owner_ch,"","","");
        llListen(_viewer_ch,"","","");
        setMainMenu();
    }
    on_rez(integer startup_param)
    {
        llResetScript();
    }
    changed(integer change)
    {
        if(change & CHANGED_OWNER) {
            llResetScript();
        }
        if (change & CHANGED_INVENTORY) {
            llOwnerSay("Resetting script due to change in inventory");
            llResetScript();
        }
    }
    listen(integer ch, string name, key id, string msg)
    {
        llSetTimerEvent(0);
        llListenControl(_dlg_hdl, FALSE);
        if (ch == _owner_ch) {
            list lMsg = llParseString2List(msg, ["|"], []);
            string src = llList2String(lMsg,0);
            string tgt = llList2String(lMsg,1);
            string cmd = llList2String(lMsg,2);
            string arg = llList2String(lMsg,3);
        
            if (tgt == _me) {
                if (src == "bear hook") {
                     if (cmd == "ping") {
                        _kBearHook = id;
                         llSay(_owner_ch, _me+"|bear hook|pong|"+(string)llGetOwner());
                    }
                }
                else if (src == "bubble") {
                     if (cmd == "ping") {
                        _kBubble = id;
                         llSay(_owner_ch, _me+"|bubble|pong|"+(string)llGetOwner());
                    }
                }
            }
        }
        else if (ch == _viewer_ch) {
            string item = llList2String(_menu_all, (_attach_i*3)+1);
            if (llGetSubString(msg,0,0) == "|") {
                if (llGetSubString(msg,1,1) == "3") {
                    _menu_sub += ["■ "+item];
                }
                else if (llGetSubString(msg,1,1) == "1") {
                    _menu_sub += ["□ "+item];
                }
            }
            _attach_i++;
            buildSubMenu(_selected_type);
        }
        else if (ch == _dlg_ch && _menu_context == "main") {
            if (llListFindList(_menu_main, [msg]) != -1) {
                _menu_context = "sub";
                _selected_type = msg;
                if (_selected_type == "Attach") {
                    subMenuInit(_selected_type);
                    buildSubMenu(_selected_type);
                }
                else if (_selected_type == "Cage") {
                    _menu_context = "sub";
                    setSubMenu(_selected_type);
                    call_menu();
                }
            }
        }
        else if (ch == _dlg_ch && _menu_context == "sub") {
            if (_selected_type == "Attach") {
                string sWorn = llGetSubString(msg,0,0);
                integer worn = FALSE;
                if (sWorn == "■") worn = TRUE;
                string item = llGetSubString(msg, 2, -1);
                string fqpItem = "/"+_Avatar+"/"+_selected_type+"/"+item;
                string sRLVCmd;
                _menu_context = "main";
                if (!worn) {
                    integer i = llListFindList(_menu_sub, [msg]);
                    llInstantMessage(_id_agent, llList2String(_menu_all, (i*3)+2));
                    sRLVCmd = "@attachover:"+fqpItem+"=force";
                }
                else {
                    sRLVCmd = "@detach:"+fqpItem+"=force";
                }
                llOwnerSay(sRLVCmd);
            }
            else if (_selected_type == "Cage") {
                string item = msg;
                integer i = llListFindList(_menu_all, [item]);
                llOwnerSay("i="+(string)i);
                llInstantMessage(_id_agent, llList2String(_menu_all, i+1));
                llInstantMessage(_id_agent, "Rez the "+item+" on the ground near you");
                i = llListFindList(_menu_cages, [item]);
                llOwnerSay("i="+(string)i);
                string s = llList2String(_menu_cages, i+1);
                llOwnerSay("s="+s);
                llGiveInventory(_id_agent, s);
            }
        }
    }
    touch_start(integer num_detected)
    {
        _id_agent = llDetectedKey(0);
        call_menu();
    }
    timer()
    {
        llSetTimerEvent(0.0);
        _menu_context = "main";
        llListenControl(_dlg_hdl, FALSE);
    }
}
 

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
Thanks, I will see if anything helps with what I am doing. I think I have the basics down, the current sort of statge is coming up with a decent sort of.handshake between the remote HUD and local HUD.

On another note, there may be stuff out there doing similar but I want to learn to script. I also want code I can dump to the OpenSIM community since they are seriously lacking in weird fun script toys really. Though I am not entirely sure yet the best way to distribute it.
 

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
3,188
ETA: using "target" instead of "local", sorry. I'm used to my own terminology.
ETA: first step was wrong, reworded.

At it's most basic level, you can dispense with the handshake between the remote & local. The code between both will be the same, except that the remote will be tagged with the remote operator's UUID instead of your own and should not issue the llOwnerSay rlv commands, assuming you are using llDialog() and llListen(). Start by sending the remote's dialog responses to the local HUD on a hard coded channel with the local's llListen event capturing the remote's responses. Once that is working, you can expand on that in a fairly logical progression in small steps, from basics such as knowing who is allowed to operate a remote, sending on a channel from the remote using a key generated from one of the local remote's user/object ids, how the handshake works, making the transfer and operation of the remote seamless regardless of who is using it and who it is used on, up through the point where multiple users are involved as both remote targets and remote operators. One thing you will want to do early on once you have the basic operation working is create comm channels based on owner key vs creator key vs previous owner key vs the values generated with pair of object_rez and object_rezzed events, all based on your overall plan of how a remote operator (a) gets the remote and (b) gets permission to use it on you, which is an architecture decision you'll have to work through to your satisfaction. Using those keys will help with (1) preventing cross-talk between multiple remotes and targets, and (2) allowing remotes to independently derive a handshake channel to communicate with the scripts the target is wearing.

I've done this a few times before, and knowing you want to work on this as much on your own as possible so I'm only giving you the high level stuff so you'll run into fewer detours and blind alleys.
 
Last edited:

Noodles

☑️
Joined
Sep 20, 2018
Messages
3,825
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
Blind alleys are good. I find I learn best by doing. So I am totally good with put something together, test, tweak, test, no, change that back, try again.
 
  • 1Hug
Reactions: Soen Eber