You are currently browsing the tag archive for the ‘llMessageLinked’ tag.

In my previous article, I showed how to set up communication between different scripts in a set of linked prims. In this article I will explore this a little further, and show how to make it more flexible and easier to use.

First, we need a way of finding the link numbers of the prims in a set. Although it is possible to work this out manually, it is tedious and error-prone, and the more prims that are linked, the worse this becomes.

Let’s try an alternative. Give each of the prims a unique name (or at least unique within the link-set). This is probably a useful thing to do anyway.

Once this is done, you can set up variables for the prims that a script is interested in, and then locate the linked prims by name, using llGetLinkName function. Call this when the script is reset or the prim is rezzed.

Here’s a small example:

// This will hold the link number for the prim we
// want to send messages before.
integer LINK_TARGET = -1;
// A function to read the link numbers.
getLinks()
{
    integer i;
    // How many prims are linked?
    integer linkcount = llGetNumberOfPrims();
    // Reset our link target number, just to be
    // safe.
    LINK_TARGET = -1;
    // Work through the prims, looking for our
    // target.
    for (i = 1; i <= linkcount; ++i)
    {
        string str = llGetLinkName(i);
        if (str == "LINK_TARGET_NAME")
        {
            // Found it. Store the link number.
            LINK_TARGET = i;
        }
        // We can obviously extend this to
        // look for and record the link
        // numbers of other prims.
    }
    // If we didn't find our target, report this
    // to the owner.
    if (LINK_TARGET == -1)
    {
        llOwnerSay("Could not find LINK_TARGET_NAME");
    }
}
default
{
    state_entry()
    {
        getLinks();
    }
    on_rez(integer param)
    {
        getLinks();
    }
    touch_start(integer count)
    {
        // Make sure we actually found our target
        // prim.
        if (LINK_TARGET != -1)
        {
            // Send the message to it.
            llMessageLinked(LINK_TARGET, 0, "MESSAGE", NULL_KEY);
        }
    }
}

The only thing to be careful about here is that if you change the link order (by unlinking and re-linking your prims in a different order), you have to make sure that all the scripts are reset.

One convenient way of doing this is to include a RESET command which is sent from the root prim to all the other prims. When they receive this command, they should call llResetScript() to reset themselves:

link_message(integer sender, integer cmd, string param, string id)
{
    // Assume we have defined CMD_RESET somewhere.
    if (cmd == CMD_RESET)
    {
        llResetScript();
    }
}

Now, how about passing different types of parameters, other than simple strings? If you only want to pass a single value, a straight-forward typecast is all that is required. For example, suppose we want to have a message that tells another prim to move to a specific position. To do this, we want to send a vector. We can do this like this:

llMessageLinked(TARGET, CMD_MOVE, (string)<128.0,128.0,128.0>, NULL_KEY);

The receiving prim can convert this back into a vector in a similar way:

link_message(integer sender, integer cmd, string param, string id)
{
    if (cmd == CMD_MOVE)
    {
        vector pos = (vector)param;
        llSetPos(pos);
    }
}

(Of course, I am assuming you have set up a CMD_MOVE variable in both scripts, with the same value in each. It would also be sensible to provide some kind of error-trapping, in case the parameter string does not actually hold a vector. If it doesn’t, you will end up with a vector of .)

What about passing more than one parameter between the scripts? The main trick here is for the sending script to pack the parameters into a string, and then the receiving script uses llParseString2List() to extract the result.

In this example, I am separating the parameters with a semi-colon. Here is the sender’s code:

llMessageLinked(TARGET, CMD_MOVE, "<1.0,1.0,1.0>;Move!", NULL_KEY);

The receiver needs to unpack this, like so:

link_message(integer sender, integer cmd, string params, key id)
{
    if (cmd == CMD_MOVE)
    {
        list parameters = llParseString2List(params, [";"], [""]);
        vector moveBy = (vector)llList2String(parameters, 0);
        string command = llList2String(parameters, 1);
        // Do something with the results
        // ...
    }
}

Note that when extracting the vector, I am extracting the parameter from the list as a string, and then typecasting it to a vector, because the parameter is stored as a string in the list (llParseString2List will extract the contents as string), and llList2Vector will not perform a typecast itself.

By combining the getLinks() function and the ability to pass multiple parameters, you can construct a flexible and fairly robust system for passing information between linked prims.

A final caveat: don’t overdo this. Every script takes up resources. If you have an object with a large number of scripts all communicating with one another you can seriously contribute to lag. If you find yourself doing this, it is worth rethinking how your object is working, and whether there is a simpler way of doing things.

Sooner or later, if you are creating gadgets that do useful things, you will find the need to have some communication between scripts in different prims.

There are two basic methods of doing this. One is to use the llMessageLinked() function, the other is to use a chat channel. In this article I am going to look at using llMessageLinked, but it is important to understand when to use which option.

Let me start, then, by briefly looking at the chat channel option.

The main advantage of this option is that the prims which contain the communicating scripts do not have to be linked. This allows prims to communicate across an entire sim (note that there is no easy way to communicate across different sims short of using an external web-service of some kind).

The main disadvantage, and this is a biggie, is that using a chat channel normally requires the scripts to set up Listeners which are constantly active. This adds to simulator lag, and is something that therefore should be minimised.

If your scripts absolutely need to be run in unlinked prims, or in prims which are in separate objects, however, the chat channel option is probably the only way to go.

However, if your scripts are part of a set of linked prims, the llMessageLinked function is a better option, and it is this option that I am going to cover.

llMessageLinked takes a number of parameters, although you rarely need to make use of all of them. The first parameter is crucial, though, as it is the number of the prim that you are sending the message to. It can also be one of a set of special values, the most useful of which are probably the following:

LINK_ALL_OTHERS – this sends the message to all other prims except the current one.

LINK_ALL_CHILDREN – this sends the message to all prims which are children of the root prim.

LINK_ALL – this sends the message to all the prims in set, including the one that the message is sent from.

The second parameter is an integer. This can be used as an identifier of the type of message. For example, you could set up the following constants in each script:

integer CMD_INIT = 1;
integer CMD_MOVE = 2;	// I'll use this in my examples below

You would then pass the appropriate value to llMessageLinked.

The third parameter is a string. This can be used to pass parameters to the receiving scripts. If you only need one parameter, this is simple. Remember that you can use LSL’s type-casting to let you put (for example) numbers into this parameter. I’ll show an example of this later.

The final parameter is a key.

At the receiving end, the message() function will be called when a message is received from an llMessageLinked() call. It is passed five values, the first of which is the number of the sending prim, and the other four are the four parameters from llMessageLinked().

As an example, lets create a system whereby touching the root prim will move the linked prim. In itself, this isn’t exactly useful, but it could be used as the basis of (for example) some kind of ‘sliding door’ system.

Create two prims, and link them together.

Create a new script in the root prim (note that if you create a script while a link set is selected, the script will automatically be created in the root prim).

In our script, we will have a setting which tells us if the other prim is in its original position. If it is, when the owner touches the root prim, we move the other prim a short distance away. If it is not, we will move it back.

Here is the basic script:

integer hasMoved = FALSE;
float moveBy = 1.25;
// Commands
integer CMD_MOVE = 1;
integer CMD_RESTORE = 2;
default
{
    touch_start(integer count)
    {
        // Only respond to our owner.
        if (llDetectedKey(0) == llGetOwner())
        {
            if (hasMoved)
            {
                // Tell the other prim to restore its original position.
                llMessageLinked(2, CMD_RESTORE, "", NULL_KEY);
                hasMoved = FALSE;
            }
            else
            {
                // Tell the other prim to move by a specified distance. Note
                // that we are type-casting the distance to a string.
                llMessageLinked(2, CMD_MOVE, (string)moveBy, NULL_KEY);
                hasMoved = TRUE;
            }
        }
    }
}

Save the script.

Now, tick the ‘Edit individual prims’ checkbox, and select the other prim. Create a script in this prim. In this script, we will detect the message from the root prim, and respond accordingly:

vector originalPos;
// Commands
integer CMD_MOVE = 1;
integer CMD_RESTORE = 2;
default
{
    state_entry()
    {
        originalPos = llGetLocalPos();
    }
    on_rez(integer param)
    {
        originalPos = llGetLocalPos();
    }
    link_message(integer sender, integer num, string params, key id)
    {
        // Check that this came from the main script.
        if (sender == 1)
        {
            // Find out which command was received
            if (num == CMD_MOVE)
            {
                // Read our local position (i.e. relative
                // to the link-set object)
                vector pos = llGetLocalPos();
                // Adjust it -- note that we can typecast
                // the string parameter to a float.
                pos.x += (float)params;
                // Set the new position
                llSetPos(pos);
            }
            else if (num == CMD_RESTORE)
            {
                // Restore our original position
                llSetPos(originalPos);
            }
        }
    }
}

Save the script, close the Build dialog, and test the object.

You should find that this works, and touching the object will move the second prim. Touching it again will move it back to its original position.

However, there are some problems with this system. For example, we need to know the link number of the prim that we want to send a message to. When there are only two prims (as in our example) this is simple, but if there are a number of prims, it can get difficult to keep track of this (especially if you
unlink and relink the prims while you are building your object, as is quite likely).

Also, the string that we are using to pass a parameter to the linked prim seems a little restrictive.

In the next article I’ll look at both of these issues, and some possible solutions (though as a hint for the parameters, remember that you can typecast a list).