mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-16 10:27:14 +01:00
1daf9000f0
<img width="1233" height="377" alt="image" src="https://github.com/user-attachments/assets/6e267bec-cf6a-4c8b-8371-e0347068d7eb" /> Literally pointless runtime error, except this time I'm disabling it without making it harder to port stuff from TG.
196 lines
6.5 KiB
Plaintext
196 lines
6.5 KiB
Plaintext
/**
|
|
* Register to listen for a signal from the passed in target
|
|
*
|
|
* This sets up a listening relationship such that when the target object emits a signal
|
|
* the source datum this proc is called upon, will receive a callback to the given proctype
|
|
* Use PROC_REF(procname), TYPE_PROC_REF(type,procname) or GLOBAL_PROC_REF(procname) macros to validate the passed in proc at compile time.
|
|
* PROC_REF for procs defined on current type or its ancestors, TYPE_PROC_REF for procs defined on unrelated type and GLOBAL_PROC_REF for global procs.
|
|
* Return values from procs registered must be a bitfield
|
|
*
|
|
* Arguments:
|
|
* * datum/target The target to listen for signals from
|
|
* * signal_type A signal name
|
|
* * proctype The proc to call back when the signal is emitted
|
|
* * override If a previous registration exists you must explicitly set this
|
|
*/
|
|
/datum/proc/RegisterSignal(datum/target, signal_type, proctype, override = TRUE)
|
|
if(QDELETED(src) || QDELETED(target))
|
|
return
|
|
|
|
if (islist(signal_type))
|
|
var/static/list/known_failures = list()
|
|
var/list/signal_type_list = signal_type
|
|
var/message = "([target.type]) is registering [signal_type_list.Join(", ")] as a list, the older method. Change it to RegisterSignals."
|
|
|
|
if (!(message in known_failures))
|
|
known_failures[message] = TRUE
|
|
stack_trace("[target] [message]")
|
|
|
|
RegisterSignals(target, signal_type, proctype, override)
|
|
return
|
|
|
|
var/list/procs = (_signal_procs ||= list())
|
|
var/list/target_procs = (procs[target] ||= list())
|
|
var/list/lookup = (target._listen_lookup ||= list())
|
|
|
|
var/exists = target_procs[signal_type]
|
|
target_procs[signal_type] = proctype
|
|
|
|
if(exists)
|
|
if(!override)
|
|
var/override_message = "[signal_type] overridden. Use override = TRUE to suppress this warning.\nTarget: [target] ([target.type]) Existing Proc: [exists] New Proc: [proctype]"
|
|
log_signal(override_message)
|
|
stack_trace(override_message)
|
|
return
|
|
|
|
var/list/looked_up = lookup[signal_type]
|
|
|
|
if(isnull(looked_up)) // Nothing has registered here yet
|
|
lookup[signal_type] = src
|
|
else if(!islist(looked_up)) // One other thing registered here
|
|
lookup[signal_type] = list(looked_up, src)
|
|
else // Many other things have registered here
|
|
looked_up += src
|
|
|
|
/// Registers multiple signals to the same proc.
|
|
/datum/proc/RegisterSignals(datum/target, list/signal_types, proctype, override = TRUE)
|
|
for (var/signal_type in signal_types)
|
|
RegisterSignal(target, signal_type, proctype, override)
|
|
|
|
/**
|
|
* Stop listening to a given signal from target
|
|
*
|
|
* Breaks the relationship between target and source datum, removing the callback when the signal fires
|
|
*
|
|
* Doesn't care if a registration exists or not
|
|
*
|
|
* Arguments:
|
|
* * datum/target Datum to stop listening to signals from
|
|
* * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically
|
|
*/
|
|
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
|
|
var/list/lookup = target._listen_lookup
|
|
var/list/procs = _signal_procs
|
|
|
|
// If neither side has anything, bail.
|
|
if(!lookup && (!procs || !procs[target]))
|
|
return
|
|
|
|
if(!islist(sig_type_or_types))
|
|
sig_type_or_types = list(sig_type_or_types)
|
|
|
|
var/list/target_procs = procs ? procs[target] : null
|
|
|
|
for(var/sig in sig_type_or_types)
|
|
var/has_proc = target_procs && target_procs[sig]
|
|
|
|
// Always try to clean the emitter side if lookup exists
|
|
if(lookup && (sig in lookup))
|
|
var/current = lookup[sig]
|
|
|
|
switch(length(current))
|
|
if(2)
|
|
// 2-element list: remove src, collapse to single datum
|
|
var/list/new_list = current - src
|
|
if(new_list.len)
|
|
lookup[sig] = new_list.len == 1 ? new_list[1] : new_list
|
|
else
|
|
// if somehow both gone, just drop the key
|
|
lookup -= sig
|
|
if(1)
|
|
// Single-length list (weird state, but handle it)
|
|
if(islist(current) && (src in current))
|
|
lookup -= sig
|
|
if(0)
|
|
// Non-list: either src or somebody else
|
|
if(current == src)
|
|
lookup -= sig
|
|
else
|
|
// >2 listeners
|
|
current -= src
|
|
|
|
if(lookup && !lookup.len)
|
|
target._listen_lookup = null
|
|
|
|
// Listener side cleanup, only if we actually had a proc
|
|
if(has_proc)
|
|
target_procs -= sig
|
|
|
|
// Final tidy: if target_procs is empty, remove that target from _signal_procs
|
|
if(target_procs && !target_procs.len)
|
|
procs -= target
|
|
|
|
/**
|
|
* Internal proc to handle most all of the signaling procedure
|
|
*
|
|
* Will runtime if used on datums with an empty lookup list
|
|
*
|
|
* Use the [SEND_SIGNAL] define instead
|
|
*/
|
|
/datum/proc/_SendSignal(sigtype, list/arguments)
|
|
var/target = _listen_lookup?[sigtype]
|
|
|
|
// No listeners at all for this signal
|
|
if(!target)
|
|
return NONE
|
|
|
|
. = NONE
|
|
|
|
// SINGLE LISTENER
|
|
if(!islist(target))
|
|
var/datum/listening_datum = target
|
|
if(!listening_datum)
|
|
return NONE
|
|
|
|
var/list/proc_map = listening_datum?._signal_procs?[src]
|
|
|
|
if(!proc_map)
|
|
// Listener has completely forgotten this source;
|
|
// clean the emitter side and log.
|
|
stack_trace("Signal mismatch (single): [src] has [sigtype] for [listening_datum], but listener has no _signal_procs entry")
|
|
if(_listen_lookup[sigtype] == listening_datum)
|
|
_listen_lookup -= sigtype
|
|
return NONE
|
|
|
|
var/proc_name = proc_map[sigtype]
|
|
if(!proc_name)
|
|
// Listener still has a table for this source, but not this signal.
|
|
stack_trace("Signal mismatch (single): [src] has [sigtype] for [listening_datum], but listener has no matching proc entry")
|
|
if(_listen_lookup[sigtype] == listening_datum)
|
|
_listen_lookup -= sigtype
|
|
return NONE
|
|
|
|
// Happy path
|
|
return NONE | call(listening_datum, proc_name)(arglist(arguments))
|
|
|
|
// MULTIPLE LISTENERS
|
|
var/list/listeners = target
|
|
var/list/queued_calls = list()
|
|
|
|
for(var/i in 1 to listeners.len)
|
|
var/datum/listening_datum = listeners[i]
|
|
if(!listening_datum)
|
|
continue
|
|
|
|
var/list/proc_map = listening_datum?._signal_procs?[src]
|
|
if(!proc_map)
|
|
// Listener forgot this source, but the source still remembers the listener.
|
|
stack_trace("Signal mismatch: [src] has [sigtype] in _listen_lookup but [listening_datum] has no _signal_procs entry for it")
|
|
// Self-heal: remove stale listener from src's listener list.
|
|
listeners.Cut(i, i + 1)
|
|
i--
|
|
continue
|
|
|
|
var/proc_name = proc_map[sigtype]
|
|
if(!proc_name)
|
|
// Listener still has a table for this source, but not this signal.
|
|
stack_trace("Signal mismatch: [src] has [sigtype] in _listen_lookup but [listening_datum] has no proc entry for it")
|
|
listeners.Cut(i, i + 1)
|
|
i--
|
|
continue
|
|
|
|
queued_calls += list(listening_datum, proc_name)
|
|
|
|
for(var/i in 1 to queued_calls.len step 2)
|
|
. |= call(queued_calls[i], queued_calls[i + 1])(arglist(arguments))
|