aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/1chatter.lsl
diff options
context:
space:
mode:
authoronefang2019-06-30 11:16:41 +1000
committeronefang2019-06-30 11:16:41 +1000
commit1e5b49a55b7ebd03c6e57be993668f38ac20f841 (patch)
tree5cabe2fdc2e51e46bcb6f5ca5ce5110b5be3bdb0 /1chatter.lsl
parentLine wrapping is a thing, but not in notecards. (diff)
download1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.zip
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.gz
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.bz2
1ring-1e5b49a55b7ebd03c6e57be993668f38ac20f841.tar.xz
Include the actual source code this time.
Diffstat (limited to '1chatter.lsl')
-rw-r--r--1chatter.lsl1597
1 files changed, 1597 insertions, 0 deletions
diff --git a/1chatter.lsl b/1chatter.lsl
new file mode 100644
index 0000000..fdad263
--- /dev/null
+++ b/1chatter.lsl
@@ -0,0 +1,1597 @@
1
2// Library of generic functions for other scripts to use.
3
4string Version = "1chatter v0.1 dev version";
5
6// BEGIN boilerplate.
7integer DEBUG = FALSE;
8float Start;
9string ScriptName;
10key ScriptKey;
11key LibraryKey;
12key Owner;
13string URL;
14
15list Commands;
16integer cNAME = 0;
17integer cARGS = 1;
18integer cAUTH = 2;
19integer cSTRIDE = 3;
20
21list Scanners = []; // Current set of scans.
22integer nMENU = 0; // Menu that started the scan.
23integer nID = 1; // Avatar UUID we are doing this scan for.
24integer nTYPE = 2; // The type of scan.
25integer nTITLE = 3; // Title of the menu to show.
26integer nCMD = 4; // Command to run with the result.
27integer nSTRIDE = 5;
28
29// utilities commands, "l"ibrary.
30string lSEP = "$!#"; // Used to seperate lists when sending them as strings.
31integer lRESET = -1;
32integer lRESET_DONE = -2;
33integer lALIAS = -3;
34integer lALIAS_DONE = -4;
35integer lSETTING = -5;
36integer lSETTING_DONE = -6;
37integer lSUBSTITUTE = -7;
38integer lSUBSTITUTE_DONE = -8;
39integer lNEXT_WORD = -9;
40integer lNEXT_WORD_DONE = -10;
41integer lCONTROL = -13;
42integer lCONTROL_DONE = -14;
43integer lCMD = -15;
44integer lCMD_DONE = -16;
45integer lSETTINGS = -17;
46integer lSETTINGS_DONE = -18;
47integer lSCAN = -19;
48integer lDYNAMIC = -20;
49integer lMENU = -21;
50
51d(string m) {if (DEBUG) llInstantMessage(Owner, llGetScriptName() + ": " + m);}
52D(string m) {llRegionSay(DEBUG_CHANNEL, llGetScriptName() + ": " + m);}
53s(string m) {s(Owner, m);}
54s(key id, string m) {if (id == Owner) llOwnerSay(m); else llInstantMessage(id, m);}
55sendScript(integer cmd, list args) {sendScript(LibraryKey, cmd, ScriptName, args);}
56sendScript(key them, integer cmd, list args) {sendScript(them, cmd, inKey2Name(them), args);}
57sendScript(integer cmd, string name, list args) {sendScript(LibraryKey, cmd, name, args);}
58sendScript(key them, integer cmd, string name, list args)
59{
60 llMessageLinked(LINK_SET, cmd, llDumpList2String([ScriptKey, name] + args, lSEP), them);
61}
62sendPrim(key them, string cmd, list args) {osMessageObject(them, llDumpList2String([ScriptName] + args, lSEP));}
63
64string inKey2Name(key k)
65{
66 if (k == LibraryKey) return "1chatter";
67 integer i = llGetInventoryNumber(INVENTORY_SCRIPT);
68 while (i-- > 0)
69 {
70 string n = llGetInventoryName(INVENTORY_SCRIPT, i);
71 if (llGetInventoryKey(n) == k) return n;
72 }
73 return k;
74}
75
76integer uSubStringLastIndex(string hay, string pin)
77{
78 integer i2 = -1;
79 integer i;
80
81 if (pin == "")
82 return 0;
83 while (~i)
84 {
85 i = llSubStringIndex(llGetSubString(hay, ++i2, -1), pin);
86 i2 += i;
87 }
88 return i2;
89}
90
91integer listFindString(list lst, string name, integer stride)
92{
93 integer f = llListFindList(lst, [name]);
94 integer ix = f / stride;
95
96 // Round to nearest stride.
97 ix = ix * stride;
98
99 // Sanity check, make sure we found a name, not something else, else do it the slow way.
100 if ((-1 != f) && (ix != f))
101 {
102 integer l = llGetListLength(lst);
103 integer i;
104
105 f = -1;
106 for (i = 0; i < l; i += stride)
107 {
108 if (llList2String(lst, i) == name)
109 {
110 f = i;
111 i = l;
112 }
113 }
114 }
115 return f;
116}
117
118string getFor(string name)
119{
120 string fr;
121 integer i = llSubStringIndex(name, ".");
122
123 if (-1 != i)
124 fr = llGetSubString(name, 0, i);
125 return fr;
126}
127
128string noFor(string name)
129{
130 string r = name;
131 integer i = llSubStringIndex(name, ".");
132
133 if (-1 != i)
134 r = llGetSubString(name, i + 1, -1);
135 return r;
136}
137
138list validateName(string var, string type)
139{
140 list v = llParseStringKeepNulls(var, ["."], []);
141 if (2 != llGetListLength(v))
142 {
143 d("Invalid " + type + " name - " + var);
144 return ["", var];
145 }
146 var = llList2String(v, 1);
147 if ("setting" == type)
148 var = llToUpper(var);
149 return [llList2String(v, 0) + ".", var];
150}
151
152
153list Aliases;
154integer aALIAS = 0;
155integer aNAME = 1;
156integer aSTRIDE = 2;
157
158string alias(string n)
159{
160 integer a = listFindString(Aliases, llToLower(n), 2);
161 if (-1 != a)
162 n = llList2String(Aliases, a + 1);
163 return n;
164}
165
166
167// Access "f"rom some source -
168integer fINT = 0; // Internal, not sure we need this, means the same script.
169integer fCARD = 1; // Read from a note card.
170integer fCHAT = 2; // Chat channel.
171integer fLINK = 3; // Link message.
172integer fMENU = 4; // Menu, in reality this is just a chat channel.
173integer fRLV = 5; // RLV, again from a chat channel.
174integer fRELAY = 6; // RLV relay.
175integer fOSM = 7; // osMessageObject()
176integer fHTTP = 8; // From the web.
177
178// LSL security is entirely hopeless, see the notecard "1ring security".
179// "a"ccess level -
180integer aNONE = 0;
181integer aBOSS = 1;
182integer aTRUST = 2;
183integer aGROUP = 4;
184integer aBOSSES = 3;
185integer aWEARER = 8;
186integer aPRIV = 9;
187integer aMORE = 11;
188integer aMOST = 15;
189integer aPUBLIC = 16;
190integer aALL = 31;
191list Access =
192[
193 "NONE", 0,
194 "BOSS", 1,
195 "TRUST", 2,
196 "GROUP", 4,
197 "BOSSES", 3,
198 "WEARER", 8,
199 "PRIV", 9,
200 "MORE", 11,
201 "MOST", 15,
202 "PUBLIC", 16,
203 "ALL", 31
204];
205
206integer decodeAccess(string a)
207{
208 integer r = aALL;
209 integer f = listFindString(Access, a, 2);
210 if (-1 != f)
211 r = llList2Integer(Access, f + 1);
212 return r;
213}
214
215integer access(key id, string what, string fr, integer auth, integer bitch)
216{
217 list bosses = llParseString2List(getSetting(fr + "BOSS"), [","], []);
218 integer l = llGetListLength(bosses);
219 integer j = aPUBLIC;
220 integer i;
221 integer b = FALSE;
222
223//d("access " + id + " " + what + " FOR " + fr + " " + auth);
224 if (id == Owner) j = j | aWEARER | aGROUP;
225 if (llSameGroup(id)) j = j | aGROUP;
226 if ("1" == getSetting(fr + "PUBLIC")) j = j | aTRUST;
227 for (i = 0; i < l; ++i)
228 {
229 if (id == llList2Key(bosses, i))
230 {
231 j = j | aBOSS | aTRUST;
232 i = l;
233 }
234 }
235 bosses = llParseString2List(getSetting(fr + "TRUSTEE"), [","], []);
236 l = llGetListLength(bosses);
237 for (i = 0; i < l; ++i)
238 {
239 if (id == llList2Key(bosses, i))
240 {
241 j = j | aTRUST;
242 i = l;
243 }
244 }
245 bosses = llParseString2List(getSetting(fr + "BLOCKED"), [","], []);
246 l = llGetListLength(bosses);
247 for (i = 0; i < l; ++i)
248 {
249 if (id == llList2Key(bosses, i))
250 {
251 b = TRUE;
252 j = 0;
253 i = l;
254 }
255 }
256 j = auth & j;
257
258 if (bitch && (0 == j))
259 {
260 if (b)
261 {
262 s(Owner, "Blocked user " + llKey2Name(id) + " failed to use this.");
263 s(id, "You are blocked from using this.");
264 }
265 else
266 {
267 string who;
268
269 if (aNONE == auth) who = "no ";
270 if (auth & aWEARER) who += "'" + llKey2Name(llGetKey()) + "' owner, ";
271 if (auth & aBOSS) who += noFor(alias(fr + "boss")) + ", ";
272 if (auth & aTRUST) who += noFor(alias(fr + "trusted")) + ", ";
273 if (auth & aGROUP) who += "group, ";
274 if (auth & aPUBLIC) who += "public, ";
275 if (", " == llGetSubString(who, -2, -1))
276 who = llGetSubString(who, 0, -3) + " ";
277 i = uSubStringLastIndex(who, ", ");
278 if (-1 != i)
279 who = llGetSubString(who, 0, i + 1) + "and " + llGetSubString(who, i + 2, -1);
280 s(id, "The " + what + " is allowed only for " + who + "users.");
281 }
282 }
283 return j;
284}
285
286
287string TRUEs = "t1aopswy";
288string FALSEs = "f0bgnuz";
289integer isBool(string b)
290{
291 integer r = FALSE;
292
293 if ("" != b)
294 r = (-1 != llSubStringIndex(TRUEs, llToLower(llGetSubString(b, 0, 0))));
295 return r;
296}
297
298// "s"ettings list -
299list Settings;
300integer sNAME = 0;
301integer sTYPE = 1;
302integer sVALUE = 2;
303integer sAUTH = 3;
304integer sSTRIDE = 4;
305
306string getSetting(string var)
307{
308 list v = validateName(var, "setting");
309 var = llList2String(v, 0) + llList2String(v, 1);
310 string result;
311 integer f = listFindString(Settings, var, sSTRIDE);
312 if (-1 != f)
313 result = llList2String(Settings, f + sVALUE);
314 return result;
315}
316
317integer setSetting(key id, string var, string val, integer source)
318{
319 list v = validateName(var, "setting");
320 string fr = llList2String(v, 0);
321 var = fr + llList2String(v, 1);
322 integer f = listFindString(Settings, var, sSTRIDE);
323
324 if (-1 != f)
325 {
326 integer acs = access(id, var + " setting", fr, llList2Integer(Settings, f + sAUTH), TRUE);
327 if (0 == acs)
328 return acs;
329
330 string type = llToUpper(llList2String(Settings, f + sTYPE));
331
332 if ("Y" == type)
333 val = (string) isBool(val);
334 Settings = llListReplaceList(Settings, [val], f + sVALUE, f + sVALUE);
335//d("setSetting " + var + " = " + val);
336 if (fCARD != source)
337 {
338 string card = "." + llGetSubString(fr, 0, -2) + ".settings";
339 list data;
340 if (NULL_KEY != llGetInventoryKey(card))
341 data = llParseString2List(osGetNotecard(card), ["\n"], []);
342 integer l = llGetListLength(data);
343 string ar = llList2String(v, 1);
344
345// TODO - could use a bit more sophistication here, but it works as is.
346 for (f = 0; f < l; ++f)
347 {
348 list p = llParseString2List(llList2String(data, f), ["="], []);
349 string k = llList2String(p, 0);
350 if (k == var)
351 {
352 data = llListReplaceList(data, [k + "=" + val], f, f);
353 f = l + 1;
354 }
355 if (k == ar)
356 {
357 data = llListReplaceList(data, [k + "=" + val], f, f);
358 f = l + 1;
359 }
360 }
361 if (f == l)
362 data += [ar + "=" + val];
363 llRemoveInventory(card);
364 osMakeNotecard(card, data);
365d("setSetting " + var + " saved to " + card);
366 }
367 // Hopefully this wont result in any recursive setSettings calls.
368 doThing(id, "SET " + var + "=" + val, fr, llList2String(v, 1), val, source);
369
370 return acs;
371 }
372 return 0;
373}
374
375// TODO - Hmmm, only called once.
376readSettings(string card)
377{
378 float now = llGetTimeOfDay();
379 key boss = llGetOwner();
380 string fr = card + ".";
381
382 card = "." + card + ".settings";
383 if (NULL_KEY != llGetInventoryKey(card))
384 {
385 list d = llParseString2List(osGetNotecard(card), ["\n"], []);
386 integer l = llGetListLength(d);
387 integer i;
388
389 for (i = 0; i < l; ++i)
390 {
391 string cmd = alias(llList2String(d, i));
392 integer j = llSubStringIndex(cmd, "=");
393 if (-1 == j)
394 j = llSubStringIndex(cmd, " ");
395 if (-1 == j)
396 j = llStringLength(cmd);
397 string f = fr;
398 string var = llStringTrim(llGetSubString(cmd, 0, j - 1), STRING_TRIM);
399
400 if (-1 != llSubStringIndex(var, "."))
401 f = "";
402 setSetting(boss, f + var, llStringTrim(llGetSubString(cmd, j + 1, -1), STRING_TRIM), fCARD);
403 }
404 }
405 else
406 setSetting(boss, fr + "BOSS", Owner, fCARD);
407// d("Read " + card + " in " + (string) (llGetTimeOfDay() - now) + " seconds.");
408}
409
410dumpAliases(list s, string title)
411{
412 integer l = llGetListLength(s);
413 integer i;
414
415 d("v--------------- " + title);
416 for (i = 0; i < l; i += aSTRIDE)
417 {
418 d(
419 llList2String(s, i + aALIAS) + "~" +
420 llList2String(s, i + aNAME)
421 );
422 }
423 d("^---------------- LIBRARY");
424}
425
426dumpCommands(list s, string title)
427{
428 integer l = llGetListLength(s);
429 integer i;
430
431 d("v--------------- " + title);
432 for (i = 0; i < l; i += cSTRIDE)
433 {
434 d(
435 llList2String(s, i + cNAME) + "~" +
436 llList2String(s, i + cARGS) + "~" +
437 llList2String(s, i + cAUTH)
438 );
439 }
440 d("^---------------- LIBRARY");
441}
442
443dumpSettings(list s, string title)
444{
445 integer l = llGetListLength(s);
446 integer i;
447
448 d("v--------------- " + title);
449 for (i = 0; i < l; i += sSTRIDE)
450 {
451 d(
452 llList2String(s, i + sNAME) + "~" +
453 llList2String(s, i + sTYPE) + "~" +
454 llList2String(s, i + sVALUE) + "~" +
455 llList2String(s, i + sAUTH)
456 );
457 }
458 d("^---------------- LIBRARY");
459}
460
461
462
463// "e"vents
464float NextEvent = 307584000.0; // Ten years.
465list Events = [];
466integer eTIME = 0; // Time of it's next event.
467integer eSCRIPT = 1; // Rest of scriptlet after the delay command.
468integer eKEY = 2; // Key of script asking for this timer.
469integer eSTRIDE = 3;
470
471
472addEvent(float delay, string cmds, key id)
473{
474 float now = llGetTimeOfDay();
475 list ev = [now + delay, cmds, id];
476 integer f = findEventByID(cmds);
477
478 if (0.0 >= delay)
479 {
480 if (-1 != f)
481 Events = llListReplaceList(Events, [], f, f + eSTRIDE - 1);
482 }
483 else if (-1 == f)
484 Events += ev;
485 else
486 Events = llListReplaceList(Events, ev, f, f + eSTRIDE - 1);
487 Events = llListSort(Events, eSTRIDE, TRUE);
488 if (llList2Float(Events, 0) < NextEvent)
489 {
490 NextEvent = llList2Float(Events, 0);
491 llSetTimerEvent(NextEvent - now);
492 }
493}
494
495integer findEventByID(string id)
496{
497 integer f = llListFindList(Events, [id]);
498 integer ix = f / eSTRIDE;
499
500 // Round to nearest stride.
501 ix = ix * eSTRIDE;
502
503 if ((-1 != f) && ((ix + eSCRIPT) == f))
504 return f - eSCRIPT;
505 else
506 return -1;
507}
508
509
510// For marking up menu buttons.
511string Enclosed = "ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓵⓶⓷⓸⓹⓺⓻⓼⓽ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ";
512string DownsideUp = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
513string UpsideDown = "ɐqɔpǝɟɓɥᴉſʞןɯuodbɹsʇnʌʍxʎz0lᘔƐᔭ59Ɫ86∀qↃpƎℲ⅁HIᒋʞ⅂ꟽNOԀÒᴚS⊥∩ɅMX⅄Z";
514
515string enclosed(string text)
516{
517 string result;
518 integer l = llStringLength(text);
519 integer i;
520
521 for (i = 0; i < l; ++i)
522 {
523 string c = llGetSubString(text, i, i);
524 integer f = llSubStringIndex(DownsideUp, c);
525 if (-1 != f)
526 c = llGetSubString(Enclosed, f, f);
527 result += c;
528 }
529 return result;
530}
531
532string upsideDown(string text)
533{
534 string result;
535 integer i = llStringLength(text) - 1;
536
537 for (; i > -1; --i)
538 {
539 string c = llGetSubString(text, i, i);
540 integer f = llSubStringIndex(DownsideUp, c);
541 if (-1 != f)
542 c = llGetSubString(UpsideDown, f, f);
543 result += c;
544 }
545 return result;
546}
547
548
549// "m"enus
550string MainMenu;
551list Menus; // List of menus.
552integer mNAME = 0; // Name of menu.
553integer mTITLE = 1; // Title for the menu.
554integer mAUTH = 2; // Authorised users of menu - 0 = wearer, 1 = boss, 2 = all
555integer mENTRIES = 3; // | separated list of entries.
556integer mCMDS = 4; // | separated list of commands, matching the entries.
557integer mDYN = 5; // Dynamic menu, autoremove it when done.
558integer mSTRIDE = 6;
559
560integer findMenu(string name)
561{
562 return listFindString(Menus, name, mSTRIDE);
563}
564
565saveMenu(string name, string title, integer auth, string entries, string commands, integer dyn)
566{
567 integer f = findMenu(name);
568
569 if (-1 == f)
570 Menus += [name, title, auth, entries, commands, dyn];
571 else
572 {
573 if ("" == title)
574 title = llList2String(Menus, f + mTITLE);
575 if ("" == entries)
576 entries = llList2String(Menus, f + mENTRIES);
577 if ("" == commands)
578 commands = llList2String(Menus, f + mCMDS);
579 Menus = llListReplaceList(Menus, [name, title, auth, entries, commands, dyn], f, f + mSTRIDE - 1);
580 }
581}
582
583addMenuItem(string fr, string menu, string cmd, list args)
584{
585 integer f = findMenu(menu);
586
587 if (-1 != f)
588 {
589 string e = llList2String(Menus, f + mENTRIES);
590 string c = llList2String(Menus, f + mCMDS);
591
592 if (llGetListLength(args) < 2) args += [" "];
593 if ("" != e) e += "|";
594 if ("" != c) c += "|";
595 if ("TOGGLE" == cmd)
596 e += getToggle(fr, llList2String(args, 0));
597 else
598 e += llList2String(args, 0);
599 if ("TOMENU" == cmd) e += "…";
600 c += llDumpList2String(llListReplaceList(args, [], 0, 0), " ");
601 saveMenu(menu, llList2String(Menus, f + mTITLE), llList2Integer(Menus, f + mAUTH), e, c, llList2Integer(Menus, f + mDYN));
602 }
603}
604
605dynamicMenu(key id, string menu, string name, string title, string entries, string command)
606{
607//d("dynamicMenu " + menu + " -> " + name + "\n" + id + "@" + name);
608 saveMenu(id + "@" + name, title, aALL, entries, command, TRUE);
609 saveMuser(id, id + "@" + name, entries, -1, menu);
610 showMenu(id);
611}
612
613string getToggle(string fr, string item)
614{
615 string result = getSetting(fr + item);
616
617 if ("1" != result)
618 result = "☐";
619 else
620 result = "▣";
621 return result + " " + item;
622}
623
624toggleMenu(key id, string fr, list entries, integer e, integer m)
625{
626 integer on = 0;
627 string this = llList2String(entries, e);
628 string result = llStringTrim(llGetSubString(this, 1, -1), STRING_TRIM);
629 string t = llGetSubString(this, 0, 0);
630 if ("☐" == t) {t = "▣"; on = 1;}
631 else if ("▣" == t) {t = "☐"; on = 0;}
632 else if ("○" == t) {t = "◉"; on = 1;}
633 else if ("◉" == t) {t = "○"; on = 0;}
634 else {s(id, "Not a menu toggle - " + result); return;}
635 if (0 == setSetting(id, fr + result, (string) on, fMENU))
636 return;
637//d("toggleMenu " + result + " " + on);
638// TODO - if it's a radio group, make sure one and only on is turned on.
639 entries = llListReplaceList(entries, [t + " " + result], e, e);
640 saveMenu( llList2String(Menus, m + mNAME),
641 llList2String(Menus, m + mTITLE),
642 llList2Integer(Menus, m + mAUTH),
643 llDumpList2String(entries, "|"),
644 llList2String(Menus, m + mCMDS),
645 llList2Integer(Menus, m + mDYN));
646}
647
648
649// Menu "u"sers
650list Musers; // List of menu users.
651integer uKEY = 0; // Key of user.
652integer uCHAN = 1; // Listen channel for user.
653integer uLSTN = 2; // Listen handle.
654integer uCURRENT = 3; // Current menu of user.
655integer uENTRIES = 4; // Current entries for currently in flight menu.
656integer uPAGE = 5; // Current page of long menu of user.
657integer uSTACK = 6; // | separated menu stack list.
658integer uTIME = 7; // Timestamp of last menu shown.
659integer uSTRIDE = 8;
660
661// page = -1 means the menu items fit on a single dialog, otherwise it's the page of items.
662saveMuser(key avatar, string current, string entries, integer page, string stack)
663{
664 integer f = listFindString(Musers, avatar, uSTRIDE);
665 float n = llGetTimeOfDay();
666
667//d("saveMuser " + f + " " + llKey2Name(avatar) + " = " + current + " ~ " + stack + "\n" + entries);
668 if (-1 == f)
669 {
670 stack = "|";
671 integer c = (integer) ("0x" + llGetSubString((string) avatar, -4, -1)) + (integer) llFrand(12345);
672 integer h = llListen(c, "", avatar, "");
673 Musers += [avatar, c, h, current, entries, page, stack, n];
674 }
675 else
676 {
677 string old = llList2String(Musers, f + uSTACK);
678 if ("-1" == stack)
679 stack = old;
680 else if ("-1" == current)
681 {
682 list lst = llParseStringKeepNulls(old, ["|"], []);
683 current = llList2String(lst, 0);
684 lst = llListReplaceList(lst, [], 0, 0);
685 if (1 == llGetListLength(lst))
686 {
687 llListenRemove(llList2Integer(Musers, f + uLSTN));
688 Musers = llListReplaceList(Musers, [], f, f + uSTRIDE - 1);
689 return;
690 }
691 stack = llDumpList2String(lst, "|");
692 entries = "";
693 }
694 else if (-1 != page)
695 stack = old;
696 else if ("" == stack)
697 ;
698 else if ("" != old)
699 stack += "|" + old;
700 Musers = llListReplaceList(Musers, [avatar,
701 llList2Integer(Musers, f + uCHAN), llList2Integer(Musers, f + uLSTN),
702 current, entries, page, stack, n], f, f + uSTRIDE - 1);
703 }
704//d("saveMu2er " + avatar + " = " + current + " ~ " + stack);
705}
706
707removeMenu(string r)
708{
709 integer f = listFindString(Menus, r, mSTRIDE);
710 if (-1 != f)
711 Menus = llListReplaceList(Menus, [], f, f + mSTRIDE - 1);
712 else
713 D("removeMenu() " + r + " not found!");
714}
715
716lastMenu(key id, string r)
717{
718 saveMuser(id, "-1", "", -1, "");
719 if ("" != r) removeMenu(r);
720}
721
722dumpMenus(list menus)
723{
724 integer l = llGetListLength(menus);
725 integer i;
726 for (i = 0; i < l; i += mSTRIDE)
727 {
728 d(
729 llList2String(menus, i + mNAME) + "~" +
730 llList2String(menus, i + mTITLE) + "~" +
731 llList2String(menus, i + mAUTH) + "~" +
732 llList2String(menus, i + mENTRIES) + "~" +
733 llList2String(menus, i + mCMDS) + "~" +
734 llList2String(menus, i + mDYN)
735 );
736 }
737}
738
739showMenu(key id)
740{
741 integer f = listFindString(Musers, id, uSTRIDE);
742
743 if (-1 != f)
744 {
745 string menu = llList2String (Musers, f + uCURRENT);
746 integer chan = llList2Integer(Musers, f + uCHAN);
747 integer page = llList2Integer(Musers, f + uPAGE);
748 string fr;
749
750 if ("" != menu)
751 {
752 list v = validateName(menu, "menu");
753 fr = llList2String(v, 0);
754 menu = fr + llList2String(v, 1);
755 }
756 integer m = findMenu(menu);
757
758//d("showMenu " + menu + "... " + fr);
759 if (-1 != m)
760 {
761 integer dM = llList2Integer(Menus, m + mDYN);
762 if (dM)
763 fr = llGetSubString(fr, 37, -1);
764 string version = getSetting(fr + "VERSION");
765 list entries = llParseStringKeepNulls(llList2String(Menus, m + mENTRIES), ["|"], []);
766 list cmds = llParseStringKeepNulls(llList2String(Menus, m + mCMDS), ["|"], []);
767 string title = llList2String(Menus, m + mTITLE);
768
769 if (access(id, menu + " menu", fr, llList2Integer(Menus, m + mAUTH), TRUE))
770 {
771 integer l = llGetListLength(entries);
772 integer n;
773
774 if (llList2Integer(Menus, m + mDYN))
775 {
776 for (; n < l; ++n)
777 {
778 string button = llGetSubString(llList2String(entries, n), 0, 23);
779 entries = llListReplaceList(entries, [button], n, n);
780 }
781 n = l;
782 }
783 // Show missing items as upside down text.
784 // Show no access items enclosed.
785 for (; n < l; ++n)
786 {
787 string button = llGetSubString(llList2String(entries, n), 0, 23);
788 string first = llGetSubString(button, 0, 0);
789 string last = llGetSubString(button, -1, -1);
790 entries = llListReplaceList(entries, [button], n, n);
791 if (("▲" == first) || ("◄" == first) || ("►" == last))
792 {
793 }
794 else if ("…" == last)
795 {
796 string t = button;
797 button = llGetSubString(button, 0, -2);
798 integer o = findMenu(fr + button);
799 if (" " == llList2String(cmds, n))
800 {
801 if (-1 == o)
802 t = upsideDown(last + button);
803 else if (!access(id, t, fr, llList2Integer(Menus, o + mAUTH), FALSE))
804 t = enclosed(button + last);
805 entries = llListReplaceList(entries, [t], n, n);
806 }
807 }
808 else if (("☐" == first) || ("▣" == first) || ("○" == first) || ("◉" == first))
809 {
810 button = llStringTrim(llGetSubString(button, 1, -1), STRING_TRIM);
811 string t = getToggle(fr, button);
812 integer o = listFindString(Settings, fr + llToUpper(button), sSTRIDE);
813
814 if (-1 == o)
815 t = upsideDown(button + " " + llGetSubString(t, 0, 0));
816 else if (!access(id, t, fr, llList2Integer(Settings, o + sAUTH), FALSE))
817 t = enclosed(t);
818 entries = llListReplaceList(entries, [t], n, n);
819 }
820 else
821 {
822 string frr = fr;
823 string t = button;
824 string c = llList2String(cmds, n);
825 integer o = llSubStringIndex(c, " ");
826 if (-1 != o)
827 c = llGetSubString(c, 0, o - 1);
828 o = llSubStringIndex(c, ".");
829 if (-1 != o)
830 {
831 frr = llGetSubString(c, 0, o);
832 c = llGetSubString(c, o + 1, -1);
833 }
834 o = listFindString(Commands, frr + llToUpper(c), cSTRIDE);
835 if (("" == c) || (-1 == o))
836 t = upsideDown(t);
837 else if (!access(id, c, frr, llList2Integer(Commands, o + cAUTH), FALSE))
838 t = enclosed(t);
839 entries = llListReplaceList(entries, [t], n, n);
840 }
841 }
842 string mn = menu;
843 if (dM)
844 mn = llGetSubString(mn, 37, -1);
845
846 if (l > 11)
847 {
848 if (-1 == page)
849 page = 1;
850 integer offset = page * 9;
851 llDialog(id, version + " - " + mn + "\n\n" + title,
852 ["◄ Previous", "▲ Exit", "Next ►"]
853 + llList2List(entries, offset-3, offset-1)
854 + llList2List(entries, offset-6, offset-4)
855 + llList2List(entries, offset-9, offset-7),
856 chan);
857 }
858 else
859 {
860 llDialog(id, version + " - " + mn + "\n\n" + title,
861 llList2List(entries, -2, -1) + ["▲ Exit"]
862 + llList2List(entries, -5, -3)
863 + llList2List(entries, -8, -6)
864 + llList2List(entries, -11, -9),
865 chan);
866 }
867 saveMuser(id, menu, llDumpList2String(entries, "|"), page, "-1");
868 }
869 }
870 else if ("" != menu)
871 {
872 d("'" + menu + "' menu not found!");
873 if (DEBUG)
874 dumpMenus(Menus);
875 }
876 else
877 {
878 llListenRemove(llList2Integer(Musers, f + uLSTN));
879 Musers = llListReplaceList(Musers, [], f, f + uSTRIDE - 1);
880 }
881 }
882}
883
884// return TRUE if caller should showMenu()
885integer handleMenu(integer f, key id, string button)
886{
887 string cmd = button;
888 string data;
889 string menu = llList2String(Musers, f + uCURRENT);
890 string dM;
891 string fr;
892 list entries = llParseStringKeepNulls(llList2String(Musers, f + uENTRIES), ["|"], []);
893 list lst;
894 integer page = llList2Integer(Musers, f + uPAGE);
895 integer m = findMenu(menu); // This was already checked before it was stuffed into Musers.
896 integer e = 0;
897
898//d("handleMenu(" + llKey2Name(id) + "," + button + ") " + menu + " -> " + cmd);
899 if (llList2Integer(Menus, m + mDYN))
900 dM = menu;
901 // Find the corresponding command for this button.
902 lst = llParseStringKeepNulls(llList2String(Menus, m + mCMDS), ["|"], []);
903 if (("◄ Previous" != button) && ("▲ Exit" != button) && ("Next ►" != button))
904 {
905 if (llGetListLength(lst) != 1)
906 e = llListFindList(entries, [button]);
907 if (-1 == e)
908 D("handleMenu() button |" + button + "| not found, OOPS! Was looking in -\n"
909 + llList2String(Musers, f + uENTRIES));
910 else
911 cmd = llList2String(lst, e);
912 }
913
914 f = listFindString(Aliases, llToLower(cmd), 2);
915 if (-1 != f)
916 cmd = llList2String(Aliases, f + 1);
917 f = llSubStringIndex(cmd, "=");
918 if (-1 != f)
919 {
920 data = llGetSubString(cmd, f + 1, -1);
921 cmd = llGetSubString(cmd, 0, f - 1);
922 }
923 else
924 {
925 f = llSubStringIndex(cmd, " ");
926 if (-1 != f)
927 {
928 data = llGetSubString(cmd, f + 1, -1);
929 cmd = llGetSubString(cmd, 0, f - 1);
930 }
931 }
932
933 f = llSubStringIndex(cmd, ".");
934 if (-1 != f)
935 {
936 fr = llGetSubString(cmd, 0, f);
937 cmd = llGetSubString(cmd, f + 1, -1);
938 }
939 else
940 {
941 f = llSubStringIndex(menu, ".");
942 if (-1 != f)
943 {
944 fr = llGetSubString(menu, 0, f);
945 f = llSubStringIndex(fr, "@");
946 if (-1 != f)
947 fr = llGetSubString(fr, f + 1, -1);
948 }
949 }
950 cmd = llToUpper(llStringTrim(cmd, STRING_TRIM));
951 data = llStringTrim(data, STRING_TRIM);
952
953 if (!access(id, menu + " menu", fr, llList2Integer(Menus, m + mAUTH), TRUE))
954 return FALSE;
955
956 string first = llGetSubString(button, 0, 0);
957 string last = llGetSubString(button, -1, -1);
958
959d("handleMenu(" + llKey2Name(id) + "," + button + ") " + menu + " -> " + fr + cmd + " -> " + data);
960
961 // Check if it's a special menu item type.
962 // If this is a TOMENU entry and there was no command
963 if (("…" == last) && (" " == llList2String(lst, e)))
964 {
965 data = llGetSubString(button, 0, -2);
966 f = findMenu(fr + data);
967 if (-1 != f)
968 saveMuser(id, fr + data, "", -1, menu);
969 }
970 else if ("▲" == first)
971 {
972 f = doThing(id, menu + "->" + button, fr, cmd, data, fMENU);
973 lastMenu(id, dM);
974 return f;
975 }
976 else if ("◄" == first)
977 {
978 --page;
979 if (0 > page)
980 page = llGetListLength(entries) / 9;
981 saveMuser(id, menu, "", page, "");
982 }
983 else if ("►" == last)
984 {
985 ++page;
986 if (llGetListLength(entries) < ((page - 1) * 9))
987 page = 1;
988 saveMuser(id, menu, "", page, "");
989 }
990 else if (("☐" == first) || ("▣" == first) || ("○" == first) || ("◉" == first))
991 {
992 toggleMenu(id, fr, entries, e, m);
993 return FALSE;
994 }
995 else if ("MENU" == cmd)
996 {
997 if ("" == getFor(data))
998 data = fr + data;
999 saveMuser(id, data, "", -1, menu);
1000 }
1001 else
1002 {
1003 integer i = TRUE;
1004 f = listFindString(Commands, fr + cmd, cSTRIDE);
1005 if (-1 != f)
1006 i = doThing(id, menu + "->" + button, fr, cmd, data, fMENU);
1007 if ("" != dM)
1008 lastMenu(id, dM);
1009 return i;
1010 }
1011
1012 return TRUE;
1013}
1014
1015integer doThing(key id, string command, string fr, string cmd, string data, integer source)
1016{
1017 if ("*.." == fr) return TRUE;
1018 // Coz the card reader is about to send the lot anyway.
1019 if (fCARD == source) return TRUE;
1020 key them = getSetting(fr + "SCRIPTKEY");
1021//d("doThing(" + id + "," + command + " -> " + fr + " . " + cmd + " |" + data + "|");
1022
1023 if (("" == them) && ("*." != fr))
1024 D("Client script " + llGetSubString(fr, 0, -1) + " not found!\ndoThing("
1025 + id + " does " + command + " - " + fr + " " + cmd + " | " + data + ")");
1026 else
1027 {
1028 integer f = listFindString(Commands, fr + cmd, cSTRIDE);
1029 if (-1 != f)
1030 {
1031 if (0 == access(id, cmd + " command", fr, llList2Integer(Commands, f + cAUTH), TRUE))
1032 return TRUE;
1033 }
1034
1035 if ("*." == fr)
1036 them = NULL_KEY;
1037 if ("TIMER" == cmd)
1038 {
1039 list dt = llParseString2List(data, [" "], []);
1040 float time = llList2Float(dt, 0);
1041 dt = llListReplaceList(dt, [], 0, 0);
1042 addEvent(time, fr + llDumpList2String(dt, " "), id);
1043 }
1044 else
1045 {
1046 sendScript(them, lCMD, fr, [source, id, command, cmd, data]);
1047 return FALSE;
1048 }
1049 }
1050 return TRUE;
1051}
1052
1053// TODO - Could use this from the various "save*()" functions?
1054list saveThing(list in, integer stride, list add, integer l, string fr)
1055{
1056 if (l == stride)
1057 {
1058 string first = llList2String(add, 0);
1059
1060 if ("" != fr)
1061 {
1062 first = fr + first;
1063 add = llListReplaceList(add, [first], 0, 0);
1064 }
1065
1066 integer f = listFindString(in, first, stride);
1067
1068 if (-1 == f)
1069 in += add;
1070 else
1071 in = llListReplaceList(in, add, f, f + stride - 1);
1072 }
1073 else
1074 D("Wrong number of arguments, should be " + stride + " but is " + l + "!\n"
1075 + llDumpList2String(add, " ~ "));
1076
1077 return in;
1078}
1079
1080readTheme(string card)
1081{
1082 float now = llGetTimeOfDay();
1083 string cd = card;
1084
1085 card = "~" + cd + ".alias.data";
1086 if (NULL_KEY != llGetInventoryKey(card))
1087 Aliases += llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2);
1088 card = "~" + cd + ".command.data";
1089 if (NULL_KEY != llGetInventoryKey(card))
1090 Commands += llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2);
1091 card = "~" + cd + ".setting.data";
1092 if (NULL_KEY != llGetInventoryKey(card))
1093 Settings += llList2List(llParseStringKeepNulls(osGetNotecard(card), ["\n"], []), 0, -2);
1094// s("Read " + cd + " data in " + (string) (llGetTimeOfDay() - now) + " seconds.");
1095
1096 now = llGetTimeOfDay();
1097 card = "." + cd + ".theme";
1098 if (NULL_KEY != llGetInventoryKey(card))
1099 {
1100 list data = llParseString2List(osGetNotecard(card), ["\n", ";"], []);
1101 integer l = llGetListLength(data);
1102 integer i;
1103 string fr;
1104 string menu;
1105
1106 for (i = 0; i < l; ++i)
1107 {
1108 string line = llStringTrim(llList2String(
1109 llParseStringKeepNulls(llList2String(data, i), ["//", "#"], []), 0), STRING_TRIM);
1110 if ("" != line)
1111 {
1112 list args = llParseStringKeepNulls(line, ["|", ","], []);
1113 list first;
1114 integer m = llGetListLength(args);
1115 integer j;
1116
1117 for (j = 0; j < m; ++j)
1118 first += [llStringTrim(llList2String(args, j), STRING_TRIM)];
1119 args = first;
1120 first = llParseStringKeepNulls(llList2String(args, 0), [" ", "="], []);
1121
1122 string cmd = llToUpper(llStringTrim(llList2String(first, 0), STRING_TRIM));
1123
1124 first = llListReplaceList(first, [], 0, 0);
1125 args = [llDumpList2String(first, " ")] + llListReplaceList(args, [], 0, 0);
1126 m = llGetListLength(args);
1127
1128 if ("ALIAS" == cmd)
1129 {
1130 args = llListReplaceList(args, [llToLower(llList2String(args, 0))], 0, 0);
1131 Aliases = saveThing(Aliases, aSTRIDE, args, m, fr);
1132 }
1133 else if ("COMMAND" == cmd)
1134 Commands = saveThing(Commands, cSTRIDE,
1135 llListReplaceList(args, [decodeAccess(llList2String(args, 2))], cAUTH, cAUTH),
1136 m, fr);
1137 else if ("SETTING" == cmd)
1138 {
1139// TODO - should do isBool() here if needed.
1140 Settings = saveThing(Settings, sSTRIDE,
1141 llListReplaceList(args, [decodeAccess(llList2String(args, 3))], sAUTH, sAUTH),
1142 m, fr);
1143 }
1144 else if ("FOR" == cmd)
1145 {
1146 fr = llList2String(args, 0) + ".";
1147 menu = "";
1148 }
1149 else if ("MAIN" == cmd)
1150 MainMenu = fr + llList2String(args, 0);
1151 else if ("MENU" == cmd)
1152 {
1153 menu = llList2String(args, 0);
1154 if ("" != fr) menu = fr + menu;
1155 saveMenu(menu, llList2String(args, 1), decodeAccess(llList2String(args, 2)), "", "", FALSE);
1156 }
1157 else if (("BUTTON" == cmd) || ("TOGGLE" == cmd) || ("TOMENU" == cmd))
1158 addMenuItem(fr, menu, cmd, args);
1159 else
1160 addMenuItem(fr, menu, cmd, [llList2String(args, 0), cmd + " " + llDumpList2String(args, ",")]);
1161 }
1162 }
1163 d("Read " + card + " in " + (string) (llGetTimeOfDay() - now) + " seconds.");
1164 }
1165 if ("" == MainMenu)
1166 MainMenu = llList2String(Menus, 0);
1167}
1168
1169list wipe(list o, string fr, integer stride)
1170{
1171 return wipe(o, fr, stride, 0);
1172}
1173
1174list wipe(list o, string fr, integer stride, integer f)
1175{
1176 integer l = llGetListLength(o);
1177 integer i;
1178 list n;
1179
1180 for (i = 0; i < l; i += stride)
1181 {
1182 if ((fr + ".") != getFor(llList2String(o, i + f)))
1183 n += llList2List(o, i, i + stride - 1);
1184 }
1185 return n;
1186}
1187
1188key URLrequestID;
1189key URLCheckrequestID;
1190integer Attached;
1191float VelTime;
1192vector VelPos;
1193init()
1194{
1195 Attached = (0 != llGetAttached());
1196 if (Attached)
1197 {
1198 integer i = llGetInventoryNumber(INVENTORY_SCRIPT);
1199 while (i-- > 0)
1200 {
1201 string s = llGetInventoryName(INVENTORY_SCRIPT, i);
1202 if (osGetInventoryDesc(s) == "1chatter client")
1203 {
1204 d("RESETTING " + s);
1205 llResetOtherScript(s);
1206 }
1207 }
1208 }
1209 llListen(DEBUG_CHANNEL, "", NULL_KEY, "");
1210 VelTime = llGetTimeOfDay();
1211 VelPos = llGetPos();
1212 addEvent(300, "Musers", ScriptKey);
1213 URLrequestID = llRequestURL();
1214}
1215
1216default
1217{
1218 state_entry()
1219 {
1220 Start = llGetTimeOfDay();
1221 Owner = llGetOwner();
1222d("\n\n1chatter resetting client scripts @ " + (string) Start + "\n");
1223 ScriptName = llGetScriptName(); ScriptKey = llGetInventoryKey(ScriptName);
1224 LibraryKey = ScriptKey;
1225 init();
1226 }
1227
1228 on_rez(integer param)
1229 {
1230// TODO - should clear any listeners first.
1231 Musers = [];
1232 }
1233
1234 changed(integer change)
1235 {
1236// if (change & CHANGED_INVENTORY)
1237// llResetScript();
1238 if (change & CHANGED_OWNER)
1239 llResetScript();
1240 if (change & CHANGED_REGION)
1241 {
1242 llReleaseURL(URL);
1243 URL = "";
1244 URLrequestID = llRequestURL();
1245 }
1246 }
1247
1248 dataserver(key id, string data)
1249 {
1250 list input = llParseStringKeepNulls(data, [lSEP], []);
1251 string fr = llList2String(input, 0);
1252 string cmd = llList2String(input, 1);
1253
1254 if ("PIN" == fr)
1255 {
1256 fr = "*";
1257fr="1AOor2";
1258 cmd = "PIN";
1259 input = [fr, cmd, llList2String(input, 1)];
1260 }
1261 else
1262 id = llGetOwnerKey(id);
1263 doThing(id, llDumpList2String(input, "|"), fr + ".", cmd, llDumpList2String(llList2List(input, 2, -1), ","), fOSM);
1264 }
1265
1266 http_request(key id, string method, string body)
1267 {
1268 integer responseStatus = 400;
1269 string responseBody = "Unsupported method";
1270//d("HTTP " + method + " -> " + body);
1271 if (method == URL_REQUEST_DENIED)
1272 d("Error trying to get a URL - " + body);
1273 else if (method == URL_REQUEST_GRANTED)
1274 {
1275 URLrequestID = NULL_KEY;
1276 URL = body;
1277 llSleep(0.01); // The other scripts wont have recovered from the TP yet.
1278 sendScript(NULL_KEY, lCMD, "*.", [fHTTP, ScriptKey, method, "URL", URL]);
1279 // Check for dropped URL.
1280// llSetTimerEvent(30.0);
1281 }
1282 else if (method == "POST")
1283 {
1284 list bdy = llParseStringKeepNulls(llUnescapeURL(body), ["|"], []);
1285 sendScript(NULL_KEY, lCMD, "*.", [fHTTP, ScriptKey, method, llList2String(bdy, 0), body]);
1286// responseBody = llEscapeURL("|REGION" + llGetRegionName() + "|" + (string)llGetPos());
1287 responseStatus = 200;
1288 responseBody = "";
1289 llHTTPResponse(id, responseStatus, responseBody);
1290 }
1291 }
1292
1293 http_response(key id, integer status, list metaData, string body)
1294 {
1295 if (id == URLCheckrequestID)
1296 {
1297 URLCheckrequestID = NULL_KEY;
1298 if (status != 200)
1299 d("HTTP error code " + status + "\n" + body);
1300// else
1301// llOwnerSay("self check worked - " + body);
1302 }
1303 else if (id == NULL_KEY)
1304 d("Too many HTTP requests too fast!");
1305 }
1306
1307 // Handle commands from other scripts.
1308 link_message(integer sender_num, integer num, string message, key id)
1309 {
1310 if ((id != ScriptKey) && (id != NULL_KEY)) return;
1311 list input = llParseStringKeepNulls(message, [lSEP], []);
1312 key them = llList2Key(input, 0);
1313 string fr = llList2Key(input, 1);
1314
1315//d("linky " + num + " " + message + " " + fr);
1316 if (lRESET == num)
1317 {
1318 if ("" != fr)
1319 {
1320 Aliases = wipe(Aliases, fr, aSTRIDE);
1321 Commands = wipe(Commands, fr, cSTRIDE);
1322 Events = wipe(Events, fr, eSTRIDE, eSCRIPT);
1323 Menus = wipe(Menus, fr, mSTRIDE);
1324 Musers = wipe(Musers, fr, uSTRIDE, uSTACK);
1325 Settings = wipe(Settings, fr, sSTRIDE);
1326 readTheme(fr);
1327 Settings = saveThing(Settings, sSTRIDE, ["PREFIX", "S", fr, aALL], 4, fr + ".");
1328 Settings = saveThing(Settings, sSTRIDE, ["SCRIPTKEY", "K", them, aALL], 4, fr + ".");
1329 Settings = saveThing(Settings, sSTRIDE, ["VERSION", "S", "", aALL], 4, fr + ".");
1330 sendScript(them, lALIAS_DONE, Aliases);
1331 sendScript(them, lRESET_DONE, Settings);
1332 sendScript(them, lCMD, "*.", [fHTTP, ScriptKey, URL_REQUEST_GRANTED, "URL", URL]);
1333 }
1334// resetPrimShit();
1335 }
1336 else if (lSETTINGS == num)
1337 {
1338 if ("" != fr)
1339 {
1340 readSettings(fr);
1341 sendScript(them, lSETTINGS_DONE, Settings);
1342 }
1343 }
1344 else if (lSETTING == num)
1345 {
1346 if (id == ScriptKey)
1347 {
1348 integer source = llList2Integer(input, 2);
1349 key a = llList2Key(input, 3);
1350
1351 if ("" != fr)
1352 {
1353 integer l = llGetListLength(input);
1354 integer i;
1355
1356 for (i = 4; i < l; i += 2)
1357 {
1358 setSetting(a, fr + "." + llList2String(input, i),
1359 llList2String(input, i + 1), source);
1360 }
1361// TODO - track the listen handle, delete old one here.
1362 integer channel = (integer) getSetting("1ring.CHANNEL");
1363 if (channel)
1364 llListen(channel, "", "", "");
1365//dumpSettings(Settings, "SETTING");
1366 }
1367 }
1368 }
1369 else if (lCMD == num)
1370 {
1371 doThing(llList2Key(input, 3), llList2String(input, 4), fr + ".",
1372 llList2String(input, 5), llList2String(input, 6), llList2Integer(input, 2));
1373 }
1374 else if (lCMD_DONE == num)
1375 {
1376//d("lCMD_DONE " + llKey2Name(llList2Key(input, 3)) + " -> " + llList2String(input, 2) + "| returned " + llList2String(input, 4));
1377 if (0 != llList2Integer(input, 4))
1378 showMenu(llList2Key(input, 3));
1379 }
1380 else if (lDYNAMIC == num)
1381 {
1382 string menu = llList2String(input, 3);
1383 if ("" != menu)
1384 menu = fr + "." + menu;
1385 dynamicMenu(llList2Key(input, 2), menu, fr + "." + llList2String(input, 4),
1386 llList2String(input, 5), llList2String(input, 6), llList2String(input, 7));
1387 }
1388 else if (lMENU == num)
1389 {
1390//d("lMENU " + llKey2Name(llList2Key(input, 2)) + " -> " + fr + "." + llList2String(input, 3) + " " + llList2String(input, 4));
1391 key a = llList2String(input, 2);
1392 string mn = llList2String(input, 3);
1393 string t = llList2String(input, 4);
1394 string menu;
1395 integer f = listFindString(Musers, a, uSTRIDE);
1396 integer m = findMenu(fr + "." + mn);
1397
1398 if (-1 != f)
1399 menu = llList2String(Musers, f + uCURRENT);
1400 if (-1 != m)
1401 {
1402 if ("" != t)
1403 Menus = llListReplaceList(Menus, t, m + mTITLE, m + mTITLE);
1404 if ((fr + "." + mn) == menu)
1405 menu = "-1";
1406 saveMuser(a, fr + "." + mn, "", -1, menu);
1407 showMenu(a);
1408 }
1409 }
1410 else if (lSCAN == num)
1411 {
1412d("lSCAN " + llDumpList2String(input, " ~ "));
1413 key a = llList2Key(input, 2);
1414 integer f = listFindString(Musers, a, uSTRIDE);
1415
1416 if (-1 != f)
1417 {
1418 Scanners += [llList2String(Musers, f + uCURRENT), a] + llList2List(input, 3, -1);
1419 if (nSTRIDE == llGetListLength(Scanners))
1420 llSensor("", NULL_KEY, llList2Integer(input, 3), 100.0, PI);
1421 }
1422 }
1423 }
1424
1425 listen(integer channel, string name, key id, string message)
1426 {
1427//d("listen " + channel + ", " + llKey2Name(id) + "-> " + message + " " + llGetListLength(Musers));
1428 // See if it's a prefix on the prefix channel, strip off the prefix and feed the remains to doThing().
1429 if (channel == (integer) getSetting("1ring.CHANNEL"))
1430 {
1431d("listen 0ring.CHANNEL " + message);
1432 string p = getSetting("1ring.PREFIX");
1433 if (llGetSubString(message, 0, llStringLength(p) - 1) == p)
1434 {
1435 string button = llGetSubString(message, llStringLength(p), -1);
1436 list lst = llParseStringKeepNulls(llStringTrim(button, STRING_TRIM), [" "], []);
1437 string cmd = llList2String(lst, 0);
1438 string data = llDumpList2String(llListReplaceList(lst, [], 1, 1), " ");
1439d("listen 1ring.CHANNEL " + message + " -> " + button + " " + cmd + " " + data);
1440 doThing(id, message, "1ring.", cmd, data, fCHAT);
1441 return;
1442 }
1443 }
1444
1445 integer l = llGetListLength(Musers);
1446 integer i;
1447
1448 for (i = 0; i < l; i += uSTRIDE)
1449 {
1450 if (channel == llList2Integer(Musers, i + uCHAN))
1451 {
1452 if (handleMenu(i, id, message)) showMenu(id);
1453 return;
1454 }
1455 }
1456
1457 // Catch OhSillyThreatLevel messages.
1458 if (channel == DEBUG_CHANNEL)
1459 {
1460 key root = llList2Key(llGetObjectDetails(id, [OBJECT_ROOT]), 0);
1461 if ((llGetLinkKey(LINK_ROOT) == root) && ("OSSL Runtime Error: os" == llGetSubString(message, 0, 21)))
1462 {
1463 string function = llGetSubString(message, 20, -1);
1464 integer f = llSubStringIndex(function, " ");
1465 if (-1 != f)
1466 {
1467 if (" permission denied. Script creator is not in the list of users allowed to execute this function and prim owner also has no permission." == llGetSubString(function, f, -1))
1468 {
1469 llMessageLinked(LINK_SET, DEBUG_CHANNEL, llGetSubString(function, 0, f - 1), id);
1470 llRegionSay(DEBUG_CHANNEL, "OPENSIM SUCKS!");
1471 return;
1472 }
1473 }
1474 }
1475 }
1476 }
1477
1478 no_sensor()
1479 {
1480 if (0 != llGetListLength(Scanners))
1481 {
1482 key id = llList2Key(Scanners, nID);
1483 Scanners = llListReplaceList(Scanners, [], 0, nSTRIDE - 1);
1484 if (0 != llGetListLength(Scanners))
1485 llSensor("", NULL_KEY, llList2Integer(Scanners, nTYPE), 100.0, PI);
1486 showMenu(id);
1487 }
1488 }
1489
1490 sensor(integer num)
1491 {
1492 if (0 != llGetListLength(Scanners))
1493 {
1494 string cmd = llList2String(Scanners, nCMD);
1495 list items = [];
1496 list keys = [];
1497 key id = llList2Key(Scanners, nID);
1498 string menu = llList2String(Scanners, nMENU);
1499 integer i;
1500 for (i = 0; i < num; ++i)
1501 {
1502 items += [llGetSubString(llDetectedName(i), 0, 23)];
1503 keys += [cmd + " " + llDetectedKey(i)];
1504 }
1505 Scanners = llListReplaceList(Scanners, [], 0, nSTRIDE - 1);
1506 if (0 != llGetListLength(Scanners))
1507 llSensor("", NULL_KEY, llList2Integer(Scanners, nTYPE), 100.0, PI);
1508 dynamicMenu(id, menu, menu,
1509 llList2String(Scanners, nTITLE),
1510 llDumpList2String(items, "|"), llDumpList2String(keys, "|"));
1511 }
1512 }
1513
1514 timer()
1515 {
1516 float now = llGetTimeOfDay();
1517 integer i;
1518
1519 while (NextEvent <= now)
1520 {
1521 string script = llList2String(Events, eSCRIPT);
1522 key them = llList2Key(Events, eKEY);
1523
1524 Events = llListReplaceList(Events, [], 0, eSTRIDE - 1);
1525 if (0 != llGetListLength(Events))
1526 NextEvent = llList2Float(Events, 0);
1527 else
1528 NextEvent = 307584000.0;
1529 now = llGetTimeOfDay();
1530
1531 if ("Musers" == script)
1532 {
1533 integer f = llGetListLength(Musers);
1534 list t = [];
1535
1536 for (i = 0; i < f; i += uSTRIDE)
1537 {
1538 key a = llList2Key(Musers, i + uKEY);
1539
1540 if (ZERO_VECTOR != llGetAgentSize(a))
1541 {
1542 if (now < (llList2Float(Musers, i + uTIME) + 300.0))
1543 t += llList2List(Musers, i, i + uSTRIDE - 1);
1544 else
1545 {
1546 s(a, "You have taken too long to respond to the menu, it's disabled now.");
1547 a = NULL_KEY;
1548 }
1549 }
1550 else
1551 a = NULL_KEY;
1552 if (NULL_KEY == a) // Remove those no longer in the sim, or menu timeouts.
1553 llListenRemove(llList2Integer(Musers, i + uLSTN));
1554 }
1555 Musers = t;
1556 addEvent(300, script, them);
1557 }
1558 else
1559 {
1560 list t = llParseStringKeepNulls(script, ["."], []);
1561 string fr = llList2Key(t, 0);
1562 list dt = llParseString2List(llList2String(t, 1), [" "], []);
1563 string cmd = llList2String(dt, 0);
1564 dt = llListReplaceList(dt, [], 0, 0);
1565//d("timer " + script + "->" + fr + " ... " + cmd + "=" + llDumpList2String(dt, " "));
1566 sendScript(them, lCMD, fr + ".", [fINT, them, "TimerEvent", cmd, llDumpList2String(dt, " ")]);
1567 }
1568 }
1569 llSetTimerEvent(NextEvent - now);
1570 now = llGetTimeOfDay();
1571 float time = now - VelTime;
1572 vector vp = llGetPos();
1573 float dist = llVecDist(VelPos, vp);
1574 float speed = dist / time;
1575 VelTime = now;
1576 VelPos = vp;
1577// llSetText("Velocity " + (string) speed + " m/s\n@ " + (string) now, <1.0, 1.0, 1.0>, 1.0);
1578 llSetText("", <1.0, 1.0, 1.0>, 1.0);
1579 }
1580
1581 touch_start(integer num)
1582 {
1583 for (--num; 0 <= num; --num)
1584 {
1585 key id = llDetectedKey(num);
1586 string menu = MainMenu;
1587 list v = validateName(menu, "menu");
1588 string fr = llList2String(v, 0);
1589 menu = fr + llList2String(v, 1);
1590 if (access(id, menu + " menu", fr, llList2Integer(Menus, 0 + mAUTH), TRUE))
1591 {
1592 saveMuser(id, menu, "", -1, "");
1593 showMenu(id);
1594 }
1595 }
1596 }
1597}