Merge branch 'master' of https://github.com/tgstation/tgstation into pulls-tg-to-fix-shit

This commit is contained in:
Waterpig
2024-09-08 00:59:39 +02:00
1728 changed files with 273718 additions and 2237 deletions
+304
View File
@@ -1,3 +1,4 @@
<<<<<<< HEAD
# Auxlua
---
@@ -145,6 +146,230 @@ The ckey of the user who ran the lua script in the current context. Can be unrel
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
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
A wrapper for the magic string used to tell `WrapAdminProcCall` to call a global proc.
For instance, `/datum/callback` must be instantiated with `SS13.global_proc` as its first argument to specify that it will be invoking a global proc.
The following example declares a callback which will execute the global proc `to_chat`:
@@ -152,6 +377,7 @@ The following example declares a callback which will execute the global proc `to
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))`.
@@ -166,11 +392,24 @@ SS13.new("/obj/singularity", dm.global_proc("_get_step", dm.usr, 0))
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)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
Can 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:
```lua
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
@@ -180,6 +419,7 @@ 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")`
@@ -187,6 +427,15 @@ Converts a string into a type. Equivalent to doing `dm.global_proc("_text2path",
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, ...)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
Calls `proc_to_call` on `thing_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 of `poll_ghost_candidates`:
@@ -194,59 +443,104 @@ The following example calls and awaits the return of `poll_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)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
Waits for a number of **seconds** specified with the `time` argument. You can optionally specify a timer subsystem using the `timer` argument.
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)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
Registers the Lua function `func` as a handler for `signal` on `datum`.
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.
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
The following example defines a function which will register a signal that makes `target` make a honking sound any time it moves:
```lua
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)
>>>>>>> 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. Unlike `SS13.wait`, `SS13.set_timeout` does not yield or sleep the current task, making it suitable for use in signal handlers for `SS13.register_signal`
The following example will output a message to chat after 5 seconds:
```lua
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:
```lua
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
@@ -254,7 +548,11 @@ 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)
@@ -262,6 +560,7 @@ timerid = SS13.start_loop(5, -1, function()
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.
@@ -294,3 +593,8 @@ A table of key-value-pairs, where the keys are threads, and the values are table
- name: A string containing the name of the task
- status: A string, either "sleep" or "yield"
- index: The task's index in `sleep_queue` or `yield_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.
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
+22
View File
@@ -1,3 +1,15 @@
<<<<<<< HEAD
=======
/proc/wrap_lua_get_var(datum/thing, var_name)
SHOULD_NOT_SLEEP(TRUE)
if(thing == world)
return world.vars[var_name]
if(ref(thing) == "\[0xe000001\]") //This weird fucking thing is like global.vars, but it's not a list and vars is not a valid index for it and I really don't fucking know.
return global.vars[var_name]
if(thing.can_vv_get(var_name))
return thing.vars[var_name]
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
/proc/wrap_lua_set_var(datum/thing_to_set, var_name, value)
SHOULD_NOT_SLEEP(TRUE)
thing_to_set.vv_edit_var(var_name, value)
@@ -11,8 +23,11 @@
ret = WrapAdminProcCall(thing_to_call, proc_name, arguments)
else
ret = HandleUserlessProcCall("lua", thing_to_call, proc_name, arguments)
<<<<<<< HEAD
if(isdatum(ret))
SSlua.gc_guard += ret
=======
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return ret
/proc/wrap_lua_global_proc_call(proc_name, list/arguments)
@@ -24,8 +39,11 @@
ret = WrapAdminProcCall(GLOBAL_PROC, proc_name, arguments)
else
ret = HandleUserlessProcCall("lua", GLOBAL_PROC, proc_name, arguments)
<<<<<<< HEAD
if(isdatum(ret))
SSlua.gc_guard += ret
=======
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return ret
/proc/wrap_lua_print(state_id, list/arguments)
@@ -38,6 +56,10 @@
if(!target_state)
return
var/print_message = jointext(arguments, "\t")
<<<<<<< HEAD
var/result = list("status" = "print", "param" = print_message)
=======
var/result = list("status" = "print", "message" = print_message)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
INVOKE_ASYNC(target_state, TYPE_PROC_REF(/datum/lua_state, log_result), result, TRUE)
log_lua("[target_state]: [print_message]")
+19
View File
@@ -3,20 +3,30 @@
#define PROMISE_REJECTED 2
/**
<<<<<<< HEAD
* Auxtools hooks act as "set waitfor = 0" procs. This means that whenever
* a proc directly called from auxtools sleeps, the hook returns with whatever
=======
* Byondapi hooks act as "set waitfor = 0" procs. This means that whenever
* a proc directly called from an external library sleeps, the hook returns with whatever
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
* the called proc had as its return value at the moment it slept. This may not
* be desired behavior, so this datum exists to wrap these procs.
*
* Some procs that don't sleep could take longer than the execution limit would
* allow for. We can wrap these in a promise as well.
*/
<<<<<<< HEAD
/datum/auxtools_promise
=======
/datum/promise
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
var/datum/callback/callback
var/return_value
var/runtime_message
var/status = PROMISE_PENDING
<<<<<<< HEAD
/datum/auxtools_promise/New(...)
callback = CALLBACK(arglist(args))
perform()
@@ -24,6 +34,15 @@
/datum/auxtools_promise/proc/perform()
set waitfor = 0
sleep() //In case we have to call a super-expensive non-sleeping proc (like getFlatIcon)
=======
/datum/promise/New(...)
if(!usr)
usr = GLOB.lua_usr
callback = CALLBACK(arglist(args))
INVOKE_ASYNC(src, PROC_REF(perform))
/datum/promise/proc/perform()
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
try
return_value = callback.Invoke()
status = PROMISE_RESOLVED
+152
View File
@@ -16,6 +16,15 @@
/// If set, we will force the editor to look at this chunk
var/force_view_chunk
<<<<<<< HEAD
=======
/// If set, we will force the script input to be this
var/force_input
/// If set, the latest code execution performed from the editor raised an error, and this is the message from that error
var/last_error
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
/datum/lua_editor/New(state, _quick_log_index)
. = ..()
if(state)
@@ -37,6 +46,7 @@
/datum/lua_editor/ui_state(mob/user)
return GLOB.debug_state
<<<<<<< HEAD
/datum/lua_editor/ui_static_data(mob/user)
var/list/data = list()
data["documentation"] = file2text('code/modules/admin/verbs/lua/README.md')
@@ -47,27 +57,67 @@
/datum/lua_editor/ui_data(mob/user)
var/list/data = list()
if(!CONFIG_GET(flag/auxtools_enabled) || !SSlua.initialized)
=======
/datum/lua_editor/ui_data(mob/user)
var/list/data = list()
data["ss_lua_init"] = SSlua.initialized
if(!SSlua.initialized)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return data
data["noStateYet"] = !current_state
data["showGlobalTable"] = show_global_table
if(current_state)
if(current_state.log)
<<<<<<< HEAD
data["stateLog"] = kvpify_list(refify_list(current_state.log.Copy((page*50)+1, min((page+1)*50+1, current_state.log.len+1))))
=======
var/list/logs = current_state.log.Copy((page*50)+1, min((page+1)*50+1, current_state.log.len+1))
for(var/i in 1 to logs.len)
var/list/log = logs[i]
log = log.Copy()
if(log["return_values"])
log["return_values"] = kvpify_list(prepare_lua_editor_list(deep_copy_without_cycles(log["return_values"])))
logs[i] = log
data["stateLog"] = logs
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
data["page"] = page
data["pageCount"] = CEILING(current_state.log.len/50, 1)
data["tasks"] = current_state.get_tasks()
if(show_global_table)
current_state.get_globals()
<<<<<<< HEAD
data["globals"] = kvpify_list(refify_list(current_state.globals))
data["states"] = SSlua.states
data["callArguments"] = kvpify_list(refify_list(arguments))
=======
var/list/values = current_state.globals["values"]
values = deep_copy_without_cycles(values)
values = prepare_lua_editor_list(values)
values = kvpify_list(values)
var/list/variants = current_state.globals["variants"]
data["globals"] = list("values" = values, "variants" = variants)
if(last_error)
data["lastError"] = last_error
last_error = null
data["supressRuntimes"] = current_state.supress_runtimes
data["states"] = list()
for(var/datum/lua_state/state as anything in SSlua.states)
data["states"] += state.display_name
data["callArguments"] = kvpify_list(prepare_lua_editor_list(deep_copy_without_cycles(arguments)))
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
if(force_modal)
data["forceModal"] = force_modal
force_modal = null
if(force_view_chunk)
data["forceViewChunk"] = force_view_chunk
force_view_chunk = null
<<<<<<< HEAD
=======
if(force_input)
data["force_input"] = force_input
force_input = null
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return data
/datum/lua_editor/proc/traverse_list(list/path, list/root, traversal_depth_offset = 0)
@@ -99,6 +149,7 @@
else
return root
<<<<<<< HEAD
/datum/lua_editor/ui_act(action, list/params)
. = ..()
if(.)
@@ -107,6 +158,26 @@
return
if(action == "runCodeFile")
params["code"] = file2text(input(usr, "Input File") as null|file)
=======
/datum/lua_editor/proc/run_code(code)
var/ckey = usr.ckey
current_state.ckey_last_runner = ckey
var/result = current_state.load_script(code)
var/index_with_result = current_state.log_result(result)
if(result["status"] == "error")
last_error = result["message"]
message_admins("[key_name(usr)] executed [length(code)] bytes of lua code. [ADMIN_LUAVIEW_CHUNK(current_state, index_with_result)]")
/datum/lua_editor/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
var/mob/user = ui.user
if(!check_rights_for(user.client, R_DEBUG))
return
if(action == "runCodeFile")
params["code"] = file2text(input(user, "Input File") as null|file)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
if(isnull(params["code"]))
return
action = "runCode"
@@ -116,6 +187,11 @@
if(!length(state_name))
return TRUE
var/datum/lua_state/new_state = new(state_name)
<<<<<<< HEAD
=======
if(QDELETED(new_state))
return
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
SSlua.states += new_state
LAZYREMOVEASSOC(SSlua.editors, text_ref(current_state), src)
current_state = new_state
@@ -130,11 +206,22 @@
page = 0
return TRUE
if("runCode")
<<<<<<< HEAD
var/code = params["code"]
current_state.ckey_last_runner = usr.ckey
var/result = current_state.load_script(code)
var/index_with_result = current_state.log_result(result)
message_admins("[key_name(usr)] executed [length(code)] bytes of lua code. [ADMIN_LUAVIEW_CHUNK(current_state, index_with_result)]")
=======
run_code(params["code"])
return TRUE
if("runFile")
var/code_file = input(user, "Select a script to run.", "Lua") as file|null
if(!code_file)
return TRUE
var/code = file2text(code_file)
run_code(code)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return TRUE
if("moveArgUp")
var/list/path = params["path"]
@@ -158,9 +245,15 @@
var/list/path = params["path"]
var/list/target_list = traverse_list(path, arguments)
if(target_list != arguments)
<<<<<<< HEAD
usr?.client?.mod_list_add(target_list, null, "a lua editor", "arguments")
else
var/list/vv_val = usr?.client?.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
=======
user?.client?.mod_list_add(target_list, null, "a lua editor", "arguments")
else
var/list/vv_val = user?.client?.vv_get_value(restricted_classes = list(VV_RESTORE_DEFAULT))
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
var/class = vv_val["class"]
if(!class)
return
@@ -168,19 +261,32 @@
return TRUE
if("callFunction")
var/list/recursive_indices = params["indices"]
<<<<<<< HEAD
var/list/current_list = kvpify_list(current_state.globals)
=======
var/list/current_list = kvpify_list(current_state.globals["values"])
var/list/current_variants = current_state.globals["variants"]
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
var/function = list()
while(LAZYLEN(recursive_indices))
var/index = popleft(recursive_indices)
var/list/element = current_list[index]
var/key = element["key"]
var/value = element["value"]
<<<<<<< HEAD
if(!(istext(key) || isnum(key)))
to_chat(usr, span_warning("invalid key \[[key]] for function call (expected text or num)"))
=======
var/list/variant_pair = current_variants[index]
var/key_variant = variant_pair["key"]
if(key_variant == "function" || key_variant == "thread" || key_variant == "userdata" || key_variant == "error_as_value")
to_chat(user, span_warning("invalid table key \[[key]] for function call (expected text, num, path, list, or ref, got [key_variant])"))
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return
function += key
if(islist(value))
current_list = value
<<<<<<< HEAD
else
var/regex/function_regex = regex("^function: 0x\[0-9a-fA-F]+$")
if(function_regex.Find(value))
@@ -191,18 +297,38 @@
current_state.log_result(result)
arguments.Cut()
return TRUE
=======
current_variants = variant_pair["value"]
else
if(variant_pair["value"] != "function")
to_chat(user, span_warning("invalid value \[[value]] for function call (expected list or function)"))
return
var/result = current_state.call_function(arglist(list(function) + arguments))
current_state.log_result(result)
if(result["status"] == "error")
last_error = result["message"]
arguments.Cut()
return
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
if("resumeTask")
var/task_index = params["index"]
SSlua.queue_resume(current_state, task_index, arguments)
arguments.Cut()
return TRUE
if("killTask")
<<<<<<< HEAD
var/task_info = params["info"]
SSlua.kill_task(current_state, task_info)
=======
var/is_sleep = params["is_sleep"]
var/index = params["index"]
SSlua.kill_task(current_state, is_sleep, index)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return TRUE
if("vvReturnValue")
var/log_entry_index = params["entryIndex"]
var/list/log_entry = current_state.log[log_entry_index]
<<<<<<< HEAD
var/thing_to_debug = traverse_list(params["tableIndices"], log_entry["param"])
if(isweakref(thing_to_debug))
var/datum/weakref/ref = thing_to_debug
@@ -215,6 +341,20 @@
var/datum/weakref/ref = thing_to_debug
thing_to_debug = ref.resolve()
INVOKE_ASYNC(usr.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
=======
var/thing_to_debug = traverse_list(params["indices"], log_entry["return_values"])
if(isweakref(thing_to_debug))
var/datum/weakref/ref = thing_to_debug
thing_to_debug = ref.resolve()
INVOKE_ASYNC(user.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
return FALSE
if("vvGlobal")
var/thing_to_debug = traverse_list(params["indices"], current_state.globals["values"])
if(isweakref(thing_to_debug))
var/datum/weakref/ref = thing_to_debug
thing_to_debug = ref.resolve()
INVOKE_ASYNC(user.client, TYPE_PROC_REF(/client, debug_variables), thing_to_debug)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return FALSE
if("clearArgs")
arguments.Cut()
@@ -222,12 +362,24 @@
if("toggleShowGlobalTable")
show_global_table = !show_global_table
return TRUE
<<<<<<< HEAD
=======
if("toggleSupressRuntimes")
current_state.supress_runtimes = !current_state.supress_runtimes
return TRUE
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
if("nextPage")
page = min(page+1, CEILING(current_state.log.len/50, 1)-1)
return TRUE
if("previousPage")
page = max(page-1, 0)
return TRUE
<<<<<<< HEAD
=======
if("nukeLog")
current_state.log.Cut()
return TRUE
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
/datum/lua_editor/ui_close(mob/user)
. = ..()
+182
View File
@@ -1,5 +1,6 @@
#define MAX_LOG_REPEAT_LOOKBACK 5
<<<<<<< HEAD
GLOBAL_VAR_INIT(IsLuaCall, FALSE)
GLOBAL_PROTECT(IsLuaCall)
@@ -10,6 +11,18 @@ GLOBAL_PROTECT(lua_usr)
var/name
/// The internal ID of the lua state stored in auxlua's global map
=======
GLOBAL_DATUM(lua_usr, /mob)
GLOBAL_PROTECT(lua_usr)
GLOBAL_LIST_EMPTY_TYPED(lua_state_stack, /datum/weakref)
GLOBAL_PROTECT(lua_state_stack)
/datum/lua_state
var/display_name
/// The internal ID of the lua state stored in dreamluau's state list
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
var/internal_id
/// A log of every return, yield, and error for each chunk execution and function call
@@ -18,15 +31,24 @@ GLOBAL_PROTECT(lua_usr)
/// A list of all the variables in the state's environment
var/list/globals = list()
<<<<<<< HEAD
/// A list in which to store datums and lists instantiated in lua, ensuring that they don't get garbage collected
var/list/references = list()
=======
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
/// Ckey of the last user who ran a script on this lua state.
var/ckey_last_runner = ""
/// Whether the timer.lua script has been included into this lua context state.
var/timer_enabled = FALSE
<<<<<<< HEAD
=======
/// Whether to supress logging BYOND runtimes for this state.
var/supress_runtimes = FALSE
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
/// Callbacks that need to be ran on next tick
var/list/functions_to_execute = list()
@@ -39,24 +61,46 @@ GLOBAL_PROTECT(lua_usr)
if(SSlua.initialized != TRUE)
qdel(src)
return
<<<<<<< HEAD
name = _name
internal_id = __lua_new_state()
/datum/lua_state/proc/check_if_slept(result)
if(result["status"] == "sleeping")
=======
display_name = _name
internal_id = DREAMLUAU_NEW_STATE()
if(isnull(internal_id))
stack_trace("dreamluau is not loaded")
qdel(src)
else if(!isnum(internal_id))
stack_trace(internal_id)
qdel(src)
/datum/lua_state/proc/check_if_slept(result)
if(result["status"] == "sleep")
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
SSlua.sleeps += src
/datum/lua_state/proc/log_result(result, verbose = TRUE)
if(!islist(result))
return
<<<<<<< HEAD
if(!verbose && result["status"] != "errored" && result["status"] != "bad return" \
&& !(result["name"] == "input" && (result["status"] == "finished" || length(result["param"]))))
=======
var/status = result["status"]
if(!verbose && status != "error" && status != "panic" && status != "runtime" && !(result["name"] == "input" && (status == "finished" || length(result["return_values"]))))
return
if(status == "runtime" && supress_runtimes)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
return
var/append_to_log = TRUE
var/index_of_log
if(log.len)
for(var/index in log.len to max(log.len - MAX_LOG_REPEAT_LOOKBACK, 1) step -1)
var/list/entry = log[index]
<<<<<<< HEAD
if(entry["status"] == result["status"] \
&& entry["chunk"] == result["chunk"] \
&& entry["name"] == result["name"] \
@@ -70,24 +114,61 @@ GLOBAL_PROTECT(lua_usr)
if(append_to_log)
if(islist(result["param"]))
result["param"] = weakrefify_list(encode_text_and_nulls(result["param"]))
=======
if(!compare_lua_logs(entry, result))
continue
if(!entry["repeats"])
entry["repeats"] = 0
index_of_log = index
entry["repeats"]++
append_to_log = FALSE
break
if(append_to_log)
if(islist(result["return_values"]))
add_lua_return_value_variants(result["return_values"], result["variants"])
result["return_values"] = weakrefify_list(result["return_values"])
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
log += list(result)
index_of_log = log.len
INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/lua_state, update_editors))
return index_of_log
<<<<<<< HEAD
/datum/lua_state/proc/load_script(script)
GLOB.IsLuaCall = TRUE
var/tmp_usr = GLOB.lua_usr
GLOB.lua_usr = usr
var/result = __lua_load(internal_id, script)
GLOB.IsLuaCall = FALSE
=======
/datum/lua_state/proc/parse_error(message, name)
if(copytext(message, 1, 7) == "PANIC:")
return list("status" = "panic", "message" = copytext(message, 7), "name" = name)
else
return list("status" = "error", "message" = message, "name" = name)
/datum/lua_state/proc/load_script(script)
var/tmp_usr = GLOB.lua_usr
GLOB.lua_usr = usr
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_LOAD(internal_id, script, "input")
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
GLOB.lua_usr = tmp_usr
// Internal errors unrelated to the code being executed are returned as text rather than lists
if(isnull(result))
<<<<<<< HEAD
result = list("status" = "errored", "param" = "__lua_load returned null (it may have runtimed - check the runtime logs)", "name" = "input")
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = "input")
=======
result = list("status" = "error", "message" = "load returned null (it may have runtimed - check the runtime logs)", "name" = "input")
if(istext(result))
result = parse_error(result, "input")
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
result["chunk"] = script
check_if_slept(result)
@@ -109,6 +190,7 @@ GLOBAL_PROTECT(lua_usr)
if(islist(function))
var/list/new_function_path = list()
for(var/path_element in function)
<<<<<<< HEAD
new_function_path += path_element
function = new_function_path
@@ -123,10 +205,38 @@ GLOBAL_PROTECT(lua_usr)
result = list("status" = "errored", "param" = "__lua_call returned null (it may have runtimed - check the runtime logs)", "name" = islist(function) ? jointext(function, ".") : function)
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = islist(function) ? jointext(function, ".") : function)
=======
if(isweakref(path_element))
var/datum/weakref/weak_ref = path_element
var/resolved = weak_ref.hard_resolve()
if(!resolved)
return list("status" = "error", "message" = "Weakref in function path ([weak_ref] [text_ref(weak_ref)]) resolved to null.", "name" = jointext(function, "."))
new_function_path += resolved
else
new_function_path += path_element
function = new_function_path
else
function = list(function)
var/tmp_usr = GLOB.lua_usr
GLOB.lua_usr = usr
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_CALL_FUNCTION(internal_id, function, call_args)
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
GLOB.lua_usr = tmp_usr
if(isnull(result))
result = list("status" = "error", "message" = "call_function returned null (it may have runtimed - check the runtime logs)", "name" = jointext(function, "."))
if(istext(result))
result = parse_error(result, jointext(function, "."))
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
check_if_slept(result)
return result
/datum/lua_state/proc/call_function_return_first(function, ...)
<<<<<<< HEAD
var/list/result = call_function(arglist(args))
log_result(result, verbose = FALSE)
if(length(result))
@@ -142,12 +252,37 @@ GLOBAL_PROTECT(lua_usr)
result = list("status" = "errored", "param" = "__lua_awaken returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted awaken")
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = "An attempted awaken")
=======
SHOULD_NOT_SLEEP(TRUE) // This function is meant to be used for signal handlers.
var/list/result = call_function(arglist(args))
INVOKE_ASYNC(src, PROC_REF(log_result), deep_copy_list(result), /*verbose = */FALSE)
if(length(result))
if(islist(result["return_values"]) && length(result["return_values"]))
var/return_value = result["return_values"][1]
var/variant = (islist(result["variants"]) && length(result["variants"])) && result["variants"][1]
if(islist(return_value) && islist(variant))
remove_non_dm_variants(return_value, variant)
return return_value
/datum/lua_state/proc/awaken()
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_AWAKEN(internal_id)
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
if(isnull(result))
result = list("status" = "error", "message" = "awaken returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted awaken")
if(istext(result))
result = parse_error(result, "An attempted awaken")
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
check_if_slept(result)
return result
/// Prefer calling SSlua.queue_resume over directly calling this
/datum/lua_state/proc/resume(index, ...)
var/call_args = length(args) > 1 ? args.Copy(2) : list()
<<<<<<< HEAD
var/msg = "[key_name(usr)] resumed a lua coroutine with arguments: [english_list(call_args)]"
log_lua(msg)
@@ -159,10 +294,24 @@ GLOBAL_PROTECT(lua_usr)
result = list("status" = "errored", "param" = "__lua_resume returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted resume")
if(istext(result))
result = list("status" = "errored", "param" = result, "name" = "An attempted resume")
=======
DREAMLUAU_SET_USR
GLOB.lua_state_stack += WEAKREF(src)
var/result = DREAMLUAU_RESUME(internal_id, index, call_args)
SSlua.needs_gc_cycle |= src
pop(GLOB.lua_state_stack)
if(isnull(result))
result = list("status" = "error", "param" = "resume returned null (it may have runtimed - check the runtime logs)", "name" = "An attempted resume")
if(istext(result))
result = parse_error(result, "An attempted resumt")
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
check_if_slept(result)
return result
/datum/lua_state/proc/get_globals()
<<<<<<< HEAD
globals = weakrefify_list(encode_text_and_nulls(__lua_get_globals(internal_id)))
/datum/lua_state/proc/get_tasks()
@@ -170,6 +319,36 @@ GLOBAL_PROTECT(lua_usr)
/datum/lua_state/proc/kill_task(task_info)
__lua_kill_task(internal_id, task_info)
=======
var/result = DREAMLUAU_GET_GLOBALS(internal_id)
if(isnull(result))
CRASH("get_globals returned null")
if(istext(result))
CRASH(result)
var/list/new_globals = result
var/list/values = new_globals["values"]
var/list/variants = new_globals["variants"]
add_lua_editor_variants(values, variants)
globals = list("values" = weakrefify_list(values), "variants" = variants)
/datum/lua_state/proc/get_tasks()
var/result = DREAMLUAU_LIST_THREADS(internal_id)
if(isnull(result))
CRASH("list_threads returned null")
if(istext(result))
CRASH(result)
return result
/datum/lua_state/proc/kill_task(is_sleep, index)
var/result = is_sleep ? DREAMLUAU_KILL_SLEEPING_THREAD(internal_id, index) : DREAMLUAU_KILL_YIELDED_THREAD(internal_id, index)
SSlua.needs_gc_cycle |= src
return result
/datum/lua_state/proc/collect_garbage()
var/result = DREAMLUAU_COLLECT_GARBAGE(internal_id)
if(!isnull(result))
CRASH(result)
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
/datum/lua_state/proc/update_editors()
var/list/editor_list = LAZYACCESS(SSlua.editors, text_ref(src))
@@ -177,6 +356,7 @@ GLOBAL_PROTECT(lua_usr)
for(var/datum/lua_editor/editor as anything in editor_list)
SStgui.update_uis(editor)
<<<<<<< HEAD
/// Called by lua scripts when they add an atom to var/list/references so that it gets cleared up on delete.
/datum/lua_state/proc/clear_on_delete(datum/to_clear)
RegisterSignal(to_clear, COMSIG_QDELETING, PROC_REF(on_delete))
@@ -191,4 +371,6 @@ GLOBAL_PROTECT(lua_usr)
SIGNAL_HANDLER
references -= to_clear
=======
>>>>>>> 4b4ae0958fe6b5d511ee6e24a5087599f61d70a3
#undef MAX_LOG_REPEAT_LOOKBACK