diff --git a/code/modules/admin/verbs/lua/README.md b/code/modules/admin/verbs/lua/README.md index b7b7306ed4d..17138c95fde 100644 --- a/code/modules/admin/verbs/lua/README.md +++ b/code/modules/admin/verbs/lua/README.md @@ -156,6 +156,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. @@ -200,6 +224,9 @@ SS13.set_timeout(5, function() end) ``` +### 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 diff --git a/code/modules/admin/verbs/lua/lua_state.dm b/code/modules/admin/verbs/lua/lua_state.dm index acf45f383cb..b90344d6333 100644 --- a/code/modules/admin/verbs/lua/lua_state.dm +++ b/code/modules/admin/verbs/lua/lua_state.dm @@ -168,10 +168,16 @@ 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. +/// 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 diff --git a/lua/SS13.lua b/lua/SS13.lua index b86b8ec6dfb..a17d5b50577 100644 --- a/lua/SS13.lua +++ b/lua/SS13.lua @@ -16,7 +16,7 @@ function SS13.istype(thing, type) end function SS13.new(type, ...) - local datum = dm.global_proc("_new", type, { ... }) + local datum = SS13.new_untracked(type, table.unpack({...})) if datum then local references = SS13.state.vars.references references:add(datum) @@ -25,6 +25,29 @@ function SS13.new(type, ...) end end +function SS13.type(string_type) + return dm.global_proc("_text2path", string_type) +end + +function SS13.qdel(datum) + if SS13.is_valid(datum) then + dm.global_proc("qdel", datum) + return true + end + return false +end + +function SS13.new_untracked(type, ...) + return dm.global_proc("_new", type, { ... }) +end + +function SS13.is_valid(datum) + if datum and not datum:is_null() and not datum:get_var("gc_destroyed") then + return true + end + return false +end + function SS13.await(thing_to_call, proc_to_call, ...) if not SS13.istype(thing_to_call, "/datum") then thing_to_call = SS13.global_proc @@ -38,7 +61,7 @@ function SS13.await(thing_to_call, proc_to_call, ...) sleep() end local return_value, runtime_message = promise_vars.return_value, promise_vars.runtime_message - dm.global_proc("qdel", promise) + SS13.stop_tracking(promise) return return_value, runtime_message end @@ -47,7 +70,7 @@ function SS13.wait(time, timer) local timedevent = dm.global_proc("_addtimer", callback, time * 10, 8, timer, debug.info(1, "sl")) coroutine.yield() dm.global_proc("deltimer", timedevent, timer) - dm.global_proc("qdel", callback) + SS13.stop_tracking(callback) end function SS13.register_signal(datum, signal, func, make_easy_clear_function) @@ -77,7 +100,7 @@ function SS13.register_signal(datum, signal, func, make_easy_clear_function) SS13.signal_handlers[datum]["_cleanup"] = { func = function(datum) SS13.signal_handler_cleanup(datum) - dm.global_proc("qdel", cleanup_callback) + SS13.stop_tracking(cleanup_callback) end, callback = cleanup_callback, } @@ -108,6 +131,10 @@ function SS13.register_signal(datum, signal, func, make_easy_clear_function) return callback end +function SS13.stop_tracking(datum) + SS13.state:call_proc("let_soft_delete", datum) +end + function SS13.unregister_signal(datum, signal, callback) local function clear_handler(handler_info) if not handler_info then @@ -118,7 +145,7 @@ function SS13.unregister_signal(datum, signal, callback) end local handler_callback = handler_info.callback handler_callback:call_proc("UnregisterSignal", datum, signal) - dm.global_proc("qdel", handler_callback) + SS13.stop_tracking(handler_callback) end if not SS13.signal_handlers then @@ -180,7 +207,7 @@ function SS13.set_timeout(time, func, timer) SS13.timeouts[callback] = function() SS13.timeouts[callback] = nil dm.global_proc("deltimer", timedevent, timer) - dm.global_proc("qdel", callback) + SS13.stop_tracking(callback) func() end local path = { "SS13", "timeouts", dm.global_proc("WEAKREF", callback) }