[MIRROR] Fixes SS13.register_signal throwing unclear errors when called on deleted datums (#27293)

* Cleans up the SS13_base lua file and adds a new lua file for easily handling multiple signals on different objects. (#82458)

## About The Pull Request
Cleaned up the SS13.register_signal and SS13.unregister_signal, removing
the weird list shifting.
Also adds a new lua file that can be included for the use of registering
different signals on various datums and being able to clear them all in
1 function.
Removed the make_easy_clear_function option when registering a signal
via lua because I don't think it's used by anyone and it lacks any sort
of versatility. Users can just create their own function for clearing
signals from a datum.

Also updates the documentation for HARDDELETES.md as
COMSIG_PARENT_QDELETING was renamed to COMSIG_QDELETING

## Why It's Good For The Game
New handler file makes registering signals in batches a lot easier if
you want to clear them in one go without clearing unrelated callbacks on
the same datum. The list shifting in SS13.register_signal had pretty
significant performance problems, so removing that will make registering
and unregistering signals faster.

## Changelog
🆑
admin: LUA - Adds a new library called handler_group. Include it in your
files by doing require('handler_group')
/🆑

---------

Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com>

* Fixes SS13.register_signal throwing unclear errors when called on deleted datums (#82597)

## About The Pull Request
See title

## Why It's Good For The Game
More descriptive error message

## Changelog
🆑
fix: LUA: Registering a signal on a deleted datum will throw a more
descriptive error message.
/🆑

---------

Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com>

---------

Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com>
Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com>
This commit is contained in:
SkyratBot
2024-04-18 10:21:35 +02:00
committed by GitHub
co-authored by Watermelon914 Watermelon914
parent 909dbec657
commit 7d0d702ec7
6 changed files with 189 additions and 80 deletions
+58 -68
View File
@@ -1,3 +1,6 @@
local timer = require("timer")
local state = require("state")
local SS13 = {}
__SS13_signal_handlers = __SS13_signal_handlers or {}
@@ -6,12 +9,7 @@ SS13.SSlua = dm.global_vars.vars.SSlua
SS13.global_proc = "some_magic_bullshit"
for _, state in SS13.SSlua.vars.states do
if state.vars.internal_id == dm.state_id then
SS13.state = state
break
end
end
SS13.state = state.state
function SS13.get_runner_ckey()
return SS13.state:get_var("ckey_last_runner")
@@ -25,12 +23,16 @@ function SS13.istype(thing, type)
return dm.global_proc("_istype", thing, dm.global_proc("_text2path", type)) == 1
end
function SS13.start_tracking(datum)
local references = SS13.state.vars.references
references:add(datum)
SS13.state:call_proc("clear_on_delete", datum)
end
function SS13.new(type, ...)
local datum = SS13.new_untracked(type, table.unpack({...}))
local datum = SS13.new_untracked(type, ...)
if datum then
local references = SS13.state.vars.references
references:add(datum)
SS13.state:call_proc("clear_on_delete", datum)
SS13.start_tracking(datum)
return datum
end
end
@@ -75,58 +77,44 @@ function SS13.await(thing_to_call, proc_to_call, ...)
return return_value, runtime_message
end
function SS13.register_signal(datum, signal, func, make_easy_clear_function)
function SS13.register_signal(datum, signal, func)
if not SS13.istype(datum, "/datum") then
return
end
if not __SS13_signal_handlers[datum] then
__SS13_signal_handlers[datum] = {}
if not SS13.is_valid(datum) then
error("Tried to register a signal on a deleted datum!", 2)
return
end
local datumWeakRef = dm.global_proc("WEAKREF", datum)
if not __SS13_signal_handlers[datumWeakRef] then
__SS13_signal_handlers[datumWeakRef] = {}
end
if signal == "_cleanup" then
return
end
if not __SS13_signal_handlers[datum][signal] then
__SS13_signal_handlers[datum][signal] = {}
if not __SS13_signal_handlers[datumWeakRef][signal] then
__SS13_signal_handlers[datumWeakRef][signal] = {}
end
local callback = SS13.new("/datum/callback", SS13.state, "call_function_return_first")
local callbackWeakRef = dm.global_proc("WEAKREF", callback)
callback:call_proc("RegisterSignal", datum, signal, "Invoke")
local path = { "__SS13_signal_handlers", dm.global_proc("WEAKREF", datum), signal, dm.global_proc("WEAKREF", callback), "func" }
local path = { "__SS13_signal_handlers", datumWeakRef, signal, callbackWeakRef, "func" }
callback.vars.arguments = { path }
if not __SS13_signal_handlers[datum]["_cleanup"] then
local cleanup_path = { "__SS13_signal_handlers", dm.global_proc("WEAKREF", datum), "_cleanup", "func" }
local cleanup_callback = SS13.new("/datum/callback", SS13.state, "call_function_return_first", cleanup_path)
cleanup_callback:call_proc("RegisterSignal", datum, "parent_qdeleting", "Invoke")
__SS13_signal_handlers[datum]["_cleanup"] = {
func = function(datum)
SS13.signal_handler_cleanup(datum)
SS13.stop_tracking(cleanup_callback)
end,
callback = cleanup_callback,
}
end
if signal == "parent_qdeleting" then --We want to make sure that the cleanup function is the very last signal handler called.
local comp_lookup = datum.vars._listen_lookup
if comp_lookup then
local lookup_for_signal = comp_lookup.entries.parent_qdeleting
if lookup_for_signal and not SS13.istype(lookup_for_signal, "/datum") then
local cleanup_callback_index =
dm.global_proc("_list_find", lookup_for_signal, __SS13_signal_handlers[datum]["_cleanup"].callback)
if cleanup_callback_index ~= 0 and cleanup_callback_index ~= #comp_lookup then
dm.global_proc("_list_swap", lookup_for_signal, cleanup_callback_index, #lookup_for_signal)
end
end
end
end
__SS13_signal_handlers[datum][signal][callback] = { func = func, callback = callback }
if make_easy_clear_function then
local clear_function_name = "clear_signal_" .. tostring(datum) .. "_" .. signal .. "_" .. tostring(callback)
SS13[clear_function_name] = function()
if callback then
SS13.unregister_signal(datum, signal, callback)
end
SS13[clear_function_name] = nil
if not __SS13_signal_handlers[datumWeakRef]._cleanup then
local cleanupCallback = SS13.new("/datum/callback", SS13.state, "call_function_return_first")
local cleanupPath = { "__SS13_signal_handlers", datumWeakRef, "_cleanup"}
cleanupCallback.vars.arguments = { cleanupPath }
cleanupCallback:call_proc("RegisterSignal", datum, "parent_qdeleting", "Invoke")
__SS13_signal_handlers[datumWeakRef]._cleanup = function(datum)
SS13.start_tracking(datumWeakRef)
timer.set_timeout(0, function()
SS13.signal_handler_cleanup(datumWeakRef)
SS13.stop_tracking(cleanupCallback)
SS13.stop_tracking(datumWeakRef)
end)
end
end
__SS13_signal_handlers[datumWeakRef][signal][callbackWeakRef] = { func = func, callback = callback }
return callback
end
@@ -143,51 +131,53 @@ function SS13.unregister_signal(datum, signal, callback)
return
end
local handler_callback = handler_info.callback
handler_callback:call_proc("UnregisterSignal", datum, signal)
local callbackWeakRef = dm.global_proc("WEAKREF", handler_callback)
if not SS13.istype(datum, "/datum/weakref") then
handler_callback:call_proc("UnregisterSignal", datum, signal)
end
SS13.stop_tracking(handler_callback)
end
local function clear_easy_clear_function(callback_to_clear)
local clear_function_name = "clear_signal_" .. tostring(datum) .. "_" .. signal .. "_" .. tostring(callback_to_clear)
SS13[clear_function_name] = nil
local datumWeakRef = datum
if not SS13.istype(datum, "/datum/weakref") then
datumWeakRef = dm.global_proc("WEAKREF", datum)
end
if not __SS13_signal_handlers[datum] then
if not __SS13_signal_handlers[datumWeakRef] then
return
end
if signal == "_cleanup" then
return
end
if not __SS13_signal_handlers[datum][signal] then
if not __SS13_signal_handlers[datumWeakRef][signal] then
return
end
if not callback then
for handler_key, handler_info in __SS13_signal_handlers[datum][signal] do
clear_easy_clear_function(handler_key)
for handler_key, handler_info in __SS13_signal_handlers[datumWeakRef][signal] do
clear_handler(handler_info)
end
__SS13_signal_handlers[datum][signal] = nil
__SS13_signal_handlers[datumWeakRef][signal] = nil
else
if not SS13.istype(callback, "/datum/callback") then
return
end
clear_easy_clear_function(callback)
clear_handler(__SS13_signal_handlers[datum][signal][callback])
__SS13_signal_handlers[datum][signal][callback] = nil
local callbackWeakRef = dm.global_proc("WEAKREF", callback)
clear_handler(__SS13_signal_handlers[datumWeakRef][signal][callbackWeakRef])
__SS13_signal_handlers[datumWeakRef][signal][callbackWeakRef] = nil
end
end
function SS13.signal_handler_cleanup(datum)
if not __SS13_signal_handlers[datum] then
function SS13.signal_handler_cleanup(datumWeakRef)
if not __SS13_signal_handlers[datumWeakRef] then
return
end
for signal, _ in __SS13_signal_handlers[datum] do
SS13.unregister_signal(datum, signal)
for signal, _ in __SS13_signal_handlers[datumWeakRef] do
SS13.unregister_signal(datumWeakRef, signal)
end
__SS13_signal_handlers[datum] = nil
__SS13_signal_handlers[datumWeakRef] = nil
end
return SS13