ParcelChatRelay

From Second Life Wiki
Revision as of 04:06, 3 January 2013 by Tano Toll (talk | contribs) (→‎Notes:: Added code for version 2)
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.

VERSION 2.0

Recommended and most efficient version <lsl> //PCR Version 2// //changes: //* No longer uses radar but llGetAgentList to detect agents //* Removed hover text and replaced by ownersay spam on updates //* Added configurable option for coverage (parcel, parcels by same owner, entire region) //* Made timer faster running in first 10 minutes after rez, to update faster on repositions //* Some adjustments to allow cross-parcel and region-wide chat; replaced parcel key by owner key in those situations. //* Overall, this version is more efficient and causes less lag, especially when idling, since agent list is not unneccesary updated by sensor events. It also provides better coverage and automatic support for skyboxes.

//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

//USAGE: //* Edit coverage parameter below to suit your needs //* place this script in a objects, placed in hearing range of avatars //* If you do NOT want to relay a certain place, like a skybox, simply place no relay object. However, agents in the skybox will still hear what is said near other relays. If you do not want that, please use version 1 of this script that uses radar to detect agents.

// CONFIGURATION

//choose coverage: //integer coverage=AGENT_LIST_PARCEL; integer coverage=AGENT_LIST_PARCEL_OWNER; //integer coverage=AGENT_LIST_REGION;

//Maximum range from listener object to avatar integer maxRange=6000; //sim wide, reaches all skyboxes //integer maxRange=290; //sim wide, ground level //integer maxRange=100; //radar range, like version 1

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

// NO NEED TO EDIT BELOW THIS LINE. CHANGES AT OWN RISK //

//list of detected nearby agents: list insim;

//timestamp of last check - to reduce sim load integer stamp;

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

//key based for handle, depending on setting parcel or owner key handlebase;

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

//list of detected nearby relays list repeaters;

//for less confusing setup, fast timer first minutes after rez, then slow down to appropiate interval integer timercount;


announce() {

   llRegionSay (handle, tag+(string)handlebase);   

}

default {

   state_entry()
   {
       //llSetText("Chat Relay started...",ZERO_VECTOR,1);
       llOwnerSay("Chat Relay started...");
       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);        
       
       //adjust key based on coverage. for single parcel, use parcel key. else use owner key.
       //notice that on region-wide coverage, all relays must be rezzed by same owner.
                     
       if (coverage==AGENT_LIST_PARCEL)
           handlebase=parcelkey;
       else
           handlebase=llGetOwner();
           
       handle=-10909-(integer)("0x"+llGetSubString(handlebase,1,5));
       llListen(handle, "", "", tag+(string)handlebase);
       
       //broadcast our existance about every 5 minutes, but starting with 10 second interval to avoid setup confusion
       //llSetTimerEvent(290.0+llFrand (20.0)); //about each 300 seconds, not all at once
       llSetTimerEvent (10.0);
       announce();
   }
   
   on_rez(integer n) {
       //simply reset
       llResetScript();   
   }
   timer() {        
       //announce self
       announce();
       
       //check interval
       timercount++;
       if (timercount==60) //after about 10 minutes
           llSetTimerEvent(290.0+llFrand (20.0)); //change to 5 minute interval       
       
       //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
               llOwnerSay ("Lost connection to node at "+(string)llList2Vector(repeaters, n-1));
               repeaters=llDeleteSubList(repeaters, n-2, n);
           }
           n-=3;
       }   
       //llSetText ("Node(s): "+(string)(1+llGetListLength(repeaters)/3), <1,1,1>, 1);
   }
   
   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.
           
           //see if we need to refresh our list - cache it for 15 seconds
           if ((llGetUnixTime()-stamp) > 15.0) {
               stamp=llGetUnixTime();
               insim=llGetAgentList (coverage, []);
           }
           
           //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<=maxRange) {
                   //same parcel? - edit - check skipped.
                   //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==handle) {
           //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);
           else
               llOwnerSay ("New node found at "+(string)pos);
           repeaters += [id, pos, llGetUnixTime()];
       }
       
   }

}

</lsl>


VERSION 1.0

In some cases, you might want to use the original radar-based version. Usually not recommended, listed for completeness. <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)