Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Open Source

Second Life: A Programmer's Perspective


Adding Behavior to Your Objects

Okay, now try scripting. Click the Build button and the IDE window is revealed. The cube shape and Create icon should be the defaults. Move the mouse cursor somewhere close to you and "rez" a cubic prim. While there are numerous shaping, positioning, and texturing features you can apply to a prim, focus on LSL scripting for now, beginning with an understanding of data types and moving to the structure of a script itself.

LSL Data Types

LSL supports the types in Table 1. Although some are obvious, others may surprise you. C programmers may be happy to see the explicit support for string operations, such as concatenate (via the "+" operator) and the usual operations supported for Java strings.

Data Type Usage
integer Whole number in the range -2,147,483,648 to 2,147,483,647.
float Decimal number in the range 1.175494351E-38 to 3.402823466E+38.
vector Three floats in the form < x , y , z >. Usually a position, color, or Euler rotation. An example declaration is: vector a = <1,2,3>;.
rotation Rotations consist of four floats, in the form <x,y,z,s>. The s term connotes an angular measurement.
key A UUID (specialized string) used to identify something in SL, notably an agent, object, sound, texture, other inventory item, or dataserver request.
string A sequence of characters, limited only by the amount of free memory available to the script.
list A heterogeneous collection of other data types, (excluding list itself).

Table 1: LSL data types.

In addition to C native types such as integer and float, LSL also explicitly supports the UTF-8 string type that behaves as you would expect (in Java, not C), and a key type for creating, manipulating, and representing globally unique identifiers (GUIDs). There are no user-defined constants, thus nothing like enum.

LSL is event-driven, features states, and supports 3D variable types (vector and quaternion). LSL has built-in functions for manipulating physics and social interaction among avatars. LSL adds some interesting keywords; for example, state, which is essentially a "go to" operator that forces a transition in a new state handler.

For representing collections, LSL supports only the list type. There are no arrays or dictionaries, and lists cannot contain other lists. Lists in LSL are sufficiently annoying that we provide an expanded description here.

The list type may contain heterogeneous elements comprised of lists of other primitive data types. Lists are created via comma-separated values (CSVs) of the other data types, enclosed by square brackets: "[" and "]." A typical list declaration would resemble:

// a list with a two integers, a 
// string, float, vector and rotation 
list l = [42,0, "electric sheep",
   3.1416,<1.0,2,0>,<0,0,0,1>]; 


Lists also store metadata about each list item, but also its type, accessed through list accessor primitives llGetListEntryType and llCSV2List (an annoyance with LSL is that every built-in function—and there are myriad—must begin with "ll," as if you somehow needed a reminder as to whose scripting environment you're using). Lists can only be read using accessor functions (rather than bracket notation). Thus, to retrieve the first element from the aforementioned declared list and cast it to a string, you might use:


string element; 
element = llList2String(l,0); 

Elements fetched from lists are accessed and cast to type in a single operation; llList2Float retrieves an element and casts it to float, llList2Integer casts the element to integer, and so on. One additional shortcoming: Because lists cannot contain other lists, multidimensional arrays are difficult to create and manage. One way to get around both the lack of arrays and the lack of an explicit object model is to use "list striding."

A "strided" list is simply a list with items grouped together in a fixed and repeating layout. If, for example, you needed in-memory containment to record visitors to your store, such a list might contain the visitor name, amount of Linden dollars spent, item purchased, and number of minutes spent on your premise. Although you would periodically persist this cache to a formal database, you might want a short-term cache. In this case, you might use a strided list:


integer STRIDE = 4; 
list patrons = ["Zeno Chambers",500, "Magic Broomstick","2006-10-31", 
  "Biggie Pike", 50, "Flagon of Pumpkin juice","2006-10-31",
  "Mae Moriarity",25,"Halloween Hangover Kelper","2006-11-01"]; 

Then you could use one of the strided list convenience functions to assist in managing the list. To sort (ascending) a strided list, you would use:


patrons = llListSort(patrons, STRIDE, TRUE); 

LSL Script Structure

If you select the Content tab, right-click and select New Script, you see a new script appear in the editor window. It should be identical to Listing One and your worldview should resemble Figure 2.

 1 default
 2 {
 3     state_entry()
 4     {
 5         llSay(0, "Hello, Avatar!");
 6     }
 7 
 8     touch_start(integer total_number)
 9     {
10         llSay(0, "Touched.");
11     }
12 }
Listing One

[Click image to view at full size]

Figure 2: Creating a new script.

Inspecting a default new script reveals a few things about LSL. First of all, LSL is essentially a C-like language, with roughly equivalent syntax and similar control flow. First, notice that the logic for the object is normally contained within a code block called "default." You might think of the default block as the "static void main" of LSL. The default module must always be present, and represents the event callbacks for the object in which it exists. Other states may be present either in this module or in others in the Contents folder, and it's possible to transition to some other state handler (and back) via the state keyword.

The default script has two handlers, state_entry and touch_start. Reviewing the comprehensive list of event handlers at the LSL wiki, you see that state_entry is invoked whenever the state is entered and on creation of the instance of the object. Touch_start is invoked whenever the object is touched (you'll need to save and compile the script and close the IDE to see the effects of the touch_start callback). In both handlers, the llSay function is called; llSay broadcasts its argument (a text string) to anyone within a 10-square-meters range. Now that you've seen the "hello world" script, it's time to try diving off the deep end. Delete the contents of the script for the object and replace it with the script in Listing Two.

 1 //This script forwards all surrounding chat to the via email
 2
 3
 4 string g_Mail_Addr = "somename@some_domain.com";
 5 integer g_email = FALSE;
 6 integer g_IM = FALSE;
 7 integer listen_channel = 0;
 8 integer command_channel = 2;
 9 float g_maxtimeout = 180.0;
10 string g_emailStateOn = "Recording has been enabled!";
11 string g_emailStateOff = "Recording has been disabled!";
12 string g_IMStateOn = "IM relay has been enabled!";
13 string g_IMStateOff = "IM relay has been disabled!";
14
15 default
16 {
17  on_rez(integer param) {
18      llResetScript();
19  }//on_rez
20
21  // toggle state during the touch handler
22  state_entry(){  // possible to listen on more than one channel?
23      llGiveInventory(llGetOwner(), 
           llGetInventoryName(INVENTORY_OBJECT, 0));
24      string owner = llGetOwner();
25      llListen( command_channel, "", owner, "" );
26      llListen( listen_channel, "", NULL_KEY, "" );
27      llInstantMessage(owner, g_emailStateOff+" Type: '/2 hear! 
          <email address>' to capture close proximity inputs.");
28      llInstantMessage(owner," Type: '/2 !hear' to disable capture.");
29      llInstantMessage(owner, " Type: '/2 IM!' to enable IM feedback.");
30      llSetTimerEvent(g_maxtimeout);
31
32  }//state entry
33
34  listen( integer channel, string name, key id, string message ){
35      string owner = llGetOwner();
36      if (channel == command_channel){
37          list messageContent = llParseString2List(message, [" "], []);
38          integer len = llGetListLength(messageContent);
39          message = llList2String(messageContent,0);
40          if(message == "!hear"){
41              g_email = FALSE;
42              llInstantMessage(owner, g_emailStateOff);
43          }//disable email  
44              if(message == "hear!"){
45               if (len < 2){
46               llInstantMessage(owner, "incomplete message: I need an 
                                          email address too");
47              } else {
48                  g_email = TRUE;
49                  g_Mail_Addr = llList2String(messageContent,1);
50                  llSetTimerEvent(g_maxtimeout);
51                  llInstantMessage(owner, g_emailStateOn+" sending to 
                                          "+g_Mail_Addr);
52              }    
53          }//enable email
54          if(message == "!IM"){  //default mode
55              g_IM = FALSE;
56              llInstantMessage(owner, g_IMStateOff);
57          }//disable IM
58          if(message == "IM!"){
59              g_IM = TRUE;
60              llInstantMessage(owner, g_IMStateOn);
61          }//enable IM
62      }
63      if(g_email){
64          if(g_IM){
65              //send IM to owner of chat channel relay if on
66              llInstantMessage(owner, message);
67          }
68          //send email to owner of chat channel relay
69          llEmail(g_Mail_Addr, "SL Listening", message);
70      }//end if(g_email)
71  }// end listen
72
73  timer(){ // on expire of timer, discontinue
74       g_email = FALSE;
75       g_IM = FALSE;
76  }
77
78 }//default
Listing Two


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.