/
signals.dm
126 lines (114 loc) · 4.77 KB
/
signals.dm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* 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]) 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
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))