LSL-Shared Media Communication

Introduction

A common question about Shared Media is whether or not it can communicate with Linden Script (LSL). Great question! The answer is: YES! This is great news as it opens up all sort of creative possibilities. However, it's not the easiest thing to figure out. Edelman Linden shows at least one way to do it.

This article presents the full source code for a simple example where you can send chat messages FROM Shared Media. In addition, the Shared Media displays some time of day information received from LSL.

It looks like this:

Notice that the chat there really is the text from the Shared Media:

Communication via HTTP

LSL script support receiving and replying to HTTP requests. The URL for a LSL running on a sim can be obtain using llrequestURL. It's this URL and HTTP request mechanism that are the key to communication with Share Media, in both directions!

But there's a catch! And this is a BIG catch!

As such, your domain needs to proxy requests from your Shared Media to the Linden server. The good news is that I'll show you how to do that. You can use this simple PHP script:

lslproxy.php

Takes the URL of the LSL script and the data you want to have it receive as the request body.

<php> <?php

$url = $_GET['url']; $data = $_GET['data'];

$session = curl_init($url); curl_setopt($session, CURLOPT_POST, true); curl_setopt($session, CURLOPT_POSTFIELDS, $data); curl_setopt($session, CURLOPT_HEADER, false); curl_setopt($session, CURLOPT_FOLLOWLOCATION, true); curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($session); echo $response;

curl_close($session);

?> </php>

Tip: You need to host it from your domain and be able to run PHP scripts.

LSL to Shared Media is a Many-to-One Relationship

It's very important that you understand that the relationship between LSL and Shared Media is a many-to-one relationship. That is, the LSL is running just one time on the Linden simulation server. The Shared Media is running on the computer of every Resident that is encountering your object. While you're deveopling content, it's easy to forget this, as you are probably looking at your object alone.

Important: Remember, the relationship between LSL and Shared Media is a many-to-one relationship!

Source Code for the Example

LSL script for a simple example

Here's the script on the object:

Key points:

<lsl> init(string scriptUrl) {

   string url = "http://www.uccellogames.com/media_demos/communication.html?url=" + scriptUrl;
   integer face = 4;
   llClearPrimMedia(face);
   llSetPrimMediaParams(face, [PRIM_MEDIA_CURRENT_URL, url, PRIM_MEDIA_AUTO_SCALE, TRUE, PRIM_MEDIA_AUTO_PLAY, TRUE, PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE, PRIM_MEDIA_WIDTH_PIXELS, 512, PRIM_MEDIA_HEIGHT_PIXELS, 512, PRIM_MEDIA_FIRST_CLICK_INTERACT, 1]);

}

key gRequestURL; string gURL;

default {

   state_entry()
   {
       gRequestURL = llRequestURL();
   }
   http_request(key id, string method, string body) 
   {
       if (id == gRequestURL) {
           string arg = llEscapeURL(body);
           init(arg);
       } else {
           // it's a message from the Shared Media!
           string msgType = llGetSubString(body, 0, 0);
           string data = llGetSubString(body, 1, -1);
           
           if (msgType == "1") {
               // it's a chat message!
               llSay(0, data);
               llHTTPResponse(id,200,"");
           } else if (msgType == "2") {
               // it's a time of data request
               float tod = llGetTimeOfDay( );
               string reply = "Time since last region restart or SL midnight (based on SL 4 hour day):";
               integer hours = ((integer)tod / 3600) ;
               integer minutes = ((integer)tod / 60) - (hours * 60);
               reply += (string) tod + " seconds which is "+(string) hours+"h "+(string) minutes+"m"; 
               llHTTPResponse(id,200, reply);
           }                
       }
   }

} </lsl>

communication.html

Here's the HTML for the Shared Media page:

Key points:

Otherwise, it's pretty straight-forward.

<html4strict> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

<head>

<script type="text/javascript"> function get_query_argument(name, href) {

 href = (href) || window.location.href;
 name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
 var regexS = "[\\?&]"+name+"=([^&#]*)";
 var regex = new RegExp( regexS );
 var results = regex.exec(href);
 return (results == null ? "" : results[1]);

};

function lslRequest(data, responseHandler) { var url = get_query_argument("url"); var xmlhttp=new XMLHttpRequest(); xmlhttp.open("GET", "http://www.uccellogames.com/media_demos/lslproxy.php?data=" + data + "&url=" + url, true); xmlhttp.responseHandler = responseHandler;

 xmlhttp.onreadystatechange=function() {

if (this.readyState == 4) { if (this.responseHandler) { this.responseHandler(this.responseText); } } }; xmlhttp.send(); }

function submit_chat() { var chat = escape(document.getElementById("chat").value); var data = "1" + chat; // 1 indicates it's incoming chat lslRequest(data, null); }

function get_message() { var data = "2"; // 2 indicates it's a request for the time of day lslRequest(data, function(responseText) { document.getElementById("lslmessage").value = responseText; } ); } </script>


</head>


<body onLoad="javascript:get_message()">

Example of Shared Media to LSL communication:

What you type and submit here will show up in SL chat:

<input name="chat" type="text" id="chat" size="46" maxlength="50" /> <input type="submit" name="submit" id="submit" value="Submit" onClick="javascript:submit_chat()"/>

Example of LSL to Shared Media communication:

Generally, this is done by polling from Shared Media to LSL - this example just asks once.

Here's what LSL reports as the "time of day":

<textarea name="lslmessage" id="lslmessage" cols="50" rows="5"></textarea>

</body> </html> </html4strict>

Additional Thoughts from Edelman

I hope you found this article useful. I tried to come up with a really simple example, but it's still pretty technical.

Note that if you wanted a way to keep a state machine that all new Shared Media instances could connect to, receive current state, and receive update notifications, you certainly could do it in LSL. However, if your Shared Media is Flash based, Flash Media Server (FMS) makes this easier. Server-side Javascript on the FMS server could be used both as your proxy (instead of the PHP script) AND as your server-side state machine that already has a way to broadcast to all clients. In this case, you could keep a one-to-one relationship with your LSL instance and your FMS instance, and the many-to-one relationshpe migrates to be between the FMS instance and the Shared Media Flash clients.

<lsl></lsl>