User:Kireji Haiku/SIMchat headset
| Help Portal: |
Avatar | Bug Fixes | Communication | Community | Glossary | Land & Sim | Multimedia | Navigation | Object | Video Tutorials | Viewer | Wiki | Misc |
| LSL Portal | Functions | Events | Types | Operators | Constants | Flow Control | Script Library | Categorized Library | Tutorials |
SIMchat headset
SCRIPT:
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 together with the notecard. 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 in the setup-notecard 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 and the attached setup notecard.
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) { llOwnerSay(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.
- The value for "version" can range from 0.0 to 255.255
- Comments are by leading "//"
Go to top! <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>