I've been having fun getting objects in Second Life talking to a database on my dev server using .NET code, so I thought I'd share the magic with those of you who are interested.
For the SecondLife bit, I'll use the llHTTPRequest object to request data from my external source. The real-world bit is some ASP.NET code running on a development server. In order to understand how this code works, you need to understand the underlying structure of a HTTP request. If you've not done anything like this before, here's a quick dive in.
Each time you view a web page with your browser you're making a request to a web server for a web page. In response, the server sends the requested page data back to you. Each web request consists of request headers and the body of the request. Request headers are sent as a series of key/value pairs (though each key could have many values). The request headers that are sent as part of a request from IE7 (32bit) on a Windows Vista 64 machine with a fair few applications installed may contain the following values for the Accept and User-Agent keys respectively:
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-ms-application,
application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap,
application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword,
application/x-silverlight, application/x-shockwave-flash, */*
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; WOW64;
SLCC1; .NET CLR 2.0.50727; .NET CLR 3.0.04506; Media Center PC 5.0; InfoPath.2; .NET CLR 3.5.21022; MS-RTC LM 8)
Chances are you'll be sending information like this with every request you make to a web server (note to self - that's a LOT of information!), and it helps application developers to know who their audience are and what browser they are using. In addition, if you fill in a form online and submit it to the server, you'll also be sending information as part of the body of the request. For example, I've got a simple HTML page with an input box and a submit button (which comes in handy for testing later):
<form action="VisitorTracker.aspx" method="post">
<input name="avatar" type="text" />
<input type="submit" />
</form>
Nothing special here, and no server-side code to look at, but the interesting bit is in the request body once the form is submitted. If I enter "MyAvatar Name" in the textbox and submit the form, I will have sent a key of "avatar" (because the input has a name attribute of "avatar") with the value of "MyAvatar Name" to the server. As long as your target for the submitted form, in this case, a page called VisitorTracker.aspx, exists, you can add code to that page to handle the data when it arrives and do something with it.
As well as using an HTML form to gather data and send it to the server, I can send the same request to the server from Second Life if I create a prim in-world and add some script to that prim. The following is an extract from some code I'll show you later:
llHTTPRequest("http://devsite/visitortracker.aspx",
[HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
"avatar=MyAvatar%20Name");
There are a few things to note here. The llHttpRequest method takes three arguments; a string of the url of the page you are sending the data to, a list of HTTP request parameters, and the body of the request. Since we want to send the request data using a POST (the method you'd use if submitting a form to a server), this is specified in here, along with the fact that the form data has to be URL encoded. This is why the avatar name "MyAvatar Name" has a %20 in the middle of the string, since a space character becomes a %20 when it's encoded.
Right, so we've made a request to a web server for a page and have sent some information in the body of the request. Time to tell the server how to handle the request. Because my development server runs Microsoft's IIS web server, which supports writing server-side code using the ASP.NET platform, I'm going to use a simple ASP.NET page (on other servers you might use PHP or Perl or Ruby). If you are using Visual Studio 2005/2008 (Visual Web Developer Express edition or better), you can create a new web project with just one page in it. Call it visitortracker.aspx, and make sure you check the box to place the code in a separate file. I'm a C# person, though you can do it with whatever language you feel comfortable using. Here's my code:
VisitorTracker.aspx:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="VisitorTracker.aspx.cs" Inherits="VisitorTracker" %>
(yes, that's all of it - delete everything else in the file; you don't need it)
VisitorTracker.aspx.cs:
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Linq;
public partial class VisitorTracker : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected override void Render(HtmlTextWriter writer)
{
string detectedAvatar = (string)Request.Form["avatar"];
writer.WriteLine("Detected avatar: " + detectedAvatar);
base.Render(writer);
}
}
This code essentially turns the page into a web service of sorts - I'm making a very simple request of the page, and all I'm getting back is data, without any markup or presentation logic. As it happens, the data is human-readable, and we can use this to display a confirmation message on a test prim in Second Life. You can send back whatever you like in the Render method using the writer object, but bear in mind that there is a restriction on the length of the response body in Second Life - you get a maximum of 2048 bytes; if it is longer it will be truncated (with thanks to the LSL wiki for that nugget of information!) Make sure it all builds, then, if you want to test this file without logging into SecondLife, you can create a simple HTML page containing the code I showed earlier between the HTML body tags, then try submitting that form. You should see the message:
Detected avatar: MyAvatar Name
(or whatever name you entered in the input box.)
Back in SecondLife, you can enter the following script into the script file on your prim (changing the name of the server as appropriate) to submit a nice request when the prim is touched, and handle the response received from the server:
key http_request_id;
default
{
state_entry()
{
}
http_response(key request_id, integer status, list metadata, string body)
{
if (request_id == http_request_id)
{
llOwnerSay(body);
}
}
touch_start(integer total_number)
{
integer i = 0;
while (i <= total_number)
{
if (llDetectedKey(i) != NULL_KEY)
{
string avatarName = llEscapeURL(llDetectedName(i));
string formData = "avatar="+avatarName;
http_request_id = llHTTPRequest("http://devserver/visitortracker.aspx",
[HTTP_METHOD, "POST", HTTP_MIMETYPE, "application/x-www-form-urlencoded"], formData);
}
++i;
}
}
}
Now, when you save the script and touch the prim, assuming everything worked as planned, you'll see that same confirmation message in your chat window, except with your avatar name inserted into the message. Notice the http_response handler in this code that takes the response data and tells you all about it. The truth is that there's not a lot to this stuff - send a request, handle the response - more simple than it might first appear.
One last thing you might want to try. In the request headers that come across from Second Life, there is a surprisingly large amount of data. To see this data, add the following code to your render method in your aspx.cs file (insert it just before the call to base.Render):
string[] keys = Request.Headers.AllKeys;
foreach (string key in keys)
{
if (key.Contains("SecondLife"))
{
writer.WriteLine("Key: " + key);
// Get all values under this key.
string[] values = Request.Headers.GetValues(key);
for (int i = 0; i < values.Length; i++)
{
writer.WriteLine("Value " + i + ": " + Server.HtmlEncode(values[i]));
}
}
}
Now, you will probably find that when you get this data back in Second Life that you hit the maximum for the size of the response, but in there you could well see the name of the owner of the prim, the region and co-ordinates in that region in which the prim currently resides, and much more. A full list of Second Life http headers can be found on page on llHTTPRequest on the LSL wiki. I'll leave it up to you to decide what to do with these headers and the code on the prim... You could add a sensor to the prim to detect arrivals to your plot and record the name of the visitor and the time they came to visit in a database, or you could detect visits by newcomers to your plot and send them a welcome message detailing upcoming events. There are so many different things you can do with both prims and data on the server that really it's up to you what you do with the data - the mechanism of sending data to and from Second Life and a web server in the real world is extremely simple, and that's what I find so appealing about it. I don't have to spend hours writing pages of code to get things working, and I don't need to install special components on my web server, and yet I have this elegant and simple mechanism to talk to Second Life. It's not the same as pushing data in-world (for that you will need to look into XML RPC, XML RPC in LSL, and possibly XML RPC in .NET, if you're a .NET person), but for most situations where you might want to send data in-world, you can probably find a way to pull it in instead using one of the built-in methods of LSL as a trigger (sensor, timer, listener, etc.) and handle the data appropriately on the server.