/** * 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 it's 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]) Proc: [proctype]" //log_signal(override_message) // We don't have log_signal log_world(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 if(!_signal_procs || !_signal_procs[target] || !lookup) return if(!islist(sig_type_or_types)) sig_type_or_types = list(sig_type_or_types) for(var/sig in sig_type_or_types) if(!_signal_procs[target][sig]) if(!istext(sig)) stack_trace("We're unregistering with something that isn't a valid signal \[[sig]\], you fucked up") continue switch(length(lookup[sig])) if(2) lookup[sig] = (lookup[sig]-src)[1] if(1) stack_trace("[target] ([target.type]) somehow has single length list inside _listen_lookup") if(src in lookup[sig]) lookup -= sig if(!length(lookup)) target._listen_lookup = null break if(0) if(lookup[sig] != src) continue lookup -= sig if(!length(lookup)) target._listen_lookup = null break else lookup[sig] -= src _signal_procs[target] -= sig_type_or_types if(!_signal_procs[target].len) _signal_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] if(!length(target)) var/datum/listening_datum = target return NONE | call(listening_datum, listening_datum._signal_procs[src][sigtype])(arglist(arguments)) . = NONE // This exists so that even if one of the signal receivers unregisters the signal, // all the objects that are receiving the signal get the signal this final time. // AKA: No you can't cancel the signal reception of another object by doing an unregister in the same signal. var/list/queued_calls = list() for(var/datum/listening_datum as anything in target) queued_calls[listening_datum] = listening_datum._signal_procs[src][sigtype] for(var/datum/listening_datum as anything in queued_calls) . |= call(listening_datum, queued_calls[listening_datum])(arglist(arguments))