<<<<<<< HEAD
Auxlua
Datums
DM datums are treated as lua userdata, and can be stored in fields. Due to fundamental limitations in lua, userdata is inherently truthy. Since datum userdata can correspond to a deleted datum, which would evaluate to null in DM, the function datum:is_null() is provided to offer a truthiness test consistent with DM.
Keep in mind that BYOND can't see that a datum is referenced in a lua field, and will garbage collect it if it is not referenced anywhere in DM.
datum:get_var(var)
Equivalent to DM's datum.var
datum:set_var(var, value)
Equivalent to DM's datum.var = value
datum:call_proc(procName, ...)
Equivalent to DM's datum.procName(...)
datum:is_null()
This function is used to evaluate the truthiness of a DM var. The lua statement if datum:is_null() then is equivalent to the DM statement if(datum).
datum.vars
Returns a userdatum that allows you to access and modifiy the vars of a DM datum by index. datum.vars.foo is equivalent to datum:get_var("foo"), while datum.vars.foo = bar is equivalent to datum:set_var("foo", bar)
Lists
In order to allow lists to be modified in-place across the DM-to-lua language barrier, lists are treated as userdata. Whenever running code that expects a DM value, auxlua will attempt to convert tables into lists.
List references are subject to the same limitations as datum userdata, but you are less likely to encounter these limitations for regular lists.
Some lists (vars, contents, overlays, underlays, vis_contents, and vis_locs) are inherently attached to datums, and as such, their corresponding userdata contains a weak reference to the containing datum. Use list:is_null to validate these types of lists.
list.len
Equivalent to DM's list.len
list:get(index)
Equivalent to DM's list[index]
list:set(index, value)
Equivalent to DM's list[index] = value
list:add(value)
Equivalent to DM's list.Add(value)
list:remove(value)
Equivalent to DM's list.Remove(value)
list:to_table()
Converts a DM list into a lua table.
list:of_type(type_path)
Will extract only values of type type_path.
list:is_null()
A similar truthiness test to datum:is_null(). This function only has the possibility of returning false for lists that are inherently attached to a datum (vars, contents, overlays, underlays, vis_contents, and vis_locs).
list.entries
Returns a userdatum that allows you to access and modifiy the entries of the list by index. list.entries.foo is equivalent to list:get("foo"), while list.entries.foo = bar is equivalent to list:set("foo", bar)
The dm table
The dm table consists of the basic hooks into the DM language.
dm.state_id
The address of the lua state in memory. This is a copy of the internal value used by auxlua to locate the lua state in a global hash map. state_id is a registry value that is indirectly obtained using the dm table's __index metamethod.
dm.global_proc(proc, ...)
Calls the global proc /proc/[proc] with ... as its arguments.
dm.world
A reference to DM's world, in the form of datum userdata. This reference is always valid, since world always exists.
Due to limitations inherent in the wrapper functions used on tgstation, world:set_var and world:call_proc will raise an error.
dm.global_vars
A reference to DM's global, in the form of datum userdata. Subject to the same limitations as dm.world
dm.usr
A weak reference to DM's usr. As a rule of thumb, this is a reference to the mob of the client who triggered the chain of procs leading to the execution of Lua code. The following is a list of what usr is for the most common ways of executing Lua code:
- For resumes and awakens, which are generally executed by the MC,
usris (most likely) null. SS13.waitqueues a resume, which gets executed by the MC. Therefore,usris null afterSS13.waitfinishes.- For chunk loads,
usris generally the current mob of the admin that loaded that chunk. - For function calls done from the Lua editor,
usris the current mob of the admin calling the function. SS13.register_signalcreates a/datum/callbackthat gets executed by theSEND_SIGNALmacro for the corresponding signal. As such,usris the mob that triggered the chain of procs leading to the invocation ofSEND_SIGNAL.
Execution Limit
In order to prevent freezing the server with infinite loops, auxlua enforces an execution limit, defaulting to 100ms. When a single lua state has been executing for longer than this limit, it will eventually stop and produce an error.
To avoid exceeding the execution limit, call sleep() or coroutine.yield() before the execution limit is reached.
over_exec_usage(fraction = 0.95)
This function returns whether the current run of the Lua VM has executed for longer than the specified fraction of the execution limit. You can use this function to branch to a call to sleep() or coroutine.yield() to maximize the amount of work done in a single run of the Lua VM. If nil, fraction will default to 0.95, otherwise, it will be clamped to the range [0, 1].
Task management
The Lua Scripting subsystem manages the execution of tasks for each Lua state. A single fire of the subsystem behaves as follows:
- All tasks that slept since the last fire are resumed in the order they slept.
- For each queued resume, the corresponding task is resumed.
sleep()
Yields the current thread, scheduling it to be resumed during the next fire of SSlua. Use this function to prevent your Lua code from exceeding its allowed execution duration. Under the hood, sleep performs the following:
- Sets the
sleep_flag - Calls
coroutine.yield() - Clears the sleep flag when determining whether the task slept or yielded
- Ignores the return values of
coroutine.yield()once resumed
The SS13 package
The SS13 package contains various helper functions that use code specific to tgstation.
SS13.state
A reference to the state datum (/datum/lua_state) handling this Lua state.
SS13.get_runner_ckey()
The ckey of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
SS13.get_runner_client()
Returns the client of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
SS13.global_proc
=======
Objects
Datums, lists, typepaths, static appearances, and some other objects are represented in Luau as userdata. Certain operations can be performed on these types of objects.
Common metamethods
The following metamethods are defined for all objects.
__tostring(): string
Returns the string representation of the object. This uses BYOND's internal string conversion function.
__eq(other: any): boolean
Compare the equality of two objects. While passing the same object into luau twice will return two references to the same userdata, some DM projects may override the equality operator using an __operator== proc definition.
Datum-like Objects
Datum-like objects include datums themselves, clients (if they have not been redefined to be children of /datum), static appearances, and the world.
__index(index: string): any
Access the member specified by index.
If index is a valid var for the object, the index operation will return that var's value.
If the var getting wrapper proc is set, the operation will instead call that proc with the arguments (object, index).
For objects other than static appearances, if index is a valid proc for the object, the operation will return a wrapper for that proc that can be invoked using call syntax (e.g. object:proc(...arguments)). If the object proc calling wrapper is set, calling the returned function will instead call the wrapper proc with the arguments (object, proc, {...arguments}). Note that vars will be shadowed by procs with the same name. To work around this, use the dm.get_var function.
__newindex(index: string, value: any): ()
Set the var specified by index to value, if that var exists on the object.
If the var setting wrapper proc is set, the operation will instead call that proc with the arguments (object, index, value).
Lists
Lists are syntactically similar to tables, with one crucial difference. Unlike tables, numeric indices must be non-zero integers within the bounds of the list.
__index(index: any): any
Read the list at index. This works both for numeric indices and assoc keys.
Vars lists cannot be directly read this way if the var getting wrapper proc is set.
__newindex(index: any, value: any): any
Write value to the list at index. This works both for writing numeric indices and assoc keys.
Vars lists cannot be directly written this way if the var setting wrapper proc is set.
__len(): integer
Returns the length of the list, similarly to the length builtin in DM.
Iteration
Lists support Luau's generalized iteration. Iteration this way returns pairs of numeric indices and list values.
For example, the statement for _, v in L do is logically equivalent to the DM statement for(var/v in L).
Global Fields and Modules
In addition to the full extent of Luau's standard library modules, some extra functions and modules have been added.
Global-Level Fields
sleep(): ()
Yields the active thread, without worrying about passing data into or out of the state.
Threads yielded this way are placed at the end of a queue. Call the awaken hook function from DM to execute the thread at the front of the queue.
loadstring(code: string): function
Luau does not inherently include the loadstring function common to a number of other versions of lua. This is an effective reimplementation of loadstring.
print(...any): ()
Calls the print wrapper with the passed in arguments. Raises an error if no print wrapper is set, as that means there is nothing to print with.
_state_id: integer
The handle to the underlying luau state in the dreamluau binary.
_exec
The _exec module includes volatile fields related to the current execution context.
_next_yield_index: integer
When yielding a thread with coroutine.yield, it will be inserted into an internal table at the first open integer index.
This field corresponds to that first open integer index.
_limit: integer?
If set, the execution limit, rounded to the nearest millisecond.
_time: integer
The length of successive time luau code has been executed, including recursive calls to DM and back into luau, rounded to the nearest millisecond.
dm
The dm module includes fields and functions for basic interaction with DM.
world: userdata
A static reference to the DM world.
global_vars: userdata
A static reference that functions like the DM keyword global. This can be indexed to read/write global vars.
global_procs: table
A table that can be indexed by string for functions that wrap global procs.
Due to BYOND limitations, attempting to index an invalid proc returns a function logically equivalent to a no-op.
get_var(object: userdata, var: string): function
Reads the var var on object. This function can be used to get vars that are shadowed by procs declared with the same name.
new(path: string, ...any): userdata
Creates an instance of the object specified by path, with ... as its arguments.
If the "new" wrapper is set, that proc will be called instead, with the arguments (path, {...}).
is_valid_ref(ref: any): boolean
Returns true if the value passed in corresponds to a valid reference-counted DM object.
usr: userdata?
Corresponds to the DM var usr.
list
The list module contains wrappers for the builtin list procs, along with several other utility functions for working with lists.
add(list: userdata, ...any): ()
Logically equivalent to the DM statement list.Add(...).
copy(list: userdata, start?: integer, end?: integer): userdata
Logically equivalent to the DM statement list.Copy(start, end).
cut(list: userdata, start?: integer, end?: integer): userdata
Logically equivalent to the DM statement list.Cut(start, end).
find(list: userdata, item: any, start?: integer, end?: integer): integer
Logically equivalent to the DM statement list.Find(item, start, end).
insert(list: userdata, index: integer, ...any): integer
Logically equivalent to the DM statement list.Insert(item, ...).
join(list: userdata, glue: string, start?: integer, end?: integer): string
Logically equivalent to the statement list.Join(glue, start, end).
remove(list: userdata, ...any): integer
Logically equivalent to the DM statement list.Remove(...).
remove_all(list: userdata, ...any): integer
Logically equivalent to the DM statement list.RemoveAll(...).
splice(list: userdata, start?: integer, end?: integer, ...any): ()
Logically equivalent to the DM statement list.Splice(start, end, ...).
swap(list: userdata, index_1: integer, index_2: integer): ()
Logically equivalent to the DM statement list.Swap(index_1, index_2).
to_table(list: userdata, deep?: boolean): table
Creates a table that is a copy of list. If deep is true, to_table will be called on any lists inside that list.
from_table(table: table): userdata
Creates a list that is a copy of table. This is not strictly necessary, as tables are automatically converted to lists when passed back into DM, using the same internal logic as from_table.
filter(list: userdata, path: string): userdata
Returns a copy of list, containing only elements that are objects descended from path.
pointer
The pointer module contains utility functions for interacting with pointers.
Keep in mind that passing DM pointers into luau and manipulating them in this way can bypass wrapper procs.
read(pointer: userdata): any
Gets the underlying data the pointer references.
write(pointer: userdata, value: any): ()
Writes value to the underlying data the pointer references.
unwrap(possible_pointer: any): any
If possible_pointer is a pointer, reads it. Otherwise, it is returned as-is.
The SS13 package
The SS13 package contains various helper functions that use code specific to tgstation.
SS13.state
A reference to the state datum (/datum/lua_state) handling this Lua state.
SS13.get_runner_ckey()
The ckey of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
SS13.get_runner_client()
Returns the client of the user who ran the lua script in the current context. Can be unreliable if accessed after sleeping.
SS13.global_proc
4b4ae0958fA wrapper for the magic string used to tellWrapAdminProcCallto call a global proc. For instance,/datum/callbackmust be instantiated withSS13.global_procas its first argument to specify that it will be invoking a global proc. The following example declares a callback which will execute the global procto_chat:
local callback = SS13.new("/datum/callback", SS13.global_proc, "to_chat", dm.world, "Hello World")
<<<<<<< HEAD
SS13.istype(thing, type)
Equivalent to the DM statement istype(thing, text2path(type)).
SS13.new(type, ...)
Instantiates a datum of type type with ... as the arguments passed to /proc/_new
The following example spawns a singularity at the caller's current turf:
SS13.new("/obj/singularity", dm.global_proc("_get_step", dm.usr, 0))
SS13.new_untracked(type, ...)
Works exactly like SS13.new but it does not store the value to the lua state's references list variable. This means that the variable could end up deleted if nothing holds a reference to it.
SS13.is_valid(datum)
=======
SS13.istype(thing, type)
Equivalent to the DM statement istype(thing, text2path(type)).
SS13.new(type, ...)
An alias for dm.new
SS13.is_valid(datum)
4b4ae0958fCan be used to determine if the datum passed is not nil, not undefined and not qdel'd all in one. A helper function that allows you to check the validity from only one function. Example usage:
local datum = SS13.new("/datum")
<<<<<<< HEAD
dm.global_proc("qdel", datum)
=======
dm.global_procs.qdel(datum)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
print(SS13.is_valid(datum)) -- false
local null = nil
print(SS13.is_valid(null)) -- false
local datum = SS13.new("/datum")
print(SS13.is_valid(datum)) -- true
<<<<<<< HEAD
SS13.type(string)
Converts a string into a type. Equivalent to doing dm.global_proc("_text2path", "/path/to/type")
SS13.qdel(datum)
Deletes a datum. You shouldn't try to reference it after calling this function. Equivalent to doing dm.global_proc("qdel", datum)
SS13.await(thing_to_call, proc_to_call, ...)
=======
SS13.type(string)
Converts a string into a typepath. Equivalent to doing dm.global_proc("_text2path", "/path/to/type")
SS13.qdel(datum)
Deletes a datum. You shouldn't try to reference it after calling this function. Equivalent to doing dm.global_proc("qdel", datum)
SS13.await(thing_to_call, proc_to_call, ...)
4b4ae0958fCallsproc_to_callonthing_to_call, with...as its arguments, and sleeps until that proc returns. Returns two return values - the first is the return value of the proc, and the second is the message of any runtime exception thrown by the called proc. The following example calls and awaits the return ofpoll_ghost_candidates:
local ghosts, runtime = SS13.await(SS13.global_proc, "poll_ghost_candidates", "Would you like to be considered for something?")
<<<<<<< HEAD
SS13.wait(time, timer)
=======
SS13.wait(time, timer)
4b4ae0958fWaits for a number of seconds specified with thetimeargument. You can optionally specify a timer subsystem using thetimerargument.
Internally, this function creates a timer that will resume the current task after time seconds, then yields the current task by calling coroutine.yield with no arguments and ignores the return values. If the task is prematurely resumed, the timer will be safely deleted.
<<<<<<< HEAD
SS13.register_signal(datum, signal, func, make_easy_clear_function)
=======
SS13.register_signal(datum, signal, func)
4b4ae0958fRegisters the Lua functionfuncas a handler forsignalondatum.
Like with signal handlers written in DM, Lua signal handlers should not sleep (either by calling sleep or coroutine.yield).
<<<<<<< HEAD
If make_easy_clear_function is truthy, a member function taking no arguments will be created in the SS13 table to easily unregister the signal handler.
This function returns the /datum/callback created to call func from DM.
This function returns whether the signal registration was successful.
The following example defines a function which will register a signal that makes target make a honking sound any time it moves:
function honk(target)
SS13.register_signal(target, "movable_moved", function(source)
<<<<<<< HEAD
dm.global_proc("playsound", target, "sound/items/bikehorn.ogg", 100, true)
=======
dm.global_procs.playsound(target, "sound/items/bikehorn.ogg", 100, true)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
end)
end
<<<<<<< HEAD
SS13.unregister_signal(datum, signal, callback)
Unregister a signal previously registered using SS13.register_signal. callback should be a datum/callback previously returned by SS13.register_signal. If callback is not specified, ALL signal handlers registered on datum for signal will be unregistered.
SS13.set_timeout(time, func)
=======
NOTE: if func is an anonymous function declared inside the call to SS13.register_signal, it cannot be referenced in order to unregister that signal with SS13.unregister_signal
SS13.unregister_signal(datum, signal, func)
Unregister a signal previously registered using SS13.register_signal. func must be a function for which a handler for the specified signal has already been registered. If func is nil, all handlers for that signal will be unregistered.
SS13.set_timeout(time, func)
4b4ae0958fCreates a timer which will executefuncaftertimeseconds.funcshould not expect to be passed any arguments, as it will not be passed any. UnlikeSS13.wait,SS13.set_timeoutdoes not yield or sleep the current task, making it suitable for use in signal handlers forSS13.register_signal
The following example will output a message to chat after 5 seconds:
SS13.set_timeout(5, function()
<<<<<<< HEAD
dm.global_proc("to_chat", dm.world, "Hello World!")
end)
SS13.start_loop(time, amount, func)
======= dm.global_procs.to_chat(dm.world, "Hello World!") end)
## SS13.start_loop(time, amount, func)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
Creates a timer which will execute `func` after `time` **seconds**. `func` should not expect to be passed any arguments, as it will not be passed any. Works exactly the same as `SS13.set_timeout` except it will loop the timer `amount` times. If `amount` is set to -1, it will loop indefinitely. Returns a number value, which represents the timer's id. Can be stopped with `SS13.end_loop`
Returns a number, the timer id, which is needed to stop indefinite timers.
The following example will output a message to chat every 5 seconds, repeating 10 times:
```lua
SS13.start_loop(5, 10, function()
<<<<<<< HEAD
dm.global_proc("to_chat", dm.world, "Hello World!")
=======
dm.global_procs.to_chat(dm.world, "Hello World!")
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
end)
The following example will output a message to chat every 5 seconds, until SS13.end_loop(timerid) is called:
local timerid = SS13.start_loop(5, -1, function()
<<<<<<< HEAD
dm.global_proc("to_chat", dm.world, "Hello World!")
end)
SS13.end_loop(id)
======= dm.global_proc.to_chat(dm.world, "Hello World!") end)
## SS13.end_loop(id)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
Prematurely ends a loop that hasn't ended yet, created with `SS13.start_loop`. Silently fails if there is no started loop with the specified id.
The following example will output a message to chat every 5 seconds and delete it after it has repeated 20 times:
```lua
local repeated_amount = 0
-- timerid won't be in the looping function's scope if declared before the function is declared.
local timerid
timerid = SS13.start_loop(5, -1, function()
<<<<<<< HEAD
dm.global_proc("to_chat", dm.world, "Hello World!")
=======
dm.global_procs.to_chat(dm.world, "Hello World!")
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
repeated_amount += 1
if repeated_amount >= 20 then
SS13.end_loop(timerid)
end
end)
<<<<<<< HEAD
SS13.stop_all_loops()
Stops all current running loops that haven't ended yet. Useful in case you accidentally left a indefinite loop running without storing the id anywhere.
SS13.stop_tracking(datum)
Stops tracking a datum that was created via SS13.new so that it can be garbage collected and deleted without having to qdel. Should be used for things like callbacks and other such datums where the reference to the variable is no longer needed.
Internal globals
Auxlua defines several registry values for each state. Note that there is no way to access registry values from lua code.
sleep_flag
This flag is used to designate that a yielding task should be put in the sleep queue instead of the yield table. Once auxlua determines that a task should sleep, sleep_flag is cleared.
sleep_queue
A sequence of threads, each corresponding to a task that has slept. When calling /proc/__lua_awaken, auxlua will dequeue the first thread from the sequence and resume it.
yield_table
A table of threads, each corresponding to a coroutine that has yielded. When calling /proc/__lua_resume, auxlua will look for a thread at the index specified in the index argument, and resume it with the arguments specified in the arguments argument.
task_info
A table of key-value-pairs, where the keys are threads, and the values are tables consisting of the following fields:
- name: A string containing the name of the task
- status: A string, either "sleep" or "yield"
- index: The task's index in
sleep_queueoryield_table=======
SS13.stop_all_loops()
Stops all current running loops that haven't ended yet. Useful in case you accidentally left a indefinite loop running without storing the id anywhere.