Detecting RLV

As you may know, I love scripting.  Last Halloween, I made up some RLV traps to scatter around the build for the unwary.  One was a giant skeletal hand that popped out of the ground and grabbed the unsuspecting victim hehehe.

I though you might be interested to see how the basis of RLV scripting works.  Feel free to skip this article if you have zero script interest 😉

To be affected by these type of scripts, avatars must wear an RLV relay and be using an RLV capable viewer, for example, Firestorm.  The relay acts as a bridge between the scripted object and the avatar’s viewer, receiving commands and then those commands are intercepted by the viewer.  This is how RLV can control your viewer.

The RLV relay listens on a specific channel (you can find heaps of detail about all this in the RLV Relay Protocol document).  The first thing you generally do is find a list of avatars wearing an active relay so you can perform some action on them.  That’s what this example script does, find a list of RLV enabled avatars.

#define DEBUG
#include "debug.lsl"        // See the source of debug.lsl
#define TIMEOUT 1.0         // #define is part of the Firestorm preprocessor
#define CHAN 12345          // You can just define these as normal variables
#define RLVCHAN -1812221819 // if you're not using Firestorm

list g_avis;
key g_target;
integer g_idx;
integer g_handle;
string g_command;

default {
    state_entry () {
        debug ("state default");        
    }

    touch_end (integer n) {
        llOwnerSay ("Checking for active RLVs...");
        g_avis = llGetAgentList (AGENT_LIST_PARCEL, []);
        state rlv;
    }
}

state rlv {
    
    state_entry () {
        if (llGetListLength (g_avis) > 0) {
            g_target = llList2Key (g_avis, 0);
            llListenRemove (g_handle);
            g_handle = llListen (CHAN, "", "", "");
            g_command = "testing";
            llRegionSayTo (g_target, RLVCHAN, 
                           g_command + "," + (string)g_target + ",@versionnum=" +
                           (string)CHAN);
            llSetTimerEvent(TIMEOUT);
        } else {
            llOwnerSay ("--");
            state default;
        }
    }
    
    listen(integer channel, string name, key id, string message) {
        if (llGetOwnerKey (id) != g_target) {
            return;
        }
        if (g_command == "testing") {
            llSetTimerEvent (0.0);
            llListenRemove (g_handle);
            llOwnerSay (llGetDisplayName (g_target));
            g_avis = llDeleteSubList (g_avis, 0, 0);
            state redo_rlv;
        }
    }
    
    timer() {
        llSetTimerEvent (0.0);
        llListenRemove (g_handle);
        g_avis = llDeleteSubList (g_avis, 0, 0);
        state redo_rlv;
    }    
}

state redo_rlv {
    state_entry () {
        state rlv;
    }
}

The basis of this script is the llRegionSayTo () function, the listen, and the timer in the rlv state.  Saying a string to an avatar formatted correctly (in this case, we are asking for the avatar to tell us the version of RLV they are running) will cause the avatar to send us a string back with the information, which is received by the listen.  In the event the avatar does not have a relay on, they won’t respond with anything, so eventually, we remove the listen with a timer.