Table of Contents

Start Functions Events Constants Types Language Articles

Keyword: state

This keyword has two uses: as the header of a state, it's used to declare a state other than default; as a regular statement, it's used to switch to a state.

As a declaration, the syntax for a whole state is:

state <state_name>
{
    <event_declaration>

    <event_declaration>

    ...
}

where <state_name> is the identifier that will be used in state statements. As an exception, when declaring state default we don't add the keyword state in front of it. Additionally, the default state must always appear before any other state declaration.

States can't be empty; each state must contain at least one event declaration. There can be as many event declarations as necessary, but there can't be two declarations of the same event within the same state.

No globals may appear between states; globals are only allowed before the default state.

state statement

The syntax as a statement is:

state <state_name>;

where <state_name> can be default or the name of any existing state. This statement causes a state switch, interrupting execution of the current function or event.

When a state switch statement is found, a return statement is automatically inserted at that point in the code, and when the current event finalizes, if the state is NOT the current state, the state switch happens. This triggers two events: first, the state_exit event for this state; then, the state_entry event for the state changed to.

A state switch statement that specifies the same state where it is executed, causes the interruption of the current event or function, but does not cause any other effects: neither the state_exit nor the state_entry events of the current state are executed.

State statements aren't designed to be used within user-defined functions, only within events. It's possible to use state statements in user functions, but with caveats; see hacks below.

Script execution always starts in the default state; other states aren't executed unless they are switched to.

Notes

Examples

Here's an example of a simple drawer that moves along the local X axis, implemented using states:

State-example.lsl
// State declaration of the default state (mandatory, always first)
default
{
    touch_end(integer n)
    {
        // State switch statement that switches to the 'open' state
        state open;
    }
}

// State declaration of an additional state we've called 'open'
state open
{
    state_entry()
    {
        llSetPos(llGetLocalPos() - <0.3, 0, 0>);
    }

    touch_end(integer n)
    {
        // State switch statement that switches to the default state
        state default;
    }

    state_exit()
    {
        llSetPos(llGetLocalPos() + <0.3, 0, 0>);
    }
}

The default state just waits for a click release to switch to the open state. When entering the open state, the drawer moves 0.3 metres along x, then waits for a click release. When that happens, it switches to the default state. The state switch statement triggers the state_exit event, which moves the drawer back to its original position before actually switching.

Hacks

State switches are designed to be used only within event declarations. They work by signalling the state change and adding a return statement at the point where the state switch statement is. The signal is checked when the current event terminates. That creates the illusion that the state switch acts as an immediate jump to another state, stopping execution of the current code.

This return trick, however, does not work in user-defined functions, because it would return to the caller, rather that completely interrupting the currently running event. Since it doesn't work in these circumstances, it can't be properly supported in UDFs easily, and rather than adding the necessary complexity for adding proper support for it, LL chose to not support it in UDFs at all, and implemented a check in the compiler to ensure that there is no state switch statement within a user function.

However, due to a bug, the check fails when the statement is somewhere within the block associated to:

thus allowing us to exploit that bug and use state statements even within user functions.

For example:

my_function()
{
    // You can't use a switch statement here.
    if (TRUE)
    {
        // You can use any statements here, including
        // state switch statements.
    }
    // You can't use a switch statement here.
}

But note that, against normal expectations, this will not jump to the specified state directly. Keep in mind that it works by flagging the state switch and inserting a return statement; this means that it will return to the caller of the function, which may be another function or an event. Only when the event within which these functions are executed terminates, will the state_exit event execute, if it exists, and then the actual state switch will happen.

To illustrate this, consider the following example:

my_function()
{
    if (TRUE)
    {
        state another;
        llOwnerSay("You can't see me");
    }
}

default
{
    state_entry()
    {
        my_function();
        llOwnerSay("Oops, this is visible!");
    }
}

state another
{
    state_entry()
    {
        llOwnerSay("Yep, it changed state alright");
    }
}

The output of that script will be "Oops, this is visible!" followed by "Yep, it changed state alright". The line that displays "You can't see me" isn't executed, because the state statement causes a return before reaching it. The line that displays "Oops, this is visible!", however, is executed when the function returns, therefore it will still be executed after the state switch statement. When the state_entry event finishes, the system will notice that there's a state switch pending, and change to state another.

When using this hack, it's best to use it within a function that does not return any values. If the function returns a value, the outcome depends on the virtual machine used and on the type returned.

Under Mono, it will return the default value for the corresponding type, that is 0 for integer, 0.0 for float, "" for string, (key)"" for key, ZERO_VECTOR for vector, ZERO_ROTATION for rotation and [] for list.

Under LSO, attempting to use the return value of a function that returns type string, key or list and executes a state switch, will cause a Bounds Check Error and the script will stop. If the return type is rotation, the returned value used will be <0, 0, 0, 0> (note that that's not the same as ZERO_ROTATION, which is <0, 0, 0, 1>). For the remaining types, the value returned will be the same as in Mono.

If two state switch statements are executed as a consequence of this hack (for example, one in a nested function, and one in the caller), only the last one takes effect, unless it's to the current state, in which case the previous one prevails.