LSL and OSSL feature states which scripts can define in many ways, switching back and forth between them using the state directive (example: state default;).
States are defined using the keyword state (Example: state foo {...}) with the exception of the “default” state, which is defined by using the keyword default on its own.
All scripts must have a default state, which also must be the first state entered when the script starts. If another state is defined before the default state, the compiler will report a syntax error.
States contain event handlers that are triggered by the LSL/OSSL virtual machine. All states must supply at least one event handler–it’s not really a state without one. If a state is declared without any event handlers, the compiler displays a syntax error.
When the interpreter reaches a state change statement, the event containing the state change is immediately ended. If the current state has a state_exit event, this is executed before the state change takes place. Note: previous versions may have allowed the event containing a state change to complete before the state change takes place, but this is no longer the case.
When a state changes, all pending events are cleared, and many (but not all) events that require setup (via a function) are defaulted (disabled).
state_entry
The state_entry event occurs whenever a new state is entered, including program start, and is always the first event handled. No data is passed to this event handler.
It is a common mistake to assume that the state_entry events is called when an object is rezzed out of inventory. When an object is added into an inventory, the script’s current state is saved, so there will be no state_entry during the rez. If startup code is needed every time an object is created, create a global function and call it from both state_entry and the on_rez event.
// global initialization function. init() { // Remove the old listen callback: llListenRemove(listenHandle); // Set up a listen callback for whoever owns this object. key owner = llGetOwner(); listenHandle = llListen(0, "", owner, ""); } default { state_entry() { init(); } on_rez(integer start_param) { init(); } listen(integer channel, string name, key id, string message) { llSay(0, "Hi " + name + "! You own me."); } }
state_exit
The state_exit event occurs whenever the state command is used to transition to another state. It is handled before the new state’s state_entry event.
Provide a state_exit if any kind of cleanup work is necessary before leaving the old state.
// stupid stateful stopwatch default { // stopped touch_start(integer num_detected) { state start; // go, go, go! } } state start { state_entry() { llResetTime(); } touch_start(integer num_detected) { state default; // stop it } state_exit() { llSay(0, "Stopped after " + (string)llGetTime() + " seconds."); } }
The state_exit handler is not called when an object is being deleted; all callbacks, handlers, sounds, etc, will be cleaned up automatically.
States vs. Global Variables
A state and a set of global variables can serve the same purpose, and each can be expressed in terms of the other. In general, using states over global variables allows immediate script state assumption without making comparisons–and the fewer comparisons a script makes, the more regular code statements it can run.
Example 1: States
default { // we don't need to define this as a state, because it's the default name. state_entry(){ // state_entry runs first upon entering this state. llSetColor(<0,0,0>, ALL_SIDES); // set object colour black. } touch_start(integer touched){ // touch_start waits for someone to click the object. state on; // change to state on. } } state on { // this isn't the default state, so we need to define it by saying "state on", rather than just "on". state_entry(){ // state_entry runs first upon entering this state. llSetColor(<1,1,1>, ALL_SIDES); // set object colour white. } touch_start(integer touched){ // touch_start waits for someone to click the object. state default; // change to state default. } }
In this example, we see what at first glance looks pretty complicated, but it’s actually fairly straightforward. The default state runs first, with state_entry being the first event run within that state. So the first thing the script does is what’s inside state_entry — in this case, it uses llSetColor to set the object’s colour black. The script then waits for input from the touch_start event.
When a user clicks on the object, touch_start is triggered, and the contents are run. In this case, it switches to the state on. From that point, the script does exactly what it did in default — it runs what’s in state_entry (which sets the colour to White) and then waits for input from touch_start.
Example 2: Global Variable
integer on = FALSE; // variables declared before a state mean they're global variables; // they will be recognized by all functions within the script. default { touch_start(integer touched) { if (on == TRUE) { // check to see if the variable "on" is TRUE. llSetColor(<0,0,0>, ALL_SIDES); // if it IS, set object colour black. on = FALSE; // then set "on" to be FALSE. } else { // we can do "else" here, because whether or not "on" is TRUE is a yes/no question. It either is or it's not. llSetColor(<1,1,1>, ALL_SIDES); // set object colour white. on = TRUE; // then set "on" to be TRUE. } } }
In this example, we only have a single state. Replacing the second one is a global variable, an integer named on, which starts with a value of FALSE. Upon running the script for the first time, nothing will happen — it will merely wait for someone to click on it. When clicked, the touch_start event will run, and an IF/ELSE statement will check the value of on.
If on is found to have a value of TRUE, the object’s color is set to black, and on‘s value is changed to FALSE. Otherwise, the inverse happens: the object color becomes white, and on becomes TRUE.
Conclusion:
Both these scripts perform the same basic function, but one will run more efficiently than the other: Example 1, using states, because it doesn’t have to ever check anything. Nothing needs to be compared, and no variables need to have their values set. By using states rather than a variable, the script’s logic path is linear–it just does one thing after another. In Example 2, the logic path branches, as the script checks and resets the values of its variable.
Not all scripts can necessarily be improved by using states rather than global variables and, frequently, the inverse is also true. Both states and global variables have their place, depending on the situation. As a rule, the less complex logic your script has, the faster it will run, and the better (less lag) it will be for the sim. If you’re doing something like the above example, using states is usually going to be simpler, as long as you have a good grasp of the order in which the different events are triggered. When writing a script, if you’re unsure, look up the respective entries to see when a given event will be triggered.
A: For all intents and purposes, no. There is a way to do it, though it is officially unsupported and may be broken at any time in the future.
A: While it won’t give a script error, it effectively does nothing. Calling a state from within itself does not cause a state change, and will not trigger state_entry.
default { state_entry() { llSay(0,"Hello, Avatar!"); } touch_start(integer total_number) { state restart_default; } } state restart_default { state_entry() { state default; } }
While this is a little clunky, it will allow you to restart the default state. – BurnmanBedlam
Credit to: Lslwiki.net (not working) with changes made for brevity and clarity.