diff options
Diffstat (limited to '')
-rw-r--r-- | 1chatter.lsl | 1597 |
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 | |||
4 | string Version = "1chatter v0.1 dev version"; | ||
5 | |||
6 | // BEGIN boilerplate. | ||
7 | integer DEBUG = FALSE; | ||
8 | float Start; | ||
9 | string ScriptName; | ||
10 | key ScriptKey; | ||
11 | key LibraryKey; | ||
12 | key Owner; | ||
13 | string URL; | ||
14 | |||
15 | list Commands; | ||
16 | integer cNAME = 0; | ||
17 | integer cARGS = 1; | ||
18 | integer cAUTH = 2; | ||
19 | integer cSTRIDE = 3; | ||
20 | |||
21 | list Scanners = []; // Current set of scans. | ||
22 | integer nMENU = 0; // Menu that started the scan. | ||
23 | integer nID = 1; // Avatar UUID we are doing this scan for. | ||
24 | integer nTYPE = 2; // The type of scan. | ||
25 | integer nTITLE = 3; // Title of the menu to show. | ||
26 | integer nCMD = 4; // Command to run with the result. | ||
27 | integer nSTRIDE = 5; | ||
28 | |||
29 | // utilities commands, "l"ibrary. | ||
30 | string lSEP = "$!#"; // Used to seperate lists when sending them as strings. | ||
31 | integer lRESET = -1; | ||
32 | integer lRESET_DONE = -2; | ||
33 | integer lALIAS = -3; | ||
34 | integer lALIAS_DONE = -4; | ||
35 | integer lSETTING = -5; | ||
36 | integer lSETTING_DONE = -6; | ||
37 | integer lSUBSTITUTE = -7; | ||
38 | integer lSUBSTITUTE_DONE = -8; | ||
39 | integer lNEXT_WORD = -9; | ||
40 | integer lNEXT_WORD_DONE = -10; | ||
41 | integer lCONTROL = -13; | ||
42 | integer lCONTROL_DONE = -14; | ||
43 | integer lCMD = -15; | ||
44 | integer lCMD_DONE = -16; | ||
45 | integer lSETTINGS = -17; | ||
46 | integer lSETTINGS_DONE = -18; | ||
47 | integer lSCAN = -19; | ||
48 | integer lDYNAMIC = -20; | ||
49 | integer lMENU = -21; | ||
50 | |||
51 | d(string m) {if (DEBUG) llInstantMessage(Owner, llGetScriptName() + ": " + m);} | ||
52 | D(string m) {llRegionSay(DEBUG_CHANNEL, llGetScriptName() + ": " + m);} | ||
53 | s(string m) {s(Owner, m);} | ||
54 | s(key id, string m) {if (id == Owner) llOwnerSay(m); else llInstantMessage(id, m);} | ||
55 | sendScript(integer cmd, list args) {sendScript(LibraryKey, cmd, ScriptName, args);} | ||
56 | sendScript(key them, integer cmd, list args) {sendScript(them, cmd, inKey2Name(them), args);} | ||
57 | sendScript(integer cmd, string name, list args) {sendScript(LibraryKey, cmd, name, args);} | ||
58 | sendScript(key them, integer cmd, string name, list args) | ||
59 | { | ||
60 | llMessageLinked(LINK_SET, cmd, llDumpList2String([ScriptKey, name] + args, lSEP), them); | ||
61 | } | ||
62 | sendPrim(key them, string cmd, list args) {osMessageObject(them, llDumpList2String([ScriptName] + args, lSEP));} | ||
63 | |||
64 | string 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 | |||
76 | integer 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 | |||
91 | integer 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 | |||
118 | string 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 | |||
128 | string 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 | |||
138 | list 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 | |||
153 | list Aliases; | ||
154 | integer aALIAS = 0; | ||
155 | integer aNAME = 1; | ||
156 | integer aSTRIDE = 2; | ||
157 | |||
158 | string 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 - | ||
168 | integer fINT = 0; // Internal, not sure we need this, means the same script. | ||
169 | integer fCARD = 1; // Read from a note card. | ||
170 | integer fCHAT = 2; // Chat channel. | ||
171 | integer fLINK = 3; // Link message. | ||
172 | integer fMENU = 4; // Menu, in reality this is just a chat channel. | ||
173 | integer fRLV = 5; // RLV, again from a chat channel. | ||
174 | integer fRELAY = 6; // RLV relay. | ||
175 | integer fOSM = 7; // osMessageObject() | ||
176 | integer fHTTP = 8; // From the web. | ||
177 | |||
178 | // LSL security is entirely hopeless, see the notecard "1ring security". | ||
179 | // "a"ccess level - | ||
180 | integer aNONE = 0; | ||
181 | integer aBOSS = 1; | ||
182 | integer aTRUST = 2; | ||
183 | integer aGROUP = 4; | ||
184 | integer aBOSSES = 3; | ||
185 | integer aWEARER = 8; | ||
186 | integer aPRIV = 9; | ||
187 | integer aMORE = 11; | ||
188 | integer aMOST = 15; | ||
189 | integer aPUBLIC = 16; | ||
190 | integer aALL = 31; | ||
191 | list 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 | |||
206 | integer 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 | |||
215 | integer 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 | |||
287 | string TRUEs = "t1aopswy"; | ||
288 | string FALSEs = "f0bgnuz"; | ||
289 | integer 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 - | ||
299 | list Settings; | ||
300 | integer sNAME = 0; | ||
301 | integer sTYPE = 1; | ||
302 | integer sVALUE = 2; | ||
303 | integer sAUTH = 3; | ||
304 | integer sSTRIDE = 4; | ||
305 | |||
306 | string 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 | |||
317 | integer 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); | ||
365 | d("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. | ||
376 | readSettings(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 | |||
410 | dumpAliases(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 | |||
426 | dumpCommands(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 | |||
443 | dumpSettings(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 | ||
464 | float NextEvent = 307584000.0; // Ten years. | ||
465 | list Events = []; | ||
466 | integer eTIME = 0; // Time of it's next event. | ||
467 | integer eSCRIPT = 1; // Rest of scriptlet after the delay command. | ||
468 | integer eKEY = 2; // Key of script asking for this timer. | ||
469 | integer eSTRIDE = 3; | ||
470 | |||
471 | |||
472 | addEvent(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 | |||
495 | integer 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. | ||
511 | string Enclosed = "ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ⓪⓵⓶⓷⓸⓹⓺⓻⓼⓽ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ"; | ||
512 | string DownsideUp = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
513 | string UpsideDown = "ɐqɔpǝɟɓɥᴉſʞןɯuodbɹsʇnʌʍxʎz0lᘔƐᔭ59Ɫ86∀qↃpƎℲ⅁HIᒋʞ⅂ꟽNOԀÒᴚS⊥∩ɅMX⅄Z"; | ||
514 | |||
515 | string 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 | |||
532 | string 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 | ||
550 | string MainMenu; | ||
551 | list Menus; // List of menus. | ||
552 | integer mNAME = 0; // Name of menu. | ||
553 | integer mTITLE = 1; // Title for the menu. | ||
554 | integer mAUTH = 2; // Authorised users of menu - 0 = wearer, 1 = boss, 2 = all | ||
555 | integer mENTRIES = 3; // | separated list of entries. | ||
556 | integer mCMDS = 4; // | separated list of commands, matching the entries. | ||
557 | integer mDYN = 5; // Dynamic menu, autoremove it when done. | ||
558 | integer mSTRIDE = 6; | ||
559 | |||
560 | integer findMenu(string name) | ||
561 | { | ||
562 | return listFindString(Menus, name, mSTRIDE); | ||
563 | } | ||
564 | |||
565 | saveMenu(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 | |||
583 | addMenuItem(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 | |||
605 | dynamicMenu(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 | |||
613 | string 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 | |||
624 | toggleMenu(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 | ||
650 | list Musers; // List of menu users. | ||
651 | integer uKEY = 0; // Key of user. | ||
652 | integer uCHAN = 1; // Listen channel for user. | ||
653 | integer uLSTN = 2; // Listen handle. | ||
654 | integer uCURRENT = 3; // Current menu of user. | ||
655 | integer uENTRIES = 4; // Current entries for currently in flight menu. | ||
656 | integer uPAGE = 5; // Current page of long menu of user. | ||
657 | integer uSTACK = 6; // | separated menu stack list. | ||
658 | integer uTIME = 7; // Timestamp of last menu shown. | ||
659 | integer uSTRIDE = 8; | ||
660 | |||
661 | // page = -1 means the menu items fit on a single dialog, otherwise it's the page of items. | ||
662 | saveMuser(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 | |||
707 | removeMenu(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 | |||
716 | lastMenu(key id, string r) | ||
717 | { | ||
718 | saveMuser(id, "-1", "", -1, ""); | ||
719 | if ("" != r) removeMenu(r); | ||
720 | } | ||
721 | |||
722 | dumpMenus(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 | |||
739 | showMenu(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() | ||
885 | integer 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 | |||
959 | d("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 | |||
1015 | integer 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? | ||
1054 | list 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 | |||
1080 | readTheme(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 | |||
1169 | list wipe(list o, string fr, integer stride) | ||
1170 | { | ||
1171 | return wipe(o, fr, stride, 0); | ||
1172 | } | ||
1173 | |||
1174 | list 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 | |||
1188 | key URLrequestID; | ||
1189 | key URLCheckrequestID; | ||
1190 | integer Attached; | ||
1191 | float VelTime; | ||
1192 | vector VelPos; | ||
1193 | init() | ||
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 | |||
1216 | default | ||
1217 | { | ||
1218 | state_entry() | ||
1219 | { | ||
1220 | Start = llGetTimeOfDay(); | ||
1221 | Owner = llGetOwner(); | ||
1222 | d("\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 = "*"; | ||
1257 | fr="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 | { | ||
1412 | d("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 | { | ||
1431 | d("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), " "); | ||
1439 | d("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 | } | ||