Difference between revisions of "User:Kireji Haiku/SIMchat headset"

From Second Life Wiki
Jump to: navigation, search
m (HELP header removed)
Line 29: Line 29:
 
==Script:==
 
==Script:==
 
<lsl>
 
<lsl>
string  ProtocolSignature = "ENC";//your signature
+
string  ProtocolSignature;
float  ProtocolVersion = 0.3; // can range from 0.0 to 255.255
+
float  ProtocolVersion;
string  Password = "P@ssw0rd";//your password
+
string  Password;
integer communicationsChannel = 9;//the channel you use for communication
+
integer communicationsChannel;
integer mChannel = 8989;//channel for dialog-menu
+
string  strHex = "0123456789ABCDEF";//hex-password
+
 
+
 
+
 
+
//##############################################################################
+
//do not change anything below if you are not 100% sure about what you are doing
+
//##############################################################################
+
 
+
 
+
 
string  Header;
 
string  Header;
integer Debug = TRUE;
+
string  strHex;
 +
integer Debug;
 
integer listener;
 
integer listener;
 
integer gListenID = -1;
 
integer gListenID = -1;
Line 52: Line 43:
 
key    user;
 
key    user;
 
list    mainmenu = ["RegionCount","SLURL","SIMchatOFF","Chat info","Chat howto"];
 
list    mainmenu = ["RegionCount","SLURL","SIMchatOFF","Chat info","Chat howto"];
 +
integer ConfigRequired = TRUE; string ConfigNotecardSuffix = ".cfg"; float ConfigTimeout = 60.0; integer ConfigLineIndex; key ConfigRequestID; list ConfigCards; string ConfigCardName; integer ConfigCardIndex;
 
string  Name;
 
string  Name;
 
string  SLURL;
 
string  SLURL;
Line 60: Line 52:
 
         ";
 
         ";
 
string  menu_info_2 = "Choose one of the following.";
 
string  menu_info_2 = "Choose one of the following.";
 +
integer mChannel = 8989;
 
string  prim_name;
 
string  prim_name;
 
string  prim_desc;
 
string  prim_desc;
Line 66: Line 59:
 
integer AUTO_START = TRUE;
 
integer AUTO_START = TRUE;
 
list    particle_parameters=[];
 
list    particle_parameters=[];
list    target_parameters=[];
 
  
string error(string message)
+
config_init()
 +
{
 +
    strHex = "0123456789ABCDEF";
 +
    Password = "P@ssw0rd";
 +
    ProtocolVersion = 0.3;
 +
    ProtocolSignature = "ENC";
 +
    mChannel = 8989;
 +
    communicationsChannel = 9;
 +
}
 +
 
 +
config_dump()
 +
{
 +
    say("You are using channel " + (string)communicationsChannel + " for chatting.");
 +
    say("Your password is: " + Password);
 +
    say("Your protocol version is " + (string)ProtocolVersion + ", your protocol signature " + ProtocolSignature + " and your stringHex is " + strHex);
 +
    say("The menu for the dialog-channel is: " + (string)mChannel);
 +
}
 +
 
 +
config_parse(string str, string cardName, integer lineNum)
 +
{
 +
    str = llStringTrim(str, STRING_TRIM_HEAD);
 +
    if (llGetSubString(str,0,0) == "//") {
 +
        return;
 +
    }
 +
    list ldata  = llParseStringKeepNulls(str, ["="], [""]);
 +
    string cmd  = llToUpper(llStringTrim(llList2String(ldata,0),STRING_TRIM));
 +
    string arg1 = llStringTrim(llList2String(ldata,1),STRING_TRIM);
 +
    if (cmd == "STRHEX") { strHex = arg1; }
 +
    else if (cmd == "CHANNEL") { communicationsChannel = (integer)arg1; }
 +
    else if (cmd == "PASSWORD") { Password = arg1; }
 +
    else if (cmd == "VERSION") { ProtocolVersion = (float)arg1; }
 +
    else if (cmd == "SIGNATURE") { ProtocolSignature = arg1; }
 +
    else if (cmd == "DIALOG") { mChannel = (integer)arg1; }
 +
    else if (cmd == "debug") { Debug = (integer) arg1; }
 +
}
 +
 
 +
config_done() { if (Debug) { config_dump(); } say("Config done"); }
 +
 
 +
say(string str) { llOwnerSay(llGetScriptName() + ": " + str); }
 +
 
 +
debug(string str) { if (Debug) { say(str); } }
 +
 
 +
integer next_card()
 +
{
 +
    if (ConfigCardIndex >= llGetListLength(ConfigCards)) {
 +
        ConfigCards = [];
 +
        return (FALSE);
 +
    }
 +
    ConfigLineIndex = 0;
 +
    ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);
 +
    ConfigCardIndex++;
 +
    ConfigRequestID = llGetNotecardLine(ConfigCardName, ConfigLineIndex);
 +
    say("Reading " + ConfigCardName);
 +
    return (TRUE);
 +
}
 +
 
 +
string  error(string message)
 
{
 
{
 
     if(Debug) llSay(DEBUG_CHANNEL, message);
 
     if(Debug) llSay(DEBUG_CHANNEL, message);
Line 74: Line 122:
 
}
 
}
  
string decrypt(string password, string message)
+
string decrypt(string password, string message)
 
{
 
{
 
     integer signatureLength = llStringLength(ProtocolSignature);
 
     integer signatureLength = llStringLength(ProtocolSignature);
Line 92: Line 140:
 
     string oneTimePad = llMD5String(password, nonce);
 
     string oneTimePad = llMD5String(password, nonce);
 
     while(llStringLength(oneTimePad) < (llStringLength(message) / 2 * 3))
 
     while(llStringLength(oneTimePad) < (llStringLength(message) / 2 * 3))
        oneTimePad += llMD5String(oneTimePad, nonce);
+
    oneTimePad += llMD5String(oneTimePad, nonce);
 
     oneTimePad = llStringToBase64(oneTimePad);
 
     oneTimePad = llStringToBase64(oneTimePad);
 
     message = llXorBase64StringsCorrect(message, oneTimePad);
 
     message = llXorBase64StringsCorrect(message, oneTimePad);
Line 103: Line 151:
 
}
 
}
  
string hex(integer value)
+
string hex(integer value)
 
{
 
{
 
     integer digit = value & 0xF;
 
     integer digit = value & 0xF;
Line 121: Line 169:
 
}
 
}
  
string encrypt(string password, string message)
+
string encrypt(string password, string message)
 
{
 
{
 
     integer nonce = (integer)llFrand(0x7FFFFFFF);
 
     integer nonce = (integer)llFrand(0x7FFFFFFF);
Line 127: Line 175:
 
     string oneTimePad = llMD5String(password, nonce);
 
     string oneTimePad = llMD5String(password, nonce);
 
     integer count = (llStringLength(message) - 1) / 32;
 
     integer count = (llStringLength(message) - 1) / 32;
     if(count)
+
     if(count) do oneTimePad += llMD5String(oneTimePad, nonce);
        do
+
            oneTimePad += llMD5String(oneTimePad, nonce);
+
 
     while(--count);
 
     while(--count);
 
     return Header + llGetSubString("00000000" + hex(nonce), -8, -1) + llXorBase64StringsCorrect(llStringToBase64(message), llStringToBase64(oneTimePad));
 
     return Header + llGetSubString("00000000" + hex(nonce), -8, -1) + llXorBase64StringsCorrect(llStringToBase64(message), llStringToBase64(oneTimePad));
Line 157: Line 203:
 
     state_entry()
 
     state_entry()
 
     {
 
     {
 +
        llSetText("", <1.0,1.0,1.0>, 1.0);
 +
        state s_config;
 +
    }
 +
}
 +
 +
state s_reconfig { state_entry() { state s_config; } }
 +
 +
state s_config
 +
{
 +
    state_entry()
 +
    {
 +
        config_init();
 +
        string item;
 +
        ConfigCards = [];
 +
        integer n = llGetInventoryNumber(INVENTORY_NOTECARD);
 +
        while (n-- > 0) { item = llGetInventoryName(INVENTORY_NOTECARD, n); if (llSubStringIndex(item, ConfigNotecardSuffix) != -1) { ConfigCards += [item]; }}
 +
        ConfigCardIndex = 0;
 +
        if (next_card()) { llSetTimerEvent(ConfigTimeout); }
 +
        else if (ConfigRequired) { say("Configuration notecard missing."); state s_configRetry; }
 +
        else { state s_active; }
 +
    }
 +
 +
    dataserver(key query_id, string data)
 +
    {
 +
        if (query_id == ConfigRequestID)
 +
        {
 +
            if (data == EOF) { if (! next_card()) { config_done(); state s_active; } }
 +
            else { config_parse(data, ConfigCardName, ConfigLineIndex); ConfigRequestID = llGetNotecardLine(ConfigCardName, ++ConfigLineIndex); llSetTimerEvent(ConfigTimeout); }
 +
        }
 +
    }
 +
 +
    timer() { say("Dataserver time out: touch to retry"); state s_configRetry; }
 +
 +
    on_rez(integer num) { state s_reconfig; }
 +
 +
    changed(integer change)
 +
    {
 +
        if (change & CHANGED_OWNER) { llResetScript(); }
 +
        if (change & CHANGED_INVENTORY) { state s_reconfig; }
 +
    }
 +
 +
    state_exit() { llSetTimerEvent(0); }
 +
}
 +
 +
state s_configRetry
 +
{
 +
    touch_start(integer tot) { if (llDetectedKey(0) == llGetOwner()) { state s_config; } }
 +
 +
    changed(integer change)
 +
    {
 +
        if (change & CHANGED_OWNER) { llResetScript(); }
 +
        if (change & CHANGED_INVENTORY) { state s_config; }
 +
    }
 +
}
 +
 +
state s_unconfigured
 +
{
 +
    state_entry() { llSetText("Configuration missing", <1.0,1.0,1.0>, 1.0); }
 +
 +
    changed(integer change)
 +
    {
 +
        if (change & CHANGED_OWNER) { llResetScript(); }
 +
        if (change & CHANGED_INVENTORY) { state s_reconfig; }
 +
    }
 +
 +
    state_exit() { llSetText("", <1.0,1.0,1.0>, 1.0); }
 +
}
 +
 +
state s_active
 +
{
 +
    state_entry()
 +
    {
 +
        llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
 
         llListenRemove(gListenID);
 
         llListenRemove(gListenID);
 
         gListenID = llListen(communicationsChannel, "", "", "");
 
         gListenID = llListen(communicationsChannel, "", "", "");
 
         init1();
 
         init1();
 
         init2();
 
         init2();
         llOwnerSay("
+
         say("
 
             Initialisation complete!
 
             Initialisation complete!
 
             SIMchat online!
 
             SIMchat online!
Line 171: Line 290:
 
         llSetObjectDesc(prim_desc);
 
         llSetObjectDesc(prim_desc);
 
         particle_parameters = [
 
         particle_parameters = [
             PSYS_PART_START_SCALE, <0.02, 0.02, FALSE>, PSYS_PART_END_SCALE, <0.5,0.5, FALSE>,
+
             PSYS_PART_START_SCALE,<0.02,0.02,FALSE>,PSYS_PART_END_SCALE,<0.5,0.5, FALSE>,
             PSYS_PART_START_COLOR, <0.94118, 0.00000, 0.23529>,   PSYS_PART_END_COLOR, <1,0,0>,
+
             PSYS_PART_START_COLOR,<0.94118,0.00000,0.23529>,PSYS_PART_END_COLOR,<1,0,0>,
             PSYS_PART_START_ALPHA, (float) 0.5,         PSYS_PART_END_ALPHA, (float) 0.0,
+
             PSYS_PART_START_ALPHA,(float)0.5,PSYS_PART_END_ALPHA,(float)0.0,
             PSYS_SRC_BURST_PART_COUNT, (integer)   4,
+
             PSYS_SRC_BURST_PART_COUNT,(integer)4,
             PSYS_SRC_BURST_RATE,         (float) 7.5,
+
             PSYS_SRC_BURST_RATE,(float)7.5,
             PSYS_PART_MAX_AGE,           (float) 0.5,
+
             PSYS_PART_MAX_AGE,(float)0.5,
             PSYS_SRC_PATTERN, (integer) 1,
+
             PSYS_SRC_PATTERN,(integer)1,
             PSYS_PART_FLAGS, (integer) ( 0
+
             PSYS_PART_FLAGS,(integer)(0 |PSYS_PART_INTERP_COLOR_MASK
                | PSYS_PART_INTERP_COLOR_MASK
+
                                        |PSYS_PART_INTERP_SCALE_MASK
            | PSYS_PART_INTERP_SCALE_MASK
+
                                        |PSYS_PART_EMISSIVE_MASK
            | PSYS_PART_EMISSIVE_MASK
+
                                        |PSYS_PART_FOLLOW_SRC_MASK
 
+
                                        |PSYS_PART_TARGET_POS_MASK)];
            | PSYS_PART_FOLLOW_SRC_MASK
+
            | PSYS_PART_TARGET_POS_MASK
+
            )
+
                ];
+
 
+
 
         if ( AUTO_START ) llParticleSystem( particle_parameters );
 
         if ( AUTO_START ) llParticleSystem( particle_parameters );
 
     }
 
     }
Line 193: Line 307:
 
     on_rez(integer start_param)
 
     on_rez(integer start_param)
 
     {
 
     {
         init1();
+
         say("Headset resetting...");
        init2();
+
 
         llResetScript();
 
         llResetScript();
 
     }
 
     }
  
     attach(key id)
+
     touch_start(integer total_number)
 
     {
 
     {
         if (id)
+
         if (llDetectedKey(0) == llGetOwner())
 
         {
 
         {
             llOwnerSay("Headset has been reset.");
+
             user = llGetOwner();
             llResetScript();
+
             llDialog(user, menu_info_1 + menu_info_2,mainmenu,mChannel);
 +
            llListen(mChannel,llKey2Name(user),user,"");
 
         }
 
         }
 
    }
 
 
    touch_start(integer total_number)
 
    {
 
        user = llDetectedKey(0);
 
        llDialog(user, menu_info_1 + menu_info_2,mainmenu,mChannel);
 
        llListen(mChannel,llKey2Name(user),user,"");
 
 
     }
 
     }
  
Line 226: Line 332:
 
                 Y = (integer)Where.y;
 
                 Y = (integer)Where.y;
 
                 Z = (integer)Where.z;
 
                 Z = (integer)Where.z;
                 SLURL = "SLURL for this location is:\n" + "http://slurl.com/secondlife/" + Name + "/" + (string)X + "/" + (string)Y + "/" + (string)Z + "/";
+
                 SLURL = "SLURL for this location is:\n\n" + "http://slurl.com/secondlife/" + Name + "/" + (string)X + "/" + (string)Y + "/" + (string)Z + "/";
 
                 llSay(0, SLURL);
 
                 llSay(0, SLURL);
 
                 return;
 
                 return;
 
             }
 
             }
             if(message=="SIMchatOFF")
+
             else if(message=="SIMchatOFF")
 
             {
 
             {
 
                 state sleeping;
 
                 state sleeping;
 
                 return;
 
                 return;
 
             }
 
             }
             if(message=="Chat info")
+
             else if(message=="Chat info")
 
             {
 
             {
 
                 llOwnerSay("Type /" + (string)communicationsChannel + " and then your message to talk to people that have their channel set the same.");
 
                 llOwnerSay("Type /" + (string)communicationsChannel + " and then your message to talk to people that have their channel set the same.");
 
                 return;
 
                 return;
 
             }
 
             }
             if(message=="Chat howto")
+
             else if(message=="Chat howto")
 
             {
 
             {
                 llOwnerSay("Type in local chat: /" + (string)communicationsChannel + " ...
+
                 llOwnerSay("Type in local chat: /" + (string)communicationsChannel + " ... \nFor example: /" + (string)communicationsChannel + " hello, my name is Nick. \nEverybody in the same SIM using this SIMchat function on channel " + (string)communicationsChannel + " will be able to read what you write.\nRemember to use channel" + (string)communicationsChannel + "for anything you want to write with this SIMchat-tool. Local chat is limited to 20m radius and will not be encrypted.");
                    For example: /" + (string)communicationsChannel + " hello, my name is Nick.
+
                        Everybody in the same SIM using this SIMchat function on channel " + (string)communicationsChannel + " will be able to read what you write.
+
                            Remember to use channel" + (string)communicationsChannel + "for anything you want to write with this SIMchat-tool. Local chat is limited to 20m radius.");
+
 
                 return;
 
                 return;
 
             }
 
             }
             if(message=="RegionCount")
+
             else if(message=="RegionCount")
 
             {
 
             {
 
                 llOwnerSay("  Region count is now:" + (string)llGetRegionAgentCount());
 
                 llOwnerSay("  Region count is now:" + (string)llGetRegionAgentCount());
Line 270: Line 373:
 
                 llOwnerSay((string)name + " said: " + (string)message);
 
                 llOwnerSay((string)name + " said: " + (string)message);
 
             }
 
             }
 +
        }
 +
    }
 +
 +
    control(key id,integer held, integer change)
 +
    {
 +
        return;
 +
    }
 +
 +
    run_time_permissions(integer perms)
 +
    {
 +
        if (perms & PERMISSION_TAKE_CONTROLS)
 +
        {
 +
            llTakeControls(CONTROL_UP,TRUE,TRUE);
 
         }
 
         }
 
     }
 
     }
Line 288: Line 404:
 
         {
 
         {
 
             llOwnerSay("SIMchat back online! Touch headset for further informations.");
 
             llOwnerSay("SIMchat back online! Touch headset for further informations.");
             state default;
+
             state s_active;
 
         }
 
         }
 
     }
 
     }
Line 300: Line 416:
 
[[#top|Go to top!]]
 
[[#top|Go to top!]]
  
 +
==Configuration-Notecard: (name must contain ".cfg", for example "config.cfg", "headset.cfg") ==
 +
<lsl>
 +
//version from 0.0 to 255.255
 +
//comments by leading "//"
 +
//
 +
strhex = 123456789ABCDEF
 +
channel = 9
 +
password = P@ssw0rd
 +
version = 0.3
 +
signature = ENC
 +
dialog = 8989
 +
debug = 1
 +
</lsl>
 
</div></div>
 
</div></div>

Revision as of 11:20, 5 August 2010

Check out all my other projects!


SIMchat headset

General information:

This script is being provided "as is". It's been thoroughly tested in May 2010.

If you want to use it, you should drop it into a childprim of a linkset. This setup will give you the possibilty to forward your chat on a certain channel within the whole region your are in and encrypt it. Just make sure anybody you want to talk to has the same variables as you do.

You can turn off the SIMchat function (if you are being spammed on that channel...you could of course change the channel, too), get a SLURL to your current location, get the current count of avatars within the region or get a quick reminder of the currently-used-channel and a howto by touching the childprim containing this script.


This is what it could look like. Shown in the picture is the active state of the headset with blinking particle light.
Yellow marker shows the main prim, red marker shows the invisible childprim containing the script.


Go to top!

Script:

<lsl> string ProtocolSignature; float ProtocolVersion; string Password; integer communicationsChannel; string Header; string strHex; integer Debug; integer listener; integer gListenID = -1; integer X; integer Y; integer Z; key user; list mainmenu = ["RegionCount","SLURL","SIMchatOFF","Chat info","Chat howto"]; integer ConfigRequired = TRUE; string ConfigNotecardSuffix = ".cfg"; float ConfigTimeout = 60.0; integer ConfigLineIndex; key ConfigRequestID; list ConfigCards; string ConfigCardName; integer ConfigCardIndex; string Name; string SLURL; string menu_info_1 = "

   This popup shows up because you touched your SIMchat headset.
       ";

string menu_info_2 = "Choose one of the following."; integer mChannel = 8989; string prim_name; string prim_desc; vector Where; string CONTROLLER_ID = "A"; integer AUTO_START = TRUE; list particle_parameters=[];

config_init() {

   strHex = "0123456789ABCDEF";
   Password = "P@ssw0rd";
   ProtocolVersion = 0.3;
   ProtocolSignature = "ENC";
   mChannel = 8989;
   communicationsChannel = 9;

}

config_dump() {

   say("You are using channel " + (string)communicationsChannel + " for chatting.");
   say("Your password is: " + Password);
   say("Your protocol version is " + (string)ProtocolVersion + ", your protocol signature " + ProtocolSignature + " and your stringHex is " + strHex);
   say("The menu for the dialog-channel is: " + (string)mChannel);

}

config_parse(string str, string cardName, integer lineNum) {

   str = llStringTrim(str, STRING_TRIM_HEAD);
   if (llGetSubString(str,0,0) == "//") {
       return;
   }
   list ldata  = llParseStringKeepNulls(str, ["="], [""]);
   string cmd  = llToUpper(llStringTrim(llList2String(ldata,0),STRING_TRIM));
   string arg1 = llStringTrim(llList2String(ldata,1),STRING_TRIM);
   if (cmd == "STRHEX") { strHex = arg1; }
   else if (cmd == "CHANNEL") { communicationsChannel = (integer)arg1; }
   else if (cmd == "PASSWORD") { Password = arg1; }
   else if (cmd == "VERSION") { ProtocolVersion = (float)arg1; }
   else if (cmd == "SIGNATURE") { ProtocolSignature = arg1; }
   else if (cmd == "DIALOG") { mChannel = (integer)arg1; }
   else if (cmd == "debug") { Debug = (integer) arg1; }

}

config_done() { if (Debug) { config_dump(); } say("Config done"); }

say(string str) { llOwnerSay(llGetScriptName() + ": " + str); }

debug(string str) { if (Debug) { say(str); } }

integer next_card() {

   if (ConfigCardIndex >= llGetListLength(ConfigCards)) {
       ConfigCards = [];
       return (FALSE);
   }
   ConfigLineIndex = 0;
   ConfigCardName = llList2String(ConfigCards, ConfigCardIndex);
   ConfigCardIndex++;
   ConfigRequestID = llGetNotecardLine(ConfigCardName, ConfigLineIndex);
   say("Reading " + ConfigCardName);
   return (TRUE);

}

string error(string message) {

   if(Debug) llSay(DEBUG_CHANNEL, message);
   return "";

}

string decrypt(string password, string message) {

   integer signatureLength = llStringLength(ProtocolSignature);
   integer headerLength = signatureLength + 12;
   if(llStringLength(message) < signatureLength + 44)
       return error("Too small for secret message.");
   if(llSubStringIndex(message, ProtocolSignature) != 0)
       return error("Unknown protocol.");
   integer index = signatureLength;
   string major = "0x" + llGetSubString(message, index, ++index);
   string minor = "0x" + llGetSubString(message, ++index, ++index);
   float version = (float)((string)((integer)major) + "." + (string)((integer)minor));
   if(version != ProtocolVersion)
       return error("Unknown version.");
   integer nonce = (integer)("0x" + llGetSubString(message, ++index, index + 7));
   message = llGetSubString(message, headerLength, -1);
   string oneTimePad = llMD5String(password, nonce);
   while(llStringLength(oneTimePad) < (llStringLength(message) / 2 * 3))
   oneTimePad += llMD5String(oneTimePad, nonce);
   oneTimePad = llStringToBase64(oneTimePad);
   message = llXorBase64StringsCorrect(message, oneTimePad);
   message = llBase64ToString(message);
   string digest = llGetSubString(message, 0, 31);
   message = llGetSubString(message, 32, -1);
   if(llMD5String(message, nonce) != digest)
       return error("Message digest was not valid.");
   return message;

}

string hex(integer value) {

   integer digit = value & 0xF;
   string text = llGetSubString(strHex, digit, digit);
   value = (value >> 4) & 0xfffFFFF;
   integer odd = TRUE;
   while(value)
   {
       digit = value & 0xF;
       text = llGetSubString(strHex, digit, digit) + text;
       odd = !odd;
       value = value >> 4;
   }
   if(odd)
       text = "0" + text;
   return text;

}

string encrypt(string password, string message) {

   integer nonce = (integer)llFrand(0x7FFFFFFF);
   message = llMD5String(message, nonce) + message;
   string oneTimePad = llMD5String(password, nonce);
   integer count = (llStringLength(message) - 1) / 32;
   if(count) do oneTimePad += llMD5String(oneTimePad, nonce);
   while(--count);
   return Header + llGetSubString("00000000" + hex(nonce), -8, -1) + llXorBase64StringsCorrect(llStringToBase64(message), llStringToBase64(oneTimePad));

}

init1() {

   list versions = llParseString2List((string)ProtocolVersion, ["."], []);
   string minor = llList2String(versions, 1);
   integer p = 0;
   while(llGetSubString(minor, --p, p) == "0");
   Header = ProtocolSignature + hex(llList2Integer(versions, 0)) + hex((integer)llGetSubString(minor, 0xFF000000, p));

}

init2() {

   if(listener != 0)
   {
       llListenRemove(listener);
       listener = 0;
   }
   listener = llListen(communicationsChannel, "", NULL_KEY, "");

}

default {

   state_entry()
   {
       llSetText("", <1.0,1.0,1.0>, 1.0);
       state s_config;
   }

}

state s_reconfig { state_entry() { state s_config; } }

state s_config {

   state_entry()
   {
       config_init();
       string item;
       ConfigCards = [];
       integer n = llGetInventoryNumber(INVENTORY_NOTECARD);
       while (n-- > 0) { item = llGetInventoryName(INVENTORY_NOTECARD, n); if (llSubStringIndex(item, ConfigNotecardSuffix) != -1) { ConfigCards += [item]; }}
       ConfigCardIndex = 0;
       if (next_card()) { llSetTimerEvent(ConfigTimeout); }
       else if (ConfigRequired) { say("Configuration notecard missing."); state s_configRetry; }
       else { state s_active; }
   }

   dataserver(key query_id, string data)
   {
       if (query_id == ConfigRequestID)
       {
           if (data == EOF) { if (! next_card()) { config_done(); state s_active; } }
           else { config_parse(data, ConfigCardName, ConfigLineIndex); ConfigRequestID = llGetNotecardLine(ConfigCardName, ++ConfigLineIndex); llSetTimerEvent(ConfigTimeout); }
       }
   }

   timer() { say("Dataserver time out: touch to retry"); state s_configRetry; }

   on_rez(integer num) { state s_reconfig; }

   changed(integer change)
   {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_reconfig; }
   }

   state_exit() { llSetTimerEvent(0); }

}

state s_configRetry {

   touch_start(integer tot) { if (llDetectedKey(0) == llGetOwner()) { state s_config; } }

   changed(integer change)
   {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_config; }
   }

}

state s_unconfigured {

   state_entry() { llSetText("Configuration missing", <1.0,1.0,1.0>, 1.0); }

   changed(integer change)
   {
       if (change & CHANGED_OWNER) { llResetScript(); }
       if (change & CHANGED_INVENTORY) { state s_reconfig; }
   }

   state_exit() { llSetText("", <1.0,1.0,1.0>, 1.0); }

}

state s_active {

   state_entry()
   {
       llRequestPermissions(llGetOwner(), PERMISSION_TAKE_CONTROLS);
       llListenRemove(gListenID);
       gListenID = llListen(communicationsChannel, "", "", "");
       init1();
       init2();
       say("
           Initialisation complete!
           SIMchat online!
           Type /" + (string)communicationsChannel + " and then your message to talk to people that have their channel set the same.
           Touch headset for options.");
       prim_name = "SIMchat(" + llKey2Name(llGetOwner()) + ")";
       prim_desc = "Last reset was: " + llGetTimestamp();
       llSetObjectName(prim_name);
       llSetObjectDesc(prim_desc);
       particle_parameters = [
           PSYS_PART_START_SCALE,<0.02,0.02,FALSE>,PSYS_PART_END_SCALE,<0.5,0.5, FALSE>,
           PSYS_PART_START_COLOR,<0.94118,0.00000,0.23529>,PSYS_PART_END_COLOR,<1,0,0>,
           PSYS_PART_START_ALPHA,(float)0.5,PSYS_PART_END_ALPHA,(float)0.0,
           PSYS_SRC_BURST_PART_COUNT,(integer)4,
           PSYS_SRC_BURST_RATE,(float)7.5,
           PSYS_PART_MAX_AGE,(float)0.5,
           PSYS_SRC_PATTERN,(integer)1,
           PSYS_PART_FLAGS,(integer)(0 |PSYS_PART_INTERP_COLOR_MASK
                                       |PSYS_PART_INTERP_SCALE_MASK
                                       |PSYS_PART_EMISSIVE_MASK
                                       |PSYS_PART_FOLLOW_SRC_MASK
                                       |PSYS_PART_TARGET_POS_MASK)];
       if ( AUTO_START ) llParticleSystem( particle_parameters );
   }
   on_rez(integer start_param)
   {
       say("Headset resetting...");
       llResetScript();
   }
   touch_start(integer total_number)
   {
       if (llDetectedKey(0) == llGetOwner())
       {
           user = llGetOwner();
           llDialog(user, menu_info_1 + menu_info_2,mainmenu,mChannel);
           llListen(mChannel,llKey2Name(user),user,"");
       }
   }
   listen(integer channel, string name, key id, string message)
   {
       if(id==user)
       {
           if(message=="SLURL")
           {
               Name = llGetRegionName();
               Where = llGetPos();
               X = (integer)Where.x;
               Y = (integer)Where.y;
               Z = (integer)Where.z;
               SLURL = "SLURL for this location is:\n\n" + "http://slurl.com/secondlife/" + Name + "/" + (string)X + "/" + (string)Y + "/" + (string)Z + "/";
               llSay(0, SLURL);
               return;
           }
           else if(message=="SIMchatOFF")
           {
               state sleeping;
               return;
           }
           else if(message=="Chat info")
           {
               llOwnerSay("Type /" + (string)communicationsChannel + " and then your message to talk to people that have their channel set the same.");
               return;
           }
           else if(message=="Chat howto")
           {
               llOwnerSay("Type in local chat: /" + (string)communicationsChannel + " ... \nFor example: /" + (string)communicationsChannel + " hello, my name is Nick. \nEverybody in the same SIM using this SIMchat function on channel " + (string)communicationsChannel + " will be able to read what you write.\nRemember to use channel" + (string)communicationsChannel + "for anything you want to write with this SIMchat-tool. Local chat is limited to 20m radius and will not be encrypted.");
               return;
           }
           else if(message=="RegionCount")
           {
               llOwnerSay("   Region count is now:" + (string)llGetRegionAgentCount());
               return;
           }
           else
           {
               //do nothing
           }
       }
       if (channel == communicationsChannel)
       {
           if (id == llGetOwner())
           {
               llRegionSay(communicationsChannel, encrypt(Password, message));
               llOwnerSay("You said: " + message);
           }
           if (llGetAgentSize(id) == ZERO_VECTOR)
           {
               string message = decrypt(Password, message);
               llOwnerSay((string)name + " said: " + (string)message);
           }
       }
   }
   control(key id,integer held, integer change)
   {
       return;
   }
   run_time_permissions(integer perms)
   {
       if (perms & PERMISSION_TAKE_CONTROLS)
       {
           llTakeControls(CONTROL_UP,TRUE,TRUE);
       }
   }

}

state sleeping {

   state_entry()
   {
       llOwnerSay("SIMchat offline! Touch headset to go back online.");
       particle_parameters = [];
       if ( AUTO_START ) llParticleSystem( particle_parameters );
   }
   touch_start(integer n)
   {
       if (llDetectedKey(0) == llGetOwner())
       {
           llOwnerSay("SIMchat back online! Touch headset for further informations.");
           state s_active;
       }
   }
   on_rez(integer start_param)
   {
       llResetScript();
   }

} </lsl> Go to top!

Configuration-Notecard: (name must contain ".cfg", for example "config.cfg", "headset.cfg")

<lsl> //version from 0.0 to 255.255 //comments by leading "//" // strhex = 123456789ABCDEF channel = 9 password = P@ssw0rd version = 0.3 signature = ENC dialog = 8989 debug = 1 </lsl>