corrade-lsl-templates – Blame information for rev 39

Subversion Repositories:
Rev:
Rev Author Line No. Line
4 office 1 ///////////////////////////////////////////////////////////////////////////
29 office 2 // Copyright (C) Wizardry and Steamworks 2014 - License: CC BY 2.0 //
4 office 3 ///////////////////////////////////////////////////////////////////////////
4  
5 ///////////////////////////////////////////////////////////////////////////
39 office 6 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 7 ///////////////////////////////////////////////////////////////////////////
8 string wasKeyValueGet(string k, string data) {
9 if(llStringLength(data) == 0) return "";
10 if(llStringLength(k) == 0) return "";
39 office 11 list a = llParseStringKeepNulls(data, ["&", "="], []);
12 integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
13 if(i != -1) return llList2String(a, 2*i+1);
4 office 14 return "";
15 }
16  
17 ///////////////////////////////////////////////////////////////////////////
29 office 18 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 19 ///////////////////////////////////////////////////////////////////////////
20 string wasKeyValueEncode(list data) {
21 list k = llList2ListStrided(data, 0, -1, 2);
22 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
23 data = [];
24 do {
25 data += llList2String(k, 0) + "=" + llList2String(v, 0);
26 k = llDeleteSubList(k, 0, 0);
27 v = llDeleteSubList(v, 0, 0);
28 } while(llGetListLength(k) != 0);
29 return llDumpList2String(data, "&");
30 }
31  
32 ///////////////////////////////////////////////////////////////////////////
29 office 33 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 34 ///////////////////////////////////////////////////////////////////////////
35 // escapes a string in conformance with RFC1738
36 string wasURLEscape(string i) {
37 string o = "";
38 do {
39 string c = llGetSubString(i, 0, 0);
40 i = llDeleteSubString(i, 0, 0);
41 if(c == "") jump continue;
42 if(c == " ") {
43 o += "+";
44 jump continue;
45 }
46 if(c == "\n") {
47 o += "%0D" + llEscapeURL(c);
48 jump continue;
49 }
50 o += llEscapeURL(c);
51 @continue;
52 } while(i != "");
53 return o;
54 }
55  
56 ///////////////////////////////////////////////////////////////////////////
29 office 57 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 58 ///////////////////////////////////////////////////////////////////////////
59 // unescapes a string in conformance with RFC1738
60 string wasURLUnescape(string i) {
61 return llUnescapeURL(
62 llDumpList2String(
63 llParseString2List(
64 llDumpList2String(
65 llParseString2List(
66 i,
67 ["+"],
68 []
69 ),
70 " "
71 ),
72 ["%0D%0A"],
73 []
74 ),
75 "\n"
76 )
77 );
78 }
79  
80 ///////////////////////////////////////////////////////////////////////////
29 office 81 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 82 ///////////////////////////////////////////////////////////////////////////
83 list wasCSVToList(string csv) {
84 list l = [];
85 list s = [];
86 string m = "";
87 do {
88 string a = llGetSubString(csv, 0, 0);
89 csv = llDeleteSubString(csv, 0, 0);
90 if(a == ",") {
91 if(llList2String(s, -1) != "\"") {
92 l += m;
93 m = "";
94 jump continue;
95 }
96 m += a;
97 jump continue;
98 }
99 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
100 m += a;
101 csv = llDeleteSubString(csv, 0, 0);
102 jump continue;
103 }
104 if(a == "\"") {
105 if(llList2String(s, -1) != a) {
106 s += a;
107 jump continue;
108 }
109 s = llDeleteSubList(s, -1, -1);
110 jump continue;
111 }
112 m += a;
113 @continue;
114 } while(csv != "");
115 // postcondition: length(s) = 0
116 return l + m;
117 }
118  
119 // corrade data
120 key CORRADE = NULL_KEY;
121 string GROUP = "";
122 string PASSWORD = "";
123  
124 // for holding the callback URL
125 string callback = "";
126  
127 // for notecard reading
128 integer line = 0;
129  
130 // key-value data will be read into this list
131 list tuples = [];
132  
133  
134 default {
135 state_entry() {
136 if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
137 llOwnerSay("Sorry, could not find a configuration inventory notecard.");
138 return;
139 }
140 // DEBUG
141 llOwnerSay("Reading configuration file...");
142 llGetNotecardLine("configuration", line);
143 }
144 dataserver(key id, string data) {
145 if(data == EOF) {
146 // invariant, length(tuples) % 2 == 0
147 if(llGetListLength(tuples) % 2 != 0) {
148 llOwnerSay("Error in configuration notecard.");
149 return;
150 }
151 CORRADE = llList2Key(
152 tuples,
153 llListFindList(
154 tuples,
155 [
156 "corrade"
157 ]
158 )
159 +1
160 );
161 if(CORRADE == NULL_KEY) {
162 llOwnerSay("Error in configuration notecard: corrade");
163 return;
164 }
165 GROUP = llList2String(
166 tuples,
167 llListFindList(
168 tuples,
169 [
170 "group"
171 ]
172 )
173 +1
174 );
175 if(GROUP == "") {
176 llOwnerSay("Error in configuration notecard: group");
177 return;
178 }
179 PASSWORD = llList2String(
180 tuples,
181 llListFindList(
182 tuples,
183 [
184 "password"
185 ]
186 )
187 +1
188 );
189 if(PASSWORD == "") {
190 llOwnerSay("Error in configuration notecard: password");
191 return;
192 }
193 // DEBUG
194 llOwnerSay("Read configuration notecard...");
195 tuples = [];
196 state url;
197 }
198 if(data == "") jump continue;
199 integer i = llSubStringIndex(data, "#");
200 if(i != -1) data = llDeleteSubString(data, i, -1);
201 list o = llParseString2List(data, ["="], []);
202 // get rid of starting and ending quotes
203 string k = llDumpList2String(
204 llParseString2List(
205 llStringTrim(
206 llList2String(
207 o,
208  
209 ),
210 STRING_TRIM),
211 ["\""], []
212 ), "\"");
213 string v = llDumpList2String(
214 llParseString2List(
215 llStringTrim(
216 llList2String(
217 o,
218 1
219 ),
220 STRING_TRIM),
221 ["\""], []
222 ), "\"");
223 if(k == "" || v == "") jump continue;
224 tuples += k;
225 tuples += v;
226 @continue;
227 llGetNotecardLine("configuration", ++line);
228 }
229 on_rez(integer num) {
230 llResetScript();
231 }
232 changed(integer change) {
233 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
234 llResetScript();
235 }
236 }
237 }
238  
239 state url {
240 state_entry() {
241 // DEBUG
242 llOwnerSay("Requesting URL...");
243 llRequestURL();
244 }
245 http_request(key id, string method, string body) {
246 if(method != URL_REQUEST_GRANTED) return;
247 callback = body;
248 // DEBUG
249 llOwnerSay("Got URL...");
250 state detect;
251 }
252 on_rez(integer num) {
253 llResetScript();
254 }
255 changed(integer change) {
256 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
257 llResetScript();
258 }
259 }
260 }
261  
262 state detect {
263 state_entry() {
264 // DEBUG
265 llOwnerSay("Detecting if Corrade is online...");
266 llSetTimerEvent(5);
267 }
268 timer() {
269 llRequestAgentData((key)CORRADE, DATA_ONLINE);
270 }
271 dataserver(key id, string data) {
272 if(data != "1") {
273 // DEBUG
274 llOwnerSay("Corrade is not online, sleeping...");
275 llSetTimerEvent(30);
276 return;
277 }
278 state initialize;
279 }
280 on_rez(integer num) {
281 llResetScript();
282 }
283 changed(integer change) {
284 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
285 llResetScript();
286 }
287 }
288 }
289  
290 state initialize {
291 state_entry() {
292 // DEBUG
293 llOwnerSay("Creating the database if it does not exist...");
294 llInstantMessage(
295 (key)CORRADE,
296 wasKeyValueEncode(
297 [
298 "command", "database",
299 "group", wasURLEscape(GROUP),
300 "password", wasURLEscape(PASSWORD),
301 "SQL", wasURLEscape(
302 "CREATE TABLE IF NOT EXISTS visitors (
303 'firstname' TEXT NOT NULL,
304 'lastname' TEXT NOT NULL,
305 'lastseen' TEXT NOT NULL,
306 'time' INTEGER NOT NULL,
307 'memory' INTEGER NOT NULL,
308 PRIMARY KEY ('firstname', 'lastname')
309 )"
310 ),
311 "callback", wasURLEscape(callback)
312 ]
313 )
314 );
315 // alarm 60
316 llSetTimerEvent(60);
317 }
318 timer() {
319 // DEBUG
320 llOwnerSay("Timeout creating table...");
321 llResetScript();
322 }
323 http_request(key id, string method, string body) {
324 llHTTPResponse(id, 200, "OK");
325 if(wasKeyValueGet("command", body) != "database") return;
326 if(wasKeyValueGet("success", body) != "True") {
327 // DEBUG
328 llOwnerSay("Failed to create the table: " +
329 wasURLUnescape(
330 wasKeyValueGet(
331 "error",
332 body
333 )
334 )
335 );
336 return;
337 }
338 // DEBUG
339 llOwnerSay("Table created...");
340 state show;
341 }
342 on_rez(integer num) {
343 llResetScript();
344 }
345 changed(integer change) {
346 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
347 llResetScript();
348 }
349 }
350 state_exit() {
351 llSetTimerEvent(0);
352 }
353 }
354  
355 state show {
356 state_entry() {
357 // DEBUG
358 llOwnerSay("Updating display with the number of recorded visitors...");
359 llInstantMessage(
360 (key)CORRADE,
361 wasKeyValueEncode(
362 [
363 "command", "database",
364 "group", wasURLEscape(GROUP),
365 "password", wasURLEscape(PASSWORD),
366 "SQL", wasURLEscape(
367 "SELECT
368 COUNT(*) AS 'Visits',
369 AVG(time) AS 'Time',
370 AVG(memory) AS 'Memory'
371 FROM visitors"
372 ),
373 "callback", wasURLEscape(callback)
374 ]
375 )
376 );
377 // alarm 60
378 llSetTimerEvent(60);
379 }
380 timer() {
381 // DEBUG
382 llOwnerSay("Timeout reading rows from visitors table...");
383 llResetScript();
384 }
385 http_request(key id, string method, string body) {
386 llHTTPResponse(id, 200, "OK");
387 if(wasKeyValueGet("command", body) != "database") return;
388 if(wasKeyValueGet("success", body) != "True") {
389 // DEBUG
390 llOwnerSay("Failed to enumerate visitors: " +
391 wasURLUnescape(
392 wasKeyValueGet(
393 "error",
394 body
395 )
396 )
397 );
398 llResetScript();
399 }
400 list data = wasCSVToList(
401 wasURLUnescape(
402 wasKeyValueGet(
403 "data",
404 body
405 )
406 )
407 );
408 integer visits = llList2Integer(
409 data,
410 llListFindList(
411 data,
412 (list)"Visits"
413 ) + 1
414 );
415 integer time = llList2Integer(
416 data,
417 llListFindList(
418 data,
419 (list)"Time"
420 ) + 1
421 );
422 integer memory = llList2Integer(
423 data,
424 llListFindList(
425 data,
426 (list)"Memory"
427 ) + 1
428 );
429 llMessageLinked(LINK_ROOT, 204000, "V:" + (string)visits, "0");
430 llMessageLinked(LINK_ROOT, 204000, "T:" + (string)time + "m", "1");
431 llMessageLinked(LINK_ROOT, 204000, "M:" + (string)memory + "k", "2");
432 state scan;
433 }
434 link_message(integer sender_num, integer num, string str, key id) {
435 if(str == "reset")
436 state reset;
437 if(str == "display") {
438 line = 0;
439 state display;
440 }
441 }
442 on_rez(integer num) {
443 llResetScript();
444 }
445 changed(integer change) {
446 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
447 llResetScript();
448 }
449 }
450 state_exit() {
451 llSetTimerEvent(0);
452 }
453 }
454  
455 state scan {
456 state_entry() {
457 // DEBUG
458 llOwnerSay("Scanning for visitors...");
459 // Scan for visitors every 60 seconds.
460 llSetTimerEvent(60);
461 }
462 timer() {
463 // Check if Corrade is online.
464 llRequestAgentData((key)CORRADE, DATA_ONLINE);
465 // Get agents
466 list as = llGetAgentList(AGENT_LIST_REGION, []);
467 do {
468 key a = llList2Key(as, 0);
469 as = llDeleteSubList(as, 0, 0);
470 list name = llParseString2List(llKey2Name(a), [" "], []);
471 if(llGetListLength(name) != 2) return;
472 string fn = llList2String(name, 0);
473 string ln = llList2String(name, 1);
474 // The command sent to Corrade responsible for adding a visitor
475 // or updating the data for the visitor in case the visitor is
476 // already entered into the visitors table. This is performed
477 // with an INSER OR REPLACE sqlite command given the first name
478 // and the last name of the avatar are unique primary keys.
479 llInstantMessage(
480 (key)CORRADE,
481 wasKeyValueEncode(
482 [
483 "command", "database",
484 "group", wasURLEscape(GROUP),
485 "password", wasURLEscape(PASSWORD),
486 "SQL", wasURLEscape(
487 "INSERT OR REPLACE INTO visitors (
488 firstname,
489 lastname,
490 lastseen,
491 time,
492 memory
493 ) VALUES(
494 '" + fn + "', '" + ln + "', '" + llGetTimestamp() + "',
495 COALESCE(
496 (
497 SELECT time FROM visitors WHERE
498 firstname='" + fn + "' AND lastname='" + ln + "'
499 ) + 1
500 ,
501 1
502 ), " +
503 (string)(
504 (integer)(
505 llList2Float(
506 llGetObjectDetails(
507 a,
508 [OBJECT_SCRIPT_MEMORY]
509 ),
510  
511 )
512 /
513 1024 /*in kib, to mib 1048576*/
514 )
515  
516 ) +
517 ")"
518 )
519 ]
520 )
521 );
522 } while(llGetListLength(as));
523 state show;
524 }
525 link_message(integer sender_num, integer num, string str, key id) {
526 if(str == "reset")
527 state reset;
528 if(str == "display") {
529 line = 0;
530 state display;
531 }
532 }
533 dataserver(key id, string data) {
534 if(data == "1") return;
535 // DEBUG
536 llOwnerSay("Corrade is not online, sleeping...");
537 // Switch to detect loop and wait there for Corrade to come online.
538 state detect;
539 }
540 on_rez(integer num) {
541 llResetScript();
542 }
543 changed(integer change) {
544 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
545 llResetScript();
546 }
547 }
548 state_exit() {
549 llSetTimerEvent(0);
550 }
551 }
552  
553 state display_trampoline {
554 state_entry() {
555 ++line;
556 state display;
557 }
558 link_message(integer sender_num, integer num, string str, key id) {
559 if(str == "reset")
560 state reset;
561 }
562 }
563  
564 state display {
565 state_entry() {
566 llInstantMessage(
567 (key)CORRADE,
568 wasKeyValueEncode(
569 [
570 "command", "database",
571 "group", wasURLEscape(GROUP),
572 "password", wasURLEscape(PASSWORD),
573 "SQL", wasURLEscape(
574 "SELECT * FROM visitors
575 ORDER BY lastseen DESC
576 LIMIT 1
577 OFFSET " + (string)line
578 ),
579 "callback", wasURLEscape(callback)
580 ]
581 )
582 );
583 // alarm 60
584 llSetTimerEvent(60);
585 }
586 http_request(key id, string method, string body) {
587 llHTTPResponse(id, 200, "OK");
588 if(wasKeyValueGet("command", body) != "database") return;
589 if(wasKeyValueGet("success", body) != "True") {
590 // DEBUG
591 llOwnerSay("Failed to query the table: " +
592 wasURLUnescape(
593 wasKeyValueGet(
594 "error",
595 body
596 )
597 )
598 );
599 return;
600 }
601 // Grab the data key if it exists.
602 string dataKey = wasURLUnescape(
603 wasKeyValueGet(
604 "data",
605 body
606 )
607 );
608  
609 // We got no more rows, so switch back to scanning.
610 if(dataKey == "")
611 state scan;
612  
613 list data = wasCSVToList(dataKey);
614  
615 string firstname = llList2String(
616 data,
617 llListFindList(
618 data,
619 (list)"firstname"
620 ) + 1
621 );
622 string lastname = llList2String(
623 data,
624 llListFindList(
625 data,
626 (list)"lastname"
627 ) + 1
628 );
629 string lastseen = llList2String(
630 data,
631 llListFindList(
632 data,
633 (list)"lastseen"
634 ) + 1
635 );
636  
637 llOwnerSay(firstname + " " + lastname + " @ " + lastseen);
638 state display_trampoline;
639 }
640 link_message(integer sender_num, integer num, string str, key id) {
641 if(str == "reset")
642 state reset;
643 }
644 timer() {
645 // DEBUG
646 llOwnerSay("Timeout reading rows from visitors table...");
647 llResetScript();
648 }
649 on_rez(integer num) {
650 llResetScript();
651 }
652 changed(integer change) {
653 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
654 llResetScript();
655 }
656 }
657 state_exit() {
658 llSetTimerEvent(0);
659 }
660 }
661  
662 state reset {
663 state_entry() {
664 // DEBUG
665 llOwnerSay("Resetting all visitors...");
666 llInstantMessage(
667 (key)CORRADE,
668 wasKeyValueEncode(
669 [
670 "command", "database",
671 "group", wasURLEscape(GROUP),
672 "password", wasURLEscape(PASSWORD),
673 "SQL", "DROP TABLE visitors",
674 "callback", wasURLEscape(callback)
675 ]
676 )
677 );
678 // alarm 60
679 llSetTimerEvent(60);
680 }
681 timer() {
682 // DEBUG
683 llOwnerSay("Timeout deleting database...");
684 llResetScript();
685 }
686 http_request(key id, string method, string body) {
687 llHTTPResponse(id, 200, "OK");
688 if(wasKeyValueGet("command", body) != "database") return;
689 if(wasKeyValueGet("success", body) != "True") {
690 // DEBUG
691 llOwnerSay("Failed to drop the visitors table: " +
692 wasURLUnescape(
693 wasKeyValueGet(
694 "error",
695 body
696 )
697 )
698 );
699 llResetScript();
700 }
701 // DEBUG
702 llOwnerSay("Table dropped...");
703 llResetScript();
704 }
705 on_rez(integer num) {
706 llResetScript();
707 }
708 changed(integer change) {
709 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
710 llResetScript();
711 }
712 }
713 state_exit() {
714 llSetTimerEvent(0);
715 }
716 }