mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-07-18 19:39:42 +01:00
cb82199c4e
The Schlorrgo test fail has stumped me for too long, on a complete whim I threw it at the AI I normally use for math tutoring. Please do not merge this, I'm making the runners do this repeatedly in order to attempt to actually find out why the test fail is happening. I also tested this to verify the game actually still works. <img width="1081" height="579" alt="image" src="https://github.com/user-attachments/assets/32b9bf86-c4cc-4a09-8776-e0127a964fd1" />
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 = FALSE)
|
|
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 = FALSE)
|
|
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))
|