Perform handshake between two prims

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
633
What this does is, on a common channel, two prims will inform each other of their respective UUID's. This can be used to extend a particle chain from one prim to another, such as a leash, or any other functionality dependent on the UUID's of two respecitve prims. This has been tested ONLY for prims which are part of a linkset (and not for objects which are only a single prim). It functions regardless of ownership of the prims, and it also does not check if there are multiple prims responding.

How it works: the script uses two variables, _me, and _target, to differentiate between the two prims. In this example, one prim has a copy with _me set to "collar", and _target set to "leash". The leash script will switch those to "leash" and "collar", respectively. The leash (controlling prim) should also set _master from FALSE to TRUE.

When either prim is rezzed, it announces itself on the common channel. If it's partner is also rezzed, it will respond to the first prim's announcement and establish the handshake. This works regardless of which prim is rezzed first, so the handshake is automatic once both prims are rezzed. Once a handshake is formed, the prim with the variable "_master" set to TRUE is allowed to continue beyond the handshake, controlling any future interactions between the two prims.

One possible extension is to define _me, _target, and _master through the description field, removing the need to manually code these three variables.

Otherwise both scripts are identical, aside from the assignment of the variables "_me", "_target", and "_master".

This code is not directly usable by someone without scripting experience, but can be a good foundation for someone familiar with scripting to establish a handshake connection with their own projects.

Instructions:

Code:
string _me = "collar";
//string _me = "leash";
key    _id_me;
string _target = "leash";
//string _target = "collar";
key    _id_target;
integer _ch_link = -107;
integer _debug = TRUE;
integer _master = FALSE;

debug(string s)
{
    if (_debug) {
        llOwnerSay(s);
    }
}
init()
{
    integer i;
    for (i=1; i <= llGetNumberOfPrims(); i++) {
        if (llGetLinkName(i) == _me) {
            _id_me = llGetLinkKey(i);
            send_ch(_ch_link, _me+"|"+_target+"|ping|"+(string)_id_me);
        }
    }
    llListen(_ch_link, "", NULL_KEY, "");
}
send_ch(integer ch, string s)
{
    debug("send ("+(string)ch+"): "+s);
    llSay(ch, s);
}
recv_ch(integer ch, string s)
{
    debug("recv ("+(string)ch+"): "+s);
    llSleep(0.1); // give things a chance to catch up
}

default
{
    state_entry()
    {
        init();
    }
    on_rez(integer startup_param)
    {
        llResetScript();
    }
    changed(integer change)
    {
        if(change & CHANGED_OWNER) {
            llResetScript();
        }
    }
    touch_end(integer NumberOfTouches)
    {
        init();
    }
    listen(integer ch, string name, key id, string msg)
    {
        recv_ch(ch, msg);
        if (ch == _ch_link) {
            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 (src = _target) {
                if (tgt == _me) {
                    if (cmd == "ping") {
                        _id_target = (key)arg;
                        send_ch(_ch_link, _me+"|"+_target+"|pong|"+(string)_id_me);
                    }
                    else if (cmd == "pong") {
                        _id_target = (key)arg;
                        send_ch(_ch_link, _me+"|"+_target+"|ack|"+(string)_id_me);
                    }
                    else if (cmd == "ack") {
                        llOwnerSay("***handshake complete***");
                        debug("***_id_me    ="+(string)_id_me);
                        debug("***_id_target="+(string)_id_target);
                        if (_master) {
                            debug("initiating followup actions");
                            // initiate followup actions such as particle chains, menus to control the target, etc
                        }
                    }
                }
            }
        }
    }
}
Here is a sample run with debugging turned on:
Code:
[06:55] leash: send (-107): leash|collar|ping|20ae3b24-306f-0556-a59b-da4ae9ebe657
[06:55] collar: recv (-107): leash|collar|ping|20ae3b24-306f-0556-a59b-da4ae9ebe657
[06:55] collar: send (-107): collar|leash|pong|ea791cc3-78dd-10c1-28cf-1084ea964a73
[06:55] leash: recv (-107): collar|leash|pong|ea791cc3-78dd-10c1-28cf-1084ea964a73
[06:55] leash: send (-107): leash|collar|ack|20ae3b24-306f-0556-a59b-da4ae9ebe657
[06:55] collar: recv (-107): leash|collar|ack|20ae3b24-306f-0556-a59b-da4ae9ebe657
[06:55] collar: ***handshake complete***
[06:55] collar: ***_id_me    =ea791cc3-78dd-10c1-28cf-1084ea964a73
[06:55] collar: ***_id_target=20ae3b24-306f-0556-a59b-da4ae9ebe657
[06:55] leash: initiating followup actions
It is possible a send might repeat itself due to timing issues, but can be safely ignored (I've set a 0.1 second sleep to work around this, but it may still crop up in lagged conditions). A repeated send has no effect on the performance of the script.
 
Last edited:
  • 1Like
Reactions: Khamon

Soen Eber

Vatican mole
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
633
Well, it would be more clever if I were driving the handshake from the description field instead of hardcoding some values, but I'm on a rush to finish a project, so being lazy here.

Thanks :)
 

Jopsy Pendragon

Censor Dickweed Trump
Joined
Sep 20, 2018
Messages
688
Location
Hillcrest, San Diego, CA
SL Rez
2004
Joined SLU
2007
SLU Posts
11308
Handshakes are so annoying to account for all the exceptions:
Partner never shows up .. keep trying or give up and retry later? And if so, how do we know when?
Partner goes AWOL .. do we find another or not?
Race condition where multiple eligible partnerA's and partnerB's get rezzed at the same time, how do we make sure the right A&B's find each other without All A's connecting to the first B that ping-backs?
etc etc etc.


I've been working on a handshakec/ommunication thing between an object and a mind-wiped in-world clone of the object. (I call'm BAD COPIES. Caused by using Edit + Shift-Drag or Ctrl-D). Several of my things 'learn' and need to remember what
they learned when used in builds. It's easy to forget to always rez a new copy from inventory.

So, using a pair of keys in the desc field, some manually 'typed' strided lists on key-based 'private' channels ... I've come up with a module that will try to figure out if it's the victim of BAD COPYING ... and then asks it's original to pass it the data it needs to know.

I used to just encode/condense all the data into the desc field and just import it from there, but 127 isn't much and I was forced to tolerate rounding errors to do it that way. Now, a full set of particle parameters simply won't fit no matter what I do. (and trust me, I was resorting to things like converting to base 91 (all printable desc characters) to make it happen before. ;)

Asking the last known good copy of itself to pass data forward is -soooo- much better.
 
Last edited:
  • 1Like
Reactions: Khamon

Noodles

Queen of Ramen
Joined
Sep 20, 2018
Messages
662
Location
Illinois
SL Rez
2006
Joined SLU
04-28-2010
SLU Posts
6947
I just want to say that this is twice in like a week that I have heard about strided lists in LSL and I still am not sure of the benefit versus a list of lists.
 

Clara D.

FOR PRESIDENT 2020!
Joined
Dec 24, 2018
Messages
2,120
Location
Phoenix, AZ, USA
SL Rez
2006
Joined SLU
Back in the day.
SLU Posts
100000000
I just want to say that this is twice in like a week that I have heard about strided lists in LSL and I still am not sure of the benefit versus a list of lists.
I usually do parallel lists instead of strided because it's easier to wrap my head around.
 

Innula Zenovka

Nasty Brit
VVO Supporter 🍦🎈👾❤
Joined
Sep 20, 2018
Messages
5,374
SLU Posts
18459
I just want to say that this is twice in like a week that I have heard about strided lists in LSL and I still am not sure of the benefit versus a list of lists.
Depends what you're used to using, I think,

There are two different libraries of userfunctions for use with strided lists that I find very helpful, one group in the wiki and another (which I refer to quite frequently) in the old lsl wiki.
 
  • 1Like
Reactions: Noodles

Jopsy Pendragon

Censor Dickweed Trump
Joined
Sep 20, 2018
Messages
688
Location
Hillcrest, San Diego, CA
SL Rez
2004
Joined SLU
2007
SLU Posts
11308
I just want to say that this is twice in like a week that I have heard about strided lists in LSL and I still am not sure of the benefit versus a list of lists.
Given that most of what I do is with the ParticleSystem which basically expects a strided list anyway [ PARAMETER1, value1, PARAMETER2, value2, etc ] I've just gotten used to thinking in terms of strides. And as far as passing data through chat from one object to another, if I have to past multiple lists that means I have to put in some kind of "END OF LIST1 / START OF LIST 2 / ... " detection/handling.

llList2CSV ends up making all list values into strings... which llParticleSystem will choke on. So I've got to put them back into integers, floats, and vectors.

And, heh, doing so I've done something really lazy and probably less efficient just so that I can get it done with a smaller block of code. Like instead of:
Code:
list TypedCSV2List(list typedlist) {
   integer i; integer len = llGetListLength(typedlist);
    list retlist;
    for (; i < len; i += 2) {
            string v = llList2String(typedlist, i + 1);
            integer entrytype = llList2Integer(typedlist, i);
            if (entrytype==TYPE_INTEGER ) retlist += [(integer)v];
            else if ( entrytype==TYPE_FLOAT ) retlist +=[(float)v];
            else if ( entrytype==TYPE_VECTOR ) retlist +=[(vector)v];
            else if ( entrytype==TYPE_ROTATION ) retlist +=[(rotation)v];
            else if ( entrytype==TYPE_KEY ) retlist += [(key)v];
            else retlist+= [ v ];
    }
    return retlist;
}
I've done this instead:

Code:
list TypedCSV2List(list typedlist) {
    integer i; integer len = llGetListLength(typedlist);
    list retlist;
    for (; i < len; i += 2) {
            string v = llList2String(typedlist, i + 1);
            integer entrytype = llList2Integer(typedlist, i);
            if (entrytype < 1 || entrytype > 6) entrytype = 3; // default to TYPE_STRING(3)
            retlist += llList2List(
                   [0,  (integer)v, (float) v,(string)v, (key)v, (vector)v, (rotation)v],
                 entrytype, entrytype  );
    }
    return retlist;
}
Generally if there's a 1 to 1 mapping of items... I'd rather use a strided list than parallel lists. As long as it's not causing data redundancy.
If it's 1-to-many or many-to-1 data, then separate lists is definitely the way to go.
 
Last edited: