User:Ama Omega/archive/lsl hacks

Shared Media and HTTP-In

Pre-Viewer 2.0 interfacing with an LSL script is a jumble of llDialog, chats, touches and possibly external web sites. With Shared Media that all changes.

One step at a time .......

llSetPrimMediaParams

default
{
    state_entry()
    {
        llSetPrimMediaParams(0,                             // Side to display the media on.
            [PRIM_MEDIA_AUTO_PLAY, TRUE,                    // Show this page immediately
             PRIM_MEDIA_CURRENT_URL, "http://google.com",   // The url currently showing
             PRIM_MEDIA_HOME_URL, "http://google.com",      // The url if they hit 'home'
             PRIM_MEDIA_HEIGHT_PIXELS, 512,                 // Height/width of media texture will be
             PRIM_MEDIA_WIDTH_PIXELS, 512]);                //   rounded up to nearest power of 2.
    }
}

data: urls

There is a special url type: data: that lets you send the html in the url. Paste the code below into your browser's address bar.

data:text/html,<h1>This is a test</h1><h2>This is a test</h2><h3>This is a test</h3>

llSetPrimMediaParams for data: urls

show(string html)
{
    html = "data:text/html," + llEscapeURL(html);
    llSetPrimMediaParams(0,                  // Side to display the media on.
            [PRIM_MEDIA_AUTO_PLAY,TRUE,      // Show this page immediately
             PRIM_MEDIA_CURRENT_URL, html,   // The url currently showing
             PRIM_MEDIA_HOME_URL, html,      // The url if they hit 'home'
             PRIM_MEDIA_HEIGHT_PIXELS, 512,  // Height/width of media texture will be
             PRIM_MEDIA_WIDTH_PIXELS, 512]); //   rounded up to nearest power of 2.
}
default
{
    state_entry()
    {
        show("<h1>This is a test</h1><h2>This is a test</h2><h3>This is a test</h3>");
    }
}

HTTP-In

default
{
    state_entry()
    {
        llRequestURL();
    }
    
    http_request(key id, string method, string body)
    {
        if (method == URL_REQUEST_GRANTED)
        {
            llSay(0, "URL is: " + body);
        }
        else if (method == "GET")
        {
            llSay(0, "Received page request.");
            llHTTPResponse(id, 200, "OK");
        }
    }
}

llSetPrimMediaParams for data: urls that link back to HTTP-In

show(string html)
{
    html = "data:text/html," + llEscapeURL(html);
    llSetPrimMediaParams(0,                  // Side to display the media on.
            [PRIM_MEDIA_AUTO_PLAY, TRUE,     // Show this page immediately
             PRIM_MEDIA_CURRENT_URL, html,   // The url currently showing
             PRIM_MEDIA_HOME_URL, html,      // The url if they hit 'home'
             PRIM_MEDIA_HEIGHT_PIXELS, 512,  // Height/width of media texture will be
             PRIM_MEDIA_WIDTH_PIXELS, 512]); //   rounded up to nearest power of 2.
}

string replace_all(string src, string target, string replace)
{
    return llDumpList2String(llParseString2List(src, [target], []), replace);
}

string get_query(key id, string name)
{
    string query = llGetHTTPHeader(id, "x-query-string");
    query = replace_all(query, "+", " ");
    query = llUnescapeURL(query);
    list q = llParseString2List(query, ["=", "&", ";"], []);
    integer i = llListFindList(q, [name]);
    if (i != -1)
    {
        return llList2String(q, i + 1);
    }
    
    return "";
}

default
{
    state_entry()
    {
        show("<h1>This is a test</h1><h2>This is a test</h2><h3>This is a test</h3>");
        llRequestURL();
    }
    
    http_request(key id, string method, string body)
    {
        if (method == URL_REQUEST_GRANTED)
        {
            show("<h1><a href='" + body + "/?get=owner'>Owner ID</a><br><br><a href='" + body + "/?get=object'>Object ID</a></h1>");
        }
        else if (method == "GET")
        {
            string get = get_query(id, "get");
            if (get == "owner")
            {
                llHTTPResponse(id, 200, "Owner is: " + (string)llGetOwner());
            }
            else if (get == "object")
            {
                llHTTPResponse(id, 200, "Object is: " + (string)llGetKey());
            }
            else
            {            
                llHTTPResponse(id, 400, "huh?");
            }
        }
    }
}

Forms

data:text/html,<form action="http://google.com/search" method="GET">Search:<input type="text" name="q"><input type="submit" value="Submit"></form>

The Magic

string html base = 
"<h1><form action='%url%' method='GET'>
Floating Text:<input type='text' name='text'><br>
<input type='submit' value='Set'>
</form></h1>";

string url;

integer r;
show(string html)
{
    html = "data:text/html," + llEscapeURL(html) + "<span " + (string)((++r) % 10) + "/>";
    
    llSetPrimMediaParams(0,                  // Side to display the media on.
            [PRIM_MEDIA_AUTO_PLAY, TRUE,     // Show this page immediately
             PRIM_MEDIA_CURRENT_URL, html,   // The url currently showing
             PRIM_MEDIA_HOME_URL, html,      // The url if they hit 'home'
             PRIM_MEDIA_HEIGHT_PIXELS, 512,  // Height/width of media texture will be
             PRIM_MEDIA_WIDTH_PIXELS, 512]); //   rounded up to nearest power of 2.
}

string replace_all(string src, string target, string replace)
{
    return llDumpList2String(llParseString2List(src, [target], []), replace);
}

string get_query(key id, string name)
{
    string query = llGetHTTPHeader(id, "x-query-string");
    query = replace_all(query, "+", " ");
    query = llUnescapeURL(query);
    list q = llParseString2List(query, ["=", "&", ";"], []);
    integer i = llListFindList(q, [name]);
    if (i != -1)
    {
        return llList2String(q, i + 1);
    }
    
    return "";
}

default
{
    state_entry()
    {
        llRequestURL();
    }
    
    http_request(key id, string method, string body)
    {
        if (method == URL_REQUEST_GRANTED)
        {
            url = body + "/";
            
            show(replace_all(html_base, "%url%", url));
        }
        else if (method == "GET")
        {
            llSetText(get_query(id, "text"), <1.0, 1.0, 0.0>, 1);
            show(replace_all(html_base, "%url%", url));
            llHTTPResponse(id, 200, "Loading....");
        }
    }
}

The Limits

The Tricks

The future?

I think there is a lot more potential here than what I have outlined... if you have ideas or work out great new hacks let me know! Also let me know if it is ok to share those hacks with others. :)

DHTML / Javascript (Tali Rosca)

Tali Rosca sent me the code to render the output of HTTP-In directly. I've wrapped her magic into the build_url and build_response functions in the example below. This example renders a page that is around 3.5k bytes - well past the 1k limit of urls.
Pros:

Cons:

string url;     // Our base http-in url
integer r;      // Used for uniqueness in PrimMedia urls
integer page;   // The page number.

show(string html)
{
    html += "<span " + (string)((++r) % 10) + "/>";
    
    llSetPrimMediaParams(0,                  // Side to display the media on.
            [PRIM_MEDIA_AUTO_PLAY, TRUE,     // Show this page immediately
             PRIM_MEDIA_CURRENT_URL, html,   // The url if they hit 'home'
             PRIM_MEDIA_HOME_URL, html,      // The url currently showing
             PRIM_MEDIA_HEIGHT_PIXELS, 512,  // Height/width of media texture will be
             PRIM_MEDIA_WIDTH_PIXELS, 512]); //   rounded up to nearest power of 2.
}

// This creates a data: url that will render the output of the http-in url 
// given.
string build_url(string url)
{
    return "data:text/html," 
        + llEscapeURL("<html><head><script src='" + url 
        + "' type='text/javascript'></script></head><body onload='init()'></body></html>");
}

// This wraps the html you want to display so that it will be shown from links 
// made with build_url
string build_response(string body)
{
    return "function init() {document.getElementsByTagName('body')[0].innerHTML='" + body + "';}";
}

default
{
    state_entry()
    {
        llRequestURL();
    }
    
    http_request(key id, string method, string body)
    {
        if (method == URL_REQUEST_GRANTED)
        {
            url = body + "/";
            
            show(build_url(url + "page" + (string)(page)));
        }
        else if (method == "GET")
        {
            string path = llGetHTTPHeader(id, "x-path-info");
            string content = "<h1>path: " + path + "</h1>";
            page = (integer)llGetSubString(path, 5, 5);
            if (page > 0) content += "<a href=\"" + build_url(url + "page" + (string)(page - 1)) + "\">Previous</a> ";
            if (page < 9) content += "<a href=\"" + build_url(url + "page" + (string)(page + 1)) + "\">Next</a><br>";
            integer k;
            for(;k<10;++k)
            {
                if (k == page)
                    content += "<br>Page " + (string)k;
                else
                    content += "<br><a href=\"" + build_url(url + "page" + (string)(k)) + "\">Page " + (string)k + "</a>";
            }
            content += "<br><br>This page is " + (string)(llStringLength(build_response(content)) + 37) + " bytes long.";
            llHTTPResponse(id, 200, build_response(content));
        }
    }
}

Another JavaScript boot-strapping method (Vegas Silverweb)

This one makes it pretty easy to build out some arbitrary length web pages, using http-in's output directly. Loading between pages is kinda slow.

string URL;

string get(string page)
{
    if (page == "index")
    {
        return "<h1>Index</h1>This is the index page.<br><br><a href='page1.html'>Link to page 1</a>";
    }
    else if (page == "page1")
    {
        return "<h1>Page 1</h1>This is page 1!.<br><br><a href ='index.html'>Link to the index page.</a>";   
    }
    else return "Unknown page.";
}

string serve(string page)
{
    return "document.write(atob('" + llStringToBase64(get(page)) + "'));";
}

integer R;
load(string page)
{
    string content="data:text/html;base64,"
        + llStringToBase64("<html><head><base href='"+URL+"/'></head><body>"
                        + "<script src='" + page + ".js' type='text/javascript'>"
                        + "</script><span r='" +  (string)((++R) % 10) + "</body></html>");
    
    llSetPrimMediaParams(0,[
                    PRIM_MEDIA_CURRENT_URL, content,
                    PRIM_MEDIA_HOME_URL, content,
                    PRIM_MEDIA_AUTO_ZOOM, FALSE,
                    PRIM_MEDIA_FIRST_CLICK_INTERACT, TRUE,
                    PRIM_MEDIA_PERMS_INTERACT, PRIM_MEDIA_PERM_ANYONE,
                    PRIM_MEDIA_PERMS_CONTROL, PRIM_MEDIA_PERM_NONE,
                    PRIM_MEDIA_AUTO_PLAY, TRUE
                    ]);
}

default
{
    state_entry()
    {
        llRequestURL();
    }

    http_request(key id, string method, string body)
    {
        if (method == URL_REQUEST_GRANTED)
        {
            URL = body;
            load("index");
        }
        else if (method == URL_REQUEST_DENIED)
        {
            llOwnerSay("Unable to get url!");
        }
        else if (method == "GET")
        {
            string path_raw = llGetHTTPHeader(id, "x-path-info");
            if (path_raw == "")
            {
                // No path, assume index.html
                path_raw = "/index.html";
            }
            
            list path = llParseString2List(llGetSubString(path_raw, 1, -1), ["."], []);
            string page = llList2String(path, 0);
            string type = llList2String(path, 1);
            
            if (type == "js")
            {
                llHTTPResponse(id, 200, serve(page));
            }
            else if (type == "html")
            {
                llHTTPResponse(id, 100, "");
                load(page);
            }
        }
    }
}