[MIRROR] Adds an admin signal handler circuit component (#8218)

* Adds an admin signal handler circuit component

* Update FundamentalTypes.js

Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com>
Co-authored-by: Gandalf <jzo123@hotmail.com>
This commit is contained in:
SkyratBot
2021-09-19 23:10:10 +02:00
committed by GitHub
parent 9269849f62
commit c302124fd5
15 changed files with 610 additions and 15 deletions
@@ -42,7 +42,6 @@
output_value = add_output_port("Output Value", PORT_TYPE_ANY)
/obj/item/circuit_component/proccall/input_received(datum/port/input/port)
var/called_on
if(proccall_options.value == COMP_PROC_OBJECT)
called_on = entity.value
@@ -58,6 +57,9 @@
if(!to_invoke)
return
INVOKE_ASYNC(src, .proc/do_proccall, called_on, to_invoke, params)
/obj/item/circuit_component/proccall/proc/do_proccall(called_on, to_invoke, params)
GLOB.AdminProcCaller = "CHAT_[parent.display_name]" //_ won't show up in ckeys so it'll never match with a real admin
var/result = WrapAdminProcCall(called_on, to_invoke, params)
GLOB.AdminProcCaller = null
@@ -0,0 +1,203 @@
#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. Registering a new object will automatically unregister the old."
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
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 from the current registered entity.
var/datum/port/input/unregister
/// 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 entity that has the signal registered on it
var/datum/weakref/current_registered_entity
/// 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()
register = add_input_port("Register", PORT_TYPE_SIGNAL, order = 2, trigger = .proc/register_signals)
unregister = add_input_port("Unregister Current", PORT_TYPE_SIGNAL, order = 2, trigger = .proc/unregister_signals)
add_source_entity()
event_triggered = add_output_port("Triggered", PORT_TYPE_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_ATOM, order = 1, trigger = null)
entity = add_output_port("Source Entity", PORT_TYPE_ATOM, 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 == port)
custom_signal = FALSE
if(current_registered_entity)
unregister_signals(port)
var/last_registered_signal = registered_signal
registered_signal = signal_id.value
if(registered_signal != last_registered_signal)
var/list/data = signal_map[registered_signal]
if(data)
load_new_ports(data)
unregister_signals(port)
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)
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()
/obj/item/circuit_component/signal_handler/proc/register_signals(datum/port/input/port)
CIRCUIT_TRIGGER
if(current_registered_entity)
unregister_signals(port)
var/datum/target_datum = target.value
if(signal_handler_options.value == COMP_SIGNAL_HANDLER_GLOBAL)
target_datum = SSdcs
if(target_datum)
RegisterSignal(target_datum, registered_signal, .proc/handle_signal_received)
current_registered_entity = 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 = 1, trigger = .proc/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(datum/port/input/port)
CIRCUIT_TRIGGER
var/datum/registered_datum = current_registered_entity?.resolve()
if(!registered_datum)
return
UnregisterSignal(registered_datum, registered_signal)
current_registered_entity = null
/obj/item/circuit_component/signal_handler/proc/handle_signal_received(...)
SIGNAL_HANDLER
var/list/arguments = args.Copy()
// 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
SScircuit_component.queue_instant_run()
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)
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
return_values["bitflag"] |= input_signal_ports[port]
#undef COMP_SIGNAL_HANDLER_GLOBAL
#undef COMP_SIGNAL_HANDLER_OBJECT
@@ -0,0 +1,103 @@
/proc/circuit_signal_response(name, bitflag)
SHOULD_BE_PURE(TRUE)
return list(
"name" = name,
"bitflag" = bitflag,
"is_response" = TRUE
)
/proc/circuit_signal_param(name, type)
SHOULD_BE_PURE(TRUE)
return list(
"name" = name,
"type" = type,
"is_response" = FALSE
)
GLOBAL_LIST_INIT(integrated_circuit_signal_ids, generate_circuit_signal_list())
/proc/generate_circuit_signal_list()
var/cancel_attack = circuit_signal_response("Cancel Attack", COMPONENT_CANCEL_ATTACK_CHAIN)
var/target = circuit_signal_param("Target", PORT_TYPE_ATOM)
var/user = circuit_signal_param("User", PORT_TYPE_ATOM)
var/item = circuit_signal_param("Item", PORT_TYPE_ATOM)
var/entity = circuit_signal_param("Entity", PORT_TYPE_ATOM)
return list(
COMSIG_PARENT_QDELETING = list(),
COMSIG_PARENT_ATTACKBY = list(circuit_signal_response("Cancel Attack", COMPONENT_NO_AFTERATTACK), item, user),
COMSIG_PARENT_ATTACKBY_SECONDARY = list(circuit_signal_response("Cancel Attack", COMPONENT_NO_AFTERATTACK), item, user),
COMSIG_PARENT_EXAMINE = list(user),
COMSIG_ATOM_ATTACK_HAND = list(cancel_attack, user),
COMSIG_ATOM_ATTACK_GHOST = list(cancel_attack, user),
COMSIG_ATOM_BUMPED = list(entity),
COMSIG_ATOM_HITBY = list(entity),
COMSIG_ITEM_ATTACK = list(cancel_attack, target, user),
COMSIG_ITEM_PRE_ATTACK = list(cancel_attack, target, user),
COMSIG_ITEM_AFTERATTACK = list(cancel_attack, target, user),
COMSIG_ITEM_ATTACK_SECONDARY = list(circuit_signal_response("Cancel Attack", COMPONENT_SECONDARY_CANCEL_ATTACK_CHAIN), target, user),
COMSIG_ITEM_ATTACK_SELF = list(cancel_attack, user),
COMSIG_ITEM_ATTACK_SELF_SECONDARY = list(cancel_attack, user),
)
GLOBAL_LIST_INIT(integrated_circuit_global_signal_ids, generate_global_circuit_signal_list())
/proc/generate_global_circuit_signal_list()
var/client_ent = circuit_signal_param("Client", PORT_TYPE_ATOM)
var/entity = circuit_signal_param("Entity", PORT_TYPE_ATOM)
return list(
COMSIG_GLOB_MOB_DEATH = list(entity, circuit_signal_param("Gibbed", PORT_TYPE_NUMBER)),
COMSIG_GLOB_MOB_CREATED = list(entity),
COMSIG_GLOB_CLIENT_CONNECT = list(client_ent),
COMSIG_GLOB_JOB_AFTER_LATEJOIN_SPAWN = list(circuit_signal_param("Job Subsystem", PORT_TYPE_ATOM), entity, client_ent),
COMSIG_GLOB_CREWMEMBER_JOINED = list(circuit_signal_param("Crewmember", PORT_TYPE_ATOM), circuit_signal_param("Rank", PORT_TYPE_STRING))
)
/obj/item/circuit_component/signal_handler/ui_state(mob/user)
return GLOB.admin_state
/obj/item/circuit_component/signal_handler/ui_static_data(mob/user)
. = list()
.["global_port_types"] = GLOB.wiremod_fundamental_types
/obj/item/circuit_component/signal_handler/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "CircuitSignalHandler", name)
ui.open()
ui.set_autoupdate(FALSE)
/obj/item/circuit_component/signal_handler/ui_act(action, list/params, datum/tgui/ui)
. = ..()
if(.)
return
var/signal_id = params["signal_id"]
if(!istext(signal_id))
return
var/list/responses = params["responses"]
var/list/parameters = params["parameters"]
if(!islist(responses) || !islist(parameters))
return
var/list/sanitized_data = list()
for(var/list/data as anything in responses)
sanitized_data += list(circuit_signal_response(data["name"], text2num(data["bitflag"])))
for(var/list/data as anything in parameters)
sanitized_data += list(circuit_signal_param(data["name"], data["datatype"]))
var/extra_info = ""
if(params["global"])
GLOB.integrated_circuit_global_signal_ids[signal_id] = sanitized_data
extra_info = " as a global signal"
else
GLOB.integrated_circuit_signal_ids[signal_id] = sanitized_data
balloon_alert(usr, "successfully added [signal_id][extra_info]")
+7 -1
View File
@@ -10,11 +10,17 @@
/// The shell this component is attached to.
var/datum/port/output/output
/// The signal sent when the status is updated.
var/datum/port/output/shell_received
/obj/item/circuit_component/self/populate_ports()
output = add_output_port("Self", PORT_TYPE_ATOM)
output = add_output_port("Shell", PORT_TYPE_ATOM)
shell_received = add_output_port("Shell Updated", PORT_TYPE_SIGNAL)
/obj/item/circuit_component/self/register_shell(atom/movable/shell)
output.set_output(shell)
shell_received.set_output(COMPONENT_SIGNAL)
/obj/item/circuit_component/self/unregister_shell(atom/movable/shell)
output.set_output(null)
shell_received.set_output(COMPONENT_SIGNAL)