Sending data from Second Life to the web

Expired

Written by Kahiro Watanabe

 

If you are an LSL coder then you know that LSL scripts have little memory (64 k as of February 13 2010) and there’s no way to store larges amount of data or store sensible data and keep it safe. The only way to achieve this is by sending it out of the grid and store it somewhere in the World Wide Web. In this post I will explain you how to do that from scratch without any previous knowledge in PHP or HTML, let’s see how it goes!.

Q: What are we going to do?

A: We are going to send data from an LSL script in Second Life to a PHP script. For this we are going to use the llHTTPRequest function

The lsl script:

 

key myRequest;
default
{
    touch_start(integer number)
    {
           myRequest = llHTTPRequest("http://www.mysite.com/index.php",[HTTP_METHOD, "POST",HTTP_MIMETYPE, "application/x-www-form-urlencoded"],"name=Jane&age=22");
    }

    http_response(key request_id, integer status, list metadata, string body)
    {
            if (request_id == myRequest)
            {
                  llSay(0,body);
            }
    }
}

 Output: Jane is 22 years old

 

 

The PHP Script:

 

 

index.php

 

 

In this example we learned how easy is to send data from an LSL script to an url, in this case a PHP script and then return its values back again to SL.

The http_response method is simply used to know what happens after you send the data and keys are used to handle different requests, I’ll show you an example of that in future posts.

 

Smart gift giver (or how to store data in a database)

Expired

 

Written by Kahiro Watanabe

 

In the previous post we’ve seen a basic example of how to exchange data between SL and the Web, but there was not much utility in it. Now we are going to build a more powerful tool:

Let’s assume that we have a store and we want to give a gift to each visitor we have, but only once. If a visitor comes back again the gift will not be given.

How can we do that? The classic LSL option is storing the values in a list, but as I mentioned in the previous post there are memory and stability problems when storing data in LSL scripts, a much better option is to store it in a database so we can assure that our information will be safe and that we can store large amounts of it.

Before starting to code we first need to elaborate the basic concept of our application, if we start coding without having a basic structure of the project, then the result will not be good and will take us much more time to finish it… think about that before starting a big project.

Project name: Smart gift giver

Description: this application gives a gift to the visitor when touched only if the visitor didn’t get the gift in the past otherwise the visitor will receive a “Welcome back” message.

LSL script function: send key and name to the PHP script, wait for a response:

  • Response 1: “exists,userkey” will result in a “Welcome back” message to ‘userkey’
  • Response 2: “new,userkey” will give the object and will give a first welcome message to ‘userkey’

PHP script function: if a parameter key is received it will be checked against the database to see if it already exists.

  • Case 1: It exists, return Response 1 (“exists,userkey”)
  • Case 2: It doesn’t exists. Key and Name are added to the database and return Reponse 2 (“new,userkey”).

The table in the database:

How to design a database structure for an application is a large subject and I will try to give bits of information in each example, what you need to know now is that in a table we must find a way to uniquely identify each record, in this case we are going to use the user’s key, that will the primary key of the table.

Table Name: ‘customers’
Structure:

  • user_key (primary key) datatype: varchar (36)
  • user_name datatype: varchar (100)
    • Database Create - coming soon
    • table create
    • Create Fields - coming soon
    • The LSL script
    • // Object to give
      string giftName = "Shirt";
      
      // Url to send data
      string url = "http://www.mywebsite.com/index.php";
      
      // Request query
      key giftQuery;
      
      default
      {
          touch_start(integer total_number)
          {
              // get key and name from toucher
              key userKey = llDetectedKey(0);
              string name = llKey2Name(userKey);
      
              // build parameters for query
              string parameters = "userkey="+(string)userKey+"&username="+name;
      
              // send it to the url
              giftQuery = llHTTPRequest(url,[HTTP_METHOD,"POST",HTTP_MIMETYPE,"application/x-www-form-urlencoded"],parameters);
          }
      
          http_response(key request_id, integer status, list metadata, string body)
          {
              if (request_id == giftQuery)
              {
                  //we receive the message and the userkey whom the message/object must be sent
                  list result = llParseString2List(body,[","],[]);
      
                  string message = llList2String(result,0);
                  key userKey = llList2Key(result,1);
      
                  if (message == "exists")
                  {
                      llInstantMessage(userKey,"Welcome back");
                  }
                  else if (message == "new")
                  {
                      llInstantMessage(userKey,"Welcome to my land!");
                      llGiveInventory(userKey,giftName);
                  }
              }
          }
      }

       

      • The php script:

        index.php

        • 
          

           

          I will let you digest all this information, if there are some PHP functions or syntax that you don’t understand I highly recommend you to take a look at these two websites:
          PHP Tutorial from W3Schools
          The official php website

          Note: in this post I didn’t cover security issues, this is an important subject and I will adress in future posts as the complexity of the examples advances.

Making clean and lagless menus in LSL (part 2 of 2)

Expired

Written by Kahiro Watanabe

 

Go to part 1

While in the first part we’ve seen some generic examples of menus, submenus and how to make them browsable, the scripts were not useful at all. In this second and last part we are going to make an useful menu script that controls the light property of a prim and can limit the access to the menu. So, lets get to work!

The main menu has two buttons:

Light -> On, Off, << Back
Access -> Owner, Group, Anyone, << Back

The Light menu is very simple: it just turns on/off the light property of the prim and it has a << Back button to go back to the main menu
The Access menu limits the access to certain levels: Owner only, Group members (same group as setted in prim) and Anyone (anyone can use the menu)

Lets take a look at the script:

 

// Example made by Kahiro Watanabe, visit modernclix.info for lsl and php/mysql tutorials
// You can share this script with anyone

integer listener;  // Listener for handling different channels
integer mainMenuChannel;
integer lightChannel;
integer accessChannel;

vector green = <0.0,1.0,0.0>;

string access = "Owner"; // by default only owner can access the menu

// Function that returns a random number (used for generating a random channel)
integer randomNumber()
{
    return((integer)(llFrand(99999.0)*-1));
}

menu(key id, integer channel, string title, list buttons) // Generic menu creator
{
    llListenRemove(listener);
    listener = llListen(channel,"",id,"");
    llDialog(id,title,buttons,channel);
    llSetTimerEvent(10.0);   // if no menu button is pressed in 10 seconds the timer is triggered to kill the listener
}

// These function call the menu creator
mainMenu(key id)
{
    mainMenuChannel = randomNumber();
    menu(id,mainMenuChannel,"Select an option",["Light","Access"]);
}

lightMenu(key id)
{
    lightChannel = randomNumber();
    menu(id,lightChannel,"Select an option",["On","Off","<< Back"]);
}

accessMenu(key id)
{
    accessChannel = randomNumber();
    menu(id,accessChannel,"Select an option",["Owner","Group","Anyone","<< Back"]);
}

lightOn(vector color, float intensity, float radius, float falloff)
{
    llSetPrimitiveParams([PRIM_POINT_LIGHT, TRUE, color, intensity, radius, falloff]);
}

lightOff()
{
    llSetPrimitiveParams([PRIM_POINT_LIGHT,FALSE,ZERO_VECTOR,0.0,0.0,0.0]);
}

lightController(string message) // Function that control lights function
{
    if (message == "On")
    {
        lightOn(green,1.0,10.0,0.75);
    }
    else
    {
        lightOff();
    }
}

// Function that checks if user has permissions to use the menu
integer allowed(key id)
{
    return (id == llGetOwner() || (llDetectedGroup(0) && (access == "Group")) || (access == "Anyone"));
}

default
{

    touch_start(integer num)
    {
        key toucher = llDetectedKey(0);
        if (allowed(toucher)) // function that checks if use has permissions to access the menu
        {
            mainMenu(toucher);
        }
    }

    listen (integer channel, string name, key id, string message)
    {
        if (message == "<< Back") // lets go back to the main menu
        {
            mainMenu(id);
            return; //event stops here
        }

        // Now we need to know what channel is being listened
        if (channel == mainMenuChannel)
        {
            if (message == "Light")
            {
                lightMenu(id);
            }
            else if (message == "Access")
            {
                if (id == llGetOwner()) // Only owner cann use the "Access" menu
                {
                    accessMenu(id);
                }
                else // Show a message and show Main Menu again
                {
                    llWhisper(0,"Sorry, only owner can access to this menu");
                    mainMenu(id);
                }
            }
        }
        else if (channel == lightChannel)
        {
            lightController(message);
            lightMenu(id);
        }
        else if (channel == accessChannel)
        {
            access = message;
            llOwnerSay("Access level: " + access);
            accessMenu(id);
        }
    }

    timer()
    {
        llListenRemove(listener); // kill the listener
        llSetTimerEvent(0.0); // stop the timer
    }
}

 

As you can see, it’s very similar to the previous example, two buttons and one submenu each. The only difference is the functions triggered in the listen event. In this case the “lightController” function receives a message (“On”/”Off”) and according to that messages turns on/off the light thanks to the functions “lightOn()“/”lightOff().

There’s also a function called “allowed()” used in the touch_start event. This function returns only TRUE if:

1) Toucher is owner
OR
2) Toucher is a group member (same group as the one setted in the prim) and Acess level is “Group”
OR
3) Acess level is “Anyone” (simply let anyone use the menu)


LSL is an event oriented programming language. Unfortunately you can’t use the Object Oriented paradigm here or design an application using a Model View Controller pattern to separate the logic (functions) from the representation (menu). But in this example if you check the listen event, that is where all the menu work is done, it only does what it has to: check what channel is listening and trigerring functions, nothing else. I didn’t add the logic to turn on/off the light inside the menu logic, there’s a separate function for controlling light. The menu simply sends the message, doesn’t even check if it’s On or Off, the “lightController” function does that.