// Avatar Shadow v2.4 // Last Modified 4/26/10 // CONSTANTS string SHADOW_VERSION = "2.4"; // Used by the server to ensure the data sent matches the current database setup string DEBUG = "false"; // Set to true for debugging printouts in SL string BASE_URL = "http://ai.stanford.edu/secondlife/update.php?"; float GAP = 10.0; // seconds between data collection snapshots float RANGE = 20.0; // meters to look within for nearby avatars string LOG_ALL_CHAT = "true"; //whether to log chat from all users, or just the Shadow's owner string LOG_300_CHAT = "true"; //whether to log chat from channel 300 (used for testing/debugging) // VARIABLES string sensorString; // Filled with avatar UUIDs by the sensor, then sent to the database with the snapshot data // FUNCTIONS string getURL() { string url = BASE_URL + "shadow_version=" + SHADOW_VERSION; // Avatar data url += "&a_id=" + (string)llGetOwner(); url += "&a_name=" + llEscapeURL(llKey2Name(llGetOwner())); // Location data vector pos = llGetPos(); url += "&local_x=" + (string)pos.x; url += "&local_y=" + (string)pos.y; url += "&local_z=" + (string)pos.z; // Parcel data list parcel = llGetParcelDetails(pos, [0,1,2,3,4,5]); url += "&p_name="+llEscapeURL(llList2String(parcel,0)); url += "&p_description="+llEscapeURL(llList2String(parcel,1)); url += "&p_owner="+llList2String(parcel,2); url += "&p_group="+llList2String(parcel,3); url += "&p_area="+llList2String(parcel,4); url += "&p_id="+llList2String(parcel,5); url += "®ion="+llEscapeURL(llGetRegionName()); url += sensorString; url += "&unixtime="+(string)llGetUnixTime(); return url; } string getChatURL(integer channel, string name, key id, string message){ return getURL() + "&chat=1" + "&c_channel=" + llEscapeURL((string)channel) + "&message=" + llEscapeURL(message) + "&speaker=" + llEscapeURL(name) + "&speaker_id=" + llEscapeURL(id); } string getTeleportURL(){ return getURL() + "&teleport=1"; } key sendData(string URL) { if(DEBUG == "true") llOwnerSay(URL); return llHTTPRequest(URL, [HTTP_METHOD,"POST"], ""); } default { state_entry() { // Activate the timer listener every GAP seconds llSetTimerEvent(GAP); // Set up chat callback event, listening for the owner on channel 0 if(LOG_ALL_CHAT == "true"){ llListen(0, "", "", ""); } else { llListen(0, "", llGetOwner(), ""); } // Set up chat callback event, listening for data on channel 300 if(LOG_300_CHAT == "true"){ llListen(300, "", "", ""); } // Set up avatar sensor to trigger every GAP/2 seconds llSensorRepeat("", "", AGENT, RANGE, TWO_PI, GAP/2); } timer() { sendData(getURL()); } http_response(key request_id, integer status, list metadata, string body) { if(DEBUG == "true") { if(body!="") llOwnerSay("HTTP Response:\n"+body); } } listen(integer channel, string name, key id, string message) { sendData(getChatURL(channel, name, id, message)); if(DEBUG == "true") llOwnerSay("Logged chat: " + message); } // Teleport event tracker changed(integer change) { if (change & CHANGED_TELEPORT) { sendData(getTeleportURL()); } } sensor (integer numberDetected) { sensorString = "&avatars="; integer a; for (a = 0; a < numberDetected; a++) { if(a != 0) sensorString += "//"; sensorString += llEscapeURL(llDetectedKey(a)) + "/" + llEscapeURL(llDetectedName(a)); } if (DEBUG == "true") { string msg = "Detected "+(string) numberDetected+" avatar(s):\n"; integer i; msg += llDetectedName(0); for (i = 1; i < numberDetected; i++) { msg += ",\n"; msg += llDetectedName(i); } llOwnerSay(msg); } } no_sensor() { sensorString = ""; if (DEBUG == "true") llOwnerSay("No avatars in sensor radius."); } }