corrade-lsl-templates – Blame information for rev 40

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 }
40 office 16  
4 office 17 ///////////////////////////////////////////////////////////////////////////
40 office 18 // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
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 ///////////////////////////////////////////////////////////////////////////
40 office 33 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
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 ///////////////////////////////////////////////////////////////////////////
40 office 57 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
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 ///////////////////////////////////////////////////////////////////////////
40 office 81 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
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  
40 office 119  
120  
121 ///////////////////////////////////////////////////////////////////////////
122 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
123 ///////////////////////////////////////////////////////////////////////////
124 string wasListToCSV(list l) {
125 list v = [];
126 do {
127 string a = llDumpList2String(
128 llParseStringKeepNulls(
129 llList2String(
130 l,
131  
132 ),
133 ["\""],
134 []
135 ),
136 "\"\""
137 );
138 if(llParseStringKeepNulls(
139 a,
140 [" ", ",", "\n", "\""], []
141 ) !=
142 (list) a
143 ) a = "\"" + a + "\"";
144 v += a;
145 l = llDeleteSubList(l, 0, 0);
146 } while(l != []);
147 return llDumpList2String(v, ",");
148 }
149  
150 ///////////////////////////////////////////////////////////////////////////
151 // SQL STATEMENT DEFINITIONS //
152 ///////////////////////////////////////////////////////////////////////////
153  
154 string SQL_INSERT_VISITOR = "INSERT OR REPLACE INTO visitors (firstname, lastname, lastseen, time, memory) VALUES(:firstname, :lastname, :time, COALESCE((SELECT time FROM visitors WHERE firstname=:firstname AND lastname=:lastname), 0) + 1, :memory)";
155 string SQL_CREATE_TABLE = "CREATE TABLE IF NOT EXISTS visitors ('firstname' TEXT NOT NULL, 'lastname' TEXT NOT NULL, 'lastseen' TEXT NOT NULL, 'time' INTEGER NOT NULL, 'memory' INTEGER NOT NULL, PRIMARY KEY ('firstname', 'lastname'))";
156 string SQL_COUNT_VISITORS = "SELECT COUNT(*) AS 'Visits', AVG(time) AS 'Time', AVG(memory) AS 'Memory' FROM visitors";
157 string SQL_SELECT_VISITOR = "SELECT * FROM visitors ORDER BY lastseen DESC LIMIT 1 OFFSET :offset";
158 string SQL_DROP_TABLE = "DROP TABLE IF EXISTS visitors";
159  
160 ///////////////////////////////////////////////////////////////////////////
161  
4 office 162 // corrade data
163 key CORRADE = NULL_KEY;
164 string GROUP = "";
165 string PASSWORD = "";
166  
167 // for holding the callback URL
168 string callback = "";
169  
170 // for notecard reading
171 integer line = 0;
172  
173 // key-value data will be read into this list
174 list tuples = [];
40 office 175 list agents = [];
176 string agentName = "";
4 office 177  
178  
179 default {
180 state_entry() {
181 if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
182 llOwnerSay("Sorry, could not find a configuration inventory notecard.");
183 return;
184 }
185 // DEBUG
186 llOwnerSay("Reading configuration file...");
187 llGetNotecardLine("configuration", line);
188 }
189 dataserver(key id, string data) {
190 if(data == EOF) {
191 // invariant, length(tuples) % 2 == 0
192 if(llGetListLength(tuples) % 2 != 0) {
193 llOwnerSay("Error in configuration notecard.");
194 return;
195 }
196 CORRADE = llList2Key(
197 tuples,
198 llListFindList(
199 tuples,
200 [
201 "corrade"
202 ]
203 )
204 +1
205 );
206 if(CORRADE == NULL_KEY) {
207 llOwnerSay("Error in configuration notecard: corrade");
208 return;
209 }
210 GROUP = llList2String(
211 tuples,
212 llListFindList(
213 tuples,
214 [
215 "group"
216 ]
217 )
218 +1
219 );
220 if(GROUP == "") {
221 llOwnerSay("Error in configuration notecard: group");
222 return;
223 }
224 PASSWORD = llList2String(
225 tuples,
226 llListFindList(
227 tuples,
228 [
229 "password"
230 ]
231 )
232 +1
233 );
234 if(PASSWORD == "") {
235 llOwnerSay("Error in configuration notecard: password");
236 return;
237 }
238 // DEBUG
239 llOwnerSay("Read configuration notecard...");
240 tuples = [];
241 state url;
242 }
243 if(data == "") jump continue;
244 integer i = llSubStringIndex(data, "#");
245 if(i != -1) data = llDeleteSubString(data, i, -1);
246 list o = llParseString2List(data, ["="], []);
247 // get rid of starting and ending quotes
248 string k = llDumpList2String(
249 llParseString2List(
250 llStringTrim(
251 llList2String(
252 o,
253  
254 ),
255 STRING_TRIM),
256 ["\""], []
257 ), "\"");
258 string v = llDumpList2String(
259 llParseString2List(
260 llStringTrim(
261 llList2String(
262 o,
263 1
264 ),
265 STRING_TRIM),
266 ["\""], []
267 ), "\"");
268 if(k == "" || v == "") jump continue;
269 tuples += k;
270 tuples += v;
271 @continue;
272 llGetNotecardLine("configuration", ++line);
273 }
274 on_rez(integer num) {
275 llResetScript();
276 }
277 changed(integer change) {
278 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
279 llResetScript();
280 }
281 }
282 }
283  
284 state url {
285 state_entry() {
286 // DEBUG
287 llOwnerSay("Requesting URL...");
288 llRequestURL();
289 }
290 http_request(key id, string method, string body) {
291 if(method != URL_REQUEST_GRANTED) return;
292 callback = body;
293 // DEBUG
294 llOwnerSay("Got URL...");
295 state detect;
296 }
297 on_rez(integer num) {
298 llResetScript();
299 }
300 changed(integer change) {
301 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
302 llResetScript();
303 }
304 }
305 }
306  
307 state detect {
308 state_entry() {
309 // DEBUG
310 llOwnerSay("Detecting if Corrade is online...");
311 llSetTimerEvent(5);
312 }
313 timer() {
40 office 314 llRequestAgentData(CORRADE, DATA_ONLINE);
4 office 315 }
316 dataserver(key id, string data) {
317 if(data != "1") {
318 // DEBUG
319 llOwnerSay("Corrade is not online, sleeping...");
320 llSetTimerEvent(30);
321 return;
322 }
323 state initialize;
324 }
325 on_rez(integer num) {
326 llResetScript();
327 }
328 changed(integer change) {
329 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
330 llResetScript();
331 }
332 }
333 }
334  
335 state initialize {
336 state_entry() {
337 // DEBUG
338 llOwnerSay("Creating the database if it does not exist...");
339 llInstantMessage(
40 office 340 CORRADE,
4 office 341 wasKeyValueEncode(
342 [
343 "command", "database",
344 "group", wasURLEscape(GROUP),
345 "password", wasURLEscape(PASSWORD),
40 office 346 "SQL", wasURLEscape(SQL_CREATE_TABLE),
4 office 347 "callback", wasURLEscape(callback)
348 ]
349 )
350 );
351 // alarm 60
352 llSetTimerEvent(60);
353 }
354 timer() {
355 // DEBUG
356 llOwnerSay("Timeout creating table...");
357 llResetScript();
358 }
359 http_request(key id, string method, string body) {
360 llHTTPResponse(id, 200, "OK");
40 office 361 if(wasKeyValueGet("command", body) == "database") {
362 integer success = wasKeyValueGet("success", body) == "True";
363 if(!success) {
364 // DEBUG
365 llOwnerSay("Failed to create the table: " +
366 wasURLUnescape(
367 wasKeyValueGet(
368 "error",
369 body
370 )
371 )
372 );
373 return;
374 }
375  
4 office 376 // DEBUG
40 office 377 llOwnerSay("Table created...");
378 state show;
4 office 379 }
380 }
40 office 381 link_message(integer sender_num, integer num, string str, key id) {
382 if(str == "reset") {
383 state reset;
384 }
385 }
4 office 386 on_rez(integer num) {
387 llResetScript();
388 }
389 changed(integer change) {
390 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
391 llResetScript();
392 }
393 }
394 state_exit() {
395 llSetTimerEvent(0);
396 }
397 }
398  
399 state show {
400 state_entry() {
401 // DEBUG
402 llOwnerSay("Updating display with the number of recorded visitors...");
403 llInstantMessage(
40 office 404 CORRADE,
4 office 405 wasKeyValueEncode(
406 [
407 "command", "database",
408 "group", wasURLEscape(GROUP),
409 "password", wasURLEscape(PASSWORD),
40 office 410 "SQL", wasURLEscape(SQL_COUNT_VISITORS),
4 office 411 "callback", wasURLEscape(callback)
412 ]
413 )
414 );
415 // alarm 60
416 llSetTimerEvent(60);
417 }
418 timer() {
419 // DEBUG
420 llOwnerSay("Timeout reading rows from visitors table...");
421 llResetScript();
422 }
423 http_request(key id, string method, string body) {
424 llHTTPResponse(id, 200, "OK");
40 office 425 if(wasKeyValueGet("command", body) == "database") {
426 integer success = wasKeyValueGet("success", body) == "True";
427 if(!success) {
428 // DEBUG
429 llOwnerSay("Failed to enumerate visitors: " +
430 wasURLUnescape(
431 wasKeyValueGet(
432 "error",
433 body
434 )
435 )
436 );
437 llResetScript();
438 }
439 list data = wasCSVToList(
4 office 440 wasURLUnescape(
441 wasKeyValueGet(
40 office 442 "data",
4 office 443 body
444 )
445 )
446 );
40 office 447  
448 // DEBUG
449 //llOwnerSay("Data: " + llDumpList2String(data, ","));
450  
451 integer visits = llList2Integer(
4 office 452 data,
40 office 453 llListFindList(
454 data,
455 (list)"Visits"
456 ) + 1
457 );
458 integer time = llList2Integer(
4 office 459 data,
40 office 460 llListFindList(
461 data,
462 (list)"Time"
463 ) + 1
464 );
465 integer memory = llList2Integer(
4 office 466 data,
40 office 467 llListFindList(
468 data,
469 (list)"Memory"
470 ) + 1
471 );
472  
473 // Send message to xyz script.
474 llMessageLinked(LINK_ROOT, 204000, "V:" + (string)visits, "0");
475 llMessageLinked(LINK_ROOT, 204000, "T:" + (string)time + "m", "1");
476 llMessageLinked(LINK_ROOT, 204000, "M:" + (string)memory + "k", "2");
477  
478 // Get the list of agents.
479 agents = llGetAgentList(AGENT_LIST_REGION, []);
480 state insert_trampoline;
481 }
4 office 482 }
483 link_message(integer sender_num, integer num, string str, key id) {
484 if(str == "reset")
485 state reset;
486 if(str == "display") {
487 line = 0;
488 state display;
489 }
490 }
491 on_rez(integer num) {
492 llResetScript();
493 }
494 changed(integer change) {
495 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
496 llResetScript();
497 }
498 }
499 state_exit() {
500 llSetTimerEvent(0);
501 }
502 }
503  
40 office 504 state insert_trampoline {
4 office 505 state_entry() {
40 office 506 llSetTimerEvent(1);
4 office 507 }
508 timer() {
40 office 509 state insert;
4 office 510 }
511 link_message(integer sender_num, integer num, string str, key id) {
512 if(str == "reset")
513 state reset;
514 if(str == "display") {
515 line = 0;
516 state display;
517 }
518 }
40 office 519 on_rez(integer num) {
520 llResetScript();
521 }
522 changed(integer change) {
523 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
524 llResetScript();
525 }
526 }
527 state_exit() {
528 llSetTimerEvent(0);
529 }
530 }
531  
532 state insert {
533 state_entry() {
534 // Once the list is empty, go back to display.
535 if(llGetListLength(agents) == 0) {
536 state show;
537 }
538  
539 key agent = llList2Key(agents, 0);
540 agents = llDeleteSubList(agents, 0, 0);
541 list name = llParseString2List(llKey2Name(agent), [" "], []);
542 if(llGetListLength(name) != 2) {
543 return;
544 }
545 string firstname = llList2String(name, 0);
546 string lastname = llList2String(name, 1);
547 agentName = firstname + " " + lastname;
548 string memory = (string)(
549 (integer)(
550 llList2Float(
551 llGetObjectDetails(
552 agent,
553 [OBJECT_SCRIPT_MEMORY]
554 ),
555  
556 )
557 /
558 1024 /*in kib, to mib 1048576*/
559 )
560  
561 );
4 office 562 // DEBUG
40 office 563 //llOwnerSay("Memory: " + memory);
564 // The command sent to Corrade responsible for adding a visitor
565 // or updating the data for the visitor in case the visitor is
566 // already entered into the visitors table. This is performed
567 // with an INSER OR REPLACE sqlite command given the first name
568 // and the last name of the avatar are unique primary keys.
569 llInstantMessage(
570 CORRADE,
571 wasKeyValueEncode(
572 [
573 "command", "database",
574 "group", wasURLEscape(GROUP),
575 "password", wasURLEscape(PASSWORD),
576 "SQL", wasURLEscape(SQL_INSERT_VISITOR),
577 "data", wasListToCSV(
578 [
579 "firstname",
580 firstname,
581 "lastname",
582 lastname,
583 "time",
584 llGetTimestamp(),
585 "memory",
586 memory
587 ]
588 ),
589 "callback", wasURLEscape(callback)
590 ]
591 )
592 );
593  
594 // Command timeout.
595 llSetTimerEvent(60);
4 office 596 }
40 office 597 http_request(key id, string method, string body) {
598 llHTTPResponse(id, 200, "OK");
599  
600 // DEBUG
601 //llOwnerSay(wasURLUnescape(body));
602  
603 if(wasKeyValueGet("command", body) == "database") {
604 integer success = wasKeyValueGet("success", body) == "True";
605 if(!success) {
606 // DEBUG
607 llOwnerSay("Failed to insert visitor " + agentName + " due to: " +
608 wasURLUnescape(
609 wasKeyValueGet(
610 "error",
611 body
612 )
613 )
614 );
615 state insert_trampoline;
616 }
617  
618 // DEBUG
619 llOwnerSay("Processed visitor named " + agentName + "...");
620  
621 state insert_trampoline;
622 }
623 }
624 timer() {
625 // DEBUG
626 llOwnerSay("Inserting visitors has timed out, resetting...");
627  
628 state insert_trampoline;
629 }
630 link_message(integer sender_num, integer num, string str, key id) {
631 if(str == "reset") {
632 state reset;
633 }
634 if(str == "display") {
635 line = 0;
636 state display;
637 }
638 }
4 office 639 on_rez(integer num) {
640 llResetScript();
641 }
642 changed(integer change) {
643 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
644 llResetScript();
645 }
646 }
647 state_exit() {
648 llSetTimerEvent(0);
649 }
650 }
651  
652 state display_trampoline {
653 state_entry() {
654 ++line;
655 state display;
656 }
657 link_message(integer sender_num, integer num, string str, key id) {
658 if(str == "reset")
659 state reset;
660 }
661 }
662  
663 state display {
664 state_entry() {
665 llInstantMessage(
40 office 666 CORRADE,
4 office 667 wasKeyValueEncode(
668 [
669 "command", "database",
670 "group", wasURLEscape(GROUP),
671 "password", wasURLEscape(PASSWORD),
40 office 672 "SQL", wasURLEscape(SQL_SELECT_VISITOR),
673 "data", wasListToCSV(
674 [
675 "offset",
676 (string)line
677 ]
4 office 678 ),
679 "callback", wasURLEscape(callback)
680 ]
681 )
682 );
40 office 683  
4 office 684 // alarm 60
685 llSetTimerEvent(60);
686 }
687 http_request(key id, string method, string body) {
688 llHTTPResponse(id, 200, "OK");
40 office 689  
690 if(wasKeyValueGet("command", body) == "database") {
691 integer success = wasKeyValueGet("success", body) == "True";
692 if(!success) {
693 // DEBUG
694 llOwnerSay("Failed to query the visitors table: " +
695 wasURLUnescape(
696 wasKeyValueGet(
697 "error",
698 body
699 )
4 office 700 )
40 office 701 );
702 return;
703 }
704  
705 // Grab the data key if it exists.
706 string dataKey = wasURLUnescape(
707 wasKeyValueGet(
708 "data",
709 body
4 office 710 )
711 );
712  
40 office 713 // We got no more rows, so switch back to scanning.
714 if(dataKey == "")
715 state show;
716  
717 list data = wasCSVToList(dataKey);
718  
719 string firstname = llList2String(
4 office 720 data,
40 office 721 llListFindList(
722 data,
723 (list)"firstname"
724 ) + 1
725 );
726 string lastname = llList2String(
4 office 727 data,
40 office 728 llListFindList(
729 data,
730 (list)"lastname"
731 ) + 1
732 );
733 string lastseen = llList2String(
4 office 734 data,
40 office 735 llListFindList(
736 data,
737 (list)"lastseen"
738 ) + 1
739 );
740  
741 llOwnerSay(firstname + " " + lastname + " @ " + lastseen);
742  
743 state display_trampoline;
744 }
4 office 745 }
746 link_message(integer sender_num, integer num, string str, key id) {
40 office 747 if(str == "reset") {
4 office 748 state reset;
40 office 749 }
750 // If the display button is pressed again, go back to the display and stop.
751 if(str == "display") {
752 state show;
753 }
4 office 754 }
755 timer() {
756 // DEBUG
757 llOwnerSay("Timeout reading rows from visitors table...");
758 llResetScript();
759 }
760 on_rez(integer num) {
761 llResetScript();
762 }
763 changed(integer change) {
764 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
765 llResetScript();
766 }
767 }
768 state_exit() {
769 llSetTimerEvent(0);
770 }
771 }
772  
773 state reset {
774 state_entry() {
775 // DEBUG
776 llOwnerSay("Resetting all visitors...");
777 llInstantMessage(
40 office 778 CORRADE,
4 office 779 wasKeyValueEncode(
780 [
781 "command", "database",
782 "group", wasURLEscape(GROUP),
783 "password", wasURLEscape(PASSWORD),
40 office 784 "SQL", wasURLEscape(SQL_DROP_TABLE),
4 office 785 "callback", wasURLEscape(callback)
786 ]
787 )
788 );
40 office 789  
4 office 790 // alarm 60
791 llSetTimerEvent(60);
792 }
793 timer() {
794 // DEBUG
795 llOwnerSay("Timeout deleting database...");
796 llResetScript();
797 }
798 http_request(key id, string method, string body) {
799 llHTTPResponse(id, 200, "OK");
40 office 800 if(wasKeyValueGet("command", body) == "database") {
801 integer success = wasKeyValueGet("success", body) == "True";
802 if(!success) {
803 // DEBUG
804 llOwnerSay("Failed to drop the visitors table: " +
805 wasURLUnescape(
806 wasKeyValueGet(
807 "error",
808 body
809 )
810 )
811 );
812 llResetScript();
813 }
4 office 814 // DEBUG
40 office 815 llOwnerSay("Table dropped...");
4 office 816 llResetScript();
817 }
818 }
819 on_rez(integer num) {
820 llResetScript();
821 }
822 changed(integer change) {
823 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
824 llResetScript();
825 }
826 }
827 state_exit() {
828 llSetTimerEvent(0);
829 }
830 }