corrade-lsl-templates

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 33  →  ?path2? @ 34
/source/grid-avatar-tracker/configuration.txt
@@ -0,0 +1,21 @@
############################# START CONFIGURATION ############################
 
# This is a configuration notecard based on the key-value data Wizardry and Steamworks reader.
# Everything following the "#" character is ignored along with blank lines. Values can be set for various
# parameters in a simple and understandable format with the following syntax:
# KEY = "VALUE"
# Every time you change this configuration notecard, the script will reset itself and the new configuration
# will be read from this notecard.
 
# The "corrade", "group" and "password" settings must correspond to your Corrade settings.
 
# This is the UUID of the Corrade bot.
corrade = "fa6d6450-48f0-430f-868c-c6bbb7298983"
 
# The name of the group - it can also be the UUID of the group.
group = "My Group"
 
# The password for the group.
password = "mypassword"
 
############################# END CONFIGURATION #############################
/source/grid-avatar-tracker/dot.lsl
@@ -0,0 +1,180 @@
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
///////////////////////////////////////////////////////////////////////////
string wasListToCSV(list l) {
list v = [];
do {
string a = llDumpList2String(
llParseStringKeepNulls(
llList2String(
l,
0
),
["\""],
[]
),
"\"\""
);
if(llParseStringKeepNulls(
a,
[" ", ",", "\n", "\""], []
) !=
(list) a
) a = "\"" + a + "\"";
v += a;
l = llDeleteSubList(l, 0, 0);
} while(l != []);
return llDumpList2String(v, ",");
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
///////////////////////////////////////////////////////////////////////////
list wasCSVToList(string csv) {
list l = [];
list s = [];
string m = "";
do {
string a = llGetSubString(csv, 0, 0);
csv = llDeleteSubString(csv, 0, 0);
if(a == ",") {
if(llList2String(s, -1) != "\"") {
l += m;
m = "";
jump continue;
}
m += a;
jump continue;
}
if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
m += a;
csv = llDeleteSubString(csv, 0, 0);
jump continue;
}
if(a == "\"") {
if(llList2String(s, -1) != a) {
s += a;
jump continue;
}
s = llDeleteSubList(s, -1, -1);
jump continue;
}
m += a;
@continue;
} while(csv != "");
// postcondition: length(s) = 0
return l + m;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
float wasMapValueToRange(float value, float xMin, float xMax, float yMin, float yMax) {
return yMin + (
(
yMax - yMin
)
*
(
value - xMin
)
/
(
xMax - xMin
)
);
}
 
vector position = ZERO_VECTOR;
string region = "";
string avatar = "";
 
default
{
touch_start(integer num) {
// Do not send the SLURL if no avatar has been reported.
if(position == ZERO_VECTOR ||
region == "" ||
avatar == "")
return;
llInstantMessage(
llDetectedKey(0),
"Avatar: " +
avatar +
" is currently at: " +
"http://maps.secondlife.com/secondlife/" +
llEscapeURL(region )+
"/" +
(string)llFloor(position.x) +
"/" +
(string)llFloor(position.y) +
"/0"
);
}
link_message(integer link, integer value, string message, key id) {
// DEBUG
//llOwnerSay("Data received: " + message);
list data = wasCSVToList(message);
 
avatar = llList2String(
data,
llListFindList(
data,
[
"avatar"
]
) + 1
);
region = llList2String(
data,
llListFindList(
data,
[
"region"
]
) + 1
);
 
position = (vector)llList2String(
data,
llListFindList(
data,
[
"position"
]
) + 1
);
 
vector scale = (vector)llList2String(
data,
llListFindList(
data,
[
"scale"
]
) + 1
);
float x = wasMapValueToRange(position.x, 0, 256, 0, scale.x) - scale.x / 2;
float y = wasMapValueToRange(position.y, 0, 256, 0, scale.y) - scale.y / 2;
// Set the dot position.
llSetPos(<x, y, 0>);
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if((change & CHANGED_INVENTORY) ||
(change & CHANGED_REGION_START) ||
(change & CHANGED_OWNER)) {
llResetScript();
}
}
state_exit() {
llSetTimerEvent(0);
}
}
/source/grid-avatar-tracker/map.lsl
@@ -0,0 +1,643 @@
///////////////////////////////////////////////////////////////////////////
// Copyright (C) Wizardry and Steamworks 2013 - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueEncode(list kvp) {
if(llGetListLength(kvp) < 2) return "";
string k = llList2String(kvp, 0);
kvp = llDeleteSubList(kvp, 0, 0);
string v = llList2String(kvp, 0);
kvp = llDeleteSubList(kvp, 0, 0);
if(llGetListLength(kvp) < 2) return k + "=" + v;
return k + "=" + v + "&" + wasKeyValueEncode(kvp);
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
// escapes a string in conformance with RFC1738
string wasURLEscape(string i) {
string o = "";
do {
string c = llGetSubString(i, 0, 0);
i = llDeleteSubString(i, 0, 0);
if(c == "") jump continue;
if(c == " ") {
o += "+";
jump continue;
}
if(c == "\n") {
o += "%0D" + llEscapeURL(c);
jump continue;
}
o += llEscapeURL(c);
@continue;
} while(i != "");
return o;
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
// unescapes a string in conformance with RFC1738
string wasURLUnescape(string i) {
return llUnescapeURL(
llDumpList2String(
llParseString2List(
llDumpList2String(
llParseString2List(
i,
["+"],
[]
),
" "
),
["%0D%0A"],
[]
),
"\n"
)
);
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
///////////////////////////////////////////////////////////////////////////
string wasKeyValueGet(string k, string data) {
if(llStringLength(data) == 0) return "";
if(llStringLength(k) == 0) return "";
list a = llParseString2List(data, ["&", "="], []);
integer i = llListFindList(llList2ListStrided(a, 0, -1, 2), [ k ]);
if(i != -1) return llList2String(a, 2*i+1);
return "";
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
///////////////////////////////////////////////////////////////////////////
string wasListToCSV(list l) {
list v = [];
do {
string a = llDumpList2String(
llParseStringKeepNulls(
llList2String(
l,
0
),
["\""],
[]
),
"\"\""
);
if(llParseStringKeepNulls(
a,
[" ", ",", "\n", "\""], []
) !=
(list) a
) a = "\"" + a + "\"";
v += a;
l = llDeleteSubList(l, 0, 0);
} while(l != []);
return llDumpList2String(v, ",");
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2015 Wizardry and Steamworks - License: CC BY 2.0 //
///////////////////////////////////////////////////////////////////////////
list wasCSVToList(string csv) {
list l = [];
list s = [];
string m = "";
do {
string a = llGetSubString(csv, 0, 0);
csv = llDeleteSubString(csv, 0, 0);
if(a == ",") {
if(llList2String(s, -1) != "\"") {
l += m;
m = "";
jump continue;
}
m += a;
jump continue;
}
if(a == "\"" && llGetSubString(csv, 0, 0) == a) {
m += a;
csv = llDeleteSubString(csv, 0, 0);
jump continue;
}
if(a == "\"") {
if(llList2String(s, -1) != a) {
s += a;
jump continue;
}
s = llDeleteSubList(s, -1, -1);
jump continue;
}
m += a;
@continue;
} while(csv != "");
// postcondition: length(s) = 0
return l + m;
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
integer wasMenuIndex = 0;
list wasDialogMenu(list input, list actions, string direction) {
integer cut = 11-wasListCountExclude(actions, [""]);
if(direction == ">" && (wasMenuIndex+1)*cut+wasMenuIndex+1 < llGetListLength(input)) {
++wasMenuIndex;
jump slice;
}
if(direction == "<" && wasMenuIndex-1 >= 0) {
--wasMenuIndex;
jump slice;
}
@slice;
integer multiple = wasMenuIndex*cut;
input = llList2List(input, multiple+wasMenuIndex, multiple+cut+wasMenuIndex);
input = wasListMerge(input, actions, "");
return input;
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
integer wasListCountExclude(list input, list exclude) {
if(llGetListLength(input) == 0) return 0;
if(llListFindList(exclude, (list)llList2String(input, 0)) == -1)
return 1 + wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
return wasListCountExclude(llDeleteSubList(input, 0, 0), exclude);
}
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2013 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
list wasListMerge(list l, list m, string merge) {
if(llGetListLength(l) == 0 && llGetListLength(m) == 0) return [];
string a = llList2String(m, 0);
if(a != merge) return [ a ] + wasListMerge(l, llDeleteSubList(m, 0, 0), merge);
return [ llList2String(l, 0) ] + wasListMerge(llDeleteSubList(l, 0, 0), llDeleteSubList(m, 0, 0), merge);
}
 
///////////////////////////////////////////////////////////////////////////
// Copyright (C) 2019 Wizardry and Steamworks - License: GNU GPLv3 //
///////////////////////////////////////////////////////////////////////////
float wasMapValueToRange(float value, float xMin, float xMax, float yMin, float yMax) {
return yMin + (
(
yMax - yMin
)
*
(
value - xMin
)
/
(
xMax - xMin
)
);
}
 
// Move the dot over the local positon of the avatar on the map.
updateDot(string message) {
if(active == "")
return;
 
integer idx = llListFindList(avatars, [ message ]);
if(idx == -1) {
// DEBUG
llOwnerSay("Avatar not found in tracked list...");
return;
}
 
// Stride 4: Avatar name x region name x local position x map UUID
list update = llList2List(avatars, idx, idx + 3);
 
string region = llList2String(update, 1);
vector position = (vector)llList2String(update, 2);
key mapUUID = llList2Key(update, 3);
 
// DEBUG
//llOwnerSay("Updating map display...");
 
// Set the map textue.
llSetTexture((string)mapUUID, 0);
 
// Compute the offset to move the dot and send it to the link set.
vector scale = llGetScale();
 
// DEBUG
//llOwnerSay("position:" + (string)position + " x: " + (string)x + " y:" + (string)y);
 
llMessageLinked(
LINK_SET,
0,
wasListToCSV(
[
"avatar",
message,
"region",
region,
"position",
(string)position,
"scale",
(string)scale
]
),
NULL_KEY
);
}
 
// corrade data
string CORRADE = "";
string GROUP = "";
string PASSWORD = "";
 
 
// for holding the callback URL
string callback = "";
 
// menu for selecting avatars
list menu = [];
 
// Stride 4: Avatar name x region name x local position x map UUID
list avatars = [];
 
// the active avatar
string active = "";
// key-value data will be read into this list
list tuples = [];
 
// for notecard reading
integer line = 0;
default {
state_entry() {
if(llGetInventoryType("configuration") != INVENTORY_NOTECARD) {
llOwnerSay("Sorry, could not find an inventory notecard.");
return;
}
// DEBUG
llOwnerSay("Reading configuration file...");
llGetNotecardLine("configuration", line);
}
dataserver(key id, string data) {
if(data == EOF) {
// invariant, length(tuples) % 2 == 0
if(llGetListLength(tuples) % 2 != 0) {
llOwnerSay("Error in configuration notecard.");
return;
}
CORRADE = llList2String(
tuples,
llListFindList(
tuples,
[
"corrade"
]
)
+1);
if(CORRADE == "") {
llOwnerSay("Error in configuration notecard: corrade");
return;
}
GROUP = llList2String(
tuples,
llListFindList(
tuples,
[
"group"
]
)
+1);
if(GROUP == "") {
llOwnerSay("Error in configuration notecard: password");
return;
}
PASSWORD = llList2String(
tuples,
llListFindList(
tuples,
[
"password"
]
)
+1);
if(GROUP == "") {
llOwnerSay("Error in configuration notecard: group");
return;
}
// DEBUG
llOwnerSay("Read configuration file...");
state url;
}
if(data == "") jump continue;
integer i = llSubStringIndex(data, "#");
if(i != -1) data = llDeleteSubString(data, i, -1);
list o = llParseString2List(data, ["="], []);
// get rid of starting and ending quotes
string k = llDumpList2String(
llParseString2List(
llStringTrim(
llList2String(
o,
0
),
STRING_TRIM),
["\""], []
), "\"");
string v = llDumpList2String(
llParseString2List(
llStringTrim(
llList2String(
o,
1
),
STRING_TRIM),
["\""], []
), "\"");
if(k == "" || v == "") jump continue;
tuples += k;
tuples += v;
@continue;
llGetNotecardLine("configuration", ++line);
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
llResetScript();
}
}
}
 
state url {
state_entry() {
// DEBUG
llOwnerSay("Requesting URL...");
llRequestURL();
}
http_request(key id, string method, string body) {
if(method != URL_REQUEST_GRANTED) return;
callback = body;
// DEBUG
llOwnerSay("Got URL...");
state bind_tracker;
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if((change & CHANGED_INVENTORY) || (change & CHANGED_REGION_START)) {
llResetScript();
}
}
}
state bind_tracker {
state_entry() {
// DEBUG
llOwnerSay("Binding to tracker notification...");
llInstantMessage(CORRADE,
wasKeyValueEncode(
[
"command", "notify",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
"action", "set",
"type", "tracker",
"URL", wasURLEscape(callback),
"callback", wasURLEscape(callback)
]
)
);
llSetTimerEvent(60);
}
http_request(key id, string method, string body) {
llHTTPResponse(id, 200, "OK");
if(wasKeyValueGet("command", body) != "notify" ||
wasKeyValueGet("success", body) != "True") {
// DEBUG
llOwnerSay("Unable to bind to tracker notification: " +
wasURLUnescape(
wasKeyValueGet("error", body)
)
);
 
llResetScript();
}
// DEBUG
llOwnerSay("Tracking notification bound...");
state tracker;
}
timer() {
// DEBUG
llOwnerSay("Timeout binding to tracker notification...");
llResetScript();
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if((change & CHANGED_INVENTORY) ||
(change & CHANGED_REGION_START) ||
(change & CHANGED_OWNER)) {
llResetScript();
}
}
state_exit() {
llSetTimerEvent(0);
}
}
 
state tracker {
state_entry() {
// DEBUG
llOwnerSay("Tracking...");
}
touch_start(integer num_detected) {
if(llGetListLength(avatars) == 0) {
llSay(0, "No avatars have been registered yet.");
return;
}
menu = llList2ListStrided(avatars, 0, -1, 4);
 
// DEBUG
llOwnerSay("Sending menu with tracked avatars: " + llDumpList2String(menu, ","));
integer comChannel = ((integer)("0x"+llGetSubString((string)llDetectedKey(0),-8,-1)) & 0x3FFFFFFF) ^ 0xBFFFFFFF;
llListen(comChannel, "", llDetectedKey(0), "");
llDialog(llDetectedKey(0), "Please select an avatar from the list of avatars to display on the map.", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], ""), comChannel);
}
listen(integer channel, string name, key id, string message) {
if(message == "⟵ Back") {
llDialog(id, "Please select an avatar from the list of avatars to display on the map.", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], "<"), channel);
return;
}
if(message == "Next ⟶") {
llDialog(id, "Please select an avatar from the list of avatars to display on the map.", wasDialogMenu(menu, ["⟵ Back", "", "Next ⟶"], ">"), channel);
return;
}
// DEBUG
llOwnerSay("Chosen avatar: " + message);
 
// Set the active avatar.
active = message;
 
llSetTimerEvent(1);
}
timer() {
// Update the avatar on the map.
updateDot(active);
}
http_request(key id, string method, string body) {
llHTTPResponse(id, 200, "OK");
// Process tracker notification by retrieving the map for the region.
if(wasKeyValueGet("type", body) == "tracker") {
// DEBUG
llOwnerSay("Tracker: " + wasURLUnescape(body));
string firstname = wasKeyValueGet("firstname", body);
string lastname = wasKeyValueGet("lastname", body);
string region = wasKeyValueGet("region", body);
 
// DEBUG
llOwnerSay("Requesting region map UUID...");
// Send the request to retrieve the map UUID.
llInstantMessage(CORRADE,
wasKeyValueEncode(
[
"command", "getgridregiondata",
"group", wasURLEscape(GROUP),
"password", wasURLEscape(PASSWORD),
"region", region,
"data", wasListToCSV(
[
"MapImageID"
]
),
// pass the region name and avatar
// name through afterburn
"_avatar", (firstname + " " + lastname),
"_region", region,
"_position", wasKeyValueGet("position", body),
// sent to URL
"callback", wasURLEscape(callback)
]
)
);
 
return;
}
 
// Store the map
if(wasKeyValueGet("command", body) == "getgridregiondata" &&
wasKeyValueGet("success", body) == "True") {
 
// Retrive returned data and extract the map UUID.
list data = wasCSVToList(
wasURLUnescape(
wasKeyValueGet("data", body)
)
);
key mapUUID = llList2Key(
data,
llListFindList(
data,
[
"MapImageID"
]
) + 1
);
if(mapUUID == NULL_KEY) {
// DEBUG
llOwnerSay("Failed to retrive remote region map UUID...");
return;
}
 
// Stride 4: Avatar name x region name x local position x map UUID
string avatar = wasURLUnescape(
wasKeyValueGet("_avatar", body)
);
string region = wasURLUnescape(
wasKeyValueGet("_region", body)
);
string position = wasURLUnescape(
wasKeyValueGet("_position", body)
);
 
// DEBUG
llOwnerSay("Remote region map ID for region: " + region + " is: " + (string)mapUUID);
integer idx = llListFindList(avatars, [ avatar ]);
 
// If the avatar is not registered then add the avatar to the list.
if(idx == -1) {
// DEBUG
llOwnerSay("Adding new avatar to tracking list: " + avatar);
 
llSetTimerEvent(0);
 
avatars += avatar;
avatars += region;
avatars += position;
avatars += (string)mapUUID;
 
llSetTimerEvent(1);
 
return;
}
// Extract the avatar from the list and update the details.
llSetTimerEvent(0);
 
list update = llList2List(avatars, idx, idx + 3);
avatars = llDeleteSubList(avatars, idx, idx + 3);
update = llListReplaceList(update, [region], 1, 1);
update = llListReplaceList(update, [position], 2, 2);
update = llListReplaceList(update, [mapUUID], 3, 3);
 
avatars += update;
 
llSetTimerEvent(1);
 
// DEBUG
llOwnerSay("Update list is: " + llDumpList2String(update, ","));
llOwnerSay("Full list is: " + llDumpList2String(avatars, ","));
 
return;
}
}
on_rez(integer num) {
llResetScript();
}
changed(integer change) {
if((change & CHANGED_INVENTORY) ||
(change & CHANGED_REGION_START) ||
(change & CHANGED_OWNER)) {
llResetScript();
}
}
state_exit() {
llSetTimerEvent(0);
}
}