corrade-lsl-templates – Blame information for rev 29

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 // This is a device meant to scan regions and show metrics for the Corrade
6 // Second Life / OpenSim bot. You can find more details about the bot
7 // by following the URL: http://was.fm/secondlife/scripted_agents/corrade
8 //
9 // The script works in conjunction with a "configuration" notecard and a
10 // "regions" notecard that must both be placed in the same primitive.
11 // The purpose of this script is to demonstrate scanning with Corrade and
29 office 12 // you are free to use, change, and commercialize it under the CC BY 2.0
13 // license at: https://creativecommons.org/licenses/by/2.0
4 office 14 //
15 ///////////////////////////////////////////////////////////////////////////
16  
17 ///////////////////////////////////////////////////////////////////////////
29 office 18 // Copyright (C) 2014 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 19 ///////////////////////////////////////////////////////////////////////////
20 string wasKeyValueGet(string k, string data) {
21 if(llStringLength(data) == 0) return "";
22 if(llStringLength(k) == 0) return "";
23 list a = llParseString2List(data, ["&", "="], []);
24 integer i = llListFindList(a, [ k ]);
25 if(i != -1) return llList2String(a, i+1);
26 return "";
27 }
28  
29 ///////////////////////////////////////////////////////////////////////////
29 office 30 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 31 ///////////////////////////////////////////////////////////////////////////
32 string wasKeyValueEncode(list data) {
33 list k = llList2ListStrided(data, 0, -1, 2);
34 list v = llList2ListStrided(llDeleteSubList(data, 0, 0), 0, -1, 2);
35 data = [];
36 do {
37 data += llList2String(k, 0) + "=" + llList2String(v, 0);
38 k = llDeleteSubList(k, 0, 0);
39 v = llDeleteSubList(v, 0, 0);
40 } while(llGetListLength(k) != 0);
41 return llDumpList2String(data, "&");
42 }
43  
44 ///////////////////////////////////////////////////////////////////////////
29 office 45 // Copyright (C) 2013 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 46 ///////////////////////////////////////////////////////////////////////////
47 integer wasListCountExclude(list input, list exclude) {
48 if(llGetListLength(input) == 0) return 0;
49 if(llListFindList(exclude, (list)llList2String(input, 0)) == -1)
50 return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
51 return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
52 }
53  
54 ///////////////////////////////////////////////////////////////////////////
29 office 55 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 56 ///////////////////////////////////////////////////////////////////////////
57 string wasListToCSV(list l) {
58 list v = [];
59 do {
60 string a = llDumpList2String(
61 llParseStringKeepNulls(
62 llList2String(
63 l,
64  
65 ),
66 ["\""],
67 []
68 ),
69 "\"\""
70 );
71 if(llParseStringKeepNulls(a, [" ", ",", "\n"], []) != (list) a)
72 a = "\"" + a + "\"";
73 v += a;
74 l = llDeleteSubList(l, 0, 0);
75 } while(l != []);
76 return llDumpList2String(v, ",");
77 }
78  
79 ///////////////////////////////////////////////////////////////////////////
29 office 80 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 81 ///////////////////////////////////////////////////////////////////////////
82 list wasCSVToList(string csv) {
83 list l = [];
84 list s = [];
85 string m = "";
86 do {
87 string a = llGetSubString(csv, 0, 0);
88 csv = llDeleteSubString(csv, 0, 0);
89 if(a == ",") {
90 if(llList2String(s, -1) != "\"") {
91 l += m;
92 m = "";
93 jump continue;
94 }
95 m += a;
96 jump continue;
97 }
98 if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
99 m += a;
100 csv = llDeleteSubString(csv, 0, 0);
101 jump continue;
102 }
103 if(a == "\"") {
104 if(llList2String(s, -1) != a) {
105 s += a;
106 jump continue;
107 }
108 s = llDeleteSubList(s, -1, -1);
109 jump continue;
110 }
111 m += a;
112 @continue;
113 } while(csv != "");
114 // invariant: length(s) = 0
115 return l + m;
116 }
117  
118 ///////////////////////////////////////////////////////////////////////////
29 office 119 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 120 ///////////////////////////////////////////////////////////////////////////
121 // escapes a string in conformance with RFC1738
122 string wasURLEscape(string i) {
123 string o = "";
124 do {
125 string c = llGetSubString(i, 0, 0);
126 i = llDeleteSubString(i, 0, 0);
127 if(c == "") jump continue;
128 if(c == " ") {
129 o += "+";
130 jump continue;
131 }
132 if(c == "\n") {
133 o += "%0D" + llEscapeURL(c);
134 jump continue;
135 }
136 o += llEscapeURL(c);
137 @continue;
138 } while(i != "");
139 return o;
140 }
141  
142 ///////////////////////////////////////////////////////////////////////////
29 office 143 // Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
4 office 144 ///////////////////////////////////////////////////////////////////////////
145 // unescapes a string in conformance with RFC1738
146 string wasURLUnescape(string i) {
147 return llUnescapeURL(
148 llDumpList2String(
149 llParseString2List(
150 llDumpList2String(
151 llParseString2List(
152 i,
153 ["+"],
154 []
155 ),
156 " "
157 ),
158 ["%0D%0A"],
159 []
160 ),
161 "\n"
162 )
163 );
164 }
165  
166 // corrade data
167 string CORRADE = "";
168 string GROUP = "";
169 string PASSWORD = "";
170  
171 // for holding the callback URL
172 string callback = "";
173  
174 // for notecard reading
175 integer line = 0;
176  
177 // key-value data will be read into this list
178 list tuples = [];
179 // regions will be stored here
180 list regions = [];
181 string region = "";
182  
183 default {
184 state_entry() {
185 if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
186 llOwnerSay("Sorry, could not find a configuration inventory notecard.");
187 return;
188 }
189 // DEBUG
190 llOwnerSay("Reading configuration file...");
191 llGetNotecardLine("configuration", line);
192 }
193 dataserver(key id, string data) {
194 if(data == EOF) {
195 // invariant, length(tuples) % 2 == 0
196 if(llGetListLength(tuples) % 2 != 0) {
197 llOwnerSay("Error in configuration notecard.");
198 return;
199 }
200 CORRADE = llList2String(
201 tuples,
202 llListFindList(
203 tuples,
204 [
205 "corrade"
206 ]
207 )
208 +1);
209 if(CORRADE == "") {
210 llOwnerSay("Error in configuration notecard: corrade");
211 return;
212 }
213 GROUP = llList2String(
214 tuples,
215 llListFindList(
216 tuples,
217 [
218 "group"
219 ]
220 )
221 +1);
222 if(GROUP == "") {
223 llOwnerSay("Error in configuration notecard: group");
224 return;
225 }
226 PASSWORD = llList2String(
227 tuples,
228 llListFindList(
229 tuples,
230 [
231 "password"
232 ]
233 )
234 +1);
235 if(PASSWORD == "") {
236 llOwnerSay("Error in configuration notecard: password");
237 return;
238 }
239 // DEBUG
240 llOwnerSay("Read configuration notecard...");
241 state read;
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 read {
285 state_entry() {
286 if(llGetInventoryType("regions") != INVENTORY_NOTECARD) {
287 llOwnerSay("Sorry, could not find a regions inventory notecard.");
288 return;
289 }
290 // DEBUG
291 llOwnerSay("Reading regions notecard...");
292 line = 0;
293 llGetNotecardLine("regions", line);
294 }
295 dataserver(key id, string data) {
296 if(data == EOF) {
297 // DEBUG
298 llOwnerSay("Read regions notcard...");
299 state url;
300 }
301 if(data == "") jump continue;
302 regions += data;
303 @continue;
304 llGetNotecardLine("regions", ++line);
305 }
306 on_rez(integer num) {
307 llResetScript();
308 }
309 changed(integer change) {
310 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
311 llResetScript();
312 }
313 }
314 }
315  
316 state url {
317 state_entry() {
318 // DEBUG
319 llOwnerSay("Requesting URL...");
320 llRequestURL();
321 }
322 http_request(key id, string method, string body) {
323 if(method != URL_REQUEST_GRANTED) return;
324 callback = body;
325 // DEBUG
326 llOwnerSay("Got URL...");
327 state detect;
328 }
329 on_rez(integer num) {
330 llResetScript();
331 }
332 changed(integer change) {
333 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
334 llResetScript();
335 }
336 }
337 }
338  
339 state detect {
340 state_entry() {
341 // DEBUG
342 llOwnerSay("Detecting if Corrade is online...");
343 llSetTimerEvent(5);
344 }
345 timer() {
346 llRequestAgentData((key)CORRADE, DATA_ONLINE);
347 }
348 dataserver(key id, string data) {
349 if(data != "1") {
350 // DEBUG
351 llOwnerSay("Corrade is not online, sleeping...");
352 llSetTimerEvent(30);
353 return;
354 }
355 state teleport;
356 }
357 on_rez(integer num) {
358 llResetScript();
359 }
360 changed(integer change) {
361 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
362 llResetScript();
363 }
364 }
365 }
366  
367 state teleport {
368 state_entry() {
369 // Timeout in one minute.
370 llSetTimerEvent(60);
371 // Check that Corrade is online.
372 llSensorRepeat("", NULL_KEY, AGENT, 0.1, 0.1, 5);
373 // Shuffle the regions and grab the next region.
374 region = llList2String(regions, 0);
375 regions = llDeleteSubList(regions, 0, 0);
376 regions += region;
377 // DEBUG
378 llOwnerSay("Teleporting to: " + region);
379 llInstantMessage(
380 (key)CORRADE,
381 wasKeyValueEncode(
382 [
383 "command", "teleport",
384 "group", wasURLEscape(GROUP),
385 "password", wasURLEscape(PASSWORD),
386 "entity", "region",
387 "region", wasURLEscape(region),
388 "callback", wasURLEscape(callback)
389 ]
390 )
391 );
392 }
393 http_request(key id, string method, string body) {
394 llHTTPResponse(id, 200, "OK");
395 if(wasKeyValueGet("command", body) != "teleport" ||
396 wasKeyValueGet("success", body) != "True") {
397 // DEBUG
398 llOwnerSay("Failed to teleport to " + region + " due to: " +
399 wasURLUnescape(
400 wasKeyValueGet(
401 "error",
402 body
403 )
404 )
405 );
406 // Jump to trampoline for re-entry.
407 state teleport_trampoline;
408 }
409 // DEBUG
410 llOwnerSay("Teleported successfully to: " + region);
411 state stats_trampoline;
412 }
413 no_sensor() {
414 llRequestAgentData((key)CORRADE, DATA_ONLINE);
415 }
416 dataserver(key id, string data) {
417 if(data != "1") {
418 // DEBUG
419 llOwnerSay("Corrade is not online, sleeping...");
420 state detect;
421 }
422 }
423 timer() {
424 state teleport_trampoline;
425 }
426 on_rez(integer num) {
427 llResetScript();
428 }
429 changed(integer change) {
430 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
431 llResetScript();
432 }
433 }
434 state_exit() {
435 llSetTimerEvent(0);
436 }
437 }
438  
439 state teleport_trampoline {
440 state_entry() {
441 // DEBUG
442 llOwnerSay("Sleeping...");
443 llSetTimerEvent(30);
444 }
445 timer() {
446 state teleport;
447 }
448 on_rez(integer num) {
449 llResetScript();
450 }
451 changed(integer change) {
452 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
453 llResetScript();
454 }
455 }
456 state_exit() {
457 llSetTimerEvent(0);
458 }
459 }
460  
461 state stats_trampoline {
462 state_entry() {
463 // DEBUG
464 llOwnerSay("Sleeping...");
465 llSetTimerEvent(10);
466 }
467 timer() {
468 state stats;
469 }
470 on_rez(integer num) {
471 llResetScript();
472 }
473 changed(integer change) {
474 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
475 llResetScript();
476 }
477 }
478 state_exit() {
479 llSetTimerEvent(0);
480 }
481 }
482  
483 state stats {
484 state_entry() {
485 // Timeout in one minute.
486 llSetTimerEvent(60);
487 // Check that Corrade is online.
488 llSensorRepeat("", NULL_KEY, AGENT, 0.1, 0.1, 5);
489 // DEBUG
490 llOwnerSay("Fetching region statistics...");
491 llInstantMessage(
492 (key)CORRADE,
493 wasKeyValueEncode(
494 [
495 "command", "getregiondata",
496 "group", wasURLEscape(GROUP),
497 "password", wasURLEscape(PASSWORD),
498 "data", wasListToCSV([
499 // For a full list see: http://was.fm/secondlife/scripted_agents/corrade/application_programming_interface#get_region_data
500 "Stats.LastLag",
501 "Stats.Agents",
502 "Stats.Dilation",
503 "Stats.FPS",
504 "Stats.ActiveScripts",
505 "Stats.ScriptTime",
506 "Stats.Objects",
507 "Stats.PhysicsFPS",
508 "Stats.ScriptTime"
509 ]),
510 "callback", wasURLEscape(callback)
511 ]
512 )
513 );
514 }
515 http_request(key id, string method, string body) {
516 llHTTPResponse(id, 200, "OK");
517 if(wasKeyValueGet("command", body) != "getregiondata" ||
518 wasKeyValueGet("success", body) != "True") {
519 // DEBUG
520 llOwnerSay("Failed to get stats for " + region + " due to: " +
521 wasURLUnescape(
522 wasKeyValueGet(
523 "error",
524 body
525 )
526 )
527 );
528 // Jump to trampoline for teleport.
529 state teleport_trampoline;
530 }
531 // DEBUG
532 llOwnerSay("Got stats for region: " + region);
533 // Get the stats and unescape.
534 list stat = wasCSVToList(
535 wasURLUnescape(
536 wasKeyValueGet(
537 "data",
538 body
539 )
540 )
541 );
542 llSetText("-:[ " + region + " ]:- \n" +
543 // Show the stats in the overhead text.
544 "Agents: " + llList2String(
545 stat,
546 llListFindList(
547 stat,
548 (list)"Stats.Agents"
549 )+1
550 ) + "\n" +
551 "LastLag: " + llList2String(
552 stat,
553 llListFindList(
554 stat,
555 (list)"Stats.LastLag"
556 )+1
557 ) + "\n" +
558 "Time Dilation: " + llList2String(
559 stat,
560 llListFindList(
561 stat,
562 (list)"Stats.Dilation"
563 )+1
564 ) + "\n" +
565 "FPS: " + llList2String(
566 stat,
567 llListFindList(
568 stat,
569 (list)"Stats.FPS"
570 )+1
571 ) + "\n" +
572 "Physics FPS: " + llList2String(
573 stat,
574 llListFindList(
575 stat,
576 (list)"Stats.PhysicsFPS"
577 )+1
578 ) + "\n" +
579 "Scripts: " + llList2String(
580 stat,
581 llListFindList(
582 stat,
583 (list)"Stats.ActiveScripts"
584 )+1
585 ) + "\n" +
586 "Script Time: " + llList2String(
587 stat,
588 llListFindList(
589 stat,
590 (list)"Stats.ScriptTime"
591 )+1
592 ) + "\n" +
593 "Objects: " + llList2String(
594 stat,
595 llListFindList(
596 stat, (list)"Stats.Objects"
597 )+1
598 ),
599 <1, 0, 0>,
600 1.0
601 );
602 stat = [];
603 state teleport_trampoline;
604 }
605 no_sensor() {
606 llRequestAgentData((key)CORRADE, DATA_ONLINE);
607 }
608 dataserver(key id, string data) {
609 if(data != "1") {
610 // DEBUG
611 llOwnerSay("Corrade is not online, sleeping...");
612 state detect;
613 }
614 }
615 timer() {
616 state teleport_trampoline;
617 }
618 on_rez(integer num) {
619 llResetScript();
620 }
621 changed(integer change) {
622 if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
623 llResetScript();
624 }
625 }
626 state_exit() {
627 llSetTimerEvent(0);
628 }
629 }
630