|
OpenCollarAttachmentProtocol
This is about the in OC 3.3 new added protocol for attachments to communicate with an OpenCollar
Type-Dev IntroductionSo far we used one Object Channel for any kind of object to communicate with the Collar. DetailsGeneral Protocol DescriptionThe channel which the Collar listens to and shall be used by all attachments who want to communicate with the collar is calculated by the Collar-wearers UUID: interfaceChannel = (integer)("0x" + llGetSubString(wearer,30,-1));
if (interfaceChannel > 0)
{
interfaceChannel = -interfaceChannel;
}Attachment shall ping for the Collar and after they got a reply limit their listener to the Collar UUID only! The Collar listens on this channel all time and depending on the message will do 4 possible things: 1) The Attachment sends a "Is there a collar?" request. The request has to be:llWhisper(interfaceChannel, "OpenCollar?");This will be replied with:llWhisper(interfaceChannel, "OpenCollar=Yes");This is meant to check if there is a collar that hears me at all. And once it got detected, the Attachment-listener can be reduced to the Collar's UUID. On Detach the collar sends:llWhisper(interfaceChannel, "OpenCollar=No");To announce its disappearance. 2) The Attachment can send auth-requests which will be parsed through the Collar auth systems and replied on. The protocol syntax for this is:string authRequest = "0|myPersonalCommandOrMessage|" + (string)UserID + "|" + (string)objectID; llWhisper(interfaceChannel, authRequest);Where "0" is the Collar internal code for COMMAND_NOAUTH, UserID is the UUID of the to authenticate id and objectID the UUID of the attachment itself (to later verify: it was my request). The reply from the collar auth - script will be:string authReply = "RequestReply|" + (string)AUTH + "|myPersonalCommandOrMessage|" + (string)UserID + "|" + (string)objectID; llWhisper(interfaceChannel, authReply); 3) The Attachment wants to send a command to the AO which does not require authentication (e.g. the cuffs need to the AO to pause): The Attachment sends its command to the Collar with a prefixed auth of COMMAND_COLLAR = 499.string attach2attach_Command = "499|" + (string)what_ever_i_have_to_say;The collar with relay the message directly:llWhisper(interfaceChannel, message); 4) The Attachment has a command for a AddOn in the collar: Just send a message on the interfaceChannel that does not match any of the above criteria (means it is not "OpenCollar?" or does not start with "0|" or "499|" : Usage for Collar AddOn - scriptsThere is no need to use an own listener in your AddOn for your AddOn attachments anymore, the Collar listener will pass any message send on the interfaceChannel on with ATTACHMENT_COMMAND = 602... this is the only thing you need to include in your AddOn message map and act on it. If you need to reply on this, you should add the interfaceChannel into your AddOn - script like described above and reply on that channel: interfaceChannel = (integer)("0x" + llGetSubString(wearer,30,-1));
if (interfaceChannel > 0)
{
interfaceChannel = -interfaceChannel;
}OpenCollarAttch - interface - scriptThis is as example the script used in the new AO with lock feature based on authentication vs the Collar: //Licensed under the GPLv2, with the additional requirement that these scripts remain "full perms" in Second Life. See "OpenCollar License" for details.
//script for attachments to ask the OpenCollar for ownership
integer interfaceChannel = -12587429;
integer listenHandle;
integer COMMAND_NOAUTH = 0;
integer COMMAND_AUTH = 42;
integer COMMAND_COLLAR = 499;
integer COMMAND_OWNER = 500;
integer COLLAR_INT_REQ = 610;
integer COLLAR_INT_REP = 611;
integer collarIntegration;
key wearer;
key objectID;
string separator = "|";
list authList; //strided list [uuid, auth]
integer counter;
key collarID;
string messageType; // how the collar sends a message" "RequestReply", "CollarCommand"
debug(string message)
{
//llOwnerSay(llGetScriptName() + " DEBUG: " + message);
}
init()
{
objectID = llGetKey();
//listen first to the full interfaceChannel and start to ping every 10 secs for a collar
llListenRemove(listenHandle);
listenHandle = llListen(interfaceChannel, "", "", "");
//we dont know what was changed in the collar so lets starts fresh with our cache
authList = [];
collarID = NULL_KEY;
llWhisper(interfaceChannel, "OpenCollar?");
counter = 0;
llSetTimerEvent(10.0);
}
default
{
state_entry()
{
wearer = llGetOwner();
interfaceChannel = (integer)("0x" + llGetSubString(wearer,30,-1));
if (interfaceChannel > 0) interfaceChannel = -interfaceChannel;
init();
}
on_rez(integer start)
{
if( wearer != llGetOwner())
{
llResetScript();
}
init();
}
link_message(integer sender, integer num, string str, key id)
{
debug("LinkMsg: " + str);
if (num == COLLAR_INT_REQ)
{
if (collarID != NULL_KEY && str == "CollarOn")
{
collarIntegration = TRUE;
authList = [];
}
else if (str == "CollarOff")
{
collarIntegration = FALSE;
authList = [];
}
//send back if we know the collarID (means if != NULL_KEY we are able to interact fully
llMessageLinked(LINK_THIS, COLLAR_INT_REP, str, collarID);
}
else if (num == COMMAND_NOAUTH)
{
if (collarIntegration)
{
integer index = llListFindList(authList, [id]);
if ( index == -1)
{
llWhisper(interfaceChannel, "0|" + str + separator + (string)id + separator + (string)objectID);
}
else
{
string auth = llList2String(authList, index + 1);
llMessageLinked(LINK_THIS, (integer)auth, str, id);
}
}
else
{
llMessageLinked(LINK_THIS, COMMAND_OWNER, str, id);
}
}
else if (num == COMMAND_AUTH && str == "ZHAO_RESET")
{
llResetScript();
}
}
listen(integer channel, string name, key id, string message)
{
debug("Listen: " + message);
//do nothing if wearer isnt owner of the object
if (llGetOwnerKey(id) != wearer)
{
return;
}
//Collar announces itself
if (message == "OpenCollar=Yes")
{
collarID = id;
llListenRemove(listenHandle);
listenHandle = llListen(interfaceChannel, "", collarID, "");
//llMessageLinked(LINK_THIS, COLLAR_INT, message, "");
llMessageLinked(LINK_THIS, COLLAR_INT_REQ, "CollarOn", "");
return;
}//Collar said it got detached
else if (message == "OpenCollar=No")
{
collarID = NULL_KEY;
authList = [];
llListenRemove(listenHandle);
listenHandle = llListen(interfaceChannel, "", "", "");
llMessageLinked(LINK_THIS, COLLAR_INT_REQ, "CollarOff", "");
return;
}
//messageType + SEPARATOR + (string)num + SEPARATOR + msg + SEPARATOR + (string)id SEPARATOR + objectID
integer index = llSubStringIndex(message, separator);
messageType = llGetSubString(message, 0, index - 1);
debug(messageType);
if (messageType == "RequestReply")
{
key checkID = (key)llGetSubString(message, llStringLength(message) - 36, -1);
debug("IDcheck= " + (string)checkID);
if (checkID != objectID)
{//if this isnt my id then the message was not for me<br>
return;
}
//cut off my own id, no more needed
message = llGetSubString(message, 0, llStringLength(message) - 38);
}
//cut off the message type
message = llGetSubString(message, index + 1, -1);
debug(message);
//check if we get a auth request at all here
index = llSubStringIndex(message, separator);
integer auth = (integer)llGetSubString(message, 0, index - 1);
if (auth)
{//auth has to be an integer > 0 else it cannot be a collar message
message = llGetSubString(message, index + 1, -1);
index = llSubStringIndex(message, separator);
string command = llGetSubString(message, 0, index - 1);
//Collar tells me owners have changed reset my authList
if (auth == COMMAND_COLLAR)
{
if (command == "OwnerChange")
{
authList = [];
}
else if (command == "safeword")
{
llMessageLinked(LINK_THIS, auth, message, "");
}
else
{
llMessageLinked(LINK_THIS, auth, message, "");
}
}
else
{
key UUID = (key)llGetSubString(message, index + 1, -1);
authList += [UUID, (string)auth];
llMessageLinked(LINK_THIS, auth, command, UUID);
}
}
}
timer()
{
if (collarID != NULL_KEY)
{
if (llKey2Name(collarID) == "") //the collar is somehow gone...
{//check 2 times again if the collar is really gone, then switch to CollarRequest mode
if (counter <= 2)
{
counter++;
llSetTimerEvent(5.0);
}
else
{
llSetTimerEvent(10.0);
collarID = NULL_KEY;
llListenRemove(listenHandle);
listenHandle = llListen(interfaceChannel, "", "", "");
counter = 0;
llWhisper(interfaceChannel, "OpenCollar?");
llMessageLinked(LINK_THIS, COLLAR_INT_REQ, "CollarOff", "");
}
}
}
else
{
llWhisper(interfaceChannel, "OpenCollar?");
}
}
}
| |