Files
Bubberstation/code/modules/wiremod/components/admin/signal_handler/signal_handler.dm
AnturK 4d6a8bc537 515 Compatibility (#71161)
Makes the code compatible with 515.1594+

Few simple changes and one very painful one.
Let's start with the easy:
* puts call behind `LIBCALL` define, so call_ext is properly used in 515
* Adds `NAMEOF_STATIC(_,X)` macro for nameof in static definitions since
src is now invalid there.
* Fixes tgui and devserver. From 515 onward the tmp3333{procid} cache
directory is not appened to base path in browser controls so we don't
check for it in base js and put the dev server dummy window file in
actual directory not the byond root.
* Renames the few things that had /final/ in typepath to ultimate since
final is a new keyword

And the very painful change:
`.proc/whatever` format is no longer valid, so we're replacing it with
new nameof() function. All this wrapped in three new macros.
`PROC_REF(X)`,`TYPE_PROC_REF(TYPE,X)`,`GLOBAL_PROC_REF(X)`. Global is
not actually necessary but if we get nameof that does not allow globals
it would be nice validation.
This is pretty unwieldy but there's no real alternative.
If you notice anything weird in the commits let me know because majority
was done with regex replace.

@tgstation/commit-access Since the .proc/stuff is pretty big change.

Co-authored-by: san7890 <the@san7890.com>
Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com>
2022-11-15 03:50:11 +00:00

230 lines
8.4 KiB
Plaintext

#define COMP_SIGNAL_HANDLER_GLOBAL "Global"
#define COMP_SIGNAL_HANDLER_OBJECT "Object"
/**
* # Signal Handler Component
*
* A component that registers signals on events and listens for them.
*/
/obj/item/circuit_component/signal_handler
display_name = "Signal Handler"
desc = "A component that listens for signals on an object."
category = "Admin"
circuit_flags = CIRCUIT_FLAG_ADMIN|CIRCUIT_FLAG_INSTANT
/// Whether it is a global or object signal
var/datum/port/input/option/signal_handler_options
/// The list of signal IDs that can be selected as an option.
var/datum/port/input/option/signal_id
/// Whether this executes instantly or not. If set to 0, this will not execute instantly.
var/datum/port/input/instant
var/list/signal_map
/// Entity to register the signal on
var/datum/port/input/target
/// Registers the signal
var/datum/port/input/register
/// Unregisters the signal on the target.
var/datum/port/input/unregister
/// Unregisters the signal from everyone.
var/datum/port/input/unregister_all
/// The custom signal ports from the current signal type. Used for saving and loading.
var/list/signal_ports
/// The custom input from the current signal type.
var/list/datum/port/input/input_signal_ports = list()
/// The custom output from the current signal type.
var/list/datum/port/output/output_signal_ports = list()
/// The entity received from the event.
var/datum/port/output/entity
/// The event has been triggered
var/datum/port/output/event_triggered
/// The current entities that have the signal registered on it
var/list/datum/weakref/registered_entities = list()
/// The current registered signal
var/registered_signal
/// Whether it is a custom signal id or not.
var/custom_signal = FALSE
/obj/item/circuit_component/signal_handler/populate_options()
var/static/list/component_options = list(
COMP_SIGNAL_HANDLER_OBJECT,
COMP_SIGNAL_HANDLER_GLOBAL,
)
signal_handler_options = add_option_port("Signal Handler Options", component_options, trigger = null)
signal_id = add_option_port("Signal ID", GLOB.integrated_circuit_signal_ids, trigger = null)
signal_map = GLOB.integrated_circuit_signal_ids
/obj/item/circuit_component/signal_handler/populate_ports()
instant = add_input_port("Instant", PORT_TYPE_NUMBER, order = 0.5, trigger = null, default = 1)
register = add_input_port("Register", PORT_TYPE_SIGNAL, order = 2, trigger = PROC_REF(register_signals))
unregister = add_input_port("Unregister", PORT_TYPE_SIGNAL, order = 2, trigger = PROC_REF(unregister_signals))
unregister_all = add_input_port("Unregister All", PORT_TYPE_SIGNAL, order = 2, trigger = PROC_REF(unregister_signals_all))
add_source_entity()
event_triggered = add_output_port("Triggered", PORT_TYPE_INSTANT_SIGNAL, order = 2)
/obj/item/circuit_component/signal_handler/proc/add_source_entity()
if(target)
remove_input_port(target)
if(entity)
remove_output_port(entity)
target = add_input_port("Target", PORT_TYPE_DATUM, order = 1, trigger = null)
entity = add_output_port("Source Entity", PORT_TYPE_DATUM, order = 0)
/obj/item/circuit_component/signal_handler/save_data_to_list(list/component_data)
. = ..()
component_data["signal_id"] = signal_id.value
component_data["signal_port_data"] = signal_ports
/obj/item/circuit_component/signal_handler/load_data_from_list(list/component_data)
signal_id.set_value(component_data["signal_id"], force = TRUE)
registered_signal = signal_id.value
load_new_ports(component_data["signal_port_data"])
custom_signal = TRUE
return ..()
/obj/item/circuit_component/signal_handler/pre_input_received(datum/port/input/port)
if(signal_id.value != registered_signal)
custom_signal = FALSE
unregister_signals_all(port)
registered_signal = signal_id.value
var/list/data = signal_map[registered_signal]
if(data)
load_new_ports(data)
if(signal_handler_options == port)
set_signal_options(port)
/obj/item/circuit_component/signal_handler/proc/set_signal_options(datum/port/input/port)
CIRCUIT_TRIGGER
switch(signal_handler_options.value)
if(COMP_SIGNAL_HANDLER_GLOBAL)
signal_id.possible_options = GLOB.integrated_circuit_global_signal_ids
signal_map = GLOB.integrated_circuit_global_signal_ids
remove_output_port(entity)
remove_input_port(target)
target = null
entity = null
if(COMP_SIGNAL_HANDLER_OBJECT)
signal_id.possible_options = GLOB.integrated_circuit_signal_ids
signal_map = GLOB.integrated_circuit_signal_ids
add_source_entity()
if(!custom_signal)
signal_id.set_value(null, TRUE)
unregister_signals_all(port)
/obj/item/circuit_component/signal_handler/proc/register_signals(datum/port/input/port)
CIRCUIT_TRIGGER
var/datum/target_datum = target?.value
if(signal_handler_options.value == COMP_SIGNAL_HANDLER_GLOBAL)
target_datum = SSdcs
if(target_datum)
log_admin_circuit("[parent.get_creator()] registered the signal '[registered_signal]' on [target_datum]")
// We override because an admin may try registering a signal on the same object/datum again, so this prevents any runtimes from occuring
RegisterSignal(target_datum, registered_signal, PROC_REF(handle_signal_received), override = TRUE)
registered_entities |= WEAKREF(target_datum)
/obj/item/circuit_component/signal_handler/proc/load_new_ports(list/ports_to_load)
for(var/datum/port/input/input_port as anything in input_signal_ports)
remove_input_port(input_port)
for(var/datum/port/output/output_port as anything in output_signal_ports)
remove_output_port(output_port)
input_signal_ports = list()
output_signal_ports = list()
signal_ports = ports_to_load
for(var/list/data in signal_ports)
if(data["is_response"])
var/datum/port/input/bitflag_input = add_input_port(data["name"], PORT_TYPE_SIGNAL, order = 3, trigger = PROC_REF(handle_bitflag_received))
input_signal_ports[bitflag_input] = data["bitflag"]
else
output_signal_ports += add_output_port(data["name"], data["type"], order = 1)
/obj/item/circuit_component/signal_handler/proc/unregister_signals_all(datum/port/input/port)
CIRCUIT_TRIGGER
for(var/datum/weakref/weakref_of_object as anything in registered_entities)
var/datum/datum_to_unregister = weakref_of_object.resolve()
if(!datum_to_unregister)
continue
UnregisterSignal(datum_to_unregister, registered_signal)
registered_entities.Cut()
/obj/item/circuit_component/signal_handler/proc/unregister_signals(datum/port/input/port)
CIRCUIT_TRIGGER
var/datum/registered_datum = target?.value
if(signal_handler_options.value == COMP_SIGNAL_HANDLER_GLOBAL)
registered_datum = SSdcs
if(!registered_datum)
return
UnregisterSignal(registered_datum, registered_signal)
registered_entities -= WEAKREF(registered_datum)
/obj/item/circuit_component/signal_handler/proc/run_ports_on_args(list/arguments)
var/first_arg = popleft(arguments)
if(entity)
entity.set_output(first_arg)
for(var/datum/port/output/port as anything in output_signal_ports)
port.set_output(popleft(arguments))
event_triggered.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/signal_handler/proc/handle_signal_received(...)
SIGNAL_HANDLER
var/list/arguments = args.Copy()
if(!instant.value)
run_ports_on_args(arguments)
return
// usr is not supposed to be defined whilst these execute, which it can be for some signal IDs.
// Especially if you try to proccall something - it'll fail because of this reason.
// No other way to solve this problem without refactoring proccall code, but it's admin tooling so it's whatever.
var/temp_usr = usr
usr = null
var/list/displayArgs = arguments.Copy()
log_admin_circuit("[parent.get_creator()] received a signal from [popleft(displayArgs)] ([registered_signal]) with the parameters \[[displayArgs.Join(", ")]]")
SScircuit_component.queue_instant_run()
run_ports_on_args(arguments)
var/list/output = SScircuit_component.execute_instant_run()
usr = temp_usr
if(!output)
message_admins("[parent.get_creator_admin()] took too much CPU time trying to handle a signal. Reduce the amount of circuit components attached to your [name] circuit component.")
return
return output["bitflag"] || NONE
/obj/item/circuit_component/signal_handler/proc/handle_bitflag_received(datum/port/input/port, list/return_values)
CIRCUIT_TRIGGER
if(!return_values)
return
if(!return_values["bitflag"])
return_values["bitflag"] = NONE
var/bitflag = input_signal_ports[port]
log_admin_circuit("[parent.get_creator()] received bitflag [bitflag] for '[registered_signal]'")
return_values["bitflag"] |= bitflag
#undef COMP_SIGNAL_HANDLER_GLOBAL
#undef COMP_SIGNAL_HANDLER_OBJECT