Robot image saying I Am Online

Figuring out your online status, revisited

Three years ago, when I was learning how to integrate Second Life with external web servers in the summer of 2004, I posted some code for figuring out your online status from a website, blog, forum, etc. The tricky bit at the time was that XML-RPC requires a ‘channel’, which is created from scratch every time you register an object to handle XML-RPC calls, but this channel changes over time as sims get rebooted, or SL gets upgraded, or the asset server goes down, etc.

XML-RPC only allows queries from outside to an in-world object (but you can return an answer); in those days, the only way to store that channel on your web server, to make sure you knew where to send the XML-RPC messages, was to send the channel ID by email. Parsing emails on a web server, however, is an option that most low-cost web hosting services do not give you; it’s also not trivial, and very often requires you to have mail set up correctly (also, forget doing that under a Windows server). I just sent the channel to my own email address and manually changed it on my website.

Since Linden Lab introduced llHTTPRequest(), things are much easier. Using it, you can also call a webserver from any object — so now you have a simple and easy way to inform your website that the channel ID has changed! The PHP code I had was also designed for PHP4, using an external XML-RPC library that was quite easy to use. PHP4 has been made obsolete now, so it’s time to change the code to adapt. For me, it meant finding a new XML-RPC library. PHP5 has one, PEAR has another, and a lot of those are around, but almost all are hugely complex and, in some cases, you need to install it first, which may not be an option for the low-end hosting services. By chance, I was browsing through WordPress to see what library they were using — and I found one that works well under PHP4 and 5, comes with every WordPress installation, and it’s even simpler to use than Keith Devens‘ own, who never troubled himself to upgrade his library to PHP5. So here follows the code. The first bit is the LSL script to place in your object. As you can see, it follows the same model as my 2004 code pretty closely. If your object changes XML-RPC channel by any chance, just reset it or rez it again. Note that the webserver will always store the channel ID from the last object rezzed (but all should retrieve the same information from LL’s database servers, so it’s irrelevant).

// Code by Gwyneth Llewelyn to show online status and let it
// be retrieved by external XML-RPC calls

// Global Variables
key avatar;
string avatarName;
string onlineStatus = "status unknown"; // preserve it
key onlineStatusRequest;
key webResponse;	// we send the RPC channel ID to the webserver

default
{
	state_entry()
	{
		llSetTimerEvent(60.0);  // call a timer event
						// every 60 seconds.
		avatar = llGetOwner();
		avatarName = llKey2Name(avatar);
		llOwnerSay("Registering with RPC server and retrieving user data from database server...");
		llOpenRemoteDataChannel();	// this sets the object up to accept external XML-RPC calls
	}	

	on_rez(integer startParam)
	{
		llResetScript();
	}

	timer()
	{
		// Every 60 seconds, retrieve the online status from the SL database server
		onlineStatusRequest = llRequestAgentData(avatar, DATA_ONLINE);
		llSetText(avatarName + " is " + onlineStatus, ZERO_VECTOR, 1.0);	// set the hover text
	}

	dataserver(key queryid, string data)
	{
		// this is what SL returns to us when we call llRequestAgentData()
		if (queryid == onlineStatusRequest)	// check if it's the correct request
		{
			if (data == "1")	// online status is just 0 or 1
				onlineStatus = "online";
			else
				onlineStatus = "offline";
		}
	}

	remote_data(integer event_type, key channel, key message_id, string sender, integer idata, string sdata)
	{
		if (event_type == REMOTE_DATA_CHANNEL)
		{
			// SL has set up a channel for the XML-RPC calls
			llOwnerSay("Channel id "+(string)channel);
			llOwnerSay("Sending channel id to web server...");
			// We'll save it, by sending it to our server
			webResponse = llHTTPRequest("http://insert.your.domain.here.tld/save-channel.php",
						[HTTP_METHOD, "POST",
						HTTP_MIMETYPE, "application/x-www-form-urlencoded"],
						"channel=" + (string)channel);
		}
		if (event_type == REMOTE_DATA_REQUEST)
		{
			// This is a call made from our web site/blog asking us for the online status
			llRemoteDataReply(channel, message_id, onlineStatus, idata);
		}
	}

	http_response(key request_id, integer status, list metadata, string body)
	{
		if (request_id == webResponse)
		{
			if (status == 200)	// we'll get a 200 OK status if the web server stored the XML-RPC channel correctly
				llOwnerSay("Channel id successfully saved");
			else llOwnerSay("Webserver error " + (string)status + ": " + body);
		}
	}
}Code language: LSL (Linden Scripting Language) (lsl)

Now it’s time to take a look at the web-side code. The first bit is the handler for the new channel. I’ve done a quick-and-dirty thingy that simply writes to a local file. Since this is meant to be used inside things like your own hosted blogs/websites/forums, just pick a local directory that is writable and use it. For WordPress, I’m using the uploads directory and the filename I’ve picked was channels.inc. Just copy and paste the following code into a file called save-channel.php where your web server can find it:

<?php
if ($_REQUEST['channel'])
{
	include('./wp-config.php');
	$filename = ABSPATH . 'wp-content/uploads/channel.inc';

	$fd = fopen($filename, 'w');
	if (fwrite($fd, $_REQUEST['channel']) === FALSE)
	{
		header("HTTP/1.0 503 Service Unavailable");
		echo "File $filename not writeable - channel not saved";
	}
	else
	{
		fclose($fd);
		header("Content-type: text/plain; charset=utf-8");
		echo "Channel " . $_REQUEST['channel'] . " saved.";
	}
}
else
{
	header("HTTP/1.0 405 Method Not Allowed");
	echo "No channel specified";
}Code language: PHP (php)

And finally comes the last bit — adding the relevant code on your site/blog/forum to check if you’re in-world or not. This requires including class-IXR.php, which is available from Incutio was developed by Incutio and still exists in various forms on GitHub (e.g. PHP7+ compatible replacement for IXR_Library.php, but there are more); another reliable source for a well-maintained port is WordPress itself, which still ships the library (albeit on a slightly different form) and gets regular updates. However, the WordPress guys have continued to update this XML-RPC library and have a more recent version.

<?php
include_once(ABSPATH . WPINC . '/class-IXR.php');

$client = new IXR_Client('http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi');

$payload["Channel"] = file_get_contents(ABSPATH . 'wp-content/uploads/channel.inc');
$payload["IntValue"] = 0;
$payload["StringValue"] = "online status";

print "I am ";
if ($client->query('llRemoteData', $payload))
{
	$response = $client->getResponse();
	print $response["StringValue"] . " in Second Life.";
}
else
{
	print "having problems contacting RPC server...";
}Code language: PHP (php)

Enjoy!

UPDATE: If you’re on a self-hosted WordPress blog, you might be interested in testing my Online Status inSL WordPress Plugin, which should be way easier to install 🙂

UPDATE: (2022/07/13) Notice the comment about Incutio’s XML-RPC library. It was popular at a time (because it was so well-written, easy to understand, and very, very easy to use), but, sadly, Incutio disappeared. Fortunately, it continues to be maintained by the WordPress team for their own built-in XML-RPC service and is therefore reasonably up to date with the proper security patches and the changes required to work on any PHP version above 5.

In any case, remember, the access to LL’s XML-RPC server is considered deprecated, besides having tons of limitations, compared to the newer way of doing things (using llHTTPRequest(), which I describe a bit better elsewhere).

Also, temporarily at least, for some reason Automattic is blocking access to my WP plugin, but you can get a reasonably recent version from GitHub instead.