Skip to content

Commit

Permalink
Auxlua updates (Monkestation#1432)
Browse files Browse the repository at this point in the history
* Fixes SS13.register_signal on parent_qdeleting signal in lua causing an error (#76282)

## About The Pull Request
Potato renamed comp_lookup to _listen_lookup, which wasn't updated in
the lua script. This PR changes the SS13 lua script to properly access
the correct variable

## Why It's Good For The Game
Bugfix

## Changelog

Co-authored-by: Watermelon914 <[email protected]>

* Fixed lua-created atoms from hard deleting (#77391)

## About The Pull Request
Lua created atoms hard delete because they get added to a references
list without ever being cleared. This fixes that by registering a
qdeleting signal on them and removing them from the list on delete.

## Why It's Good For The Game
Harddel fixes

## Changelog
:cl:
fix: Fixed a hard delete that would occur with lua-created atoms.
/:cl:

---------

Co-authored-by: Watermelon914 <[email protected]>

* Fixes some lua problems, specifically with qdeling callbacks and adds lua utility functions (#77468)

## About The Pull Request
See title. Callbacks were getting deleted in SS13.lua which is
non-ideal. They should be automatically collected by the garbage
collector.
Also adds some utility lua functions like SS13.is_valid,
SS13.stop_tracking, SS13.new_untracked, SS13.type and SS13.qdel

## Why It's Good For The Game
Fixes some harddel issues, as well as callbacks clogging up the garbage
subsystem.

## Changelog
:cl:
admin: Added new lua functions: SS13.is_valid, SS13.stop_tracking,
SS13.new_untracked, SS13.type and SS13.qdel
/:cl:

---------

Co-authored-by: Watermelon914 <[email protected]>

* Expands the SS13.lua module by adding loop helpers and functions to get the script runner. (#79081)

## About The Pull Request
`SS13.get_runner_client()` and `SS13.get_runner_ckey` will return the
client and the ckey respectively of the user who ran the lua script. Can
be unreliable after the first sleep or yield.
The SS13 module can now be made local as the tables that need to be
accessed globally have been moved to their own global variables.
Added `SS13.start_loop(time, amount, func)`, `SS13.end_loop(id)`,
`SS13.stop_all_loops()` that allow lua scripts to more easily make
loops. Removed the `timer` parameter from these functions, which
specified the timer subsystem to use.
Documentation on all new added functions have been added in the
lua/README.md

## Why It's Good For The Game
Getting the client who ran the script and the ckey that ran the script
is useful for self contained scripts that are looking for an entrypoint
(e.g. location to spawn some item/mob/structure). `dm.usr` is a special
variable used for other purposes and can be unreliable when used this
way even if there haven't been any sleeps or yields yet, so having a
dedicated variable and function to handle it makes things easier.

Being able to make the SS13 module local allows for more self-contained
scripts. Although this isn't super helpful because `require` will still
load the same object for all lua scripts loaded on the same state.

Basic looping helpers allow for lua scripts to more easily create loops
without needing to recursively do a `set_timeout` or to do a
`while(true) do SS13.wait(1) end` loop.

## Changelog
:cl:
admin: Added SS13.get_runner_ckey() and SS13.get_runner_client() which
stores the ckey and returns the client of the user who ran the lua
script. Can be unreliable if accessed after sleeping.
admin: Added timer loop helpers to the SS13.lua module, check the docs
admin: The SS13.lua module can now be made local without causing any
errors.
/:cl:

---------

Co-authored-by: Watermelon914 <[email protected]>

* More lua harddel fixes (#77556)

## About The Pull Request
Fixes some more lua harddel problems with lingering refs on the gc_guard
variable. This variable has been changed to a list instead and will get
cleared every time the SSlua subsystem fires so that lua instantiated
objects that are not tracked by the subsystem will essentially delete
themselves on the next tick, aka whenever the lua script sleeps.

Also removed the unnecessary and not completely functional
lua_reference_cleanup proc which wasn't even being called reliably
because the signal handler was the datum itself.

## Why It's Good For The Game
Fixes more harddel bugs, increases consistency.

Co-authored-by: Watermelon914 <[email protected]>

* Update auxlua DLL to work on the latest BYOND

* Official auxlua update is out, use that instead of my personal build

---------

Co-authored-by: Watermelon914 <[email protected]>
Co-authored-by: Watermelon914 <[email protected]>
  • Loading branch information
3 people authored Mar 19, 2024
1 parent c1e098b commit 411ef8c
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 72 deletions.
Binary file modified auxlua.dll
Binary file not shown.
4 changes: 3 additions & 1 deletion code/controllers/subsystem/lua.dm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ SUBSYSTEM_DEF(lua)
var/list/current_run = list()

/// Protects return values from getting GCed before getting converted to lua values
var/gc_guard
/// Gets cleared every tick.
var/list/gc_guard = list()

/datum/controller/subsystem/lua/Initialize()
if(!CONFIG_GET(flag/auxtools_enabled))
Expand Down Expand Up @@ -99,6 +100,7 @@ SUBSYSTEM_DEF(lua)
sleeps.Cut()
resumes.Cut()

gc_guard.Cut()
var/list/current_sleeps = current_run["sleeps"]
var/list/affected_states = list()
while(length(current_sleeps))
Expand Down
69 changes: 69 additions & 0 deletions code/modules/admin/verbs/lua/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,12 @@ The `SS13` package contains various helper functions that use code specific to t
### 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
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.
Expand All @@ -156,6 +162,30 @@ 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)
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")
dm.global_proc("qdel", datum)
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
```

### 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, ...)
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.
Expand Down Expand Up @@ -200,6 +230,45 @@ SS13.set_timeout(5, function()
end)
```

### SS13.start_loop(time, amount, func)
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()
dm.global_proc("to_chat", dm.world, "Hello World!")
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()
dm.global_proc("to_chat", dm.world, "Hello World!")
end)
```

### SS13.end_loop(id)
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()
dm.global_proc("to_chat", dm.world, "Hello World!")
repeated_amount += 1
if repeated_amount >= 20 then
SS13.end_loop(timerid)
end
end)
```

### 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
Expand Down
8 changes: 2 additions & 6 deletions code/modules/admin/verbs/lua/_wrappers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@
else
ret = HandleUserlessProcCall("lua", thing_to_call, proc_name, arguments)
if(isdatum(ret))
SSlua.gc_guard = ret
var/datum/ret_datum = ret
ret_datum.RegisterSignal(ret_datum, COMSIG_QDELETING, TYPE_PROC_REF(/datum, lua_reference_cleanup), override = TRUE)
SSlua.gc_guard += ret
return ret

/proc/wrap_lua_global_proc_call(proc_name, list/arguments)
Expand All @@ -27,9 +25,7 @@
else
ret = HandleUserlessProcCall("lua", GLOBAL_PROC, proc_name, arguments)
if(isdatum(ret))
SSlua.gc_guard = ret
var/datum/ret_datum = ret
ret_datum.RegisterSignal(ret_datum, COMSIG_QDELETING, TYPE_PROC_REF(/datum, lua_reference_cleanup), override = TRUE)
SSlua.gc_guard += ret
return ret

/proc/wrap_lua_print(state_id, list/arguments)
Expand Down
14 changes: 0 additions & 14 deletions code/modules/admin/verbs/lua/helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,3 @@
#undef PROMISE_PENDING
#undef PROMISE_RESOLVED
#undef PROMISE_REJECTED

/**
* When a datum is created from lua, it gets held in `SSlua.gc_guard`, and later,
* in the calling state datum's `var/list/references`, just in case it would be garbage
* collected due to there not being any references that BYOND recognizes. To avoid harddels,
* we register this proc as a signal handler any time a DM function called from lua returns
* a datum.
*/
/datum/proc/lua_reference_cleanup()
SIGNAL_HANDLER
if(SSlua.gc_guard == src)
SSlua.gc_guard = null
for(var/datum/lua_state/state in SSlua.states)
state.references -= src
1 change: 1 addition & 0 deletions code/modules/admin/verbs/lua/lua_editor.dm
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
return TRUE
if("runCode")
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)]")
Expand Down
17 changes: 17 additions & 0 deletions code/modules/admin/verbs/lua/lua_state.dm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ GLOBAL_PROTECT(lua_usr)
/// A list in which to store datums and lists instantiated in lua, ensuring that they don't get garbage collected
var/list/references = list()

/// Ckey of the last user who ran a script on this lua state.
var/ckey_last_runner = ""

/datum/lua_state/vv_edit_var(var_name, var_value)
. = ..()
if(var_name == NAMEOF(src, internal_id))
Expand Down Expand Up @@ -168,4 +171,18 @@ GLOBAL_PROTECT(lua_usr)
for(var/datum/lua_editor/editor as anything in editor_list)
SStgui.update_uis(editor)

/// 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))

/// Called by lua scripts when an atom they've added should soft delete and this state should stop tracking it.
/// Needs to unregister all signals.
/datum/lua_state/proc/let_soft_delete(datum/to_clear)
UnregisterSignal(to_clear, COMSIG_QDELETING, PROC_REF(on_delete))
references -= to_clear

/datum/lua_state/proc/on_delete(datum/to_clear)
SIGNAL_HANDLER
references -= to_clear

#undef MAX_LOG_REPEAT_LOOKBACK
2 changes: 1 addition & 1 deletion dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,4 @@ export PYTHON_VERSION=3.9.0
export AUXLUA_REPO=tgstation/auxlua

#auxlua git tag
export AUXLUA_VERSION=1.4.1
export AUXLUA_VERSION=1.4.4
Loading

0 comments on commit 411ef8c

Please sign in to comment.