Download Wiley Scripting Your World: The Official Guide to Second Life Scripting
Transcript
D TE GH RI PY CO RI TE MA AL Chapter 1 Getting a Feel for the Linden Scripting Language What is so compelling about Second Life? As Linden Lab Founder Philip Rosedale explained in an October 19, 2006 interview in The New York Times, in Second Life avatars can move around and do everything they do in the real world, but without constraints such as the laws of physics: "When you are at Amazon.com [using current web technology] you are actually there with 10,000 concurrent other people, but you cannot see them or talk to them," Rosedale said. "At Second Life, everything you experience is inherently experienced with others." Much of the reason Rosedale can talk convincingly about shared experience is because of scripting in Second Life. To be sure, Second Life is a place of great physical beauty: in a well-crafted SL environment, a feeling of mood and cohesive appearance lend a level of credibility to the experience of existing and interacting in a specific and sometimes unique context. But consider how sterile Second Life would be without scripting. Builders and artists create beautiful vistas, but without interaction the world is static; little more than a fancy backdrop. Scripts give the world life, they allow avatars to be more realistic, and they enhance the residents' ability to react to and interact with each other and the environment, whether making love or making war, snorkeling, or just offering a cup of java to a new friend. This chapter covers essential elements of scripting and script structure. It is intended to be a guide, and may be a review for you; if that's the case then skim it for nuggets that enhance your understanding. If you are new to Second Life scripting or even programming in general, consider this chapter an introduction to the weird, wonderful world of SL scripting and the Linden Scripting Language, LSL. If you don't understand something, don't worry! You might find it easier to skip ahead and return here to get the details later. NOTE Throughout the book, you'll see references to the LSL wiki. There are actually several such wikis, of which http://wiki.secondlife.com/wiki/LSL_Portal is the "official" one, and http://lslwiki.net is one of many unofficial ones. Typing out the full URL is cumbersome and hard to read, so if you see a reference to the wiki, you'll see only the keyword. For example, if you see, "you'll find more about the particle system on the LSL wiki at llParticleSystem," it means http://wiki.secondlife.com/wiki/LlParticleSystem, http://www.lslwiki.net/lslwiki/wakka.php?wakka=llParticleSystem or http:// rpgstats.com/wiki/index.php?title=LlParticleSystem. All of these wikis have search functions and convenient indexes of topics. In general, all examples in this book are available at the Scripting Your World Headquarters (SYW HQ) in Second Life at Hennepin <38, 136, 108>* and on the Internet at http://syw.fabulo.us. There are also several extras that didn't get included in the book due to space limitations. Enjoy browsing! *Visit http://slurl.com/secondlife/Hennepin/38/138/108/ or simply search in-world for "SYWHQ." scripting structure 101 CHAPTER 1 S cripting Structure 101 Types Variables A script is a Second Life asset, much like a notecard or any other Inventory item. When it is placed in a prim, one of the building blocks of all simulated physical objects, it can control that prim's behavior, appearance, and relationship with other prims it is linked with, allowing it to move; change shape, color, or texture; or interact with the world. F low C ontrol NOTE O perators F unctions A prim is the basic primitive building block of SL: things like cubes, spheres, and cylinders. An object is a set of one or more linked prims. When you link the prims, the root prim is the one that was selected last; the remaining prims are called children. The root prim acts as the main reference point for every other prim in the object, such as the name of the object and where it attaches. E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary Whether or not you've already begun exploring how to script, you've probably created a new object and then clicked the New Script button. The result is that a script with no real functionality is added to the prim's Content folder. Left-clicking on the script opens it in the in-world editor and you see the script shown in Listing 1.1. It prints "Hello, Avatar!" to your chat window and then prints "Touched." each time you click the object that's holding it. Listing 1.1: Default New Script default { state_entry() { llSay(0, "Hello, Avatar!"); } touch_start(integer total_number) { llSay(0, "Touched."); } } This simple script points out some elements of script structure. First of all, scripting in SL is done in the Linden Scripting Language, usually referred to as LSL. It has a syntax similar to the common C or Java programming languages, and is event-driven, meaning that the flow of the program is determined by events such as receiving messages, collisions with other objects, or user actions. LSL has an explicit state model and it models scripts as finite state machines, meaning that different classes of behaviors can be captured in separate states, and there are explicit transitions between the states. The state model is described in more detail in the section "States" later in this chapter. LSL has some unusual built-in data types, such as vectors and quaternions, as well as a wide variety of functions for manipulating the simulation of the physical world, for interacting with player avatars, and for communicating with the real world beyond SL. 4 The following list describes a few of the key characteristics of an LSL script. If you're new to programming, don't worry if it doesn't make much sense just yet; the rest of this chapter explains it all in more detail. The section "An LSL Style Guide" ties things together again. • All statements must be terminated by a semicolon (;). • LSL is block-oriented, where blocks of associated functionality are delimited by opening and closing curly braces ({ block }). • Variables are typed and declared explicitly: you must always specify exactly which type a variable is going to be, such as string or integer. • At a bare minimum, a script must contain the default state, which must define at least one event handler, a subroutine that handles inputs received in a program, such as messages from other objects or avatars, or sensor signals. • Scripts may contain user-defined functions and global variables. Listing 1.2 shows a rather more complete script, annotated to point out other structural features. This script controls the flashing neon sign on the front of the Scripting Your World visitor center. Do not be discouraged if you don't understand what is going on here! Although this script is relatively complex, it is here to illustrate that you don't need to understand the details to see how a script is put together. This first discussion won't focus on the function of the neon sign, but rather on the structure commonly seen in LSL scripts. A script contains four parts, generally in the following order: • Constants (colored orange in Listing 1.2) • Variables (green) CHAPTER 1 CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces • Functions (purple) • States, starting with default (light blue, with the event handlers that make a state in dark blue) While common convention uses this order for constants, variables, and user-defined functions, they are permitted to occur in any order. They must all be defined before the default state, however. Additionally, you are required to have the default state before any user-defined states. NOTE Constants are values that are never expected to change during the script. Some constants are true for all scripts, and part of the LSL standard, including PI (3.141592653), TRUE (1), and STATUS_PHYSICS (which indicates whether the object is subject to the Second Life laws of physics). You can create named constants for your script; examples might include TIMER_INTERVAL (a rate at which a timer should fire), COMMS_CHANNEL (a numbered communications channel), or ACCESS_LIST (a list of avatars with permission to use the object). Variables, meanwhile, provide temporary storage for working values. Examples might include the name of the avatar who touched an object, counts of items seen, or the current position of an object. The section "Variables" later in this chapter describes variables in more detail. Functions are a mechanism for programmers to break their code up into smaller, more manageable chunks that do specific subtasks. They increase readability of the code and allow the programmer to reuse the same capability in multiple places. The section "User-Defined Functions" describes functions in more detail. 5 Listing 1.2: Flipping Textures by Chat and by Timer Comments constants variables functions states event handlers CHAPTER 1 S cripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary // Texture Flipper for a neon sign // Constants integer TIMER_INTERVAL = 2; // timer interval string NEON_OFF_TEXTURE = "bcf8cd82-f8eb-00c6-9d61-e610566f81c5"; string NEON_ON_TEXTURE = "6ee46522-5c60-c107-200b-ecb6e037293e"; // global variables integer gOn = TRUE; // If the neon is burning integer gSide = 0; // which side to flip integer gListenChannel = 989; // control channel // functions fliptexture(string texture) { llSetTexture(texture, gSide); } usage(){ llOwnerSay("Turn on by saying: /"+(string)gListenChannel+" sign-on"); llOwnerSay("Turn off by saying: /"+(string)gListenChannel+" sign-off"); } // states default { state_entry() { llSetTimerEvent(TIMER_INTERVAL); llListen( gListenChannel, "", llGetOwner(), "" ); } listen(integer channel, string name, key id, string msg) { if (msg == "sign-on") { fliptexture(NEON_ON_TEXTURE); gOn = TRUE; llSetTimerEvent(TIMER_INTERVAL);// start the timer } else if (msg == "sign-off") { fliptexture(NEON_OFF_TEXTURE); // start the timer gOn = FALSE; llSetTimerEvent(0.0); } else { usage(); } } timer() { if (gOn){ fliptexture(NEON_OFF_TEXTURE); gOn = FALSE; } else { fliptexture(NEON_ON_TEXTURE); gOn = TRUE; } } } 6 Two forward slashes (//) indicate a comment. The slashes and the entire rest of the line are completely ignored by SL. They remain part of the script but have no effect, so you can use them to add a copyright notice or a description of what's going on in the script, or even to disable lines when you are trying to debug a problem. Likewise, empty lines and extra spaces play no part in the execution of a script: indentation helps readability but SL ignores it. Declarations of global constants and variables have script-wide scope; that is, the entire rest of the script can use them. Most programmers are taught that global variables are evil, but in LSL there is no other way to communicate information between states. Since most LSL scripts are fairly short, it's relatively easy to keep track of these beasties, eliminating one of the major reasons that global variables are discouraged in other languages. Although technically the LSL compiler does not distinguish between user-defined constants and variables, the examples in this book name constants with all capital letters, and global variables using mixed case beginning with the lowercase letter g. Next you will notice a couple of code segments that seem to be major structural elements; these are called fliptexture() and usage(), respectively. These are user-defined functions. Functions are global in scope and available to all states, event handlers, and other user-defined functions in the same script. Functions can return values with a return command. The "Functions" section in this chapter provides considerably more detail. Linden Library functions are readily identifiable, as they (without exception) begin with the letters ll, as in llSetTimerEvent(). The last elements of a script are the states. A state is a functional description of how the script should react to the world. For example, you could think of a car being in one of two states: when it is on the engine is running, it is making noises, it can move, it can be driven. When it is off it is little more than a hunk of metal; it is quiet, immobile, and cannot be driven. An LSL script represents an object's state of being by describing how it should react to events in each situation. Every script must have at least the one state, default, describing how it behaves, but you can define more states if it makes sense. An event is a signal from the Second Life simulation that something has happened to the object, for example that it has moved, been given money, or been touched by an avatar. When an event happens to a Second Life object, each script in the object is told to run the matching event handler: As an example, when an avatar touches an object, SL will run the touch_start(), touch(), and touch_end() event handlers in the active state of each script in the object, if the active state has those handlers. LSL has defined a set number of event handlers. (The SYW website has a complete list of event handlers and how they are used.) The three event handlers in Listing 1.2, state_entry(), listen(), and timer(), execute in a finite state machine managed by the simulator in which the object exists. More details on the state model are presented in the section "States," as it is one of the more interesting aspects of LSL. CHAPTER 1 CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces You may well ask, "So what does this script do?" It's really pretty simple. Whenever a couple of seconds tick off the clock (the time interval defined by the constant TIMER_INTERVAL), the timer event fires, and the texture on the front face of the object is replaced either with the "on" texture referenced by the key in the string NEON_ON_TEXTURE or with the "off" texture, NEON_OFF_TEXTURE. The script also listens for input by anyone who knows the secret channel to talk to the object (989, declared as the variable gListenChannel). If the object hears anyone chat sign-off or sign-on on the secret channel*, it will activate or deactivate the sign. Come by SYW HQ and tell our sign to turn off (or on, as the case may be). Figure 1.1 shows the script in action. Chapter 3, "Communications," talks more about channels and how to communicate with objects. * When typing in the chat window, the channel number is preceded by a slash, as in /989 sign-off. 7 CHAPTER 1 ON OFF Scripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers Figure 1.1: Textureflipping in action States anaging M S cripted O bjects A n LSL Style G uide S ummary TYPES A type is a label on a piece of data that tells the computer (and the programmer) something about what kind of data is being represented. Common data types include integers, floating-point numbers, and alphanumeric strings. If you are familiar with the C family of languages (C, C++, C#, Java, and JavaScript) you'll notice similarities between at least a few of them and LSL. Table 1.1 summarizes the valid LSL variable types. Different data types have different constraints about what operations may be performed on them. Operators in general are covered in the "Operators" section of this chapter, and some of the sections that cover specific types also mention valid operations. Because all variables in LSL are typed, type coercion is awkward. Most coercion must be done manually with explicit casting, as in integer i=5; llOwnerSay((string)i); In many cases, LSL does the "right thing" when coercing (implicit casting) types. Almost everything can be successfully cast into a string, integers and floats are usually interchangeable, and other conversions usually result in the null or zero-equivalent. The discussion later, in Table 1.7, of llList2<type>() functions gives a good overview of what happens. Look on the Types page of the LSL wiki for an expanded example of coercion. 8 Table 1.1: LSL Variable Types 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 A three-dimensional structure in the form <x, y, z>, where each component is a float. Used to describe values such as a position, a color, or a direction. CHAPTER 1 rotation quaternion A four-dimensional structure consisting of four floats, in the form <x, y, z, s>, that is the natural way to represent rotations. Also known as a quaternion, the two type names are interchangeable. CHAPTER 2 key A UUID (specialized string) used to identify something in SL, notably an agent, object, sound, texture, other inventory item, or data-server request. string A sequence of characters, limited only by the amount of free memory available to the script (although many functions have limits on the size they will accept or return). list A heterogeneous collection of values of any of the other data types, for instance [1, "Hello", 4.5]. CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 NOTE CHAPTER 10 The value of a variable will never change unless your code reassigns it, either explicitly with = or implicitly with an operator such as ++, which includes a reassignment: CHAPTER 12 a = "xyzzy"; b = a; a = "plugh"; CHAPTER 11 CHAPTER 13 CHAPTER 14 // b is also "xyzzy" // a is now "plugh" but b is still "xyzzy"! This holds true for all types, including lists! In keeping with this immutability, all function parameters are pass-by-value (meaning only the value is sent) in LSL. CHAPTER 15 APPENDIces Integer Integers are signed (positive or negative) 32-bit whole numbers. LSL does not provide any of the common variations on integer types offered in most other languages. Integers are also used to represent a few specific things in LSL: • Channels are integer values used to communicate in "chat" between both objects and avatars. See the section "Talking to an Object (and Having It Listen)" in Chapter 3 for a deeper discussion of channels and their use. • Booleans are implemented as integer types with either of the constant values: TRUE (1) or FALSE (0). • Event counters are integer arguments to event handlers that indicate how many events are pending. Inside such an event handler, the llDetected*() family of library functions can be used to determine which avatars touched an object, which other objects collided with yours, or which objects are nearby. • Listen handles are returned by llListen() and enable code to have explicit control over the listen stream. (Other things you might think would be handles are actually returned as keys.) See Chapter 2, "Making Your Avatar Stand Up and Stand Out," for examples of llListen(). 9 • Bit patterns (or bit fields) are single integers that represent a whole set of Boolean values at once. Different bits can be combined to let you specify more than one option or fact at once. For instance, in the llParticleSystem() library function, you can indicate that particles should bounce and drift with the wind by combining the constant values PSYS_PART_BOUNCE_MASK and PSYS_PART_WIND_MASK by saying CHAPTER 1 PSYS_PART_BOUNCE_MASK | PSYS_PART_WIND_MASK Scripting Structure 101 Float Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary A float in LSL is a 32-bit floating-point value ranging from ±1.401298464E–45 to ±3.402823466E+38. Floats can be written as numbers, such as 1.0 or 9.999, and they can be written in scientific notation, as in 1.234E–2 or 3.4E+38, meaning 1.234 × 10−2 and 3.4 × 1038. A float has a 24-bit signed mantissa (the number), and an 8-bit signed exponent. Thus for a float 1.2345E+23, the number 1.2345 is the mantissa, and 23 is the exponent. Because one bit represents the sign of the number (positive or negative), a 23-bit mantissa gives a precision equivalent to approximately 7 decimal digits—more precisely log10(223). This means values are rarely stored exactly. For example, if you do something like float foo = 101.101101; and print the result, it will report 101.101105, so you should expect some rounding inaccuracy. Even 10E6 × 10E6 isn't 10E12, instead printing 100000000376832.000000. Often more disturbingly, addition or subtraction of two numbers of vastly different magnitudes might yield unexpected results, as the mantissa can't hold all the significant digits. When an operation yields a number that is too big to fit into a float, or when it yields something that is not a number (such as 1.0 / 0.0), your script will generate a run-time Math Error. Vector Vectors are the currency of three-dimensional environments, and so are found throughout LSL code. In addition, anything that can be expressed as a triplet of float values is expressed in a vector type. If you were guessing about the kinds of concepts readily expressed by a set of three values, you'd probably come up with positioning and color, but there are also others, shown in Table 1.2. Table 1.2: Common uses for Vectors and What They Represent Vector Concept What Vector Represents 10 Position Meters. Always relative to some base positioning (the sim, the avatar, or the root prim). Size Meters, sometimes also called scale. Color Red, green, blue. Each component is interpreted in a range from 0.0 to 1.0; thus yellow is vector yellow = <1.0, 1.0, 0.0>; Direction Unitless. It is usually a good idea to normalize directions (see llVecNorm()); since directions are often multiplied with other values, non-unit direction vectors can have an unexpected proportional effect on the results of such operations. Velocity An offset from a position in meters traveled per second. You can also think of velocity as a combination of direction and speed in meters per second. Acceleration Meters per second squared. Impulse Force (mass × velocity). Rotation Radians of yaw, pitch, and roll. Also known formally as the Euler form of a rotation. The x, y, and z components of a vector are floats, and therefore it is slightly more efficient to write a vector with float components—for instance, <0.0, -1.0, 123.0>—than with integer components. Here are some examples of ways to access vectors, including the built-in constant ZERO_VECTOR for <0.0,0.0,0.0>: vector aVector = <1.0, 2.0, 3.0>; float xPart = 1.0; vector myVec = <xPart, 2.0, 3.0>; float yPart = myVec.y; float zPart = myVec.z; myVec.y = 0.0; vector zeroVec = ZERO_VECTOR; llOwnerSay("The empty vector is "+(string)ZERO_VECTOR); Vectors may be operated on by scalar floats (regular numbers); for instance, you could convert the yellow color vector in Table 1.2 to use the Internet-standard component ranges of 0 to 255 with the expression <1.0, 1.0, 0.0>*255.0. Vector pairs may be transformed through addition, subtraction, vector dot product, and vector cross product. Table 1.3 shows the results of various operations on two vectors: vector a = <1.0, 2.0, 3.0>; vector b = <-1.0, 10.0, 100.0>; Table 1.3: Mathematical Operations on Vectors Operation Meaning Vector + Add a+b = < 0.0, 12.0, 103.0 > - Subtract a-b = < 2.0, -8.0, -97.0> * Vector dot product a*b = 319.0 (1 * -1) + (2 * 10) + (3 * 100) % Vector cross product a%b = < 170.0, -103.0, 12.0 > <(2 * 100) - (3 * 10), (3 * -1) - (1 * 100), (1 * 10) - (2 * -1) > CHAPTER 1 CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces Coordinates in SL can be confusing. There are three coordinate systems in common use, and no particular annotation about which is being used at any given time. • Global coordinates. A location anywhere on the Second Life grid with a unique vector. While not often used, every place on the grid has a single unique vector value when represented in global coordinates. Useful functions that return global coordinates include llGetRegionCorner() and llRequestSimulatorData(). • Region coordinates. A location that is relative to the southwest corner of the enclosing sim (eastward is increasing x, northward is increasing y, up is increasing z), so the southwest corner of a sim at altitude 0 is <0.0, 0.0, 0.0>. The position or orientation of objects, when not attached to other prims or the avatar, is usually expressed in terms of regional coordinates. A region coordinate can be converted to a global coordinate by adding to it the region corner of the simulator the coordinate is relative to: vector currentGlobalPos = llGetRegionCorner() + llGetPos(); 11 CHAPTER 1 Scripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers • Local coordinates. A location relative to whatever the object is attached to. For an object in a linkset, that means relative to the root prim. For an object attached to the avatar, that means relative to the avatar. For the root prim of the linkset, that value is relative to the sim (and therefore the same as the region coordinates). If the attachment point moves (e.g., the avatar moves or the root prim rotates), the object will move relative to the attachment, even though local coordinates do not change. For example, if an avatar moves her arm, her bracelet will stay attached to her wrist; the bracelet is still the same distance from the wrist, but not in the same place in the region. Useful functions on vectors include llVecMag(vector v), llVecNorm(vector v), and llVecDist(vector v1, vector v2). llVecMag() calculates the magnitude, or length, of a vector—it's Pythagoras in three dimensions. These functions are really useful when measuring the distance between two objects, figuring out the strength of the wind or calculating the speed of an object. llVecNorm() normalizes a vector, turning it into a vector that points in the same direction but with a length of 1.0. The result can be multiplied by the magnitude to get the original vector back. llVecNorm() is useful for calculating direction, since the result is the simplest form of the vector. llVecDist(v1,v2) returns the distance between two vectors v1 and v2, and is equivalent to llVecMag(v1-v2). States M anaging S cripted O bjects A n LSL Style G uide S ummary Rotation There are two ways to represent rotations in LSL. The native rotation type is a quaternion, a fourdimensional vector of which the first three dimensions are the axes of rotation and the fourth represents the angle of rotation. quaternion and rotation can be used interchangeably in LSL, though rotation is much more common. Also used are Euler rotations, which capture yaw (x), pitch (y), and roll (z) as vector types rather than as rotation types. The LSL Object Editor shows rotations in Euler notation. Euler notation in the Object Editor uses degrees, while quaternions are represented in radians; a circle has 360 degrees or TWO_PI (6.283) radians. Euler vectors are often more convenient for human use, but quaternions are more straightforward to combine and manipulate and do not exhibit the odd discontinuities that arise when using Euler representation. For instance, in the SL build tools, small changes in object rotation can make sudden radical changes in the values indicated. Two functions convert Euler representations into quaternions (and vice versa): llEuler2Rot(vector eulerVec) and llRot2Euler(rotation quatRot). Many of your scripts can probably get away with never explicitly thinking about the guts of quaternions: // convert the degrees to radians, then convert that // vector into a quaternion rotation myQuatRot = llEuler2Rot(<45.0, 0.0, 0.0> * DEG_TO_RAD); // convert the rotation back to a vector // (the values will be in radians) vector myEulerVec = llRot2Euler(myQuatRot); The above code snippet converts the degrees to radians by multiplying the vector by DEG_TO_RAD. Two other constants—ZERO_ROTATION and RAD_TO_DEG—are useful for rotations. These constants are defined in Table 1.4. 12 Table 1.4: Constants Useful for Manipulating Rotations Constant Value Description ZERO_ROTATION <0.0, 0.0, 0.0, 1.0> A rotation constant representing a Euler angle of <0.0, 0.0, 0.0>. DEG_TO_RAD 0.01745329238 A float constant that, when multiplied by an angle in degrees, gives the angle in radians. CHAPTER 1 RAD_TO_DEG 57.29578 A float constant that, when multiplied by an angle in radians, gives the angle in degrees. CHAPTER 2 You will find much more in-depth discussion and some examples for using rotations in Chapter 4, "Making and Moving Objects." CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 Key CHAPTER 7 A key is a distinctly typed string holding the UUID for any of a variety of relatively long-lived SL entities. A UUID, or Universal Unique Identifier, is a 128-bit number assigned to any asset in Second Life, including avatars, objects, and notecards. It is represented as a string of hex numbers in the format "000000000000-0000-0000-000000000000", as in "32ae0409-83d6-97f5-80ff-6bee5f322f14". NULL_KEY is the all-zero key. A key uniquely identifies each and every long-lived item in Second Life. In addition to the unsurprising use of keys to reference assets, keys are also used any time your script needs to request information from a computer other than the one it is actually running on, for instance to web servers or to the SL dataserver, to retrieve detailed information about SL assets. In these situations, the script issues a request and receives an event when the response is waiting. This model is used to ask not just about avatars using the llRequest*Data() functions, but also to do things like read the contents of notecards with llGetNotecardLine(). Asynchronous interactions with the outside world might include HTTP requests, llHTTPRequest(), and are identified with keys so that the responses can be matched with the queries. CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces Numerous LSL functions involve the manipulation of keys. Some of the main ones are shown in Table 1.5. Table 1.5: Sample Functions that Use Keys Function Name Purpose key llGetKey() Returns the key of the prim. key llGetCreator() Returns the key of the creator of the prim. key llGetOwner() Returns the key of the script owner. key llDetectedKey() Returns the key of the sensed object. string llKey2Name(key id) Returns the name of the object whose key is id. key llGetOwnerKey(key id) Returns a key that is the owner of the object id. Note that there is no built-in function to look up the key for a named avatar who is not online. However, there are a number of ways to get this information through services, such as llRequestAgentData(). 13 String CHAPTER 1 Scripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary Strings are sequences of letters, numerals, and other characters, collected as a single value. You can specify a constant string in LSL by surrounding the sequence of characters you want with double quotes ("). You can include a double-quote symbol in a string by prefixing it with a backslash (\). Similarly, you can include a backslash with \\, a sequence of four spaces with \t, and a newline with \n. Table 1.6 shows the set of string-manipulation functions provided by LSL. String indexes are zero-based, ranges are always inclusive of the start and end index, and negative indexes indicate counting positions backwards from the end of the string (–1 is the last character of the string). Consider this example: string test = "Hello world! "; llGetSubString(test,0,0); // "H" llGetSubString(test,0,-1); // "Hello world! " llGetSubString(test, -6,-2); // "world" String values, like all other LSL values, are immutable; that is, no function will modify the original string. Rather, functions return brand-new strings that contain transformed versions. For example, the variable test in the following snippet does not change when a word is inserted into the string: string test = "Hello world! "; string x = llInsertString(test, 6, "cruel "); // x = "Hello cruel world! ", test is unchanged Table 1.6: Functions that Manipulate Strings 14 Function Name Purpose integer llStringLength(string s) Gets the length of a string. string llToLower(string s) Converts a string to lowercase. string llToUpper(string s) Converts a string to uppercase. integer llSubStringIndex(string s, string pattern) Finds the integer position of a string in another string. string llGetSubString(string s, integer start, integer end) Extracts a part of a string; returns the part. string llDeleteSubString(string s, integer start, integer end) Returns a copy of the original minus the specified part. string llInsertString(string s, integer pos, string snippet) Inserts a string snippet into a string starting at the specified position. string llStringTrim(string s, integer trimType) Trims leading and/or trailing whitespace from a string. Trim types are STRING_TRIM_HEAD for the leading spaces, STRING_TRIM_ TAIL for the trailing spaces, and STRING_TRIM for both leading and trailing spaces. string llEscapeURL(string url) Returns the string that is the URL-escaped version of url (replacing spaces with %20, etc). string llUnescapeURL(string url) Returns the string that is the URL unescaped version of url (replacing %20 with spaces, etc). List Lists in LSL are collections of values of any other types. Lists are heterogeneous: they may contain any mixture of values of any type except other lists. This makes it important to keep track of what is stored in your lists to avoid getting into trouble, but you can type-check elements at runtime if you need to. For instance, the following code extracts elements from a list: CHAPTER 1 CHAPTER 2 list mylist = [1, 2.3, "w00t", <1.0, 0.0, 0.0>]; integer count = llList2Integer(mylist, 0); float value = llList2Float(mylist, 1); string exclamation = llList2String(mylist, 2); vector color = llList2Vector(mylist, 3); CHAPTER 3 To access the individual elements, use the llList2<type>() functions, shown in Table 1.7. CHAPTER 7 You can use llGetListEntryType() to find out the type of the element. For example, llList2Float(list src, integer index); returns the float value at the specified index in the list. Thus Listing 1.3 prints 4.000000 when the object is touched. Note that it is implicitly casting the original integer value into a float. CHAPTER 8 Table 1.7: Functions that Extract Individual List Elements Function Name Purpose string llList2String(list l, integer index) Returns a string element. All other types are appropriately coerced into a string. integer llList2Integer(list l, integer index) Returns an integer element. If the element is not coercible to an integer, it will return 0. float llList2Float(list l, integer index) Returns a float element. If the element is not coercible to a float, it will return 0.0. key llList2Key(list l, integer index) Returns a key element. If the element is a regular string, it is returned unchanged. Other non-key elements return NULL_KEY. vector llList2Vector(list l, integer index) Returns a vector element. If the element is not coercible to a vector, it will return ZERO_VECTOR. rotation llList2Rot(list l, integer index) Returns a rotation element. If the element is not coercible to a rotation, it will return ZERO_ROTATION. integer llGetListEntryType(list l, integer index) Gets the type of entry of an element in the list. Returns an integer constant TYPE_INTEGER, TYPE_FLOAT, TYPE_STRING, TYPE_ KEY, TYPE_VECTOR, TYPE_ROTATION, or TYPE_INVALID. Invalid occurs when the index was out of range. CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces Listing 1.3: Coercing an Element of a List into a Float list gMyNumList=[1,2,3,4,5]; default { touch_start(integer total_number) { float f = llList2Float(gMyNumList,3); llOwnerSay((string)f); } } The upside of heterogeneous lists in the LSL context is that you can imitate associative arrays (for instance, dictionaries or reference tables) with careful searches. If you keep your lists well-structured, you're very likely to know your list contents and their types very well, even when the contents are a mixed bag. The first element in a list is at index 0, and negative indexes reference elements counting backwards from the end of the list, with –1 indicating the last one. Table 1.8 shows some of the basic list-manipulation functions. 15 Table 1.8: Some Core Functions to Manipulate Lists CHAPTER 1 Scripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers Function Name Purpose integer llGetListLength(list l) Gets the number of elements in a list. integer llListFindList(list l, list test) Returns the index of the first instance of test in l, or –1 if it isn't found. list llList2List(list l, integer start, integer end) Returns the portion of a list, with the specified elements (similar to substring). list llDeleteSubList(list l, integer start, integer end) Removes a portion of a list, returns the list minus the specified elements. list llListInsertList(list dest, list snippet, integer pos) Inserts a list into a list. If pos is longer than the length of dest, it appends the snippet to the dest. list llListReplaceList(list dest, list snippet, integer start, integer end) Replaces a part of a list with another list. If the snippet is longer than the end-start, then it serves to insert elements too. float llListStatistics(integer operation, list input) Performs statistical operations, such as min, max, and standard deviation, on a list composed of integers and floats. The operation takes one of ten LIST_STAT_* constants, which are described fully on the SYW website. States anaging M S cripted O bjects A n LSL Style G uide S ummary You can use + and += as concatenation operators: list a = [1,2]; a = a + [3]; // a now contains [1, 2, 3] a += [4]; // a now contains [1, 2, 3, 4] While you can't make nested lists, you can insert a list into another: list myL = ["a", "b"]; list myLL = llListInsertList(myL,[1,2,3,4],1); // myLL contains [a, 1, 2, 3, 4, b] Perhaps the most interesting set of string functions deals with converting back and forth between lists and strings, listed in Table 1.9. For example, the function llDumpList2String() concatenates the items in a list into a string with a delimiter character sequence separating the list items; llDumpList2String([1,2,3,4,5], "--") prints "1--2--3--4--5". The function llList2CSV() is a specialized version, using only commas as separators. Table 1.9: Functions that Convert Lists to Strings 16 Function Name Purpose string llDumpList2String(list source, string delimiter) Turns a list into a string, with the delimiter between items. list llParseString2List(string s, list delimiters, list spaces) Turns a string into a list, splitting at delimiters and spaces, keeping spaces. Consecutive delimiters are treated as one delimiter. Listing 1.4 shows an example of how to use this function. list llParseStringKeepNulls(string s, list delimiters, list spaces) Turns a string into a list; consecutive delimiters are treated separately. list llCSV2List(string csvString) Converts comma-separated values (CSVs) to a list. For instance, if csvString is a string "1,2,3" then the list will be [1, 2, 3]. string llList2CSV(list l) Converts a list to a string containing comma-separated values (CSVs). The llParseString2List() is particularly useful because it converts a string's elements to a list of strings. Listing 1.4 shows an example of how you would call it to break a string into separate items and then print them to the chat window. Listing 1.4: llParseString2List() Example default { touch_start(integer total_number) { list l = llParseString2List ("The answer to Life, the Universe"➥ + " and Everything is 42", [" "], []); integer i; integer len = llGetListLength(l); for (i = 0; i < len; i++){ llOwnerSay("item==>"+ llList2String(l, i)); } } } While not totally symmetrical to llDumpList2String(), it still produces useful results: [8:31] [8:31] [8:31] [8:31] [8:31] [8:31] [8:31] [8:31] [8:31] [8:31] Object: Object: Object: Object: Object: Object: Object: Object: Object: Object: item==>The item==>answer item==>to item==>Life, item==>the item==>Universe item==>and item==>Everything item==>is item==>42 CHAPTER 1 CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 WARNING APPENDIces If a list came from parsing a string using llParseString2List(), be aware that using llList2<type>() won't work. Instead you have to use an explicit cast, indicating that you want to change the type as in (sometype)llList2String(). The following snippet shows an example: list components = llParseString2List(message,["|"],[]); float gAngle = (float)llList2String(components,0); vector gPosition = (vector)llList2String(components,1); You can imitate objects or compound structures by using a strided list, wherein the series of contained objects is repeated in a nonvarying sequence. The stride indicates how many elements are in each compound structure. Listing 1.5 creates a strided list, with a stride of 2, populating it with a string representation of the color name paired with the LSL vector of RGB values representing that color. It prints all the color names that occur between elements 2 and 4 of the list, namely green and blue. Listing 1.5: Small Strided-List Example list COLORS = ["red", <1., 0., 0.>, "green", <0., 1., 0.>, "blue", <0., 0., 1.>, "yellow", <1., 1., 0.>, "purple", <1., 0., 1.>, "cyan", <1., 0., 1.>]; integer STRIDE = 2; default { touch_start(integer total_number) { list keys = llList2ListStrided(COLORS, 2, 4, STRIDE); llOwnerSay(llDumpList2String(keys, ", ")); } } 17 There are some provisions for manipulating strided lists in LSL, including sorting and extracting elements such as keys, shown in Table 1.10. However, you will still need to have a few more utility functions. Put Listing 1.6 on your "save for later" pile—create the script and store it in your Inventory folder under Scripts. It contains a group of utility functions that enable the fetching, update, and deletion of a specific record. CHAPTER 1 S cripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary 18 Table 1.10: Functions that Manipulate Strided Lists Function Name Purpose list llList2ListStrided(list src, integer start, integer end, integer stride) Returns a list of all the entries whose index is a multiple of stride, in the range of start to end inclusive. list llListSort(list l, integer stride, integer ascending) Sorts a list ascending (TRUE) or descending (FALSE), in blocks of length stride. Works unreliably for heterogeneous lists. list llListRandomize(list l, integer stride) Randomizes a list in blocks of length stride. Listing 1.6: Managing Records in a Strided List list gColors = ["red", <1.0, 0.0, 0.0>, "green", <0.0, 1.0, 0.0>, "blue", <0.0, 0.0, 1.0>]; integer STRIDE = 2; integer getPosition(integer recordNumber) { return recordNumber * STRIDE; } list getRecord(integer recordNumber) { integer pos = getPosition(recordNumber); if (pos < (llGetListLength(gColors) + STRIDE - 1)) return llList2List(gColors, pos, pos + STRIDE - 1); else return []; } deleteRecord(integer recordNumber) { integer pos = getPosition(recordNumber); if (pos < (llGetListLength(gColors) + STRIDE - 1)) { gColors = llDeleteSubList(gColors, pos, pos + STRIDE - 1); } } updateRecord(integer recordNumber, list newRecord) { integer pos = getPosition(recordNumber); if (pos < (llGetListLength(gColors) + STRIDE - 1)) { gColors = llListReplaceList(gColors, newRecord, pos, pos + STRIDE - 1); } } default { // embed some test code in a touch_start event handler // get record # 2 touch_start(integer total_number) { integer i = 0; list l = getRecord(2); llOwnerSay(llDumpList2String(l, " + ")); } } VARIABLES Variables provide a place to store temporary working values. Variables can be both global and local. Global variables are available to everything in the script, while local variables are available only to the block they were defined in. A variable name must start with an ASCII letter or an underscore. Numerals may be part of a variable name after the first character, and non-ASCII letters, while allowed, are ignored. Thus, valid declarations include the following: integer integer integer integer integer integer integer x; y1; _z; thisIs_ALongComplicatedVariable_42; enumerator; _enumerator; énumérateur; // same as numrateur However, you can not also declare an integer numrateur because LSL ignores the é, and thus thinks the name has previously been declared in scope; that is, numrateur is the same variable as énumérateur. Global variables (and constants) must be defined before the default state, as shown in the following code: integer gFoo = 42; default { on_rez(integer start_param) { llOwnerSay("gFoo is "+(string)gFoo); } } Variables can not be defined directly inside states (they must appear inside blocks, functions, or event handlers). Thus, a slight rearrangement of the preceding code will generate a syntax error: default { integer gFoo = 42; on_rez(integer start_param) { llOwnerSay("gFoo is "+(string)gFoo); } } Local variables must always be declared within the scope (enclosing curly braces) of a function or event handler but needn't be declared before the first executable statement. Just declare it prior to its first use and you'll be fine. Consider the following example: foo() { integer bar = 0; llOwnerSay("bar is "+(string)bar); integer baz = 0; llOwnerSay("baz is "+(string)baz); } Using a local variable before it has been declared will generate the error "Name not defined in scope." Variables are scoped to the closest enclosing code block. Variables are not accessible outside this scope. After the function foo() returns, the variables bar and baz are unavailable. Thus, in the following code snippet the variable baz is not available outside the else branch of the if (gBoolean) test: CHAPTER 1 Scripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States anaging M S cripted O bjects A n LSL Style G uide S ummary integer gBoolean = TRUE; foo() { string bar = "Ford Prefect"; if (gBoolean) { string bar = "Slarty Bartfast"; llOwnerSay(bar); } else { string baz = "Zaphod Beeblebrox"; llOwnerSay(baz); } } The snippet also demonstrates that you can shadow a variable from an outer code block with a redefinition of the same name. The innermost wins; that's why in this code example, llOwnerSay(bar) will always tell you "Slarty Bartfast" and ignore poor "Ford Prefect." It is also useful to know that global variables can be set to the value returned by a function, but they can not be intialized to the value of a function. Similarly, LSL does not allow any code evaluation outside blocks, generating a syntax error for the following snippet: float gHalfPi = PI/2; default { } FLOW CONTROL Flow control is the process of directing the computer through your program in ways other than simply executing each line one after the other. If you have some experience with other computer languages, LSL flow control will come naturally to you, as most of the familiar constructs are here. Conditionals are represented with if...else statements: they allow the conditional execution of code depending on the value of the expression following the word if: • if ( condition ) trueBranch • if ( condition ) trueBranch else falseBranch The branches can be single statements; block statements (enclosed in braces, {block}); or null statements (empty or just a semicolon ";"). There is no LSL equivalent to the switch or case statements found in many other languages. LSL provides a standard set of loop statements as well: for, do...while, and while: • do { loop } while ( condition ); 20 • for ( initializer; condition; increment ) { loop } • while ( condition ) { loop } Loop statements may also be single statements, block statements, or the empty statement ";". Loops may not declare variables in the initializer. That is, the following snippet is not allowed: for (integer i=0; i<10; i++) { // loop } CHAPTER 1 You can declare variables inside a loop block: integer i; for (i=0; i<10; i++) { float f=3.0; llOwnerSay((string)i+". "+(string)f); } A do...while loop is slightly faster than a while or for loop, and also requires fewer bytes of memory. The flow statement state is described in the "States" section later in this chapter. return is used to return a value from a function, described in the section "User-Defined Functions." jump is just like the goto statement of many other languages, allowing direct jumping around the code of your script, and should generally be avoided. It is not uncommon in LSL for jump to be used to break out of loops: integer i; integer bigNumber = 1000; for (i=0; i<bigNumber; i++) { if (weFoundOurNumber(i)) { jump doneWithIt; } } @doneWithIt; llOwnerSay("Done after "+(string)i+"steps"); CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces OPERATORS Mathematical operators are the usual suspects (arithmetic, comparison, and assignment), with approximately the same precedence as you'd expect, such as multiplication and division before addition and subtraction. The exclamation point, !, denotes negation (returns TRUE for a FALSE value and vice versa). The tilde, ~, is the bitwise complement, flipping each bit of the integer it is applied to. The plus sign, +, can be used for a variety of things, including addition, string concatenation, and list appending. NOTE Unlike in most modern computer languages, Boolean operators in LSL do not "short-circuit" the calculation of an expression. If you were expecting short-circuiting, your code can have unexpected inefficiencies or broken logic. 21 CHAPTER 1 Table 1.11 shows the operators, in approximate order of precedence. Empty cells will generate a compiler error with a type mismatch. Lists and strings are not in this table because they only support the addition operators + and +=, indicating concatenation. Note specifically that lists may not be compared with any operator, while strings can be compared with only == and != (but they compare only list length, not list contents). Table 1.12 lists the math-related library functions. WARNING Scripting Structure 101 Types There have been reports of unexpected (and unexplainable) precedence calculations in the expression parser, so use parentheses liberally. Variables F low C ontrol O perators F unctions E vents and E vent H andlers Table 1.11. Operators and Their Semantics for Different Types Operator Integer (and Boolean) Float . Vector Rotation Dot Dot ! Not M anaging S cripted O bjects ~ Bitwise complement Bitwise complement ++ -- Increment, decrement Increment, decrement A n LSL Style G uide * / % Multiply, divide, modulus Multiply, divide, modulus Dot product, N/A, cross product Addition, subtraction, N/A + - Add, subtract Add, subtract Add, subtract Legal, but semantically unlikely << >> Left / right shift < <= > >= Less than, less than or equal Less than, less than to, greater than, greater than or equal to, greater or equal to than, greater than or equal to == != Comparison equal, comparison not equal & ^ | Bitwise AND, XOR, OR || && Comparison OR, AND = += -= *= /= %= Assignment, with above semantics Assignment Assignment States S ummary Comparison equal, comparison not equal Assignment Table 1.12: LSL Math Functions 22 Function B ehavior integer llAbs(integer val) Returns an integer that is the positive version of the value val. float llFabs(float val) Returns a float that is the positive version of val. integer llRound(float val) Returns an integer that is val rounded to the closest integer. 0.0 to 0.499 are rounded down; 0.5 to 0.999 are rounded up. integer llCeil(float val) Returns the closest integer larger than val. integer llFloor(float val) Returns the closest integer smaller than val. Function B ehavior float llFrand(float max) Returns a float that is a pseudorandom number in the range [0.0, max) or (max , 0.0] if max is negative. That is, it might return 0.0 or any number up to but not including max. float llSqrt(float val) Returns a float that is the square root of val. Triggers a Math Error for imaginary results (val < 0.0). CHAPTER 1 float llLog(float val) Returns a float that is the natural logarithm of val. If val <= 0 it returns 0.0 instead. CHAPTER 2 float llLog10(float val) Returns a float that is the base-10 logarithm of val. If val <= 0 it returns zero instead. float llPow(float base, float exp) Returns a float that is base raised to the power exp. integer llModPow(integer base, integer exp, integer mod) Returns an integer that is base raised to the power exp, modulo mod: (baseexp)% mod). Causes the script to sleep for 1.0 seconds. float llSin(float theta) Returns a float that is the sine of theta (in radians). float llAsin(float val) Returns a float (theta, in radians) that is the inverse sine in radians of val; that is, sin(theta)=val. val must fall in the range [–1.0, 1.0]. CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 float llCos(float theta) Returns a float that is the cosine of theta (in radians). float llAcos(float val) Returns a float (theta, in radians) that is the inverse cosine of val; that is, cos(theta)=val. val must fall in the range [–1.0, 1.0]. CHAPTER 12 float llTan(float theta) Returns a float that is the tangent of theta (in radians). CHAPTER 13 float llAtan2(float y, float x) Returns a float (theta, in radians) that is the arctangent of x, y. Similar to tan(theta)=y/x, except it utilizes the signs of x and y to determine the quadrant. Returns zero if x and y are zero. CHAPTER 15 CHAPTER 11 CHAPTER 14 APPENDIces FUNCTIONS A function is a portion of code that performs a specific task. LSL supports two kinds of functions: built-in functions that are provided by Linden Lab, and user-defined functions that exist inside your scripts. LSL includes a large number of built-in functions that extend the language (such as the ones you've already seen for math and list-manipulation functions). Built-in functions are readily identifiable as they always begin with the letters ll. Thus, you can tell immediately that llSetTexture() and llOwnerSay() are not user-defined functions. This book describes most of these Linden Library functions along with examples of how to use many of them. User-defined functions are blocks of code that help modularize your code and improve readability. If you need to do the same thing more than once in your script, there's a good chance that code should be put into a function. User-defined functions must be defined before the default state. Functions are global in scope, and are available to all states, event handlers, and other user-defined functions in the same script (but not other scripts in an object). An example of the general form a function takes is shown in Listing 1.7. 23 Listing 1.7: Function Form Comments CHAPTER 1 S cripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary constants variables TYPES Library Functions // say something a few times // return how many times message is said integer saySomeTimes (string message) { integer count = 1 + (integer) llFrand(5.0); integer i; for ( i=0; i<count; i++ ) { llOwnerSay(message); } return count; } Keywords Function Names // up to 5 times This function prints the string passed into it, message, between one and five times to the owner's chat window. A function has a name (saySomeTimes in Listing 1.7) and can have any number of parameters, each with a declared type (message is a string). A function with no parameters is just declared as its name with an open/close parenthesis pair, as in foo(). Only parameter values are passed into the function, and the original value (in the caller's scope) is guaranteed to be unchanged. There is no concept of pass-byreference in LSL. If you have large data structures, it's probably best to keep them as global variables rather than passing them as parameters into functions, because the entire data structure will be copied—this process takes time and memory. Variables defined inside the function (count and i) have scope only inside the function block: nothing outside the function can see the variables. Be careful about naming your variables, especially if you use similar names in different scopes. It is remarkably easy to get confused about which variable is which when you reuse names. If a function has a return type, it can return a single value of that type using the return statement. Functions that don't return a value should simply not declare a return type. The function saySomeTimes() in Listing 1.7 returns an integer of the number of times it chatted the message. If you look back at Listing 1.2, you'll see the definition of the listen() event handler follows exactly the same form. In fact, an event handler is just a special function that is called by SL when certain events occur. Event handlers must be one of the event names known to SL, must be declared to have the correct type parameters (but you may choose the names of the parameters!), and may not return values. The SYW website lists all the events, and there is more detail in the "Events" section of this chapter. 24 EVENTS AND EVENT HANDLERS LSL is an event-driven language. This behavior is a major discriminating feature between LSL and many other languages. Essentially, the script's flow is determined by events such as receiving messages from other objects or avatars, or receiving sensor signals. Functionally, events are messages that are sent by a Second Life server to the scripts active in that sim. Event messages can be received by defining the matching event handlers in the state(s) of your scripts. Nothing happens in a script without event handlers: even the passing of time is marked by the delivery of timer events. Some events cannot happen without the matching event handler active somewhere in the object (in at least one of its prims). For instance, if an object doesn't have a script with a money() event handler, avatars cannot pay that object. Many library functions have effects that take relatively long periods of time to accomplish. Rather than blocking (stopping execution), LSL uses asynchronous communications: your script fires off a request that something happen, and it is notified with an event when the request is satisfied. This pattern is used any time your script makes a call to a library function that cannot be handled locally by the host sim (for instance, the function llHTTPRequest() and the http_response() event handler), when physical simulation effects aren't going to be instantaneous (llTarget() and at_target()). You could also think of repeating events (llSensorRepeat() and sensor()) or even listeners (llListen() and listen()) as following the same model: make a request and get responses later. This model of asynchronous execution allows your script to keep functioning, handling other events and interactions without stopping to wait for long-term requests to happen. CHAPTER 1 CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces As events occur, applicable ones (those that have handlers defined on the current state) are queued for execution by the script in the order they were raised. You should be aware of several important constraints on the delivery of event messages to handlers: • By default, events are delivered to a script no more frequently than every 0.1 second. You can adjust this with llMinEventDelay(float seconds) but it can not be less than 0.05 second. • A maximum of 64 events can be queued on a script—any additional events will be silently discarded! • Queued events are silently discarded when a script transitions between states. The combination of these factors means that if you have code that expects to get events rapidly and takes a relatively long time to execute (including artificial delays!), you may run the risk of missing events. As an example, sending an IM (which delays the script by 2 seconds) in response to collide events (which can happen as often as every 0.1 second) is probably asking for trouble. Similarly, the LSL function llSleep(float seconds) pauses script execution for the specified number of seconds without leaving the current event handler, similar to the way that many LSL functions introduce artificial delays. In both cases, there is a potential problem if your script is likely to handle lots of events. Some events are delivered to a script only if the script has made the appropriate request. For instance, a listen() event handler is never called unless another handler in the same state has called llListen(). The SYW website has a list of all the LSL event handlers, the functions required to enable the event, and the Second Life situation that results in that raised event. And, of course, there are lots of examples throughout the book. 25 CHAPTER 1 S cripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States anaging M S cripted O bjects A n LSL Style G uide S ummary STATES Second Life models scripts as event-driven finite state machines similar to paradigms often used to run hardware. Wikipedia defines an event-driven finite state machine as "a model of behavior composed of a finite number of states, transitions between those states, and actions"* where actions occur as a result of the observation of externally defined events. This sort of model is often used in real-world applications where entirely predictable behavior is required, especially when programs are interacting directly with hardware or external events (think automotive electronics, traffic signals, and avionics). This model is certainly apt for SL because many LSL scripts control virtual simulations of real-world mechanisms. Perhaps more importantly, it is useful as a simulator language because of how gracefully such programs scale under simulator load: under conditions of high "simulator lag," slowed event delivery might hurt performance, but nothing ought to break outright. LSL scripts must implement at least the default state: indeed, many only use the default state, either exhibiting behavior that requires simple event-driven programming, or using global variables in place of states. A script will always be in exactly one state. All script execution occurs in the context of the current state, and in the current event. States are defined in an LSL script as sets of event handlers, labeled with the key words default or state statename. Second Life calls two special event handlers, state_entry() and state_exit(), when your script changes states. EVENT DEFINITION state_entry( ) { } This event handler is invoked on any state transition and in the default state when a script first runs or after it has been reset. This event is not triggered every time the object is rezzed. The SYW website provides additional documentation. EVENT DEFINITION state_exit( ) { } This event handler is invoked when the state command is used to transition to another state, before the new state is entered. It is not invoked by llResetScript(). The SYW website provides additional documentation. 26 *http://en.wikipedia.org/wiki/Finite_state_machine A script transitions between states using a state newStateName statement. Note that transition statements are not allowed inside user functions. Listing 1.8 shows a complete script that does nothing until touched, and then transitions to the exploding state and back to the default state. Figure 1.2 shows how the script execution passes through the states and event handlers. CHAPTER 1 Listing 1.8: Example State Transitions default { touch_end(integer count) { state exploding; } state_exit() { llOwnerSay("The fuse has been lit."); } } state exploding { state_entry() { llOwnerSay("Boom!"); state default; } state_exit() { llOwnerSay("Ash is now falling."); } } CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 "Ash is now falling." CHAPTER 15 APPENDIces touch touch_end state_exit state_exit state_entry default "The fuse has been lit." exploding "Boom!" Figure 1.2: State transitions for Listing 1.9 A state transition will invoke the state_exit() event on the way out (still in the context of the origin state), allow it to run to completion, and then run the state_entry() event on the new state. In Listing 1.9 each state announces something as it is about to exit. WARNING Don't try state transitions from within a state_exit() event handler: it probably will not do what you hope for, and at the time of this writing it can result in some moderately bad (and varying) script behavior. Additionally, avoid putting code after the command to transition states. It won't be executed, as shown in the following snippet: touch_end( integer n ) { state exploding; llOwnerSay("I will never be executed!"); } 27 Any queued events, like touches or messages, that haven't been serviced yet are silently discarded during state transitions. Also, anything that was set up for delayed or repeated event handlers is invalidated: all in-progress listens, timers, sensors, and data server queries will need to be reopened. Global variable values survive state transitions, as well as granted permissions, taken controls, and XML-RPC channels. CHAPTER 1 S cripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary When to Use Multiple States (or Not!) Multiple states are good when you want to strongly separate different modes of a scripted object. Consider a highly configurable object like a vendor kiosk: you really don't want people making purchases from the vendor while you are reconfiguring it. Even simple objects like a door or an elevator might best be represented with multiple states (open versus closed, and idle versus carrying passengers versus responding to a call, respectively). On the other hand, if you have a script where two states have a great deal of repeated code (event handlers) that cannot be abstracted out to functions, it may be simpler to collapse those two states together with a global variable. For instance, the default state from the textureflipping script in Listing 1.2 could have been split into two using default as the "off" state and lit as the "on" state and eliminating the gOn variable, as shown in Listing 1.9. Listing 1.9: Neon Texture Flipper as Multiple States // Insert variables and appropriate functions from Listing 1.2 default { state_entry() { fliptexture(NEON_OFF_TEXTURE); llSetTimerEvent(TIMER_INTERVAL); } timer() { state lit; } } state lit { state_entry() { fliptexture(NEON_ON_TEXTURE); } timer() { state default; } } This snippet only sets the timer in the default state. Timers are special in that they are the only event requests not discarded when a script changes state. It would do no harm to re-invoke llSetTimerEvent() in its state_entry(). The SYW website discusses this in detail. 28 MANAGING SCRIPTED OBJECTS The mechanics of dealing with scripted objects can be challenging at times. Scripts, after all, lend behavior to inanimate objects—and if your scripts aren't exactly right, misbehavior! Not to worry, though: while Second Life doesn't offer the sorts of software development and debugging tools that professional programmers have come to expect for serious work, there are techniques and tools that can help. For instance, Chapter 14, "Dealing with Problems," is all about finding and fixing problems. The SYW website features a compendium of resources for getting (and offering!) scripting help, as well as a survey of external LSL scripting tools. Furthermore, there are a few specific tips you may find useful as you start writing more-complicated scripts. Just keep in mind that bugs happen to everyone—be prepared for them and you'll be just fine. CHAPTER 1 CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 Losing Moving Objects Normally you edit a prim or an object, create a script, and then edit the script. You can stop editing the prim and keep editing the script (assuming you leave the script window open). If the selected prim goes flying out of range into the sky, you will not be able to save changes until it is back in range. This is a pain when you lose track of the object (say, by rotating it to somewhere completely unexpected). There are some easy fixes to this problem: copy and paste the text of the old script into a new one, keep a copy in an out-of-world editor, or create a timer() event that returns the object to its original position, as in Listing 1.10. CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces Listing 1.10: Using a Timer to Reset an Object's Position and Orientation vector gInitialPosition; rotation gInitialOrientation; default { state_entry() { gInitialPosition = llGetPos(); gInitialOrientation = llGetRot(); llOwnerSay("Pos:"+(string)gInitialPosition); llOwnerSay("Rot:"+(string)gInitialOrientation); } touch_start(integer n) { llOwnerSay("Doing experimental stuff"); // add your experiment here llSetTimerEvent( 10 ); } timer() { llSetPos(gInitialPosition); llSetRot(gInitialOrientation); llSetTimerEvent( 0 ); } } 29 CHAPTER 1 Scripting Structure 101 Types Variables F low C ontrol When the script starts the state_entry() event handler, it uses the functions llGetPos() and llGetRot() to find the position and rotation of the object and cache them. (These functions are described in Chapter 4.) When the user touches the object, the touch_start() handler runs the experimental code, and, before exiting, sets up a timer event with llSetTimerEvent(). Magically, 10 seconds later when the timer() event triggers, the object is moved back to its original position using llSetPos() and llSetRot(). Note that llSetPos() is limited to moves of 10m or less; Chapter 2 shows how to move the object farther. DEFINITION llSetTimerEvent(float timerInterval) O perators F unctions E vents and E vent H andlers Cause a timer() event to be triggered once every timerInterval seconds. A value of 0.0 stops further timer events. timerInterval — Any positive nonzero value. States M anaging S cripted O bjects A n LSL Style G uide EVENT DEFINITION S ummary timer( ) { } This event handler is invoked when triggered at periodic intervals specified by the parameter to llSetTimerEvent(float interval). Only one timer can be active in the state at one time. The SYW website provides additional documentation. A simple experiment to try would be to replace the commented line in the touch_start() event handler with the following snippet: vector newPos = gInitialPosition + <1,1,1>; llSetPos(newPos); Multiple Scripts A Second Life prim may contain any number of running scripts in its inventory and, of course, SL objects are often made from multiple prims. The result is that a complex scripted object can have many scripts cooperating to produce the object's behavior. Many scripters develop a suite of small, focused scripts to accomplish different tasks, rather like programmers in other languages often build libraries of utility functions. For example, you might develop a comfy pillow that has three scripts—one for sitting, one for changing textures, and one that is a sound trigger for some mood music if an avatar is sensed nearby. A suite of scripts like this can be combined to form new behaviors. 30 This modularity also makes it easier to debug, maintain, and reuse; store them in the Scripts section of your Inventory folder. Best of all, smaller scripts are more stable. Each script executes within its own block of memory, and can not accidentally write into the memory of other scripts or the protected simulator memory; this protection makes it much harder for your scripts to crash the simulator! Multiple scripts on the same object run in parallel, as close to simultaneously as the sim server can manage, and are fired in no defined order. Each script can be in a different state, making for interesting possibilities. In Chapter 3 you'll see how to synchronize behavior among the scripts. CHAPTER 1 Resetting Scripts CHAPTER 2 Scripts reset to default values when they are placed in another object. However, if a scripted object is in your possession and you rez it or transfer ownership, the script is not reset. Scripted objects do not "instinctively" reset when transferred, and thus either an explicit reset or careful handling of the change of ownership is often required. llResetScript() forces a reset of the script, restarting it from scratch. All global variables are set to their defaults, all pending events are cleared, the default state becomes active, and its state_entry() is triggered. When writing and debugging the script, you can achieve an identical effect by clicking Reset in the script editor. CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 DEFINITION CHAPTER 10 CHAPTER 11 llResetScript() Resets the script completely, restarting it from scratch. This approach is a good way to catch ownership transfers. It does not correct persistent prim features such as sit text, a sit target, or floating text. CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces Certain persistent features of an object are not reset with llResetScript(), including floating text and sit targets; these must be explicitly deleted. Every script that maintains state must take care to reset when transferred, either by detecting transfers or resetting when rezzed. Our scripts do the latter by catching the on_rez() event, as shown in the following snippet. If you don't reset the script or doublecheck who the owner is, for example, a chatting script would still listen to the original owner. default { on_rez(integer start_param) { llResetScript(); } } EVENT DEFINITION on_rez(integer startParam) { } This event handler is invoked when an object is rezzed into the world from inventory or by another script. The SYW website provides additional documentation. startParam — The parameter the prim starts with; zero for an object rezzed from inventory, and any value if rezzed by another script. 31 You can also keep track of the expected owner in a global variable, and reset or adapt the script only when the owner changes: CHAPTER 1 Scripting Structure 101 Types Variables F low C ontrol key gOwner; default { state_entry() { gOwner = llGetOwner(); } on_rez(integer p) { if (gOwner != llGetOwner()) { llResetScript(); } } } O perators F unctions E vents and E vent H andlers States M anaging S cripted O bjects A n LSL Style G uide S ummary WARNING This book contains scripts that use llResetScript() in many different places depending on specific needs. It's usually needed in an on_rez() event handler; however, one place to never use it is in the state_entry() handler—it will result in infinite recursion. Two LSL functions allow one script to control the state of another script in the same prim: llSetScriptState(string statename, integer run), which causes a state transition in the current script to the named state, and llResetOtherScript(string scriptname), which resets the named script. AN LSL STYLE GUIDE "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." —Martin Fowler et al, Refactoring: Improving the Design of Existing Code Effective programming in LSL requires that developers use a disciplined practice for applying formatting and convention to their scripts. These guidelines are not as rigid as the rules required by the language compiler, but nonetheless are critical to creating maintainable code. The most critical aspect of a style is that you apply it consistently to the code you write. We are attempting to make a guideline, not a rigid coding style, and thus point out things that must be followed to make it compile and things that should be followed to make your scripts readable. The SYW website describes a variety of tools you can use outside of SL to help you script, but the principles are important no matter how you write your scripts. 32 Cleanliness The most important practice in effective programming is to generate readable code. Readable code is easier to debug, understand, and maintain. A short snippet that is poorly formatted may be decipherable later, but a longer program will not be. If you pay attention to your coding style early on, it will quickly become second nature. CHAPTER 1 CHAPTER 2 Here is a list of specific suggestions: CHAPTER 3 • Bracketing: You must always enclose blocks in curly braces. It is a good idea to always use braces, even for single statements. Opening braces for functions and states should start on the line after the declaration. Otherwise, place the brace on the same line as the control block. CHAPTER 4 • Indentation: On the line immediately following an open brace, you should add one indentation, usually four spaces. Remove an indentation on the line after a close brace. CHAPTER 7 CHAPTER 5 CHAPTER 6 CHAPTER 8 CHAPTER 9 NOTE The two most important guidelines are indentation and bracketing. This code, for example, is not properly indented: default{state_entry(){ llSay(0, "Hello, Avatar!"); } touch_ start(integer total_number) { llSay(0, "Touched."); }} If you look closely, you'll see that it's the same as Listing 1.1—which was much more readable! CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces • Line wrap: You should manually separate lines longer than 80 characters. • Coding styles: In general, don't mix coding styles. When editing code you didn't create, keep the existing style (or change the style throughout). • Function length: Try to keep functions short and focused. A good rule is that a function longer than a screen height should be refactored into multiple functions. • Comments: Use comments to explain what you are doing; however, avoid excessive inline comments: well-written code should explain itself. Do, however, describe what each function does in a comment near the top of the definition. • Variable naming: • Variable names must always start with a letter or underscore. • Don't abbreviate. Since case matters (x is not the same as X), longer names will cause significantly less confusion. • Global variables should begin with a lower case g; for example, gSelected. • Variables used as constants should be in all caps, and words should be separated by underscores, like this: OWNER_KEY. • You should use camel case (lowercase first letter, and then capitalize each word) for variable names, such as this: myCountingVariable. 33 • Function naming: • Function names must also start with a letter, and usually are camel case, with each word capitalized similarly to the Linden Library functions like doSomethingInteresting(). CHAPTER 1 Scripting Structure 101 Types Variables F low C ontrol O perators F unctions E vents and E vent H andlers States • Linden Lab uses the prefix ll for built-in functions, such as llGetScriptName(), so don't use this syntax for your own code—it is considered very bad form. Look for llPizza() on the LSL wiki; it has a short discussion of user functions masquerading as Linden Library functions. • If you are creating functions that belong to a family, and you are likely to reuse them regularly, you should define a consistent naming convention across the family. LSL does not have an explicit "library" concept, but if you stick to one convention, you will find it easier to maintain a collection of reusable code for your projects. NOTE To conserve space, the scripts in this book don't follow all the rules all the time, especially for comments. The prose around the listings should be sufficient explanation. M anaging S cripted O bjects A n LSL Style G uide S ummary Modularity LSL doesn't offer any tools for writing and maintaining modular or especially reusable scripts, however, here are some tricks: • Write and edit your code outside Second Life, only pulling it in for testing, integration into builds, and deployment. Not only does this approach allow you to use superior editors, but it gives you access to revision-control systems and code generators and transformers—see the SYW website for more. • Package separable parts as different scripts in the same object. This can keep the individual pieces smaller if the parts don't require close coordination. • Use worker scripts to do slow (artificially delayed) tasks, or jobs that require one script per avatar. This will usually require a "brain" script that instructs a set of subsidiary scripts what to do, so they can act in concert so that the brain isn't delayed while waiting for something to happen. 34 SUMMARY This chapter should have given you a good understanding of the LSL language and the basics of how to use it to manipulate Second Life functions. You should be aware that LSL is an evolving language; Linden Lab is fixing bugs and adding new capabilities all the time. That means you may run into something that isn't working the way you expect. Before it drives you up a tree, check the Second Life website of formal issues: http://jira.secondlife.com. The SYW website also has tips that can help get you out of a jam. Most of the rest of this book is devoted to making effective use of LSL and the library to accomplish interesting things in SL. After you've absorbed some of the ideas elsewhere in the book, come back and re-read this chapter. Even for us, the authors, re-reading has been beneficial. We wrote this chapter, then spent several weeks doing heavy-duty scripting before the editors were ready for us to make a second pass on Chapter 1. Our re-read was enlightening, helping us understand certain things better or in a different way than we did when we first wrote the text. Take it from us: there are lots of things that will make much more sense on later perusal. And now, on to the fun stuff! CHAPTER 1 CHAPTER 2 CHAPTER 3 CHAPTER 4 CHAPTER 5 CHAPTER 6 CHAPTER 7 CHAPTER 8 CHAPTER 9 CHAPTER 10 CHAPTER 11 CHAPTER 12 CHAPTER 13 CHAPTER 14 CHAPTER 15 APPENDIces 35