Variables

Containers to hold, manipulate and store data in a program.

Variables are named containers for storing objects in code. They typically function as expressions, and use the simple set/get/delete behaviour.

Variables can be recognised by the {...} curly brackets around their name. Special types of variable are recognisable by a non-alphanumeric character @$#!? at the start of their name.

Some types of variable have special behaviour in certain situations, detailed below.

Value Variables

Most variables are value variables: these store a raw value. Value variables have the standard {variable} name pattern.

Normal (value) variables are local to the current trigger section.

Variables can be set and retrieved like a normal expression.

set {var} to 100
set {var} to {var} - 1
print {var}
if {var} is 6:
    set {var} to 20

Variables can be set to each other.

set {var} to 100
set {blob} to {var}
assert {blob} is {var}

Setting one will not affect the other - these are different containers for the same object.

set {var} to 100
set {blob} to {var}
assert {blob} is {var}
set {var} to 50
assert {blob} is not {var}
// blob = 100, var = 50

Using a variable as a return or an argument will pass its value.

The function that uses it will not change the original variable.

function first:
    trigger:
        set {var} to 5
        run second({var}) // var isn't changed
        assert {var} is 5

function second (number):
    trigger:
        set {number} to {number} + 1
        assert {number} is 6

The same is true for lambdas, which will also freeze the variable value.

set {var} to 5
run a new runnable:
    set {var} to 3 // {var} here is local to this lambda
assert {var} is 5

Atomic Variables

Atomic variables use the {@variable} name pattern. They are reference variables, and store a reference to the object rather than the object's value itself.

In normal code, atomic variables function exactly the same as regular value variables.

set {@var} to 100
set {@var} to {@var} - 1
print {@var}
if {@var} is 6:
    set {@var} to 20

Remember: a variable named {@var} is different from a variable named {var}.

set {@var} to 1 // atomic
set {var} to 2 // normal
assert {var} is not {@var}

Atomics are designed for being passed to lambdas or background functions. When passed as an argument to something, the passed argument is linked to the original.

Changing the passed copy will change the original.

set {@var} to 5
run a new runnable:
    set {@var} to 3 // changes the original copy
assert {@var} is 3

This also works in function arguments, if and only if the function uses a special atomic parameter.

function first:
    trigger:
        set {@var} to 5
        run second({@var}) // var is changed
        assert {@var} is 6

function second (@number): // @name = atomic parameter
    trigger:
        set {@number} to {@number} + 1 // changes original copy
        assert {@number} is 6

If a function does not take an atomic parameter, the value will be extracted. This means that the original copy will not be changed.

function first:
    trigger:
        set {@var} to 5
        run second({@var}) // var is not changed
        assert {@var} is 4

function second (number): // non-atomic parameter,
    trigger:
        set {number} to {number} + 1 // not linked to original copy
        assert {number} is 6

The reverse is also true: passing a non-atomic argument to an atomic parameter will wrap the argument as an atomic inside that function, but will not alter the original copy.

function first:
    trigger:
        set {var} to 5 // not atomic
        run second({var}) // var is not changed
        assert {var} is 4

function second (@number): // @name = atomic parameter
    trigger:
        set {@number} to {@number} + 1 // not linked to original copy
        assert {@number} is 6

Atomics cannot be passed as return values: their value will always be extracted.

function first:
    trigger:
        set {var} to second(1) // return is not atomic
        set {@var} to second(1)

function second (@number): // @name = atomic parameter
    trigger:
        return {@number} // value is extracted, NOT atomic

For advanced users, the raw atomic handle can be extracted using the get_atomic_literal(object) function from the skript namespace.

This would allow an atomic variable to be returned from a function secretly, but re-wrapping it for use would be complex.

This will be an AtomicVariable object and may be difficult to manipulate.

Thread-Local Variables

Thread-local variables use the {_variable} name pattern. They are reference variables.

Thread-local variables are accessible anywhere on the current process (thread). This includes other functions and lambdas. Thread-local variables are not accessible from other threads.

A thread/process is like a queue of instructions, executed in order. Using the wait or sleepeffect will pause the thread.

The run ... in the background effect can be used to create a different, branching process that will run at the same time as the current one.

In normal code, thread-local variables function exactly the same as regular value variables.

set {_var} to 100
set {_var} to {_var} - 1
print {_var}
if {_var} is 6:
    set {_var} to 20

Remember: a variable named {_var} is different from a variable named {var}.

set {_var} to 1 // thread-local
set {var} to 2 // normal
assert {var} is not {_var}

Thread-local variables make it easy to pass data between triggers that are guaranteed to be executed in the same process.

function first:
    trigger:
        set {_var} to 10 // thread-local
        set {var} to 5 // normal (local to this trigger)
        run second()

function second:
    trigger:
        assert {var} is null
        assert {_var} is 10 // value is kept from before call

Thread-local variables are atomic, and any use alters the same copy of the variable.

function first:
    trigger:
        set {_var} to 10 // thread-local
        assert {_var} is 10
        run second() // value is changed in this function
        assert {_var} is 5

function second:
    trigger:
        assert {_var} is 10
        set {_var} to 5
        assert {_var} is 5

Thread-local variables can be accessed and changed from lambdas, unlike regular variables.

function my_function:
    trigger:
        set {_var} to 10 // thread-local
        set {thing} to a new runnable:
            assert {_var} is 10
            set {_var} to 5
            assert {_var} is 5
        assert {_var} is 10
        run {thing} // value is changed by the runnable
        assert {_var} is 5

However, lambdas or functions run in the background will not be able to access the value.

This is because background processes are run on a different thread, so their thread-local {_variables} are different.

function my_function:
    trigger:
        set {_var} to 10 // thread-local
        set {thing} to a new runnable:
            assert {_var} is null
            set {_var} to 5 // does NOT update the other _var
            assert {_var} is 5
        assert {_var} is 10
        run {thing} in the background // run on a DIFFERENT thread
        wait 10 milliseconds // wait for other thread to finish
        assert {_var} is 10 // _var is unchanged

Events and other entry-points are triggered on a new thread, so there is no cross-contamination between processes.

on load:
    trigger:
        assert {_var} is null // not set yet
        set {_var} to 10 // thread-local
        assert {_var} is 10

on load: // different event trigger, so run on different thread
    trigger:
        assert {_var} is null // not set on THIS thread
        set {_var} to 10 // thread-local
        assert {_var} is 10

The skript namespace has a special function to transfer thread-local variables to a different thread.

Transferring these will copy the variables, so the original copy will not be updated by changes.

function my_function:
    trigger:
        set {thread} to the current process
        set {_var} to 10 // thread-local
        set {thing} to a new runnable:
            run copy_threadlocals_from({thread})
            assert {_var} is 10 // copied from other _var
            set {_var} to 5 // does NOT update the other _var
            assert {_var} is 5
        assert {_var} is 10
        run {thing} in the background // run on a DIFFERENT thread
        assert {_var} is 10 // _var is unchanged, the COPY was changed

Global Variables

Global variables use the {!variable} name pattern. They are reference variables. These are most similar to the normal variables from original Skript.

Any copy of a global {!variable} anywhere on any process will access the same value.

Unlike in original Skript, global variables are not persistent across restarts (by default.)

In normal code, globals variables function exactly the same as regular value variables.

set {!var} to 100
set {!var} to {!var} - 1
print {!var}
if {!var} is 6:
    set {!var} to 20

Remember: a variable named {!var} is different from a variable named {var}.

set {!var} to 1 // global
set {var} to 2 // normal
assert {var} is not {!var}

Global variables make it easy to pass data between triggers and entire scripts.

function first:
    trigger:
        set {!var} to 10 // thread-local
        set {var} to 5 // normal (local to this trigger)
        run second()

function second:
    trigger:
        assert {var} is null
        assert {!var} is 10 // value is kept from before call

Global variables are atomic and can be accessed and changed from other triggers, lambdas and different threads, unlike regular variables.

function my_function:
    trigger:
        set {!var} to 10 // thread-local
        set {thing} to a new runnable:
            assert {!var} is 10
            set {!var} to 5
            assert {!var} is 5
        assert {!var} is 10
        run {thing} in the background // value is changed by the runnable
        wait 10 milliseconds // wait for other thread to finish
        assert {!var} is 5

Global variables are accessible from any process.

on load:
    trigger:
        assert {!var} is null // not set yet
        set {!var} to 10 // global
        assert {!var} is 10

on load: // different event trigger, so run on different thread
    trigger:
        wait 10 milliseconds // wait for previous trigger to finish
        assert {!var} is 10 // set everywhere
        set {!var} to 5 al
        assert {!var} is 5

Last updated