Difference between revisions of "User:SuzannaLinn Resident/ScriptingClasses/Basics 1"
Line 670: | Line 670: | ||
=== Answers to questions === | === Answers to questions === | ||
Is there an order for the events or do they all trigger when the script is fired up? | |||
* Events are triggered by SL, because the object has been touched, or there is a message to listen, etc. Events don't run because the script is reset. | |||
* There is no order, the events are triggered when something happens in the environment of the object, the order in which events are written in the script doesn't matter at all. | |||
* If, for instance, an object is never touched, the touch_start event will never run. | |||
Are our scripts stored in some local storage related to our account and there is a limit for them? | |||
* Our scripts are stored in the SL severs owned by Linden Labs. | |||
* The space used by our scripts is not counted against our account, and there is no limit on how many scripts we can create, feel free to write lots of scripts :) | |||
Can we call an event with an event ? | |||
* No, it's not possible to call an event from another event. Events can only be called by SL. | |||
What about a script with a sensor event? Would that work all the time instead of being idle? | |||
* When using a sensor, we use the function llSensor() to tell SL that we want the list of people or objects around, then the script becomes idle, and the script activates again when SL triggers the event sensor with the information. | |||
* If we use the function llSensorRepeat() all is managed by SL, that triggers the event sensor at each time interval. | |||
* The script is idle until SL calls the event sensor, then the code in the event sensor runs, and the script goes idle again. | |||
When I put a scripted object back into inventory, what happens to the script? | |||
* When we take an object to our inventory, the scripts stops | |||
* When we rez the object again the script goes on where it was, if it was previously doing something. | |||
* The script is not reset by taking the object and rezzing it again. | |||
** If we want that the script reset when rezzing, we use the event on_rez and llResetScript(). | |||
* When we take an object, all script data is saved, so it can go on exactly from where it was. | |||
* The same happens during a region restart. | |||
Would it matter what is the order of the events in the script ? | |||
* The order that we use to write the scripts doesn't matter at all. | |||
* Events only run when SL triggers them. | |||
* The script doesn't run the events, we can shuffle the order in what our events are written and everything will work the same. | |||
When we are inside of events, do the lines of code execute similar to procedural languages (line by line)? | |||
* Yes, all the code inside the event is executed line by line in a procedural way. | |||
Can events call other events? | |||
* No, events can't call other events. Events can only be called by SL. | |||
Can the a message written with llRegionSayTo() only be seen by that person? | |||
* Yes, it shows in the local chat of the person, and it's only visible by that person. | |||
What llListen(42,"","","") does? | |||
* llListen() starts listening to a channel, the channel 42 in this case. | |||
* Since there are 4 billion possible channels, we need to tell SL the channels that we want to listen to. | |||
* We do it with the function llListen(). | |||
Can I use variable inside of llListen()? like llListen(i,"","","")? | |||
* Yes, and we often do, we will see it in next classes. | |||
Can we call a function from inside another? | |||
* Yes, it's very usual to do it. | |||
* But we can't write a function inside of another. | |||
Is there a reason to use fullName instead of FullName to call the function? | |||
* No, both are good. | |||
* Choose the way that you prefer, but always use the same way. | |||
Do function parameters behave similar to variables inside the function code? | |||
* Yes, inside the function code parameters and variables behave the same. | |||
Why some of the functions in the script start with ll and some don't? | |||
* The functions starting with ll are the SL functions, that we use to call SL to get inworld information or to modify the environment. | |||
* The others are our functions, written by ourselves, to perform our things in the script. | |||
What is the "return"? Is the return required to make the string work? | |||
* The "return" keyword sets the value that we want to return to the function caller, in this case the name of the person | |||
* The "return" is what sets a value to the string that the function provides to the caller. | |||
Without the return how does the string remain? | |||
* If we define the function with a type, like string fullName , if we don't have a "return" in the function, we will get an error when saving the script. | |||
If we write any code after the "return" keyword, will it execute? | |||
* No, when the return is executed the function ends. | |||
Another example of a function: | |||
* For instance, a function that return the double of a number: | |||
** integer doubleNum(integer num) | |||
** { | |||
** return num*2; | |||
** } | |||
* It takes an integer number as parameter, and returns another integer number, instead of a string. | |||
* We call it like: | |||
** llSay( 0, doubleNum( 25 ) ); // it says 50 | |||
Should the returned value have the same data type as the function? | |||
* Yes, we must return a value that is of the same type that the function. | |||
Can we add the ll to whatever we want? | |||
* We could start our functions with ll, if they don't have the same name as an existing SL 'll' function, but it is not recommended, as it can be confusing. | |||
What type of value is an UUID? | |||
* The UUID's have a type only for them, the type "key". | |||
Is the id that we are using to call the function assigned to the parameter "avatarId"? | |||
* Yes, when the function is called, the value that we use to call it is assigned to the parameter of the function | |||
Can we write a function in the script in the section below "default"? | |||
* No, functions must be written before the "default". Events go inside the "default". its how scripts are organized. | |||
Has the declaration of a function always be on the top? | |||
* No, we just write our functions one after the other. | |||
* Functions can call other functions written below them in the script. | |||
* Unlike other languages, a declaration of the function above the function that calls it is not needed. | |||
=== More answers to questions (intermediate/advanced level) === | === More answers to questions (intermediate/advanced level) === | ||
In most of the languages the source code is compiled into machine code. How is it with LSL? Is it compiled ? Where are the 0s and 1s ? | |||
* LSL is compiled not to machine code, but to pseudo-code that is executed by a virtual machine. | |||
* It is saved in the SL servers. | |||
Are scripts executed on server side? | |||
* Yes, they are. | |||
Is LSL just a pseudo-code, or it is compiled to something else? | |||
* LSL is compiled to the code for the VM Mono, that is also used by Visual Studio, and others. | |||
Can we run LSL scripts on Visual Studio? | |||
* No we can't run LSL in Visual Studio or anywhere else, because they need to receive events from SL and to use the LL functions. | |||
When exactly is the LSL script compiled for VM Mono? Just when we put the script into the prim, save it, and rez the object? | |||
* The script is compiled when we click on "Save" in the editor window. | |||
Can many events be called at the same time? Would they run independently then? | |||
* Only one event can be running, if SL triggers another event while the previous one is still running, the new event is queued and it will be executed when the previous one ends. | |||
If there are touch_start events in three different scripts in the same object, will also the functions line up? for a single touch? | |||
* If there are several scripts in the same object, all run at the same time, with three scripts, there are three processes running. | |||
* If all the scripts have a touch event, it will be triggered in all of them, running more or less at the same time. | |||
* If all the scripts say "touched" in public chat, we will see the message in a random order, that can change for any touch. | |||
If another person creates a huge script with lots of events and puts into his object which is nearby. Can that script make some kind of lag to our script ? | |||
* All scripts running time accumulate for the sim and can cause lag, but it doesn't matter if a laggy object is placed near ours or on the other side of the region, lag is region-wide. | |||
Can there only be 64 events? | |||
* The maximum of events in the queue of events waiting to be executed is 64. | |||
* If the script receives more events than it can process, the events that don't have space in the queue are lost, but this is not a common thing at all. | |||
When we enter different regions, they run on different version of servers. Does it matter for us? | |||
* No, our scripts will work the same. | |||
Can events move the script to different state? with different events in the state? | |||
* Yes, we will study the states in a future class | |||
Is llRegionSayTo() similar to the instant message function but restricted to the region? | |||
* Yes, it's like a basic version of llInstantMessage(). |
Latest revision as of 05:12, 14 October 2024
Basics 1
Monday class
Touch
-> Object 1 Touch (a red box) -> Script 1 Touch
Right-click your red box and edit it.
Go to the contents tab. There is a script there: "script 1 Touch".
Double-click the "script 1 Touch" to open it. We are going to look at it in detail.
Keep the script opened, and close the edit window.
There are several check boxes and buttons in the script windows. Let's look at them:
- Running. If it is checking the script is executing its code. Usually we will have it checked. Sometimes, if there is an error in the script, it gets unchecked automatically. If our script doesn't work at all, look at this box, perhaps the script is not running.
- Mono: it refers to the version of the scripting language. "Mono" is the modern version. It has more memory and it is faster. This option is kept for compatibility with the older scripts. We will always have "Mono" checked.
- Experiences: when it is checked, it means that we are using the script with an experience in the sim, if the sim has any experience configured. It allows more interactions with the avatars who have accepted the experience and with the environment. This is advanced scripting, for now we will leave it unchecked.
- "reset" button. This button stops the script, no matter what it is doing, and restarts it again. Touch the "reset" button to reset your script. It says "Hello, Avatar!".
- "save" button. To save the script. It will tell if our code is ok or tell us an error message.
Now we are going to look at the script.
It's the most basic script. It reacts to touches. Touch your red box and it says "Touched.".
In this script we want to know when somebody touches the box, and SL tells it to our script.
When there is a touch on the object, SL triggers an event in our script, and we can receive this event and do something, like saying "touched".
Think of an event as a heads-up that the script gets when something occurs to the object or around it, like when someone touches it.
These events are like signals that let us write code in our script to respond to different things, such as when users interact with the object, avatars move or chat, the environment changes, when timers reach a certain point, etc.... There are lots of events, around 40, and we'll be learning many of them in this course.
The name of the event that is triggered when our object is touched is "touch_start". We have it in our script in lines 7-10 (in the viewer with yellow background).
Events' names are always followed by parentheses, "(" and ")". They can be empty or have one or several values separated by commas.
These values are additional information that comes with the event. They are called "parameters".
touch_start has one parameter, called "total_number" and it is an integer number.
Each parameter has a "type", specifying the kind of information that the parameter can hold.
total_number is of type "integer", the type for whole numbers, positive or negative, without decimals.
The events, with their parameters in parentheses, are always followed by curly brackets, "{" and "}", containing our code to answer to the event.
Parentheses are used to know where our parameters start and end, and curly brackets are used to know where our code starts and ends.
In lines 2-5, state_entry is another event.
state_entry is triggered when the script starts (or after it is reset). Our script says "hello avatar" as we have seen after using the "reset" button.
Now let's look to the code inside the events, the lines with the messages that our red box is saying.
Both lines start with "llSay".
llSay is a function of the language.
Functions are predefined set of instructions that we can use in our code, they are built-in tools to perform specific tasks.
All the functions that the linden script language provides to us start with "ll" (2 letters "l" of linden).
The language has several hundreds of functions. We will be learning many of them along this course.
The function "llSay" is used to make the object say something in the public chat.
Functions, like events, are followed by their parameters between parentheses, or by empty parentheses if there are no parameters.
As we see in the script, llSay has two parameters, the first parameter is 0 in both cases, and the second parameter is the text to be said, enclosed in quotes, " and ".
The first parameter, the 0, is the channel. A "channel" is a way for different objects to communicate with each other, a line of communication agreed by both objects. We will use channels again in a while.
The channel 0 is a special channel that says the messages to the public chat.
It's a parameter of the type integer.
The second parameter is the text to be said.
We must write the text between quotes. This way we can use anyting in the message (like "()", "{}", names of events or functions...) and avoid confusions in the script.
This parameter is of another type, the type "string".
The type "string" can contain any kind of text, numbers, or symbols.
And at the end of each of both llSay() lines there is a semicolon, a ;.
The semicolon must be added at the end of each instruction of the block of code inside the curly brackets. It's used by the script to know when the instruction ends.
A new line is not enough, because we can write a long instruction in several lines. A instruction only ends at the semicolon.
And, summarizing, what are the differences between events and functions?
- events are called by something happening outside the script.
- functions are called by the script.
- events send parameters to the script.
- the script sends parameters to the function.
- we write code in the script to answer to the events.
- the function code is already written and it answers to the script.
- events' parameters are written with its type and name (like integer total_number). We need a type and a name for each parameter to use them in our script.
- functions' parameters are written with its value (like 0 or "message"). We send the values of the right types.
Events come to us. Functions go from us.
Let's look at the "default" keyword at the start.
"default" is the name of the section in the script where we place all the event, all enclosed between { and }.
When a script starts after a reset or after being modified, it look for this "default", and then for the event "state_entry" inside the default { }.
We must have a default { } in all our scripts.
It's possible to add other groups of events, with other names instead of default. They are called "states" and we will see them in future classes.
Now we can do some practices with our script.
For instance, edit the script and change the messages in the script to any other messages that you like.
Save the script using the "Save" button and touch the object.
We are getting plenty of messaging from all the red boxes. Let's do it a bit more quiet.
We will change the script to make that it says the message only to the owner of the object.
Instead of the function llSay( 0, "message" ) we will use the function llOwnerSay( "message" ).
llOwnerSay() sends the message to the object owner only. There is no way to send to a channel using this function, so it hasn't a parameter for the channel. llOwnerSay() has one parameter, the message,
Everything in the script (events, functions, parameters,...) is case sensitive. We must write it exactly with the right caps, otherwise we will get an error when saving. llOwnerSay() has the "O" and the "S" in caps.
Change llSay(0,... to llOwnerSay(... and save the script. If you get an error when saving that you can't solve, send the script to me.
Now you will see the messages from your red box only, because you are the owner of it.
You will also see the messages from your red box if someone else touches it, but the toucher will not see the messages. Try touching the cylinders of the students around you.
Touch with comments
-> Object 2 Touch with comments (a yellow tube) ->Script 2 Touch with comments
Now let's talk about a very important thing, commenting our scripts.
We will not only learn to script, but we will learn to script well, following good scripting practices.
And this includes adding comments to our scripts.
We can add a comment at the end of a line, or as a new line, always starting with a double slash, //.
The comments that you write in your script are firstly for yourself. To explain to yourself what the script does and how the script does it. They are meant to help you when you look to the script after some time to modify it, and also while you are writing a long script.
The comments will also help other scripters when you share your scripts.
The comments are not written for the teacher. But the teacher likes a lot to see your scripts and read your comments :)
You can write as many and as long comments as you want. They don't use script memory or any resources.
At the beginning, we will comment most of the lines. Later, we will not repeat the same comments to the same things that, by then, we will have scripted several times, and we will comment what is more important or new.
My suggestion is that later today or during this week, when you study the class notes that you will get at the end of the class, you add the comments to the script.
When you have the script commented, send it to me. It will help me a lot to follow your progress and to know what needs a better explanation.
In this script there is also another event, "on_rez".
"on_rez" is triggered when the object where the script is in is rezzed. Don't worry about the parameter start_param, is for more advanced uses and we will see it in the future.
llResetScript does, just that, resets the script. The same that the "Reset" button in the script window.
Everything is restarted and the event state_entry is called . Usually is good to do it to start clean in each rezzing. We will add this code in most of our scripts.
Listen
-> Object 3 Listen (a green cylinder) -> Script 3 Listen
Now our object is listening to what people tells, but in channel 3, not in public chat.
We could listen to the public chat, channel 0, but then it would be repeating everything that we say.
To write in a channel, type / and the number of the channel before your text, for instance: /3 I'm writing in channel three
When someones chats near our object, SL triggers the event "listen".
This event is a bit more complex than the event "touch", it has four parameters.
These are the parameters: listen( integer channel, string name, key id, string message )
- channel: the number of the channel in which the message has been said.
- name: the name of the avatar or the name of the object that has said the message.
- id: the UUID of the avatar or the object that has said the message.
- message: the message that has been said.
channel is of the type integer, name and message are of the type string. We already have seen and used these types before.
id is of the type "key". The type "key" is only for the UUIDs.
Keys has numbers, letters and "-"s, so we could use the type string for them. But in LSL script we will be using UUIDs (of people, objects, textures, etc) so often that there is a type for them alone.
The parameter "name", when the the message comes from an avatar, has the legacy name. Legacy names are the usernames in the format "firtname.lastname" or "name.resident".
In lines 9 to 12 we are saying to ourselves the values of the parameters.
llOwnerSay needs a string as parameter, but some of the parameters are not of string type.
We can change one type to another with the name of the new type between parentheses before the name of the parameter, for instance: (string)channel
In line 14 we say the name, the text "has said" and the message, all in one llSay. We can add together several strings with the + sign.
Now we have our event listen ready... but it's not enough for it to work.
The event listen differs from the events touch (and many other events). The problem is that there are more than 4 billions of possible channel numbers.
And the script can't be listening to all of them. So we need to tell to the script what channel or channels we want to listen to.
There is a LSL function to do it: llListen().
llListen() has the same 4 parameters that the event listen, in the same order. We must choose a channel, the first parameter, but we can leave the other 3 parameters empty, with "".
We can use these other 3 parameters, name, id or message, to listen only to a legacy name, or a UUID, or to a specific message.
This is a way to filter the messages to receive only the events with the messages that we are interested in.
If we want to use more than one channel, we will use a function llListen() for each channel.
We have the function llListen() in line 4. We are listening to the channel 3.
We use the function llListen in the event state_entry. This way we are listening since the script starts.
Listen and Touch
-> Object 4 Listen and Touch (a blue sphere) -> Script 4 Listen and Touch
Now we using both events, listen and touch_start, to say messages, but now saying the display name of the person.
We see another ll function: llGetDisplayName( id ).
But there is a difference between llGetDisplayName() and llSay() or llOwnerSay().
llSay() and llOwnerSay() do something: saying.
llGetDisplayName(), instead of doing something, returns us information.
In this case the display name of the avatar who has the UUID that we have given to the function as parameter.
The value returned is a string and we can add with "has said" and the message to say it.
In the event touch_start, to get the UUID of the toucher we use this function:
- llDetectedKey(0)
llDetectedKey() has one parameter, the number of the toucher, starting with 0.
llDetectedKey(0) returns us the UUID of the first toucher.
Answers to questions
When are scripts "out of range"?
- If we are editing a script and we take or delete the object where the script is in the title of the script shows the script name and (out of range). This script can't be saved, we will need to copy it and paste to the script in the object.
When are scripts saved with "running" unchecked?
- "running" gets unchecked, if we save a script with errors, and then we close the script without solving the errors. The script is saved, with the "running" unchecked.
If we get an object that has a script running without the "Mono" checked, should we check it?
- The old version, LSO, has some differences, and there are a few scripts that use these differences. So if you receive a script that works and has "Mono" not checked, dont touch it.
What are the LSL versions?
- There are two LSL versions: LSO (the old one) and Mono (the new one)
Could we have the event state_entry with a parameter?
- No, we can't change the parameters of the events. They are predefined and SL triggers the events with the predefined parameters.
- We can't change the quantity or the type of the parameters, we can only change its names, if we like, but usually we always use the same names. Using the same names make the code in the event easier to read, since we already know what are the names of the parameters.
- For instance, the event touch_start has one parameter, of type "integer", we can't change this. But we can use any name for the parameter, touch_start( integer suzanna ), is ok, but not recomended.
Events are the stimulus; functions are the response?
- Events are the stimulus, and functions are the tools we use to respond to those events.
Must we always have a state_entry event, because the script automatically looks for that?
- No, we can have scripts without state_entry. SL tirggers the event state_entry when a script starts, and if state_entry is not there, it does nothing.
- But we must have a "default { }" group of events, or state, with at least one event in it (any event, state_entry or another).
Can avatars chat only on positive channels?
- No, avatars can chat also on negative channels nowadays. This limitiation existed time ago. To chat in a negative channel type /- and the number of the (negative) channel.
How to listen to only one avatar?
- using the parameter id with the UUID of the avatar: llListen(0,"","0f16c0e1-384e-4b5f-b7ce-886dda3bce41","");
- or using the parameter name with the legacy name: llListen(0,"SuzannaLinn Resident","","");
If a script is listening and then saying, in the same channel, does that put the script in an endless loop of talking to itself and responding?
- No, because the objects can't listen to themselves.
Wednesday class
Review
Let's start with a review of what we studied on Monday.
We studied 4 events:
- state_entry( )
- when the script starts (after a reset or after modifying and saving it).
- we use it to initialize things at the start, like listening to a channel.
- touch_start( integer total_number )
- when an avatar touches the object where the script is in.
- integer total_number: how many touches the object has received, we will use this parameter next week
- when an avatar touches the object where the script is in.
- listen( integer channel, string name, key id, string message )
- when an avatar or an object says something in the channel.
- integer channel: the number of the channel that the text has been said to.
- string name: the name of the avatar or the object that has said the text.
- key id: the UUID of the avatar or the object.
- string message: the text that has been said.
- when an avatar or an object says something in the channel.
- on_rez( integer start_param )
- when the object where the script is in is rezzed.
- we can use it to reset the script.
- integer start_param: thsi is used when a script rezzes other objects, we will use it later in the course.
Scripts stop running when they are derezzed and go on running when they are rezzed again.
They are not reset by taking the object to our inventory and rezzing it again.
And we studied 6 LL functions.
Let's look at them with their internal format, with their predefined types of the parameters. We will never write the type when we call the function, it's only to show you what the types are. Also, if the function returns a value, there is its type in front of the name.
- llSay( integer channel, string msg )
- says a message in the channel.
- integer channel: the number of the channel where the text will be said to.
- string msg: the text to say.
- says a message in the channel.
- llOwnerSay( string msg )
- says a message to the owner only.
- string msg: the text to say.
- says a message to the owner only.
- llListen( integer channel, string name, key id, string msg )
- starts to listen to a channel, it has the same parameters than the event listen.
- integer channel: the number of the channel to listen.
- string name: the name of the avatar or the object to listen.
- key id: the UUID of the avatar or the object.
- string message: the text to listen.
- we must provide the channel number, the other parameters can be left empty, "".
- to receive a message, it has to comply with all the parameters (if we use the name of an avatar and the key of another, we will never receive a message).
- starts to listen to a channel, it has the same parameters than the event listen.
- llResetScript( )
- resets the script.
- key llDetectedKey( integer number )
- returns the UUID of the avatar that has touched the object.
- integer number: the number 0 is the first toucher, we will use this parameter next week
- returns the UUID of the avatar that has touched the object.
- string llGetDisplayName( key id )
- returns the display name of the avatar.
- key id: UUID of the avatar.
- returns the display name of the avatar.
Practice
Now get your neurons and fingers ready, we are going to do a practice.
The practice has 3 different levels, choose the level that suits you best:
- For beginner level:
- rez to new objects.
- add a new script to both objects.
- write the script so when any of the objects is touched, the other object says that the first object has been touched
- For intermediate level:
- the same and, using scripting knowledge that we haven't seen in class yet:
- keep track of who has touched the object and how many times.
- say the touchers and how many times when the owner touches the object.
- For advanced level:
- the same and:
- when the owner touches any of the objects, say all the touchers and total touches added, and sorted by touches from more to less
Answers to questions
How many channels are available?
- All the possible values of an integer number, from −2,147,483,648 to +2,147,483,647. A total of 4,294,967,294. Or to remember it easily, 9 digits, positive or negative.
Are there dedicated channels?
- Yes, there are a few dedicated channels:
- Channel 0 for local chat (avoid that one in your scripts if you can).
- Debug Channel, accessible in your scripts as DEBUG_CHANNEL = 0x7FFFFFFF.
- There are also a number of unofficial common channels for things like RLV relays and so forth.
- Good practices:
- Reserve the smaller positive numbers for command channels from the users of the object.
- Use really big random positive or negative numbers for your object communication.
- Never depend on only your objects speaking or listening to any given channel.
Do I have to rezz the object to see the scripts inside it?
- Yes, to open the scripts inside an object, the object must be rezzed. An object attached or attach as HUD is also rezzed.
How do we listen for anyone on a specific channel?
- To listen to everyone we don't choose neither name nor id, leaving them empty: llListen( channel, "", "", "" );
When listening for a specific avatar or object, is it better to use the UUID or the name?
- It's best to use their UUID. Using names is handy when you want to do something like listen to several objects that have the same name.
What does the function llListen do?
- llListen starts listening to a channel (in the first parameter) and selects the messages that our script will receive based on the other 3 parameters. The script can't listen to all the channels, there are more than 4 million possible channels, so we need to choose what channels we want to listen to.
Have the people chatting to be in chat range of the object with the listen script?
- Yes, we will receive messages said in chat up to 20 meters away of the center of the object where the script is in. Objects only listen up to 20 meters for normal chat, 10 meters for whispering and 100 meters for shouting.
Is there a way to make the range bigger?
- No, we can't change the ranges. We can script repeaters or chat relays, but this will be a topic for a future class.
How is the event listen activated?
- The event listen is activated when an avatar or an object says something to a channel that we are listening to.
- We choose the events that we want to listen with the function llListen.
- The events, listen, touch_start, etc , are always activated by something happening externally to the script.
- We can listen to several channels, using the function llListen once for each channel, up to 64 channels.
Can a script listen to voice?
- No, only to text chat.
Can we change the channel that we listen to when the script is running?
- Yes, we can stop listening to a channel and start listening to another. We will see how to do it in a next class.
- We can use llListen anywhere, not only in the event state_entry.
Can we write llListen("","","","Hello") to listen for hello in any channel?
- No, we must always choose a channel. The first parameter can't be empty.
Is the even listen it always listen(integer channel,string name,key id,string message) ?
- Yes, the quantity of parameters and their types is predefined an never exchange. We could change the name of the parameters.
Can I write listen(5,string name,key id,string message) ?
- No, we can't use values in the parameters in the events, the values will be sent to us by SL when the event is triggered.
What the parameter start_param does in the event on_rez?
- It is used for some advanced process related to scripts rezzing objects, we will see this parameter in a future class.
Can the parameters of an event be used elsewhere?
- No, we can use the names of the parameters only inside the code of the event.
Can events communicate from one event to another event in the same script?
- No, we can't call an event from another event.
- Two events can't be executed at the same time. If, while the code of an event is running, another event arrives, the second event is queued to be executed when the first event finishes.
How can I know the UUID of my newly rezzed object?
- Every object has a unique UUID, whenever we rez a copyable object it gets a new UUID.
- We have the function llGetKey() that returns the UUID of the object where the script is in.
Is it possible to write a script that listens for the channel 0 and saves the conversations to a notecard (or somewhere else)?
- Yes, but not to a notecard. LSL doesn't include facilities for notecard writing You would have to use Linkset Data or an external data storage. We will see how to do it in a future class.
Saturday class
Review of events
I've noticed that students who are familiar with procedural languages (where code execution begins at the first line and continues sequentially through the next lines until the end) often have difficulty understanding how LSL events work.
We could think that it works like this:
- the "default" block, which contains all the events, is a kind of loop that runs continuously, and in each loop:
- checks if the script has been reset.
- if yes, runs the code in state_entry.
- checks if there is a message said on a channel the script is listening to.
- if yes, runs the code in listen.
- and so on with the other events.
- checks if the script has been reset.
- after reaching the end of the block, it loops back to default and repeats.
But this is not how it works.
What it really does when a script starts is...
Absolutely nothing.
The key to how it works is that LSL is not a standalone language. It's a language embedded in SL.
Our script is a very small program embedded in a very large program - SecondLife itself - with many thousands of lines, written in the C language.
With our script, we can customize the behavior of objects, in the way that SL provides for us. These "doors" to make our objects behave as we want are the events.
When we save a script, SL checks for errors and generates internal code to run the script. As expected, this is what all languages do.
But, unlike other languages, SL also internally registers which events we have written in our script (and, in the case of the event listen, which channels are we listening to).
It’s like subscribing to a newsletter:
- We want to be notified when our object is touched.
- We "subscribe" to the "newsletter" about touches, by adding a "touch_start" event.
- When a touch occurs, SL calls us.
The large SL program manages everything inworld. For example, in the case of touches:
- If an object has a script with a touch event (and the script is running), SL changes the mouse pointer from an arrow to a hand.
- When the object is touched, SL calls our script, something like:
- Suzanna_Red_Box . Script_Listen_and_Touch . touch_start( 1 )
- (not really like this, it uses the UUIDs of objects and scripts, but this gives the general idea.)
- SL also internally stores information about the toucher, that we can access using llDetectedKey() and other functions.
When we use:
- llSay( 0, "hello" );
We don't write "hello" into the public chat. We’re telling SL that we want to do this.
SL verifies everything is correct and writes "hello" into the public chat, while also:(among other things):
- looks for the objects in chat range.
- looks in each object if it has a listen event.
- and checks if the script is listening to the channel (channel 0 in this example).
- if yes, SL calls our script, something like this (if it's me who has said the message):
- Suzanna_Red_Box . Script_Listen_and_Touch . listen( 0, "SuzannaLinn Resident", "0f16c0e1-384e-4b5f-b7ce-886dda3bce41", "hello" )
Which are the parameters channel, name, id, message that we have in our script.
This is why we can't change the number or types of parameters. SL is calling our script events always with the predefined parameters.
But we can change the names of these parameters in our script. Because SL is providing us the values, and the names are only for us to use them in the code of the event.
When we use:
- llResetScript():
We don't reset the script. We're telling SL that we want it to reset.
SL then resets the script and, if we have an event state_entry, calls it, like:
- Suzanna_Red_Box . Script_Listen_and_Touch . state_entry( )
Most of the time, our scripts are idle, doing nothing.
A call from the very large SL program through an event is what triggers our very small script programs to execute the code of that event.
And when the code in the event is completed, our script goes back to being idle.
Solution to Wednesday's practice
-> Objects 5 Listen and Touch (a black ball and a white ball) -> Script 5 Listen and Touch 2
It's the same script in both objects. It uses the channel 25.
Some students has written the practice using two channels, which works too.
But, since the scripts can't listen to themselves, we can use only one channel.
This way we only need one script, and we use less resources.
We start listening to channel 25 in line 4.
In the event listen, in line 9, we typecast the parameter message, that is the id of the toucher, to a key and we get the display name.
And we add the parameter name, that is the name of the object that has said the message.
In the touch event, in line 14, we say the id of the toucher to the channel.
We can also use llGetDisplayName() in the touch_start instead of listen, saying the name of the toucher instead of the id.
Next week we will study the intermediate practice, storing the names of the touchers and their quantity of touches.
These are all the functions to say:
- llSay( channel, message ), range 20 meters
- llWhisper( channel, message ), 10 meters
- llShout( channel, message ), 100 meters
- llRegionSay( channel, message ), all the region, the channel can't be 0, we can't use this function to write in the public chat
- llRegionSayTo( id, channel, message ), to the avatar or object with id, all the region
- llOwnerSay( message ), to the owner, all the region
Functions
There is an improvement to add to our script when we say the name of an avatar.
It could happen that there were several people with the same name in the place and we would not know which of them has said or touched.
Display names can be repeated, usernames cannot. Let's add the username to our message. Instead of "Suzanna has..." we will change it to "Suzanna (suzannalinn) has...".
So we need to change both events, listen and touch_start, making the same change twice.
But a good scripter is lazy, a good scripter don't do the same thing several times, not even twice.
And there is a solution for it. We can create user functions.
We have seen that we receive events, with their parameters, and we write code to answer to them. We have also seen that we call LL functions, sending parameters to them, and these functions do some action or return some information.
User functions are everything together. We choose the name, we choose the parameters and their types, we write the code, and we call these functions from another place in the script.
User functions have many advantages. We will be seing these advantages in next classes.
Today we are seeing an important advantage: reusability.
We create a function when we have a piece of code that we are planning to use in several places in our script. We write the code only once, and reuse it.
Now we are going to create a function that returns us a display name with the username, like "Suzanna (suzannalinn)".
This function is going to have one parameter, the UUID.
First let's create the function, starting with chosing a name for it.
The name has to start with a letter, and after it, letters and numbers. Letters can be lowercase or uppercase. When we call our function we will have to use exactly the same name, and the same lowercases and uppercases.
We are naming our function as:
- fullName
It's very important to choose a name that tells what the function is doing. This way we will be able to read our scripts more easily.
(Imagine the opposite, we have 10 functions and call them function1, function2... to function10, and somewhere in the script it is calling function5... who could guess what that function is doing? )
It's very important to stop for a while before creating a new function to choose a good name.
To make the names easier to read we will use a way to differenciate words in the function name. In our example we are using caps.
There are several formats of naming, all of these ones are good and used by scripters:
- fullName (our example)
- fnFullName (the "fn" shows that this is the name of a function)
- FullName (similar to our example, but the first letter in caps too)
- full_name ("_" instead of caps to differenciate words, so caps are not needed)
- fn_full_name (with "_" and "fn" at the start)
In this classes we will use the first one, but all of them are good. To write your scripts choose the one that you prefer. And use always the same format.
After the name of the function we add the parameters, between parentheses and separated by commas, like in the events. If there are no parameters we add ().
We have one parameter, the UUID, which is of type "key".
We choose the name for this parameter. We will use this name in our code inside the function. For instance, we choose "avatarId":
- fullName( key avatarId )
We have seen LL functions that do actions (llSay, llOwnerSay, llListen) and LSL functions that return information (llDisplayName, llDetectedKey).
Our functions can also be of both varieties.
In our example we are returning information.
When we return information we need to add the type of the information that we return before the function name.
We are returning the display name and username of the avatar, so we are returning a type string:
- string fullName( avatarId )
Now we are going to write the code of the function.
The code has to be enclosed in curly brackets, in the same way that the code of the events:
- string fullName( avatarId )
- {
- }
To return the information we use a special keyword of the language, the "return" keyword:
- string fullName( avatarId )
- {
- return llGetDisplayName( avatarId );
- }
And we have our function ready to use. We call it like we do with the LL functions:
- fullName( id ) from the event listen
- fullName( llDetectedKey( 0 ) ) from the event touch_start
-> Object 6 Function (a light blue prism) -> Script 6 Function
The new function is in lines 0 to 2, before the block default {}.
The block default is only for the events. We can place only events there.
The user functions have to be placed before the block default.
We are calling the function in lines 13 and 18. They are calling fullName() instead of llGetDisplayName().
And the script does exactly the same than the "script 4 Listen and Touch". But know we have it better organized and ready for changes. We are able to change what lines 13 and 18 do, without touching them.
Now we are ready to add the username to the display name, changing only the function fullName, in line 1.
To get the username we use the function llGetUsername(), that, like llGetDisplayName() has one parameter, the UUID.
"Display name" are two words, so D and N are in caps. "Username" is one word, so only U is in caps.
-> Object 7 Function with username (a purple torus) -> Script 7 Function and username
Answers to questions
Is there an order for the events or do they all trigger when the script is fired up?
- Events are triggered by SL, because the object has been touched, or there is a message to listen, etc. Events don't run because the script is reset.
- There is no order, the events are triggered when something happens in the environment of the object, the order in which events are written in the script doesn't matter at all.
- If, for instance, an object is never touched, the touch_start event will never run.
Are our scripts stored in some local storage related to our account and there is a limit for them?
- Our scripts are stored in the SL severs owned by Linden Labs.
- The space used by our scripts is not counted against our account, and there is no limit on how many scripts we can create, feel free to write lots of scripts :)
Can we call an event with an event ?
- No, it's not possible to call an event from another event. Events can only be called by SL.
What about a script with a sensor event? Would that work all the time instead of being idle?
- When using a sensor, we use the function llSensor() to tell SL that we want the list of people or objects around, then the script becomes idle, and the script activates again when SL triggers the event sensor with the information.
- If we use the function llSensorRepeat() all is managed by SL, that triggers the event sensor at each time interval.
- The script is idle until SL calls the event sensor, then the code in the event sensor runs, and the script goes idle again.
When I put a scripted object back into inventory, what happens to the script?
- When we take an object to our inventory, the scripts stops
- When we rez the object again the script goes on where it was, if it was previously doing something.
- The script is not reset by taking the object and rezzing it again.
- If we want that the script reset when rezzing, we use the event on_rez and llResetScript().
- When we take an object, all script data is saved, so it can go on exactly from where it was.
- The same happens during a region restart.
Would it matter what is the order of the events in the script ?
- The order that we use to write the scripts doesn't matter at all.
- Events only run when SL triggers them.
- The script doesn't run the events, we can shuffle the order in what our events are written and everything will work the same.
When we are inside of events, do the lines of code execute similar to procedural languages (line by line)?
- Yes, all the code inside the event is executed line by line in a procedural way.
Can events call other events?
- No, events can't call other events. Events can only be called by SL.
Can the a message written with llRegionSayTo() only be seen by that person?
- Yes, it shows in the local chat of the person, and it's only visible by that person.
What llListen(42,"","","") does?
- llListen() starts listening to a channel, the channel 42 in this case.
- Since there are 4 billion possible channels, we need to tell SL the channels that we want to listen to.
- We do it with the function llListen().
Can I use variable inside of llListen()? like llListen(i,"","","")?
- Yes, and we often do, we will see it in next classes.
Can we call a function from inside another?
- Yes, it's very usual to do it.
- But we can't write a function inside of another.
Is there a reason to use fullName instead of FullName to call the function?
- No, both are good.
- Choose the way that you prefer, but always use the same way.
Do function parameters behave similar to variables inside the function code?
- Yes, inside the function code parameters and variables behave the same.
Why some of the functions in the script start with ll and some don't?
- The functions starting with ll are the SL functions, that we use to call SL to get inworld information or to modify the environment.
- The others are our functions, written by ourselves, to perform our things in the script.
What is the "return"? Is the return required to make the string work?
- The "return" keyword sets the value that we want to return to the function caller, in this case the name of the person
- The "return" is what sets a value to the string that the function provides to the caller.
Without the return how does the string remain?
- If we define the function with a type, like string fullName , if we don't have a "return" in the function, we will get an error when saving the script.
If we write any code after the "return" keyword, will it execute?
- No, when the return is executed the function ends.
Another example of a function:
- For instance, a function that return the double of a number:
- integer doubleNum(integer num)
- {
- return num*2;
- }
- It takes an integer number as parameter, and returns another integer number, instead of a string.
- We call it like:
- llSay( 0, doubleNum( 25 ) ); // it says 50
Should the returned value have the same data type as the function?
- Yes, we must return a value that is of the same type that the function.
Can we add the ll to whatever we want?
- We could start our functions with ll, if they don't have the same name as an existing SL 'll' function, but it is not recommended, as it can be confusing.
What type of value is an UUID?
- The UUID's have a type only for them, the type "key".
Is the id that we are using to call the function assigned to the parameter "avatarId"?
- Yes, when the function is called, the value that we use to call it is assigned to the parameter of the function
Can we write a function in the script in the section below "default"?
- No, functions must be written before the "default". Events go inside the "default". its how scripts are organized.
Has the declaration of a function always be on the top?
- No, we just write our functions one after the other.
- Functions can call other functions written below them in the script.
- Unlike other languages, a declaration of the function above the function that calls it is not needed.
More answers to questions (intermediate/advanced level)
In most of the languages the source code is compiled into machine code. How is it with LSL? Is it compiled ? Where are the 0s and 1s ?
- LSL is compiled not to machine code, but to pseudo-code that is executed by a virtual machine.
- It is saved in the SL servers.
Are scripts executed on server side?
- Yes, they are.
Is LSL just a pseudo-code, or it is compiled to something else?
- LSL is compiled to the code for the VM Mono, that is also used by Visual Studio, and others.
Can we run LSL scripts on Visual Studio?
- No we can't run LSL in Visual Studio or anywhere else, because they need to receive events from SL and to use the LL functions.
When exactly is the LSL script compiled for VM Mono? Just when we put the script into the prim, save it, and rez the object?
- The script is compiled when we click on "Save" in the editor window.
Can many events be called at the same time? Would they run independently then?
- Only one event can be running, if SL triggers another event while the previous one is still running, the new event is queued and it will be executed when the previous one ends.
If there are touch_start events in three different scripts in the same object, will also the functions line up? for a single touch?
- If there are several scripts in the same object, all run at the same time, with three scripts, there are three processes running.
- If all the scripts have a touch event, it will be triggered in all of them, running more or less at the same time.
- If all the scripts say "touched" in public chat, we will see the message in a random order, that can change for any touch.
If another person creates a huge script with lots of events and puts into his object which is nearby. Can that script make some kind of lag to our script ?
- All scripts running time accumulate for the sim and can cause lag, but it doesn't matter if a laggy object is placed near ours or on the other side of the region, lag is region-wide.
Can there only be 64 events?
- The maximum of events in the queue of events waiting to be executed is 64.
- If the script receives more events than it can process, the events that don't have space in the queue are lost, but this is not a common thing at all.
When we enter different regions, they run on different version of servers. Does it matter for us?
- No, our scripts will work the same.
Can events move the script to different state? with different events in the state?
- Yes, we will study the states in a future class
Is llRegionSayTo() similar to the instant message function but restricted to the region?
- Yes, it's like a basic version of llInstantMessage().