Jump to content

Linden Scripting Language: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
TheY4Kman (talk | contribs)
mNo edit summary
m tidied code examples and references a bit
Line 16: Line 16:
| website = {{URL|https://wiki.secondlife.com/wiki/LSL_Portal}}
| website = {{URL|https://wiki.secondlife.com/wiki/LSL_Portal}}
}}
}}
'''Linden Scripting Language''', or LSL, is the [[programming language]] used by residents of [[Second Life]] ('''SL'''), a [[virtual world]] by [[Linden Lab]]<ref>[https://wiki.secondlife.com/wiki/Help:Getting_started_with_LSL#What_is_LSL.3F Getting started with LSL - Second Life Wiki]. Wiki.secondlife.com (2013-07-01). Retrieved on 2013-09-21.</ref> and similar virtual worlds.
'''Linden Scripting Language''', or LSL, is the [[programming language]] used by residents of [[Second Life]] ('''SL'''), a [[virtual world]] by [[Linden Lab]]<ref>[https://wiki.secondlife.com/wiki/Help:Getting_started_with_LSL#What_is_LSL.3F Getting started with LSL]. wiki.secondlife.com (2013-07-01). Retrieved on 2013-09-21.</ref> and similar virtual worlds.


LSL has a [[programming language syntax|syntax]] similar to [[C (programming language)|C]] and allows objects to control the behavior of in-world objects of Second Life from the Internet via email, [[XML-RPC]], and most recently, [[HTTP]] requests.
LSL has a [[programming language syntax|syntax]] similar to [[C (programming language)|C]] and allows objects to control the behavior of in-world objects of Second Life from the Internet via email, [[XML-RPC]], and most recently, [[HTTP]] requests.


==LSL design==
==LSL design==
Linden Scripting Language is a state-[[Event-driven programming|event driven]] [[scripting language]], in the sense of a [[finite state machine]].<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Events Category:LSL Events - Second Life Wiki]. Wiki.secondlife.com (2011-08-08). Retrieved on 2013-09-21.</ref>
Linden Scripting Language is a state-[[Event-driven programming|event driven]] [[scripting language]], in the sense of a [[finite state machine]].<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Events Category:LSL Events]. wiki.secondlife.com (2011-08-08). Retrieved on 2013-09-21.</ref>
A script consists of variables, function definitions, and one or more named states. Each state contains a description of how to react to events that occur while the program is within that state. The system sends to the script, such as timers, movement, chat (from other agents), email, and collisions (with objects in the virtual world). Scripts can change most aspects of the state of the object and communicate with other objects and agents. As soon as a script is added to an object, and turned on, it begins to execute.
A script consists of variables, function definitions, and one or more named states. Each state contains a description of how to react to events that occur while the program is within that state. The system sends to the script, such as timers, movement, chat (from other agents), email, and collisions (with objects in the virtual world). Scripts can change most aspects of the state of the object and communicate with other objects and agents. As soon as a script is added to an object, and turned on, it begins to execute.


A script is tightly bound to the concept of virtual-world ''[[3D modeling|objects]]'' (in the 3D modeling sense rather than in the [[object-oriented]] sense) called ''primitives''. An object in Second Life represents something like a chair or a wall, or possibly something invisible. Multiple scripts may be placed inside an object, where they all execute simultaneously.
A script is tightly bound to the concept of virtual-world ''[[3D modeling|objects]]'' (in the 3D modeling sense rather than in the [[object-oriented]] sense) called ''primitives''. An object in Second Life represents something like a chair or a wall, or possibly something invisible. Multiple scripts may be placed inside an object, where they all execute simultaneously.


There are over 300 library [[function (programming)|function]]s available.<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Functions Category:LSL Functions - Second Life Wiki]. Wiki.secondlife.com (2012-09-08). Retrieved on 2013-09-21.</ref> Users can also define additional functions. LSL is a [[strongly typed language]]<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Types Category:LSL Types - Second Life Wiki]. Wiki.secondlife.com (2012-04-27). Retrieved on 2013-09-21.</ref> that is compiled to [[bytecode]] before runtime execution in a [[virtual machine]] ('''VM''') on one of Linden Lab's servers.
There are over 300 library [[function (programming)|function]]s available.<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Functions Category:LSL Functions]. wiki.secondlife.com (2012-09-08). Retrieved on 2013-09-21.</ref> Users can also define additional functions. LSL is a [[strongly typed language]]<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Types Category:LSL Types]. wiki.secondlife.com (2012-04-27). Retrieved on 2013-09-21.</ref> that is compiled to [[bytecode]] before runtime execution in a [[virtual machine]] ('''VM''') on one of Linden Lab's servers.


LSL's native [[data structure]]s includes [[integers]],<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Integer Category:LSL Integer - Second Life Wiki]. Wiki.secondlife.com (2012-12-22). Retrieved on 2013-09-21.</ref> [[floating point numbers]],<ref>https://wiki.secondlife.com/wiki/Category:LSL_Float</ref> [[String (computer science)|strings]],<ref>[https://wiki.secondlife.com/wiki/Category:LSL_String Category:LSL String - Second Life Wiki]. Wiki.secondlife.com (2013-05-19). Retrieved on 2013-09-21.</ref> keys ([[UUID]]),<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Key Category:LSL Key - Second Life Wiki]. Wiki.secondlife.com (2013-09-11). Retrieved on 2013-09-21.</ref> [[Euclidean vector|vectors]] (used for 3D coordinates and [[RGB]] color expression),<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Vector Category:LSL Vector - Second Life Wiki]. Wiki.secondlife.com (2011-10-05). Retrieved on 2013-09-21.</ref> and [[rotation]]s ([[quaternion]]s).<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Rotation Category:LSL Rotation - Second Life Wiki]. Wiki.secondlife.com (2008-10-24). Retrieved on 2013-09-21.</ref> There are also heterogeneous lists. There are no arrays; multiple list functions are used to adapt programs requiring array structures.<ref>[https://wiki.secondlife.com/wiki/List Category:LSL List - Second Life Wiki]. Wiki.secondlife.com. Retrieved on 2013-09-21.</ref> There is no built-in persistent data storage, such as a file or database (though users have found various workarounds for this like storing data in various data fields of the items containing scripts). On the other hand, scripts continue to run even when a user is not logged in, and if an object is saved (taken into inventory), and then re-introduced into the World later, it still maintains its previous state. In addition, the mechanisms for communicating via HTTP can be used to store a state externally.
LSL's native [[data structure]]s includes [[integers]],<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Integer Category:LSL Integer]. wiki.secondlife.com (2012-12-22). Retrieved on 2013-09-21.</ref> [[floating point numbers]],<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Float Category:LSL Float]. wiki.secondlife.com</ref> [[String (computer science)|strings]],<ref>[https://wiki.secondlife.com/wiki/Category:LSL_String Category:LSL String]. wiki.secondlife.com (2013-05-19). Retrieved on 2013-09-21.</ref> keys ([[UUID]]),<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Key Category:LSL Key]. wiki.secondlife.com (2013-09-11). Retrieved on 2013-09-21.</ref> [[Euclidean vector|vectors]] (used for 3D coordinates and [[RGB]] color expression),<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Vector Category:LSL Vector]. wiki.secondlife.com (2011-10-05). Retrieved on 2013-09-21.</ref> and [[rotation]]s ([[quaternion]]s).<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Rotation Category:LSL Rotation]. wiki.secondlife.com (2008-10-24). Retrieved on 2013-09-21.</ref> There are also heterogeneous lists. There are no arrays; multiple list functions are used to adapt programs requiring array structures.<ref>[https://wiki.secondlife.com/wiki/List Category:LSL List]. wiki.secondlife.com. Retrieved on 2013-09-21.</ref> There is no built-in persistent data storage, such as a file or database (though users have found various workarounds for this like storing data in various data fields of the items containing scripts). On the other hand, scripts continue to run even when a user is not logged in, and if an object is saved (taken into inventory), and then re-introduced into the World later, it still maintains its previous state. In addition, the mechanisms for communicating via HTTP can be used to store a state externally.


Some functions in LSL have built-in delays, which range from a 0.1-second delay when (for example) requesting string data from a notecard;<ref>https://wiki.secondlife.com/wiki/Category:LSL_Notecard An [[Second Life|in-world]] data storage medium mainly used for text.</ref> to a 20-second script pause after sending an [[email|e-mail message]]. The delays help prevent developers from writing LSL scripts that could overtax system resources. Memory available to LSL scripts (when compiled as [[Mono (software)|mono]]) is capped at about 64 [[Kibibyte|KiB]], which places a practical limit on how much a single script can do, however the ability to use multiple scripts and to call scripts from another script allows the user to pragmatically work around this 64 KiB cap.
Some functions in LSL have built-in delays, which range from a 0.1-second delay when (for example) requesting string data from a notecard;<ref>[https://wiki.secondlife.com/wiki/Category:LSL_Notecard Category:LSL Notecard] An [[Second Life|in-world]] data storage medium mainly used for text. wiki.secondlife.com</ref> to a 20-second script pause after sending an [[email|e-mail message]]. The delays help prevent developers from writing LSL scripts that could overtax system resources. Memory available to LSL scripts (when compiled as [[Mono (software)|mono]]) is capped at about 64 [[Kibibyte|KiB]], which places a practical limit on how much a single script can do, however the ability to use multiple scripts and to call scripts from another script allows the user to pragmatically work around this 64 KiB cap.


==Permission system==
==Permission system==
Line 45: Line 45:


==Default LSL script==
==Default LSL script==
The default script, "Hello, Avatar",<ref>[https://wiki.secondlife.com/wiki/Hello_Avatar Hello Avatar - Second Life Wiki]. Wiki.secondlife.com (2012-10-06). Retrieved on 2013-09-21.</ref> a [[hello world program]] that speaks (streams message to local chat ("heard" (printed) by all viewers ([[Client (computing)|client]]s) within 20 meters <sup>(3D virtual environment)</sup> of the task)) when the script is first saved, initialized or reset (in this case, since the command is in the default "state", the first to be run on initialization) or 'touched' (a simple user interaction usually initiated using a mouse click), looks like:
The default script, "Hello, Avatar",<ref>[https://wiki.secondlife.com/wiki/Hello_Avatar Hello Avatar]. wiki.secondlife.com (2012-10-06). Retrieved on 2013-09-21.</ref> a [[hello world program]] that speaks (streams message to local chat ("heard" (printed) by all viewers ([[Client (computing)|client]]s) within 20 meters <sup>(3D virtual environment)</sup> of the task)) when the script is first saved, initialized or reset (in this case, since the command is in the default "state", the first to be run on initialization) or 'touched' (a simple user interaction usually initiated using a mouse click), looks like:
<source lang=lsl>
<source lang=lsl>
default
default
{
{
state_entry()
state_entry()
{
{
llSay(0, "Hello, Avatar!");
llSay(0, "Hello, Avatar!");
}
}

touch_start(integer total_number)
touch_start(integer total_number)
{
{
llSay(0, "Touched.");
llSay(0, "Touched.");
}
}
}
}
</source>
</source>


==Mono==
==Mono==
The revised version of Second Life's scripting virtual machine is based on [[Mono (software)|Mono]], the open source implementation of the Microsoft.NET framework. The Mono '''VM''' was introduced to several [[Simulation#Computer simulation|simulators]] ('''sims''') on the Second Life [[Betaware|Beta]] [[Metaverse|grid]] for compatibility testing on 29 January 2008 and later that year on 20 August 2008 Linden Lab started deploying it on the production grid<ref>[http://blog.secondlife.com/2008/08/20/mono-launch/ Blogs - Second Life]. Blog.secondlife.com. Retrieved on 2013-09-21.</ref> - with the entire production grid updated to use it on 29 August 2008.
The revised version of Second Life's scripting virtual machine is based on [[Mono (software)|Mono]], the open source implementation of the Microsoft.NET framework. The Mono '''VM''' was introduced to several [[Simulation#Computer simulation|simulators]] ('''sims''') on the Second Life [[Betaware|Beta]] [[Metaverse|grid]] for compatibility testing on 29 January 2008 and later that year on 20 August 2008 Linden Lab started deploying it on the production grid<ref>[http://blog.secondlife.com/2008/08/20/mono-launch/ Blogs]. blog.secondlife.com. Retrieved on 2013-09-21.</ref> - with the entire production grid updated to use it on 29 August 2008.


While the LSL scripting language remained the same, scripts executed on the Mono underpinnings were up to 220<ref>https://jira.secondlife.com/browse/SVC-1341</ref> times faster in execution, but at the cost of a somewhat higher overhead when creating (rezzing) scripted objects and moving them from '''sim''' to '''sim'''.<ref>https://jira.secondlife.com/browse/SVC-3895</ref>
While the LSL scripting language remained the same, scripts executed on the Mono underpinnings were up to 220<ref>[https://jira.secondlife.com/browse/SVC-1341 ISSUE SVC-1341]. jira.secondlife.com</ref> times faster in execution, but at the cost of a somewhat higher overhead when creating (rezzing) scripted objects and moving them from '''sim''' to '''sim'''.<ref>[https://jira.secondlife.com/browse/SVC-3895 ISSUE SVC-3895]. jira.secondlife.com</ref>


==More complex example script==
==More complex example script==
Line 71: Line 71:


<source lang="lsl">
<source lang="lsl">
// Global variables must be declared above any functions created and above the first "default" state.
// This list is all you need to edit. List the names of the songs (each collection of sound files that makes one song)
// Many LSL scripts are open source, and can be edited by their owners.
// followed by the length of those sound clips (each song should contain clips of equal length)
// Often scripts have settings variables with instructions for editing written as comments around them.
// The list should be structured like so -
// list songs = ["First Song", 9.0, "Second Song", 9.65, "Third Song", 9.45];
// The names of the songs must be identical to some part of the sound files used for that song like so -
// In the prim inventory (along with this script) -
// Box_Of_Rain_wav_1
// Box_Of_Rain_wav_2
// Box_Of_Rain_wav_3
// Servant 1
// Servant 2
// Servant 3
// In the script -
// list songs = ["Box_Of_Rain", 9.2, "Servant", 9.8];
// The script will play the clips in alpha/numerical order so name them wisely.
list songs = ["Box_Of_Rain", 9.2, "Servant", 9.8]; // YUP! EDIT THIS BIT ;-)


// This list of sound file names would be populated by the owner.
//////////// No editing is required below here ////////////
list songs = ["Box_Of_Rain", 9.2, "Servant", 9.8];


integer volume = 10; // All variables (global or local) must have their type declared on creation.
// Global variables must be declared above any functions created and above the first "default" state.
integer volume = 10; // All variables (global or local) must have their type declared on creation
integer lis_count;
integer lis_count;
integer playing;
integer playing;
integer busy; // Values need not be applied to variable at creation
integer busy; // Values need not be applied to variable at creation.
integer part;
integer part;
integer lis;
integer lis;
Line 122: Line 100:
// Functions that return a value, must have the value type declared as the type for the function.
// Functions that return a value, must have the value type declared as the type for the function.
// Arguments passed to the function must have their types declared. the type of data passed to the function must match the argument type.
// Arguments passed to the function must have their types declared. the type of data passed to the function must match the argument type.
list StrideOfList(list src, integer stride, integer start, integer end)
list StrideOfList(list src, integer stride, integer start, integer end) {
{
list l = [];
list l = [];
integer ll = llGetListLength(src);
integer ll = llGetListLength(src);
Line 129: Line 106:
if(end < 0)end += ll;
if(end < 0)end += ll;
if(end < start) return llList2List(src, start, start);
if(end < start) return llList2List(src, start, start);
while(start <= end)
while(start <= end) {
{
l += llList2List(src, start, start);
l += llList2List(src, start, start);
start += stride;
start += stride;
Line 136: Line 112:
return l;
return l;
}
}
list Volumes(integer vol) {
list Volumes(integer vol)
{
integer v = 0;
integer v = 0;
list l = [];
list l = [];
do
do {
if(v != vol) // Single instruction conditional blocks need not be wrapped in braces.
{
if(v != vol)
l += [((string)v)];
l += [((string)v)];
}
}
Line 149: Line 122:
return l;
return l;
}
}
PageOne(key k, integer c) {
PageOne(key k, integer c)
{
llDialog(k, "\nAdjust the volume or select a song to play?", [vol_str, song_str] + cancel, c);
llDialog(k, "\nAdjust the volume or select a song to play?", [vol_str, song_str] + cancel, c);
}
}
PlaySong(string n) {
PlaySong(string n)
{
song = [];
song = [];
integer c = -1;
integer c = -1;
string name = "";
string name = "";
do { // Functions may be nested and used inline where values are returned (the values need not be stored to a variable for use).
do
{ // Functions may be nested and used inline where values are returned (the values need not be stored to a variable for use).
if(llSubStringIndex((name = llGetInventoryName(INVENTORY_SOUND, (++c))), n) != -1)
if(llSubStringIndex((name = llGetInventoryName(INVENTORY_SOUND, (++c))), n) != -1)
song += [name];
song += [name];
Line 167: Line 135:
while(name);
while(name);
delay = llList2Float(songs, (llListFindList(songs, [n]) + 1));
delay = llList2Float(songs, (llListFindList(songs, [n]) + 1));
if((sl = llGetListLength(song)))
if((sl = llGetListLength(song))) {
{
llPreloadSound(llList2String(song, (part = 0)));
llPreloadSound(llList2String(song, (part = 0)));
if(sl > 1)
if(sl > 1)
Line 176: Line 143:
}
}
}
}
integer Chan() {
integer Chan()
{
return llRound((llFrand(-5000000.0) + -500000.0));
return llRound((llFrand(-5000000.0) + -500000.0));
}
}
float ScaleVol(integer v) {
float ScaleVol(integer v)
{
return (v * 0.1);
return (v * 0.1);
}
}
Listen(integer c, key a) {
Listen(integer c, key a)
{
lis = llListen(c, "", a, "");
lis = llListen(c, "", a, "");
}
}
RemoveListen(integer b) {
RemoveListen(integer b)
{
llListenRemove(lis);
llListenRemove(lis);
lis_count = 0;
lis_count = 0;
Line 200: Line 159:
lis = 0;
lis = 0;
}
}
SetListenTimer(integer p) {
SetListenTimer(integer p)
{
if(p)
if(p)
while(((++lis_count) * llRound(delay)) < 30);
while(((++lis_count) * llRound(delay)) < 30);
else
else {
{
lis_count = 1;
lis_count = 1;
llSetTimerEvent(30.0);
llSetTimerEvent(30.0);
}
}
}
}
integer CheckWaitingRoom(integer c) {
if(waiting) {
integer CheckWaitingRoom(integer c)
{
if(waiting)
{
key a = llList2Key(waiting, 0);
key a = llList2Key(waiting, 0);
if(!c)
if(!c) {
{
RemoveListen(0);
RemoveListen(0);
Listen((c = Chan()), a);
Listen((c = Chan()), a);
Line 232: Line 184:
// All scripts must have a default state.
// All scripts must have a default state.
// However many other states a script may have, the default state must be uppermost in the script (below global vars and functions).
// However many other states a script may have, the default state must be uppermost in the script (below global vars and functions).
default
default {
// Event names are written in all lower case letters. Some contain underscores.
{
on_rez(integer param) // Event names are written in all lower case letters. Some contain underscores.
on_rez(integer param) {
{ // If an event is passed any values as it is triggered, those values will be of specific types.
// If an event is passed any values as it is triggered, those values will be of specific types.
// Writers can choose the name for the variable, but not the type.
// Writers can choose the name for the variable, but not the type.
// Even if the data contained in the variables passed to the event are not used, the event must contain reference to that data.
// Even if the data contained in the variables passed to the event are not used, the event must contain reference to that data.
Line 241: Line 193:
llResetScript();
llResetScript();
}
}
changed(integer change)
changed(integer change) {
if(change & CHANGED_INVENTORY) // Bitwise comparison
{
if(change & CHANGED_INVENTORY)
llResetScript();
llResetScript();
}
}
touch_start(integer nd)
touch_start(integer nd) {
while(nd) {
{
while(nd)
{
key agent = llDetectedKey(--nd);
key agent = llDetectedKey(--nd);
if(!busy)
if(!busy) {
{
busy = TRUE;
busy = TRUE;
integer channel = Chan();
integer channel = Chan();
Line 258: Line 206:
Listen(channel, agent);
Listen(channel, agent);
PageOne(agent, channel);
PageOne(agent, channel);
}
} else {
else
{
list a = [agent];
list a = [agent];
if(llListFindList(waiting, a) == -1)
if(llListFindList(waiting, a) == -1)
Line 267: Line 213:
}
}
}
}
listen(integer chan, string name, key id, string msg)
listen(integer chan, string name, key id, string msg) {
if(msg != llList2String(cancel, 0)) {
{
if(msg != llList2String(cancel, 0))
{
SetListenTimer(playing);
SetListenTimer(playing);
if(msg == vol_str)
if(msg == vol_str) {
{
llDialog(id, "\nChange the volume?\nThe current volume is set at \"" + ((string)volume) +
llDialog(id, "\nChange the volume?\nThe current volume is set at \"" + ((string)volume) +
"\"", cancel + Volumes(volume), chan);
"\"", cancel + Volumes(volume), chan);
return;
return;
}
}
if(msg == song_str)
if(msg == song_str) {
{
string current = "";
string current = "";
if(playlist)
if(playlist) {
{
current = "\n\nThe songs currently queued are\n\"" + llList2String(playlist, 0) +
current = "\n\nThe songs currently queued are\n\"" + llList2String(playlist, 0) +
"\" (currently playing)";
"\" (currently playing)";
Line 292: Line 233:
return;
return;
}
}
if(llListFindList(Volumes(volume), [msg]) != -1)
if(llListFindList(Volumes(volume), [msg]) != -1) {
{
llAdjustSoundVolume(ScaleVol((volume = ((integer)msg))));
llAdjustSoundVolume(ScaleVol((volume = ((integer)msg))));
PageOne(id, chan);
PageOne(id, chan);
Line 305: Line 245:
RemoveListen(1);
RemoveListen(1);
}
}
timer()
timer() {
if(playlist) {
{
if(playlist)
if(!playing) {
{
if(!playing)
{
llSetTimerEvent(delay);
llSetTimerEvent(delay);
playing = TRUE;
playing = TRUE;
}
}
llPlaySound(llList2String(song, part), ScaleVol(volume));
llPlaySound(llList2String(song, part), ScaleVol(volume));
if((++part) == sl)
if((++part) == sl) {
if(llGetListLength(playlist) > 1) {
{
if(llGetListLength(playlist) > 1)
{
song_name = llList2String((playlist = llDeleteSubList(playlist, 0, 0)), 0);
song_name = llList2String((playlist = llDeleteSubList(playlist, 0, 0)), 0);
llSleep(delay);
llSleep(delay);
PlaySong(song_name);
PlaySong(song_name);
}
} else {
else
{
llSetTimerEvent(0.0);
llSetTimerEvent(0.0);
song_name = "";
song_name = "";
Line 336: Line 269:
llPreloadSound(llList2String(song, (part + 1)));
llPreloadSound(llList2String(song, (part + 1)));
}
}
if(lis && (!(--lis_count)))
if(lis && (!(--lis_count))) {
{
if(!(CheckWaitingRoom(0)))
if(!(CheckWaitingRoom(0)))
RemoveListen(1);
RemoveListen(1);
Line 353: Line 285:
* [http://www.lslwiki.net/ LSL Wiki]&mdash;A community effort to supplement the available LSL documentation
* [http://www.lslwiki.net/ LSL Wiki]&mdash;A community effort to supplement the available LSL documentation
* [http://wiki.secondlife.com/wiki/LSL_Portal LSL Portal]&mdash;A new Wiki hosted by Second Life
* [http://wiki.secondlife.com/wiki/LSL_Portal LSL Portal]&mdash;A new Wiki hosted by Second Life
* [http://wiki.secondlife.com/wiki/Mono Mono for Second Life] &ndash; Information regarding the move to Mono
* [http://wiki.secondlife.com/wiki/Mono Mono for Second Life]&mdash;Information regarding the move to Mono
* [http://xahlee.org/sl/ls-prob.html Linden Scripting Language Problems] Criticism by Xah Lee
* [http://xahlee.org/sl/ls-prob.html Linden Scripting Language Problems]&mdash;Criticism by Xah Lee
* [http://www.free-lsl-scripts.com/cgi/freescripts.plx Linden Scripting Language Script Repository] - Privately maintained library of LSL Scripts.
* [http://www.free-lsl-scripts.com/cgi/freescripts.plx Linden Scripting Language Script Repository]&mdash;Privately maintained library of LSL Scripts.


<!-- navboxen -->
<!-- navboxen -->

Revision as of 11:25, 13 April 2016

Linden Scripting Language (LSL)
Typing disciplinestrong
OScross-platform
Websitewiki.secondlife.com/wiki/LSL_Portal
Influenced by
C

Linden Scripting Language, or LSL, is the programming language used by residents of Second Life (SL), a virtual world by Linden Lab[1] and similar virtual worlds.

LSL has a syntax similar to C and allows objects to control the behavior of in-world objects of Second Life from the Internet via email, XML-RPC, and most recently, HTTP requests.

LSL design

Linden Scripting Language is a state-event driven scripting language, in the sense of a finite state machine.[2] A script consists of variables, function definitions, and one or more named states. Each state contains a description of how to react to events that occur while the program is within that state. The system sends to the script, such as timers, movement, chat (from other agents), email, and collisions (with objects in the virtual world). Scripts can change most aspects of the state of the object and communicate with other objects and agents. As soon as a script is added to an object, and turned on, it begins to execute.

A script is tightly bound to the concept of virtual-world objects (in the 3D modeling sense rather than in the object-oriented sense) called primitives. An object in Second Life represents something like a chair or a wall, or possibly something invisible. Multiple scripts may be placed inside an object, where they all execute simultaneously.

There are over 300 library functions available.[3] Users can also define additional functions. LSL is a strongly typed language[4] that is compiled to bytecode before runtime execution in a virtual machine (VM) on one of Linden Lab's servers.

LSL's native data structures includes integers,[5] floating point numbers,[6] strings,[7] keys (UUID),[8] vectors (used for 3D coordinates and RGB color expression),[9] and rotations (quaternions).[10] There are also heterogeneous lists. There are no arrays; multiple list functions are used to adapt programs requiring array structures.[11] There is no built-in persistent data storage, such as a file or database (though users have found various workarounds for this like storing data in various data fields of the items containing scripts). On the other hand, scripts continue to run even when a user is not logged in, and if an object is saved (taken into inventory), and then re-introduced into the World later, it still maintains its previous state. In addition, the mechanisms for communicating via HTTP can be used to store a state externally.

Some functions in LSL have built-in delays, which range from a 0.1-second delay when (for example) requesting string data from a notecard;[12] to a 20-second script pause after sending an e-mail message. The delays help prevent developers from writing LSL scripts that could overtax system resources. Memory available to LSL scripts (when compiled as mono) is capped at about 64 KiB, which places a practical limit on how much a single script can do, however the ability to use multiple scripts and to call scripts from another script allows the user to pragmatically work around this 64 KiB cap.

Permission system

Certain actions in LSL require permissions:

  • take money from agent's account
  • take agent's controls
  • start or stop Animations on agent
  • attach/detach from agent
  • change links
  • track the agent's camera position and rotation
  • control the agent's camera

The permission system, however, is an issue for scripts owned by the land owner: those scripts can modify the parcel settings without requiring permissions. A malicious script can unsit avatars, ban and unban avatars, destroy the terrain that includes returning of objects by letting them fall out of the World, and gather the ip addresses of avatars by manipulating the media settings for that avatar.

Default LSL script

The default script, "Hello, Avatar",[13] a hello world program that speaks (streams message to local chat ("heard" (printed) by all viewers (clients) within 20 meters (3D virtual environment) of the task)) when the script is first saved, initialized or reset (in this case, since the command is in the default "state", the first to be run on initialization) or 'touched' (a simple user interaction usually initiated using a mouse click), looks like:

default 
{ 
	state_entry() 
	{ 
		llSay(0, "Hello, Avatar!"); 
	} 

	touch_start(integer total_number) 
	{ 
		llSay(0, "Touched."); 
	} 
}

Mono

The revised version of Second Life's scripting virtual machine is based on Mono, the open source implementation of the Microsoft.NET framework. The Mono VM was introduced to several simulators (sims) on the Second Life Beta grid for compatibility testing on 29 January 2008 and later that year on 20 August 2008 Linden Lab started deploying it on the production grid[14] - with the entire production grid updated to use it on 29 August 2008.

While the LSL scripting language remained the same, scripts executed on the Mono underpinnings were up to 220[15] times faster in execution, but at the cost of a somewhat higher overhead when creating (rezzing) scripted objects and moving them from sim to sim.[16]

More complex example script

Below is an example of a relatively simple LSL script, that when placed in a primitive along with a selection of .wav sound files, users may select the sound files to be played in series. This scripting is required to play full length songs in Second Life, as the sound file length is limited to a maximum of 10 seconds per clip at upload to SL.

// Global variables must be declared above any functions created and above the first "default" state.
// Many LSL scripts are open source, and can be edited by their owners.
// Often scripts have settings variables with instructions for editing written as comments around them.

 // This list of sound file names would be populated by the owner.
list songs = ["Box_Of_Rain", 9.2, "Servant", 9.8];

integer volume = 10; // All variables (global or local) must have their type declared on creation.
integer lis_count;
integer playing;
integer busy; // Values need not be applied to variable at creation.
integer part;
integer lis;
integer sl;
float delay;
list cancel = ["CANCEL"];
list playlist;
list waiting;
list song;
string vol_str = "Volume";
string song_str = "Songs";
string song_name;

// Variable names (as well as function names and state names) must begin with a letter (upper or lower case) or any number of underscores.
// Variable names cannot begin with a numeral, but they may contain numerals as any other character.

// User created functions can be created by simply naming the function.
// Functions that return a value, must have the value type declared as the type for the function.
// Arguments passed to the function must have their types declared. the type of data passed to the function must match the argument type.
list StrideOfList(list src, integer stride, integer start, integer end) {
	list l = [];
	integer ll = llGetListLength(src);
	if(start < 0)start += ll;
	if(end < 0)end += ll;
	if(end < start) return llList2List(src, start, start);
	while(start <= end) {
		l += llList2List(src, start, start);
		start += stride;
	}
	return l;
}
list Volumes(integer vol) {
	integer v = 0;
	list l = [];
	do {
		if(v != vol) // Single instruction conditional blocks need not be wrapped in braces.
		l += [((string)v)];
	}
	while((++v) <= 10);
	return l;
}
PageOne(key k, integer c) {
	llDialog(k, "\nAdjust the volume or select a song to play?", [vol_str, song_str] + cancel, c);
}
PlaySong(string n) {
	song = [];
	integer c = -1;
	string name = "";
	do { // Functions may be nested and used inline where values are returned (the values need not be stored to a variable for use).
		if(llSubStringIndex((name = llGetInventoryName(INVENTORY_SOUND, (++c))), n) != -1)
		song += [name];
	}
	while(name);
	delay = llList2Float(songs, (llListFindList(songs, [n]) + 1));
	if((sl = llGetListLength(song))) {
		llPreloadSound(llList2String(song, (part = 0)));
		if(sl > 1)
		llPreloadSound(llList2String(song, 1));
		playing = FALSE;
		llSetTimerEvent(0.01);
	}
}
integer Chan() {
	return llRound((llFrand(-5000000.0) + -500000.0));
}
float ScaleVol(integer v) {
	return (v * 0.1);
}
Listen(integer c, key a) {
	lis = llListen(c, "", a, "");
}
RemoveListen(integer b) {
	llListenRemove(lis);
	lis_count = 0;
	if(b)
	busy = FALSE;
	lis = 0;
}
SetListenTimer(integer p) {
	if(p)
	while(((++lis_count) * llRound(delay)) < 30);
	else {
		lis_count = 1;
		llSetTimerEvent(30.0);
	}
}
integer CheckWaitingRoom(integer c) {
	if(waiting) {
		key a = llList2Key(waiting, 0);
		if(!c) {
			RemoveListen(0);
			Listen((c = Chan()), a);
			SetListenTimer(playing);
		}
		PageOne(a, c);
		waiting = llDeleteSubList(waiting, 0, 0);
		return 1;
	}
	return 0;
}

// All scripts must have a default state.
// However many other states a script may have, the default state must be uppermost in the script (below global vars and functions).
default {
	// Event names are written in all lower case letters. Some contain underscores.
	on_rez(integer param) {
		// If an event is passed any values as it is triggered, those values will be of specific types.
		// Writers can choose the name for the variable, but not the type.
		// Even if the data contained in the variables passed to the event are not used, the event must contain reference to that data.
		llStopSound();
		llResetScript();
	}
	changed(integer change) {
		if(change & CHANGED_INVENTORY) // Bitwise comparison
		llResetScript();
	}
	touch_start(integer nd) {
 		while(nd) {
			key agent = llDetectedKey(--nd);
			if(!busy) {
 				busy = TRUE;
				integer channel = Chan();
				SetListenTimer(playing);
				Listen(channel, agent);
				PageOne(agent, channel);
			} else {
				list a = [agent];
				if(llListFindList(waiting, a) == -1)
				waiting += a;
			}
		}
	}
	listen(integer chan, string name, key id, string msg) {
		if(msg != llList2String(cancel, 0)) {
			SetListenTimer(playing);
			if(msg == vol_str) {
				llDialog(id, "\nChange the volume?\nThe current volume is set at \"" + ((string)volume) +
					"\"", cancel + Volumes(volume), chan);
				return;
			}
			if(msg == song_str) {
				string current = "";
				if(playlist) {
					current = "\n\nThe songs currently queued are\n\"" + llList2String(playlist, 0) +
						"\" (currently playing)";
					if(llGetListLength(playlist) > 1)
					current += "\n\"" + llDumpList2String(llList2List(playlist, 1, -1), "\"\n\"") + "\"";
				}
				llDialog(id, llGetSubString(("\nSelect a song to play?" + current), 0, 500), cancel +
					StrideOfList(songs, 2, 0, -1), chan);
				return;
			}
			if(llListFindList(Volumes(volume), [msg]) != -1) {
				llAdjustSoundVolume(ScaleVol((volume = ((integer)msg))));
 				PageOne(id, chan);
				return;
			}
			if(llGetListLength((playlist += [msg])) == 1)
			PlaySong((song_name = msg));
		}
		if(CheckWaitingRoom(chan))
		return;
		RemoveListen(1);
	}
	timer() {
		if(playlist) {
			if(!playing) {
				llSetTimerEvent(delay);
				playing = TRUE;
			}
			llPlaySound(llList2String(song, part), ScaleVol(volume));
			if((++part) == sl) {
				if(llGetListLength(playlist) > 1) {
					song_name = llList2String((playlist = llDeleteSubList(playlist, 0, 0)), 0);
					llSleep(delay);
					PlaySong(song_name);
				} else {
					llSetTimerEvent(0.0);
					song_name = "";
					playing = FALSE;
					playlist = [];
				}
			}
			else if(part == (sl - 1))
			llPreloadSound(llList2String(song, 0));
			else
			llPreloadSound(llList2String(song, (part + 1)));
		}
		if(lis && (!(--lis_count))) {
			if(!(CheckWaitingRoom(0)))
			RemoveListen(1);
		}
	}
}

References

  1. ^ Getting started with LSL. wiki.secondlife.com (2013-07-01). Retrieved on 2013-09-21.
  2. ^ Category:LSL Events. wiki.secondlife.com (2011-08-08). Retrieved on 2013-09-21.
  3. ^ Category:LSL Functions. wiki.secondlife.com (2012-09-08). Retrieved on 2013-09-21.
  4. ^ Category:LSL Types. wiki.secondlife.com (2012-04-27). Retrieved on 2013-09-21.
  5. ^ Category:LSL Integer. wiki.secondlife.com (2012-12-22). Retrieved on 2013-09-21.
  6. ^ Category:LSL Float. wiki.secondlife.com
  7. ^ Category:LSL String. wiki.secondlife.com (2013-05-19). Retrieved on 2013-09-21.
  8. ^ Category:LSL Key. wiki.secondlife.com (2013-09-11). Retrieved on 2013-09-21.
  9. ^ Category:LSL Vector. wiki.secondlife.com (2011-10-05). Retrieved on 2013-09-21.
  10. ^ Category:LSL Rotation. wiki.secondlife.com (2008-10-24). Retrieved on 2013-09-21.
  11. ^ Category:LSL List. wiki.secondlife.com. Retrieved on 2013-09-21.
  12. ^ Category:LSL Notecard An in-world data storage medium mainly used for text. wiki.secondlife.com
  13. ^ Hello Avatar. wiki.secondlife.com (2012-10-06). Retrieved on 2013-09-21.
  14. ^ Blogs. blog.secondlife.com. Retrieved on 2013-09-21.
  15. ^ ISSUE SVC-1341. jira.secondlife.com
  16. ^ ISSUE SVC-3895. jira.secondlife.com