corrade-lsl-templates – Diff between revs 29 and 39

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