ParcelChatRelay

From Second Life Wiki
Revision as of 19:54, 11 May 2012 by Tano Toll (talk | contribs) (Parcel Chat Relay - Communications helper script - description&code)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Parcel Chat Relay

Description:

Communicate on your land in general chat regardless of distance between avatars. Useful when building, in clubs, for roleplay, etc.

Messages will be intelligently relayed - a listener will never hear a message twice! It will either be heard normally, or relayed. Using the new llRegionSayTo() api, chat messages are only relayed to avatars that are out of range.

How to use:

  • Rez 1 or more relays on your parcel, in a way that you could hear each avatar (<20m). Do not worry overlapping, but don't overdo it either - less relays will still be less lag.
  • If you rez a relay on its' final position, it will immediately be 'tuned' and part of the network.
  • If you move or remove a relay, it might take up to 10 minutes for the network to catch up.

Features:

  • Muliple relays can be rezzed to cover each part of your parcel.
  • Dedoubling without any overhead
  • Low lag - relays do not have to communicate to dedouble messages

Limitations:

  • Works only per parcel (by design). Agents on neighboring land will not receive relayed messages.
  • Receivers have to be within radar range (96m) of the receiving relay
  • Scripts cannot distinguish normal say, whispering and shouting. Normal say is assumed. Shouts may be heard double by the receiver
  • Moving avatars might experience 'border' cases - where a message sometimes accidentally is not relayed or double relayed. Due to the fast response time unlikely
  • The displayed ('faked') name is the official linden name, and chat is colored like objects, not avatars. Also depends on viewer settings.

Remarks on the parcel limitation.. Other options (sim wide, range based) would be easily possible, but i like the item to be zero-config ('just works'). Feel free to mod the script at your demands.

License:

Modified BSD / public domain

This script is also available at marketplace for your convenience: https://marketplace.secondlife.com/p/Parcel-Chat-Relay/3157725

Notes:

Possible easy-to-adjust modifications: do not use parcel key but owner key as handle, allowing cross parcel-but-same-owner chat, or specifying a fixed key for region-wide that.

<lsl> //2011-2012 Tano Toll - TSL License//

//Short summorization: //Relays will know of eachothers existance //Relays listen to public chat //The relay _closest_ to sending agent is responsible for relaying the message //Messages will only be relayed to agents further than 20 meter away from the speaker //Messages will only be relayed to agents on the same parcel


//list of detected nearby agents: list insim;

//just a hash. change to use several systems on a single parcel. string tag="+-/";

//key of the parcel where we are rezzed key parcelkey;

//chat channel handle made with key as base integer parcelhandle;

//list of detected nearby relays list repeaters;


announce() {

   llRegionSay (parcelhandle, tag+(string)parcelkey);   

}

default {

   state_entry()
   {
       llSetText("Chat Relay started...",ZERO_VECTOR,1);
       if (llGetAttached()) {
           //owner relay only mode
           //redundant in this script.
           //llListen (0, "", "", "");
       } else {
           //relay all agents
           llListen (0, "", "", "");   
       }
       
       //scan for nearby agents
       llSensorRepeat ("", "", AGENT, 65, TWO_PI, 20);
       
       //make up a inter-relay channel, so they can find eachother
       parcelkey=llList2Key(llGetParcelDetails(llGetPos(), [PARCEL_DETAILS_ID]),0);
       parcelhandle=-10909-(integer)("0x"+llGetSubString(parcelkey,1,5));
       llListen(parcelhandle, "", "", tag+(string)parcelkey);
       
       //broadcast our existance about every 5 minutes
       llSetTimerEvent(290.0+llFrand (20.0)); //about each 300 seconds, not all at once
       announce();
   }
   
   on_rez(integer n) {
       //simply reset
       llResetScript();   
   }
   timer() {        
       //announce self
       announce();
       
       //and clear list. repeaters with an too old timestamp will be removed.
       integer n=llGetListLength(repeaters)-1;
       integer t=llGetUnixTime();
       while (n>0) {
           if ((t-llList2Integer(repeaters, n))>390) { //time out
               repeaters=llDeleteSubList(repeaters, n-2, n);
           }
           n-=3;
       }   
       llSetText ("Node(s): "+(string)(1+llGetListLength(repeaters)/3), <1,1,1>, 1);
   }
   
   sensor (integer n) {
       //add detected agents to our list
       while (~--n) {
           key id=llDetectedKey(n);
           if (!~llListFindList(insim, [id]))
               insim+=id;    
       }
       
       //clean up agents that are no longer in sim
       n=llGetListLength(insim);
       while (~--n)
           if (llGetAgentSize(llList2Key(insim,n))); else
               insim=llDeleteSubList(insim,n,n);   
   }
   
   no_sensor() {
       insim=[];
       /*
       //clean up agents that are no longer in sim
       integer n=llGetListLength(insim);
       while (~--n)
           if (llGetAgentSize(llList2Key(insim,n))); else
               insim=llDeleteSubList(insim,n,n);   
       */
   }
   
   listen (integer channel, string name, key id, string m) {
       
       
       if (!channel) { //channel 0
           //only relay agents, not objects
           if (llGetAgentSize(id));else
               return;
               
           //see if another relay is more nearby
           integer n=0;
           vector senderpos=llList2Vector(llGetObjectDetails(id, [OBJECT_POS]), 0);
           float d=llVecDist(senderpos, llGetPos());            
           while (n<llGetListLength(repeaters)) {
               if (d>llVecDist(senderpos, llList2Vector(repeaters, n+1)))
                   return;
               n+=3;
           }
           //We are nearest repeater. Our job to relay.
           
           //relay to everyone in range. that's fixed to minimum 20 mins maximum 60.
           //get position of chatter
           vector o=llList2Vector(llGetObjectDetails(id, [OBJECT_POS]), 0);
       
           n=llGetListLength(insim);
           string oname=llGetObjectName();
           llSetObjectName(name);
           while (~--n) {
               vector t=llList2Vector(llGetObjectDetails(llList2Key(insim, n), [OBJECT_POS]), 0);        
               float d=llVecDist (o,t);
               if (d>=20/* && d<=60*/) {
                   //same parcel?
                   if (parcelkey==llList2Key(llGetParcelDetails(llList2Vector(llGetObjectDetails(llList2Key(insim,n), [OBJECT_POS]),0), [PARCEL_DETAILS_ID]),0))
                       llRegionSayTo (llList2Key(insim,n), 0, m);   
               }
           }       
           llSetObjectName(oname);
       }
       else
       if (channel==parcelhandle) {
           //there's another repeater..
           vector pos=llList2Vector(llGetObjectDetails(id, [OBJECT_POS]),0);
           
           integer p=llListFindList(repeaters, [id, pos]);
           if (~p)
               repeaters=llDeleteSubList(repeaters, p, p+2);
           repeaters += [id, pos, llGetUnixTime()];
       }
       
   }

}

</lsl> --Tano Toll 19:54, 11 May 2012 (PDT)