</span></span>

<span style="font-family: 'Courier New', courier, monaco, monospace; color: #7a4707;"><span style="white-space: pre;"> <div id="_mcePaste">/////////////////////////////////////
//Radar Script - adapted from Money Server script.
//
//This script was designed to interface between radar
//objects and the server... the server can send requests
//to radar objects asking for avatars within a specific
//range and forward angle, which the radar object then
//uses to scan and return local avatars.
//
//Created by Scott Martin, 06/2010
/////////////////////////////////////
//DEBUG VARIABLES
//Set this to true to enable debug text! Uses the debug() function.
integer DEBUG_ON = FALSE;
//Sets the level to debug at.
integer TRIVIAL = 1;
integer IMPORTANT = 2;
integer CRITICAL = 3;
integer DEBUG_LEVEL = CRITICAL;
/////////////////////////////////////
//OBJECT VARIABLES
//These are object-specific variables.
//The farthest a sensor function can see
float MAX_RANGE = 96.0;
//The largest radius possible.
float MAX_RADIUS = PI;
//The largest rotation amount possible
float MAX_ROT = PI;
//holds key of owner, set on init.
key owner;
//Which agents were detected (in UUID,name strided format). Populated on sensor()
list detectedAgents;
//
//The following values are set via a server request:
//
//How far to search in meters
float range;
//The spherical radius to search (radians)
float arc;
//How many times to repeat the search
//(-1 = infinite stored in database, 0 = stop, 1 = single response with feedback to server, >1 stored in database)
float repeat_amount;
//The delay in UPDATE_INTERVAL ticks between searches
integer repeat_interval;
//Current tick for the search initializer. Ticks up to repeat_interval during timer() calls.
integer repeat_ticker = 0;
//How far to rotate the object horizontally from ZERO_ROTATION.
//Horizontal rotation is measured in radians, clockwise around the global Z (up) axis.
float horiz_rot;
//How far to rotate the object vertically from ZERO_ROTATION.
//Vertical rotation is measured in radians, clockwise around the object's local Y (left) axis.
float vert_rot;
/////////////////////////////////////
//SERVER VARIABLES
//These are settings for communication between the server and the object.
//They shouldn't have to be changed unless changes are made to the location or
//configuration of the php file at the location server_url.
//URL of the object, given during init by llRequestSecureURL()
string object_url;
//URL of the php script we communicate to the server with
string SERVER_URL = "http://grid1.yfc3d.com/world_interface/radar.php";
//Secret string used as part of authentication
string SECRET = "aje95nIlz8";
//What type of object is this? Used in the authentication process.
string OBJECT_TYPE = "radar";
//Whether or not we've received a confirmation of authentication from the server. Boolean.
integer authenticated;
//Secondlife arrays are turned into delimited text strings using llDumpList2String(list, separator)
string SEPARATOR = "|";
//Wait this many seconds between updates (sending new radar events)
float UPDATE_INTERVAL = 1.0;
//How many ticks until the next timeout update "keep alive" signal (5 minute intervals). Set on init.
integer keepalive_interval;
//Current tick for the timeout updater. Ticks up to keepalive_interval during timer() calls.
integer keepalive_ticker = 0;
//HTTPS request handles used to determine what message the server is responding to.
key auth_id; //Authentication request handle
string AUTH_FLAG = "AUTH"; //Authentication flag
key radar_id; //"Set next search" request handle
string RADAR_FLAG = "RADAR"; //"Set next search"flag
key upload_id; //"Upload avatars" request handle
string UPLOAD_FLAG = "UPLOAD"; //"Upload avatars" flag
key keepalive_id; //Keepalive request handle (see keepalive_interval)
string KEEPALIVE_FLAG = "HERE"; //Keepalive flag
/////////////////////////////////////
//SERVER COMMUNICATION FUNCTIONS
//The following functions handle standard communications with the server.
key sendData(list fields, list values)
//Sends data via HTTP Request to the server, in a format readable by the server.
//The server takes the fields and values lists and combines them to
//form an associative array.
//fields - A list containing all fields to be sent to the server
//values - A list containing all values matching the fields
{
//Convert fields and values into GET format
string args = "?uuid="+(string)llGetKey(); //Always have the object's UUID
integer i;
for (; i < llGetListLength(fields); i++)
{
args += "&" + llEscapeURL(llList2String(fields,i)) + "=" + llEscapeURL(llList2String(values,i));
}
//Grab a checksum to prevent data tamper
args += "&cs=" + llEscapeURL(llSHA1String(args+SECRET));
debug(IMPORTANT,"sendData",SERVER_URL+args);
//Append the arguments to the URL, send the request, and return the key of the HTTP request sent.
return llHTTPRequest(SERVER_URL+args,[HTTP_METHOD,"GET",HTTP_MIMETYPE,"application/x-www-form-urlencoded"],"");
}
returnData(key request_id, list data)
//Sends data via HTTP Response to the server, in a format readable by the server.
//Note that this should be used to respond to the SERVER's HTTP requests (not sendData)
//A checksum is calculated before the data is sent - matched by the server on receipt.
//request_id - a HTTP request handle. Send by the server through http_request()
//data - the data to be sent. It becomes a delimited string using the SEPARATOR variable.
{
debug_list(TRIVIAL,"returnData",data);
string dataString = llDumpList2String(data,SEPARATOR);
string checksum = llSHA1String(dataString+SECRET);
llHTTPResponse(request_id,200,checksum+SEPARATOR+dataString);
}
keepalive_request()
//Some server-side functions are time sensitive - e.g. the getRecentAvatarObjects() function, which
//only returns objects owned by an avatar that have made contact with the server within a certain
//period of time. After a set time (ten minutes) of idleness. This should be called when the
//object is idle (probably every 5 minutes) to ensure that the object doesn't "time out".
//See updateTimeout_response() for the other half of this process.
{
debug(TRIVIAL,"keepalive_request","");
keepalive_id = sendData(["action", "url"], [KEEPALIVE_FLAG, object_url]);
}
keepalive_response(key id)
//After updateTimeout_request() is called, the server sends a HTTP request to the object asking the object to confirm
//its authenticity. This returns the owner's UUID as a sort of quick check to ensure the object is what it says it is.
//id - The handle to the HTTP request sent by the server
{
debug(TRIVIAL,"keepalive_response","");
returnData(id, ["owner", llGetOwner()]);
}
authenticate_request()
//Called on initialize - pings the server and asks for it to contact us for authentication using the URL we
//provide. See authenticate_response() for the other half of this process.
{
debug(TRIVIAL,"authenticate_request","");
auth_id = sendData(["action","url"], [AUTH_FLAG,object_url]);
}
authenticate_response(key id)
//After authenticate_request() is called, the server sends a HTTP request to the object requesting more information
//through its secure HTTPS channel. The object then sends back this data in an attempt to prove it's trustworthy.
{
debug(TRIVIAL,"authenticate_response","");
returnData(id, ["pass", SECRET, "owner", llGetOwner(), "type", OBJECT_TYPE, "uuid", llGetKey()]);
}
list parseRequest(string body)
//Scrubs and manipulates the POST data received from the server into a secondlife list. Used in the http_request event.
//body - the original body of the http request sent from the server.
{
//Unescape the %20's, plus signs etc and turn them into human (and machine!) readable characters.
body = llUnescapeURL(llDumpList2String(llParseString2List(body,["+"],[]),"%20"));
//Get rid of the "m=" tag prepended to the actual response
body = llGetSubString(body,2,-1);
//Parse the string into a list.
list result = llParseString2List(body,[SEPARATOR],[]);
debug_list(IMPORTANT,"parseRequest",result);
return result;
}
/////////////////////////////////////
//OBJECT-SPECIFIC COMMUNICATION FUNCTIONS
//The following functions handle standard communications with the server specific to this object.
//NOTE: These may need to be changed if server-side scripts or this script is modified significantly.
radar_response(key id, list received)
//When the server sends a request to get avatars within a specific range, this function is called
//to set up a sensor and return the requested data.
//id - The handle to the HTTP request sent by the server
//received - data sent from the server.
{
//Get new search parameters! We'll store these once they're confirmed within range.
float tmp_range = llList2Float (received,1);
float tmp_arc = llList2Float (received,2);
float tmp_repeat_amount = llList2Float (received,3);
integer tmp_repeat_interval = (integer)(llList2Integer(received,4)/UPDATE_INTERVAL);
float tmp_horiz_rot = llList2Float (received,5);
float tmp_vert_rot = llList2Float (received,6);
string secret_conf = llList2String (received,7);
//If the server doesn't know the secret, prevent it from making the request.
if (secret_conf != SECRET)
{
returnData(id,["Authentication Failure."]);
}
//If the range is out of bounds, reject the request.
else if (tmp_range > MAX_RANGE || tmp_range <= 0)
{
returnData(id,["ERROR: range ("+(string)tmp_range+"m) out of bounds."]);
}
//If the arc is out of bounds, reject the request...
else if (tmp_arc > MAX_RADIUS || tmp_arc < 0)
{
returnData(id,["ERROR: arc ("+(string)tmp_arc+" radians) out of bounds."]);
}
//If the number of times to repeat is out of bounds, reject!
else if (tmp_repeat_amount < -1)
{
returnData(id,["ERROR: repeat amount ("+(string)tmp_repeat_amount+" times) out of bounds."]);
}
//Repeat frequency? reject! Reject! REJECT!
else if (tmp_repeat_interval < 1)
{
returnData(id,["ERROR: repeat interval ("+(string)tmp_repeat_interval+" seconds) out of bounds (update interval "+(string)UPDATE_INTERVAL+" seconds)."]);
}
//Direction rejection!
else if (tmp_horiz_rot > MAX_ROT || tmp_horiz_rot < -MAX_ROT || tmp_vert_rot > MAX_ROT || tmp_vert_rot < -MAX_ROT)
{
returnData(id,["ERROR: rotation ("+(string)tmp_horiz_rot+" radians right, "+(string)tmp_vert_rot+" radians up) out of bounds."]);
}
else //If we've passed these checks, update to use the new settings.
{
range = tmp_range;
arc = tmp_arc;
repeat_amount = tmp_repeat_amount;
repeat_interval = tmp_repeat_interval;
horiz_rot = tmp_horiz_rot;
vert_rot = tmp_vert_rot;
radar_id = id;
//If we're going to be sending results to the database
//and not just a one-off reponse to the active webpage,
//send a confirmation that we received the request.
if (repeat_amount != 1)
{
returnData(id,["Ok!"]);
radar_id = NULL_KEY;
}
}
}
upload_request()
//Starts the process of uploading user data to the server.
//Really asks the server to send us a HTTP request that we can do a HTTP response to, since there's a cap
//On the amount of data transferrable through llHTTPRequest(), but not through llHTTPResponse(). There should be
//a conditional statement in the http_request event that listens for this and calls sendData_response() when req_id is detected.
//While this is kind of generic, parts of it (after 'uuid' for example) need to be changed to be object-specific.
//See upload_response for the rest of this process.
{
debug(TRIVIAL,"upload_request","");
upload_id = sendData(["action","url"], [UPLOAD_FLAG,object_url]);
}
upload_response(key id)
//After sendData_request is called, the server sends a HTTP request BACK to the object using HTTPS requesting the actual data.
//upload_response replies to that request with the data to be uploaded.
//Note that this function will need to be modified from object to object.
//id - The handle to the HTTP request sent by the server
{
//Send previously scanned avatar data to the server!
debug_list(IMPORTANT,"upload_response",detectedAgents);
returnData(id,detectedAgents);
detectedAgents = [];
}
/////////////////////////////////////
//DEBUG FUNCTIONS
//Used when debugging. Checks DEBUG_LEVEL and DEBUG_ON at the beginning of this script
//to determine what to send.
//To toggle debugging and set the level, see DEBUG_ON and DEBUG_LEVEL at the start of this script.
debug(integer level, string header, string msg)
//Sends a debug message using a string.
//msg - The string describing what happened.
//level - The debug level/importance of the message.
//header - What to show at the beginning of the message. Used as an identifier
// to find out just where the heck this debug message is coming from.
{
if (DEBUG_ON && level >= DEBUG_LEVEL)
llOwnerSay("["+header+"]["+(string)level+"]"+msg);
}
debug_list(integer level, string header, list lst)
//Sends a debug message using a list.
//lst - The list of data to be presented to the user
//level - The debug level/importance of the message.
//header - What to show at the beginning of the message. Used as an identifier
// to find out just where the heck this debug message is coming from.
{
if (DEBUG_ON && level >= DEBUG_LEVEL)
llOwnerSay("["+header+"]["+(string)level+"]"+llDumpList2String(lst,SEPARATOR));
}
/////////////////////////////////////
//MAIN CODE
rotateFromZero(float right, float up)
//This function rotates an object from ZERO_VECTOR (i.e. facing the positive global X axis),
//"right" radians to the right and "up" radians upward.
{
rotation horiz = llAxisAngle2Rot(-<0,0,1>,right);
rotation vert = llAxisAngle2Rot(-llRot2Left(horiz),up);
llSetRot(horiz * vert);
}
default
{
state_entry()
{
debug(TRIVIAL,"init","Initializing...");
llSetPrimitiveParams([PRIM_COLOR, ALL_SIDES, <1,1,1>, 1.0]); //Turn white.
//Set how often we want to send a keepalive request to the server
//This is lower than usual for radar objects... because an attached
//radar object is a pretty good indicator also of whether an avatar is online.
keepalive_interval = (1 * 60) / (integer)UPDATE_INTERVAL;
//Get and set the uuid of our owner
owner = llGetOwner();
//We aren't authenticated yet!
authenticated = FALSE;
//Request a secure URL. see http_request for the result of this call.
llRequestSecureURL();
}
on_rez(integer number)
{
llResetScript();
}
http_request(key id, string method, string body)
{
if (method == URL_REQUEST_GRANTED) //We received a valid URL
{
debug(IMPORTANT,"http_request", "Using URL "+body);
object_url = body;
debug(TRIVIAL,"http_request","Asking server for authentication...");
authenticate_request();
}
else if (method == URL_REQUEST_DENIED) //No URLs are available in the sim
{
llOwnerSay("No URLs available for use by the object at the moment!");
llOwnerSay("Cannot communicate with the server. Please try again later.");
debug(CRITICAL,"http_request","URL_REQUEST_DENIED");
}
else if (method== "POST") //The server sent us a message!
{
debug(TRIVIAL,"http_request","Got request with body "+body);
//Scrub and parse the request body into something readable.
list received = parseRequest(body);
//Separate out the action... what the server wants us to do.
string action = llList2String(received,0);
if (auth_id != NULL_KEY && action == AUTH_FLAG) //The server wants us to provide authentication credentials
authenticate_response(id);
else if (keepalive_id != NULL_KEY && action == KEEPALIVE_FLAG) //The server wants confirmation before updating timeout
keepalive_response(id);
else if (action == RADAR_FLAG) //The server is providing new data!
radar_response(id, received);
else if (action == UPLOAD_FLAG) //Server is ready for our avatar data.
upload_response(id);
else //if none of the above are true...
debug(CRITICAL,"http_request","ERROR: Invalid server action \""+action+"\"");
}
}
http_response(key id, integer status, list metadata, string body)
{
debug(TRIVIAL,"http_response","Status: "+(string)status);
debug_list(TRIVIAL,"http_response",metadata);
debug(TRIVIAL,"http_response","Body: "+body);
if (id == auth_id && body == "SUCCESS") //Server has identified us, we're authenticated!
{
debug(TRIVIAL,"http_response","Authenticated!");
//We're authenticated.
authenticated = TRUE;
//Start up the keepalive and data sending timer
llSetTimerEvent(UPDATE_INTERVAL);
//So no one can use this key again...
auth_id = NULL_KEY;
}
else if (id == auth_id) //Uh oh. We aren't registered because we don't have permission or a script broke server-side
{
//Display text to the user so they know authentication failed.
llOwnerSay("Could not authenticate this session! Are you a group leader?");
debug(CRITICAL,"http_response","AUTH: " + body);
auth_id = NULL_KEY;
}
else if (id == keepalive_id) //On keepalive update
{
//If there's a problem, we want to know it!
if (body != "SUCCESS")
{
debug(CRITICAL,"http_response","KEEPALIVE: " + body);
}
//else accept things quietly.
keepalive_id = NULL_KEY;
}
}
timer()
//We periodically want to send updates to the server, but only if someone new needs money
//There's a limit to 25 HTTP requests in 20 seconds... so we need to distance the update requests.
{
//Increment the tickers
keepalive_ticker++;
repeat_ticker++;
//If it's time to make another sensor sweep
if (repeat_ticker >= repeat_interval)
{
//And if there's a sensor sweep actually scheduled
if (repeat_amount != 0)
{
//Set the object's rotation to the direction specified by the server
rotateFromZero(horiz_rot, vert_rot);
//And do a sensor sweep!
llSensor("",NULL_KEY,AGENT,range,arc);
//One more sweep finished. Decrement the number remaining.
if (repeat_amount > 0)
repeat_amount--;
}
repeat_ticker = 0;
}
//If it's been keepalive_interval and we need to ping the server
if (keepalive_ticker >= keepalive_interval)
{
keepalive_request();
keepalive_ticker = 0;
}
}
sensor (integer detected)
//We found some avatars!
{
debug(IMPORTANT,"sensor","Detected " + (string)detected + " avatars.");
detectedAgents = [];
integer i = 0;
//Iterate through all detected avatars and add them to the detectedAgents list
for (; i < detected; i++)
{
//llOwnerSay(llDetectedName(i));
detectedAgents += [llDetectedKey(i),llDetectedName(i),llVecDist(llDetectedPos(i),llGetPos()),llGetAgentInfo(llDetectedKey(i))];
}
//If we can still reply to the original server request (i.e. repeat_amount == 1)
//Return the data as a HTTPS response
if (radar_id != NULL_KEY)
{
returnData(radar_id,detectedAgents);
radar_id = NULL_KEY;
}
else
//Otherwise make the request ourselves.
{
upload_request();
}
}
no_sensor()
//We didn't find any avatars :(
{
detectedAgents = [];
debug(IMPORTANT,"no_sensor","No avatars found.");
//Notify the server if it's still waiting for a response.
if (radar_id != NULL_KEY)
returnData(radar_id,[]);
radar_id = NULL_KEY;
}
changed (integer change)
//Restart on region restart or if we're attached to an avatar and we've changed regions (new UUID)
{
if (change & CHANGED_REGION_START || change & CHANGED_REGION)
{
llMessageLinked(LINK_ALL_CHILDREN,0,"RESET","");
llResetScript();
}
}
}
Topic revision: r1 - 2011-06-16 - ScottMartin
 
This site is powered by the TWiki collaboration platformCopyright &© by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback