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

From Second Life Wiki
Jump to navigation Jump to search
m (color - yay!)
Line 420: Line 420:




Name this notecard with an ending ".cfg", for example "SIMchat headset.cfg" or "config.cfg".
Name this notecard with an <font color="red">ending ".cfg"</font>, for example "SIMchat headset.cfg" or "config.cfg".
Commands (like "strhex", "channel", "password", ...) are NOT case sensitive, the values for the commands however ARE case sensitive.
Commands (like "strhex", "channel", "password", ...) are NOT case sensitive, the values for the commands however ARE case sensitive.
It doesn't matter if you write "=" or " = ", both will be fine.
It doesn't matter if you write "=" or " = ", both will be fine.

Revision as of 16:58, 26 May 2010

SIMchat headset

SCRIPT:


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


Go to top! <lsl> string ProtocolSignature; float ProtocolVersion; // can range from 0.0 to 255.255 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=[]; list target_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) { llSay(0, str); }

debug(string str) { if (Debug) { say(llGetScriptName() + ": " + 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()
   {
       llListenRemove(gListenID);
       gListenID = llListen(communicationsChannel, "", "", "");
       init1();
       init2();
       llOwnerSay("
           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)
   {
       init1();
       init2();
       llResetScript();
   }
   attach(key id)
   {
       if (id)
       {
           llOwnerSay("Headset has been reset.");
           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;
           }
           if(message=="SIMchatOFF")
           {
               state sleeping;
               return;
           }
           if(message=="Chat info")
           {
               llOwnerSay("Type /" + (string)communicationsChannel + " and then your message to talk to people that have their channel set the same.");
               return;
           }
           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;
           }
           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);
           }
       }
   }

}

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>

NOTECARD:


Name this notecard with an ending ".cfg", for example "SIMchat headset.cfg" or "config.cfg". Commands (like "strhex", "channel", "password", ...) are NOT case sensitive, the values for the commands however ARE case sensitive. It doesn't matter if you write "=" or " = ", both will be fine.


Go to top! <lsl> strhex = 123456789ABCDEF channel = 9 password = P@ssw0rd version = 0.3 signature = ENC dialog = 8989 debug = 1 </lsl>