corrade-lsl-templates – Blame information for rev 38

Subversion Repositories:
Rev:
Rev Author Line No. Line
38 office 1  
2  
4 office 3 ///////////////////////////////////////////////////////////////////////////
38 office 4 // Copyright (C) Wizardry and Steamworks 2014 - License: GNU GPLv3 //
4 office 5 ///////////////////////////////////////////////////////////////////////////
38 office 6  
4 office 7 ///////////////////////////////////////////////////////////////////////////
38 office 8 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 9 ///////////////////////////////////////////////////////////////////////////
10 string wasKeyValueGet(string k, string data) {
11 if(llStringLength(data) == 0) return "";
12 if(llStringLength(k) == 0) return "";
38 office 13 list a = llParseStringKeepNulls(data, ["&", "="], []);
14 integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
15 if(i != -1) return llList2String(a, 2*i+1);
4 office 16 return "";
17 }
18  
19 ///////////////////////////////////////////////////////////////////////////
38 office 20 // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
4 office 21 ///////////////////////////////////////////////////////////////////////////
22 string wasKeyValueEncode(list data) {
23 list k = llList2ListStrided(data, 0, -1, 2);
24 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
25 data = [];
26 do {
27 data += llList2String(k, 0) + "=" + llList2String(v, 0);
28 k = llDeleteSubList(k, 0, 0);
29 v = llDeleteSubList(v, 0, 0);
30 } while(llGetListLength(k) != 0);
31 return llDumpList2String(data, "&");
32 }
38 office 33  
4 office 34 ///////////////////////////////////////////////////////////////////////////
38 office 35 // Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
4 office 36 ///////////////////////////////////////////////////////////////////////////
37 integer wasListCountExclude(list input, list exclude) {
38 if(llGetListLength(input) == 0) return 0;
38 office 39 if(llListFindList(exclude, (list)llList2String(input, 0)) == -1)
4 office 40 return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
41 return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
42 }
38 office 43  
4 office 44 ///////////////////////////////////////////////////////////////////////////
38 office 45 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
4 office 46 ///////////////////////////////////////////////////////////////////////////
47 string wasListToCSV(list l) {
48 list v = [];
49 do {
50 string a = llDumpList2String(
51 llParseStringKeepNulls(
52 llList2String(
38 office 53 l,
4 office 54  
38 office 55 ),
56 ["\""],
4 office 57 []
58 ),
59 "\"\""
60 );
61 if(llParseStringKeepNulls(a, [" ", ",", "\n"], []) != (list) a)
62 a = "\"" + a + "\"";
63 v += a;
64 l = llDeleteSubList(l, 0, 0);
65 } while(l != []);
66 return llDumpList2String(v, ",");
67 }
38 office 68  
4 office 69 ///////////////////////////////////////////////////////////////////////////
38 office 70 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
4 office 71 ///////////////////////////////////////////////////////////////////////////
72 list wasCSVToList(string csv) {
73 list l = [];
74 list s = [];
75 string m = "";
76 do {
77 string a = llGetSubString(csv, 0, 0);
78 csv = llDeleteSubString(csv, 0, 0);
79 if(a == ",") {
80 if(llList2String(s, -1) != "\"") {
81 l += m;
82 m = "";
83 jump continue;
84 }
85 m += a;
86 jump continue;
87 }
88 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
89 m += a;
90 csv = llDeleteSubString(csv, 0, 0);
91 jump continue;
92 }
93 if(a == "\"") {
94 if(llList2String(s, -1) != a) {
95 s += a;
96 jump continue;
97 }
98 s = llDeleteSubList(s, -1, -1);
99 jump continue;
100 }
101 m += a;
102 @continue;
103 } while(csv != "");
104 // invariant: length(s) = 0
105 return l + m;
106 }
38 office 107  
4 office 108 ///////////////////////////////////////////////////////////////////////////
38 office 109 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
4 office 110 ///////////////////////////////////////////////////////////////////////////
111 // escapes a string in conformance with RFC1738
112 string wasURLEscape(string i) {
113 string o = "";
114 do {
115 string c = llGetSubString(i, 0, 0);
116 i = llDeleteSubString(i, 0, 0);
117 if(c == "") jump continue;
118 if(c == " ") {
119 o += "+";
120 jump continue;
121 }
122 if(c == "\n") {
123 o += "%0D" + llEscapeURL(c);
124 jump continue;
125 }
126 o += llEscapeURL(c);
127 @continue;
128 } while(i != "");
129 return o;
130 }
38 office 131  
4 office 132 ///////////////////////////////////////////////////////////////////////////
38 office 133 // Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
4 office 134 ///////////////////////////////////////////////////////////////////////////
135 // unescapes a string in conformance with RFC1738
136 string wasURLUnescape(string i) {
137 return llUnescapeURL(
138 llDumpList2String(
139 llParseString2List(
140 llDumpList2String(
141 llParseString2List(
38 office 142 i,
143 ["+"],
4 office 144 []
38 office 145 ),
4 office 146 " "
38 office 147 ),
148 ["%0D%0A"],
4 office 149 []
38 office 150 ),
4 office 151 "\n"
152 )
153 );
154 }
38 office 155  
4 office 156 // corrade data
157 string CORRADE = "";
158 string GROUP = "";
159 string PASSWORD = "";
38 office 160 float WAIT = 5;
161  
4 office 162 // for holding the callback URL
163 string callback = "";
38 office 164  
4 office 165 // for notecard reading
166 integer line = 0;
167  
168 // key-value data will be read into this list
169 list tuples = [];
170 // regions will be stored here
171 list regions = [];
172 string region = "";
38 office 173  
4 office 174 default {
175 state_entry() {
176 if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
177 llOwnerSay("Sorry, could not find a configuration inventory notecard.");
178 return;
179 }
180 // DEBUG
181 llOwnerSay("Reading configuration file...");
182 llGetNotecardLine("configuration", line);
183 }
184 dataserver(key id, string data) {
185 if(data == EOF) {
186 // invariant, length(tuples) % 2 == 0
187 if(llGetListLength(tuples) % 2 != 0) {
188 llOwnerSay("Error in configuration notecard.");
189 return;
190 }
191 CORRADE = llList2String(
192 tuples,
193 llListFindList(
38 office 194 tuples,
4 office 195 [
196 "corrade"
197 ]
198 )
38 office 199 +1
200 );
4 office 201 if(CORRADE == "") {
202 llOwnerSay("Error in configuration notecard: corrade");
203 return;
204 }
205 GROUP = llList2String(
206 tuples,
207 llListFindList(
38 office 208 tuples,
4 office 209 [
210 "group"
211 ]
212 )
38 office 213 +1
214 );
4 office 215 if(GROUP == "") {
216 llOwnerSay("Error in configuration notecard: group");
217 return;
218 }
219 PASSWORD = llList2String(
220 tuples,
221 llListFindList(
38 office 222 tuples,
4 office 223 [
224 "password"
225 ]
226 )
38 office 227 +1
228 );
4 office 229 if(PASSWORD == "") {
230 llOwnerSay("Error in configuration notecard: password");
231 return;
232 }
38 office 233 WAIT = (float)llList2String(
234 tuples,
235 llListFindList(
236 tuples,
237 [
238 "wait"
239 ]
240 )
241 +1
242 );
243 if(WAIT == 0) {
244 llOwnerSay("Error in configuration notecard: wait");
245 return;
246 }
4 office 247 // DEBUG
248 llOwnerSay("Read configuration notecard...");
249 state read;
250 }
251 if(data == "") jump continue;
252 integer i = llSubStringIndex(data, "#");
253 if(i != -1) data = llDeleteSubString(data, i, -1);
254 list o = llParseString2List(data, ["="], []);
255 // get rid of starting and ending quotes
256 string k = llDumpList2String(
257 llParseString2List(
258 llStringTrim(
259 llList2String(
38 office 260 o,
4 office 261  
38 office 262 ),
263 STRING_TRIM),
4 office 264 ["\""], []
265 ), "\"");
266 string v = llDumpList2String(
267 llParseString2List(
268 llStringTrim(
269 llList2String(
38 office 270 o,
4 office 271 1
38 office 272 ),
273 STRING_TRIM),
4 office 274 ["\""], []
275 ), "\"");
276 if(k == "" || v == "") jump continue;
277 tuples += k;
278 tuples += v;
279 @continue;
280 llGetNotecardLine("configuration", ++line);
281 }
282 on_rez(integer num) {
283 llResetScript();
284 }
285 changed(integer change) {
286 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
287 llResetScript();
288 }
289 }
290 }
38 office 291  
4 office 292 state read {
293 state_entry() {
294 if(llGetInventoryType("regions") != INVENTORY_NOTECARD) {
295 llOwnerSay("Sorry, could not find a regions inventory notecard.");
296 return;
297 }
298 // DEBUG
299 llOwnerSay("Reading regions notecard...");
300 line = 0;
301 llGetNotecardLine("regions", line);
302 }
303 dataserver(key id, string data) {
304 if(data == EOF) {
305 // DEBUG
306 llOwnerSay("Read regions notcard...");
307 state url;
308 }
309 if(data == "") jump continue;
310 regions += data;
311 @continue;
312 llGetNotecardLine("regions", ++line);
313 }
314 on_rez(integer num) {
315 llResetScript();
316 }
317 changed(integer change) {
318 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
319 llResetScript();
320 }
321 }
322 }
323  
324 state url {
325 state_entry() {
326 // DEBUG
327 llOwnerSay("Requesting URL...");
328 llRequestURL();
329 }
330 http_request(key id, string method, string body) {
331 if(method != URL_REQUEST_GRANTED) return;
332 callback = body;
333 // DEBUG
334 llOwnerSay("Got URL...");
335 state detect;
336 }
337 on_rez(integer num) {
338 llResetScript();
339 }
340 changed(integer change) {
341 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
342 llResetScript();
343 }
344 }
345 }
346  
347 state detect {
348 state_entry() {
349 // DEBUG
350 llOwnerSay("Detecting if Corrade is online...");
351 llSetTimerEvent(5);
352 }
353 timer() {
354 llRequestAgentData((key)CORRADE, DATA_ONLINE);
355 }
356 dataserver(key id, string data) {
357 if(data != "1") {
358 // DEBUG
359 llOwnerSay("Corrade is not online, sleeping...");
360 llSetTimerEvent(30);
361 return;
362 }
38 office 363 state notify;
364 }
365 on_rez(integer num) {
366 llResetScript();
367 }
368 changed(integer change) {
369 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
370 llResetScript();
371 }
372 }
373 }
374  
375 state notify {
376 state_entry() {
377 // Timeout in 60s.
378 llSetTimerEvent(60);
379  
380 // DEBUG
381 llOwnerSay("Binding to the CAPS notification...");
382 llInstantMessage(
383 (key)CORRADE,
384 wasKeyValueEncode(
385 [
386 "command", "notify",
387 "group", wasURLEscape(GROUP),
388 "password", wasURLEscape(PASSWORD),
389 "action", "set",
390 "type", "CAPS",
391 "URL", wasURLEscape(callback),
392 "callback", wasURLEscape(callback)
393 ]
394 )
395 );
396 }
397 http_request(key id, string method, string body) {
398 llHTTPResponse(id, 200, "OK");
399 if(wasKeyValueGet("command", body) != "notify") return;
400 if(wasKeyValueGet("success", body) != "True") {
401 // DEBUG
402 llOwnerSay("Failed to bind to the CAPS notification...");
403 llResetScript();
404 }
405 // DEBUG
406 llOwnerSay("CAPS notification installed...");
4 office 407 state teleport;
408 }
38 office 409 timer() {
410 // DEBUG
411 llOwnerSay("Timeout binding to the CAPS notifications...");
412  
413 llResetScript();
414 }
4 office 415 on_rez(integer num) {
416 llResetScript();
417 }
418 changed(integer change) {
419 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
420 llResetScript();
421 }
422 }
423 }
38 office 424  
4 office 425 state teleport {
426 state_entry() {
38 office 427 // Emergency timeout
4 office 428 llSetTimerEvent(60);
38 office 429  
4 office 430 // Check that Corrade is online.
431 llSensorRepeat("", NULL_KEY, AGENT, 0.1, 0.1, 5);
38 office 432  
4 office 433 // Shuffle the regions and grab the next region.
434 region = llList2String(regions, 0);
435 regions = llDeleteSubList(regions, 0, 0);
436 regions += region;
38 office 437  
438 // The selected region is the current region so reshufle.
439 if(region == llGetRegionName()) {
440 // DEBUG
441 llOwnerSay("Already on current region " + region + ", trying the next region.");
442 region = llList2String(regions, 0);
443 regions = llDeleteSubList(regions, 0, 0);
444 regions += region;
445 }
446  
4 office 447 // DEBUG
448 llOwnerSay("Teleporting to: " + region);
38 office 449  
4 office 450 llInstantMessage(
38 office 451 (key)CORRADE,
4 office 452 wasKeyValueEncode(
453 [
454 "command", "teleport",
455 "group", wasURLEscape(GROUP),
456 "password", wasURLEscape(PASSWORD),
457 "entity", "region",
458 "region", wasURLEscape(region),
38 office 459 "position", wasURLEscape((string)<128, 128, 50>),
4 office 460 "callback", wasURLEscape(callback)
461 ]
462 )
463 );
464 }
465 http_request(key id, string method, string body) {
466 llHTTPResponse(id, 200, "OK");
38 office 467 if(wasKeyValueGet("command", body) == "teleport") {
468 if(wasKeyValueGet("success", body) == "True") {
469 // DEBUG
470 llOwnerSay("Teleported successfully to: " + region + " and waiting for capabiltiies...");
471 return;
472 }
4 office 473 // DEBUG
38 office 474 llOwnerSay("Failed to teleport to " + region + " due to: " +
4 office 475 wasURLUnescape(
476 wasKeyValueGet(
38 office 477 "error",
4 office 478 body
479 )
480 )
481 );
482 // Jump to trampoline for re-entry.
483 state teleport_trampoline;
484 }
38 office 485 if(wasKeyValueGet("notification", body) == "CAPS") {
486 string capsRegion = wasURLUnescape(
487 wasKeyValueGet(
488 "region",
489 body
490 )
491 );
492 string capsAction = wasURLUnescape(
493 wasKeyValueGet(
494 "action",
495 body
496 )
497 );
498  
499 if(capsRegion == region && capsAction == "start") {
500 llOwnerSay("Capabiltiies for region " + region + " successfully connected.");
501 state stats_trampoline;
502 }
503 }
504 }
505 timer() {
4 office 506 // DEBUG
38 office 507 llOwnerSay("Timeout receiving capabilities, attempting emergency teleport...");
508 state teleport_trampoline;
4 office 509 }
510 no_sensor() {
511 llRequestAgentData((key)CORRADE, DATA_ONLINE);
512 }
513 dataserver(key id, string data) {
514 if(data != "1") {
515 // DEBUG
516 llOwnerSay("Corrade is not online, sleeping...");
517 state detect;
518 }
519 }
520 on_rez(integer num) {
521 llResetScript();
522 }
523 changed(integer change) {
524 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
525 llResetScript();
526 }
527 }
528 state_exit() {
529 llSetTimerEvent(0);
530 }
531 }
38 office 532  
4 office 533 state teleport_trampoline {
534 state_entry() {
535 // DEBUG
536 llOwnerSay("Sleeping...");
38 office 537 llSetTimerEvent(WAIT);
4 office 538 }
539 timer() {
540 state teleport;
541 }
542 on_rez(integer num) {
543 llResetScript();
544 }
545 changed(integer change) {
546 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
547 llResetScript();
548 }
549 }
550 state_exit() {
551 llSetTimerEvent(0);
552 }
553 }
38 office 554  
4 office 555 state stats_trampoline {
556 state_entry() {
557 // DEBUG
558 llOwnerSay("Sleeping...");
38 office 559 llSetTimerEvent(1);
4 office 560 }
561 timer() {
562 state stats;
563 }
564 on_rez(integer num) {
565 llResetScript();
566 }
567 changed(integer change) {
568 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
569 llResetScript();
570 }
571 }
572 state_exit() {
573 llSetTimerEvent(0);
574 }
575 }
38 office 576  
4 office 577 state stats {
578 state_entry() {
579 // Timeout in one minute.
580 llSetTimerEvent(60);
38 office 581  
4 office 582 // Check that Corrade is online.
583 llSensorRepeat("", NULL_KEY, AGENT, 0.1, 0.1, 5);
38 office 584  
4 office 585 // DEBUG
586 llOwnerSay("Fetching region statistics...");
587 llInstantMessage(
38 office 588 (key)CORRADE,
4 office 589 wasKeyValueEncode(
590 [
591 "command", "getregiondata",
592 "group", wasURLEscape(GROUP),
593 "password", wasURLEscape(PASSWORD),
594 "data", wasListToCSV([
38 office 595 // For a full list see: https://grimore.org/secondlife/scripted_agents/corrade/api/commands/getregiondata
596 "Stats.LastLag",
4 office 597 "Stats.Agents",
598 "Stats.Dilation",
599 "Stats.FPS",
600 "Stats.ActiveScripts",
601 "Stats.ScriptTime",
602 "Stats.Objects",
603 "Stats.PhysicsFPS",
604 "Stats.ScriptTime"
605 ]),
38 office 606 "callback", wasURLEscape(callback)
4 office 607 ]
608 )
609 );
610 }
611 http_request(key id, string method, string body) {
612 llHTTPResponse(id, 200, "OK");
38 office 613 // Ignore CAPS notification here.
614 if(wasKeyValueGet("notification", body) == "CAPS") {
615 return;
616 }
4 office 617 if(wasKeyValueGet("command", body) != "getregiondata" ||
618 wasKeyValueGet("success", body) != "True") {
619 // DEBUG
38 office 620 llOwnerSay("Failed to get stats for " + region + " due to: " +
4 office 621 wasURLUnescape(
622 wasKeyValueGet(
38 office 623 "error",
4 office 624 body
625 )
626 )
627 );
628 // Jump to trampoline for teleport.
629 state teleport_trampoline;
630 }
631 // DEBUG
632 llOwnerSay("Got stats for region: " + region);
633 // Get the stats and unescape.
634 list stat = wasCSVToList(
635 wasURLUnescape(
636 wasKeyValueGet(
38 office 637 "data",
4 office 638 body
639 )
640 )
641 );
642 llSetText("-:[ " + region + " ]:- \n" +
643 // Show the stats in the overhead text.
644 "Agents: " + llList2String(
38 office 645 stat,
4 office 646 llListFindList(
38 office 647 stat,
4 office 648 (list)"Stats.Agents"
649 )+1
38 office 650 ) + "\n" +
4 office 651 "LastLag: " + llList2String(
38 office 652 stat,
4 office 653 llListFindList(
38 office 654 stat,
4 office 655 (list)"Stats.LastLag"
656 )+1
38 office 657 ) + "\n" +
4 office 658 "Time Dilation: " + llList2String(
38 office 659 stat,
4 office 660 llListFindList(
38 office 661 stat,
4 office 662 (list)"Stats.Dilation"
663 )+1
38 office 664 ) + "\n" +
4 office 665 "FPS: " + llList2String(
38 office 666 stat,
4 office 667 llListFindList(
38 office 668 stat,
4 office 669 (list)"Stats.FPS"
670 )+1
671 ) + "\n" +
672 "Physics FPS: " + llList2String(
38 office 673 stat,
4 office 674 llListFindList(
38 office 675 stat,
4 office 676 (list)"Stats.PhysicsFPS"
677 )+1
678 ) + "\n" +
679 "Scripts: " + llList2String(
38 office 680 stat,
4 office 681 llListFindList(
38 office 682 stat,
4 office 683 (list)"Stats.ActiveScripts"
684 )+1
685 ) + "\n" +
686 "Script Time: " + llList2String(
38 office 687 stat,
4 office 688 llListFindList(
38 office 689 stat,
4 office 690 (list)"Stats.ScriptTime"
691 )+1
692 ) + "\n" +
693 "Objects: " + llList2String(
38 office 694 stat,
4 office 695 llListFindList(
696 stat, (list)"Stats.Objects"
697 )+1
38 office 698 ),
699 <1, 0, 0>,
4 office 700 1.0
701 );
702 stat = [];
703 state teleport_trampoline;
704 }
705 no_sensor() {
706 llRequestAgentData((key)CORRADE, DATA_ONLINE);
707 }
708 dataserver(key id, string data) {
709 if(data != "1") {
710 // DEBUG
711 llOwnerSay("Corrade is not online, sleeping...");
712 state detect;
713 }
714 }
715 timer() {
716 state teleport_trampoline;
717 }
718 on_rez(integer num) {
719 llResetScript();
720 }
721 changed(integer change) {
722 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
723 llResetScript();
724 }
725 }
726 state_exit() {
727 llSetTimerEvent(0);
728 }
729 }