mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-26 22:50:26 +01:00
Adds instant integrated circuit execution for event handling circuits. (#61205)
This commit is contained in:
@@ -808,6 +808,11 @@
|
||||
/// from /obj/machinery/atmospherics/components/binary/valve/toggle(): (on)
|
||||
#define COMSIG_VALVE_SET_OPEN "valve_toggled"
|
||||
|
||||
// /obj access signals
|
||||
|
||||
#define COMSIG_OBJ_ALLOWED "door_try_to_activate"
|
||||
#define COMPONENT_OBJ_ALLOW (1<<0)
|
||||
|
||||
// /obj/machinery/door/airlock signals
|
||||
|
||||
//from /obj/machinery/door/airlock/open(): (forced)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
/// Helper define that can only be used in /obj/item/circuit_component/input_received()
|
||||
#define COMPONENT_TRIGGERED_BY(trigger, port) (trigger.value && trigger == port)
|
||||
|
||||
/// Define to be placed at any proc that is triggered by a port.
|
||||
#define CIRCUIT_TRIGGER SHOULD_NOT_SLEEP(TRUE)
|
||||
|
||||
// Port defines
|
||||
|
||||
#define PORT_MAX_NAME_LENGTH 50
|
||||
@@ -95,6 +98,8 @@
|
||||
#define CIRCUIT_FLAG_ADMIN (1<<3)
|
||||
/// This circuit component does not show in the menu.
|
||||
#define CIRCUIT_FLAG_HIDDEN (1<<4)
|
||||
/// This circuit component has been marked as a component that has instant execution and will show up in the UI as so. This will only cause a visual change.
|
||||
#define CIRCUIT_FLAG_INSTANT (1<<5)
|
||||
|
||||
// Datatype flags
|
||||
/// The datatype supports manual inputs
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
SUBSYSTEM_DEF(circuit_component)
|
||||
name = "Circuit Components"
|
||||
wait = 0.1 SECONDS
|
||||
priority = FIRE_PRIORITY_DEFAULT
|
||||
|
||||
var/list/callbacks_to_invoke = list()
|
||||
var/list/currentrun = list()
|
||||
|
||||
var/instant_run_tick = 0
|
||||
var/instant_run_start_cpu_usage = 0
|
||||
var/instant_run_max_cpu_usage = 10
|
||||
var/list/instant_run_callbacks_to_run = list()
|
||||
|
||||
/datum/controller/subsystem/circuit_component/fire(resumed)
|
||||
if(!resumed)
|
||||
currentrun = callbacks_to_invoke.Copy()
|
||||
callbacks_to_invoke.Cut()
|
||||
|
||||
while(length(currentrun))
|
||||
var/datum/callback/to_call = currentrun[1]
|
||||
currentrun.Cut(1,2)
|
||||
|
||||
if(QDELETED(to_call))
|
||||
continue
|
||||
|
||||
to_call.user = null
|
||||
to_call.InvokeAsync()
|
||||
qdel(to_call)
|
||||
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/**
|
||||
* Adds a callback to be invoked when the next fire() is done. Used by the integrated circuit system.
|
||||
*
|
||||
* Prevents race conditions as it acts like a queue system.
|
||||
* Those that registered first will be executed first and those registered last will be executed last.
|
||||
*/
|
||||
/datum/controller/subsystem/circuit_component/proc/add_callback(datum/port/input, datum/callback/to_call)
|
||||
if(instant_run_tick == world.time && (TICK_USAGE - instant_run_start_cpu_usage) < instant_run_max_cpu_usage)
|
||||
instant_run_callbacks_to_run += to_call
|
||||
return
|
||||
|
||||
callbacks_to_invoke += to_call
|
||||
|
||||
/// Queues any callbacks to be executed instantly instead of using the subsystem.
|
||||
/datum/controller/subsystem/circuit_component/proc/queue_instant_run()
|
||||
instant_run_tick = world.time
|
||||
instant_run_start_cpu_usage = TICK_USAGE
|
||||
instant_run_callbacks_to_run = list()
|
||||
|
||||
/**
|
||||
* Instantly executes the stored callbacks and does this in a loop until there are no stored callbacks or it hits tick limit.
|
||||
*
|
||||
* Returns a list containing any values added by any input port.
|
||||
*/
|
||||
/datum/controller/subsystem/circuit_component/proc/execute_instant_run()
|
||||
var/list/received_inputs = list()
|
||||
while(length(instant_run_callbacks_to_run))
|
||||
var/list/instant_run_currentrun = instant_run_callbacks_to_run
|
||||
instant_run_callbacks_to_run = list()
|
||||
while(length(instant_run_currentrun))
|
||||
var/datum/callback/to_call = instant_run_currentrun[1]
|
||||
instant_run_currentrun.Cut(1,2)
|
||||
to_call.user = null
|
||||
to_call.InvokeAsync(received_inputs)
|
||||
qdel(to_call)
|
||||
|
||||
instant_run_tick = 0
|
||||
if((TICK_USAGE - instant_run_start_cpu_usage) < instant_run_max_cpu_usage)
|
||||
return received_inputs
|
||||
else
|
||||
return null
|
||||
@@ -1,35 +0,0 @@
|
||||
SUBSYSTEM_DEF(circuit_component)
|
||||
name = "Circuit Components"
|
||||
wait = 0.1 SECONDS
|
||||
priority = FIRE_PRIORITY_DEFAULT
|
||||
|
||||
var/list/callbacks_to_invoke = list()
|
||||
var/list/currentrun = list()
|
||||
|
||||
/datum/controller/subsystem/circuit_component/fire(resumed)
|
||||
if(!resumed)
|
||||
currentrun = callbacks_to_invoke.Copy()
|
||||
callbacks_to_invoke.Cut()
|
||||
|
||||
while(length(currentrun))
|
||||
var/datum/callback/to_call = currentrun[1]
|
||||
currentrun.Cut(1,2)
|
||||
|
||||
if(QDELETED(to_call))
|
||||
continue
|
||||
|
||||
to_call.user = null
|
||||
to_call.InvokeAsync()
|
||||
qdel(to_call)
|
||||
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/**
|
||||
* Adds a callback to be invoked when the next fire() is done. Used by the integrated circuit system.
|
||||
*
|
||||
* Prevents race conditions as it acts like a queue system.
|
||||
* Those that registered first will be executed first and those registered last will be executed last.
|
||||
*/
|
||||
/datum/controller/subsystem/circuit_component/proc/add_callback(datum/callback/to_call)
|
||||
callbacks_to_invoke += to_call
|
||||
+15
-13
@@ -1,31 +1,33 @@
|
||||
|
||||
//returns TRUE if this mob has sufficient access to use this object
|
||||
/obj/proc/allowed(mob/M)
|
||||
/obj/proc/allowed(mob/accessor)
|
||||
if(SEND_SIGNAL(src, COMSIG_OBJ_ALLOWED, accessor) & COMPONENT_OBJ_ALLOW)
|
||||
return TRUE
|
||||
//check if it doesn't require any access at all
|
||||
if(src.check_access(null))
|
||||
return TRUE
|
||||
if(issilicon(M))
|
||||
if(ispAI(M))
|
||||
if(issilicon(accessor))
|
||||
if(ispAI(accessor))
|
||||
return FALSE
|
||||
return TRUE //AI can do whatever it wants
|
||||
if(isAdminGhostAI(M))
|
||||
if(isAdminGhostAI(accessor))
|
||||
//Access can't stop the abuse
|
||||
return TRUE
|
||||
else if(istype(M) && SEND_SIGNAL(M, COMSIG_MOB_ALLOWED, src))
|
||||
else if(istype(accessor) && SEND_SIGNAL(accessor, COMSIG_MOB_ALLOWED, src))
|
||||
return TRUE
|
||||
else if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
else if(ishuman(accessor))
|
||||
var/mob/living/carbon/human/human_accessor = accessor
|
||||
//if they are holding or wearing a card that has access, that works
|
||||
if(check_access(H.get_active_held_item()) || src.check_access(H.wear_id))
|
||||
if(check_access(human_accessor.get_active_held_item()) || src.check_access(human_accessor.wear_id))
|
||||
return TRUE
|
||||
else if(isalienadult(M))
|
||||
var/mob/living/carbon/george = M
|
||||
else if(isalienadult(accessor))
|
||||
var/mob/living/carbon/george = accessor
|
||||
//they can only hold things :(
|
||||
if(check_access(george.get_active_held_item()))
|
||||
return TRUE
|
||||
else if(isanimal(M))
|
||||
var/mob/living/simple_animal/A = M
|
||||
if(check_access(A.get_active_held_item()) || check_access(A.access_card))
|
||||
else if(isanimal(accessor))
|
||||
var/mob/living/simple_animal/animal = accessor
|
||||
if(check_access(animal.get_active_held_item()) || check_access(animal.access_card))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
/// The name of the component shown on the UI
|
||||
var/display_name = "Generic"
|
||||
|
||||
/// The colour this circuit component appears in the UI
|
||||
var/ui_color = "blue"
|
||||
|
||||
/// The integrated_circuit that this component is attached to.
|
||||
var/obj/item/integrated_circuit/parent
|
||||
|
||||
@@ -70,6 +73,8 @@
|
||||
trigger_input = add_input_port("Trigger", PORT_TYPE_SIGNAL, order = 2)
|
||||
if(circuit_flags & CIRCUIT_FLAG_OUTPUT_SIGNAL)
|
||||
trigger_output = add_output_port("Triggered", PORT_TYPE_SIGNAL, order = 2)
|
||||
if(circuit_flags & CIRCUIT_FLAG_INSTANT)
|
||||
ui_color = "orange"
|
||||
|
||||
|
||||
/obj/item/circuit_component/Destroy()
|
||||
@@ -184,8 +189,9 @@
|
||||
* Return value indicates whether the trigger was successful or not.
|
||||
* Arguments:
|
||||
* * port - Can be null. The port that sent the input
|
||||
* * return_values - Only defined if the component is receiving an input due to instant execution. Contains the values to be returned once execution has stopped.
|
||||
*/
|
||||
/obj/item/circuit_component/proc/trigger_component(datum/port/input/port)
|
||||
/obj/item/circuit_component/proc/trigger_component(datum/port/input/port, list/return_values)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
pre_input_received(port)
|
||||
if(!should_receive_input(port))
|
||||
@@ -196,9 +202,9 @@
|
||||
var/proc_to_call = port.trigger
|
||||
if(!proc_to_call)
|
||||
return FALSE
|
||||
result = call(src, proc_to_call)(port)
|
||||
result = call(src, proc_to_call)(port, return_values)
|
||||
else
|
||||
result = input_received()
|
||||
result = input_received(null, return_values)
|
||||
|
||||
if(result)
|
||||
return FALSE
|
||||
@@ -248,8 +254,9 @@
|
||||
* Return value indicates that the circuit should not send an output signal.
|
||||
* Arguments:
|
||||
* * port - Can be null. The port that sent the input
|
||||
* * return_values - Only defined if the component is receiving an input due to instant execution. Contains the values to be returned once execution has stopped.
|
||||
*/
|
||||
/obj/item/circuit_component/proc/input_received(datum/port/input/port)
|
||||
/obj/item/circuit_component/proc/input_received(datum/port/input/port, list/return_values)
|
||||
SHOULD_NOT_SLEEP(TRUE)
|
||||
return
|
||||
|
||||
@@ -274,6 +281,9 @@
|
||||
/obj/item/circuit_component/proc/get_ui_notices()
|
||||
. = list()
|
||||
|
||||
if(circuit_flags & CIRCUIT_FLAG_INSTANT)
|
||||
. += create_ui_notice("Instant Execution", "red", "tachometer-alt")
|
||||
|
||||
if(!removable)
|
||||
. += create_ui_notice("Unremovable", "red", "lock")
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit)
|
||||
"connected_to" = connected_to,
|
||||
"color" = port.color,
|
||||
"current_data" = current_data,
|
||||
"datatype_data" = port.datatype_ui_data(user),
|
||||
"datatype_data" = port.datatype_ui_data(user)
|
||||
))
|
||||
component_data["output_ports"] = list()
|
||||
for(var/datum/port/output/port as anything in component.output_ports)
|
||||
@@ -293,6 +293,7 @@ GLOBAL_LIST_EMPTY_TYPED(integrated_circuits, /obj/item/integrated_circuit)
|
||||
component_data["x"] = component.rel_x
|
||||
component_data["y"] = component.rel_y
|
||||
component_data["removable"] = component.removable
|
||||
component_data["color"] = component.ui_color
|
||||
.["components"] += list(component_data)
|
||||
|
||||
.["variables"] = list()
|
||||
|
||||
@@ -57,12 +57,12 @@
|
||||
/**
|
||||
* Updates the value of the input and calls input_received on the connected component
|
||||
*/
|
||||
/datum/port/input/proc/set_input(value)
|
||||
/datum/port/input/proc/set_input(value, list/return_values)
|
||||
if(QDELETED(src)) //Pain
|
||||
return
|
||||
set_value(value)
|
||||
if(trigger)
|
||||
connected_component.trigger_component(src)
|
||||
connected_component.trigger_component(src, return_values)
|
||||
|
||||
/datum/port/output/proc/set_output(value)
|
||||
set_value(value)
|
||||
@@ -204,7 +204,7 @@
|
||||
*/
|
||||
/datum/port/input/proc/receive_value(datum/port/output/output, value)
|
||||
SIGNAL_HANDLER
|
||||
SScircuit_component.add_callback(CALLBACK(src, .proc/set_input, value))
|
||||
SScircuit_component.add_callback(src, CALLBACK(src, .proc/set_input, value))
|
||||
|
||||
/// Signal handler proc to null the input if an atom is deleted. An update is not sent because this was not set by anything.
|
||||
/datum/port/proc/null_value(datum/source)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
. = ..()
|
||||
AddComponent( \
|
||||
/datum/component/shell, \
|
||||
unremovable_circuit_components = list(new /obj/item/circuit_component/airlock), \
|
||||
unremovable_circuit_components = list(new /obj/item/circuit_component/airlock, new /obj/item/circuit_component/airlock_access_event), \
|
||||
capacity = SHELL_CAPACITY_LARGE, \
|
||||
shell_flags = SHELL_FLAG_ALLOW_FAILURE_ACTION \
|
||||
)
|
||||
@@ -37,7 +37,7 @@
|
||||
display_name = "Airlock"
|
||||
desc = "The general interface with an airlock. Includes general statuses of the airlock"
|
||||
|
||||
/// Called when attack_hand is called on the shell.
|
||||
/// The shell, if it is an airlock.
|
||||
var/obj/machinery/door/airlock/attached_airlock
|
||||
|
||||
/// Bolts the airlock (if possible)
|
||||
@@ -127,3 +127,65 @@
|
||||
INVOKE_ASYNC(attached_airlock, /obj/machinery/door/airlock.proc/open)
|
||||
if(COMPONENT_TRIGGERED_BY(close, port) && !attached_airlock.density)
|
||||
INVOKE_ASYNC(attached_airlock, /obj/machinery/door/airlock.proc/close)
|
||||
|
||||
|
||||
/obj/item/circuit_component/airlock_access_event
|
||||
display_name = "Airlock Access Event"
|
||||
desc = "An event that can be handled through circuit components to determine if the door should open or not for an entity that might be trying to access it."
|
||||
circuit_flags = CIRCUIT_FLAG_INSTANT
|
||||
|
||||
/// The shell, if it is an airlock.
|
||||
var/obj/machinery/door/airlock/attached_airlock
|
||||
|
||||
/// Tells the event to open the airlock.
|
||||
var/datum/port/input/open_airlock
|
||||
|
||||
/// The person trying to open the airlock.
|
||||
var/datum/port/output/accessing_entity
|
||||
|
||||
/// The signal sent when this event is triggered
|
||||
var/datum/port/output/event_triggered
|
||||
|
||||
|
||||
/obj/item/circuit_component/airlock_access_event/register_shell(atom/movable/shell)
|
||||
. = ..()
|
||||
if(istype(shell, /obj/machinery/door/airlock))
|
||||
attached_airlock = shell
|
||||
RegisterSignal(shell, COMSIG_OBJ_ALLOWED, .proc/handle_allowed)
|
||||
|
||||
/obj/item/circuit_component/airlock_access_event/unregister_shell(atom/movable/shell)
|
||||
attached_airlock = null
|
||||
UnregisterSignal(shell, list(
|
||||
COMSIG_OBJ_ALLOWED,
|
||||
))
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/item/circuit_component/airlock_access_event/populate_ports()
|
||||
open_airlock = add_input_port("Should Open Airlock", PORT_TYPE_SIGNAL, trigger = .proc/should_open_airlock)
|
||||
accessing_entity = add_output_port("Accessing Entity", PORT_TYPE_ATOM)
|
||||
event_triggered = add_output_port("Event Triggered", PORT_TYPE_SIGNAL)
|
||||
|
||||
|
||||
/obj/item/circuit_component/airlock_access_event/proc/should_open_airlock(datum/port/input/port, list/return_values)
|
||||
CIRCUIT_TRIGGER
|
||||
if(!return_values)
|
||||
return
|
||||
return_values["should_open"] = TRUE
|
||||
|
||||
/obj/item/circuit_component/airlock_access_event/proc/handle_allowed(datum/source, mob/accesser)
|
||||
SIGNAL_HANDLER
|
||||
if(!attached_airlock)
|
||||
return
|
||||
|
||||
SScircuit_component.queue_instant_run()
|
||||
accessing_entity.set_output(accesser)
|
||||
event_triggered.set_output(COMPONENT_SIGNAL)
|
||||
var/list/result = SScircuit_component.execute_instant_run()
|
||||
|
||||
if(!result)
|
||||
attached_airlock.visible_message(span_warning("[attached_airlock]'s circuitry overheats!"))
|
||||
return
|
||||
|
||||
if(result["should_open"])
|
||||
return COMPONENT_OBJ_ALLOW
|
||||
|
||||
+1
-1
@@ -324,10 +324,10 @@
|
||||
#include "code\controllers\subsystem\blackbox.dm"
|
||||
#include "code\controllers\subsystem\blackmarket.dm"
|
||||
#include "code\controllers\subsystem\chat.dm"
|
||||
#include "code\controllers\subsystem\circuit_component.dm"
|
||||
#include "code\controllers\subsystem\communications.dm"
|
||||
#include "code\controllers\subsystem\dbcore.dm"
|
||||
#include "code\controllers\subsystem\dcs.dm"
|
||||
#include "code\controllers\subsystem\delay_component.dm"
|
||||
#include "code\controllers\subsystem\discord.dm"
|
||||
#include "code\controllers\subsystem\disease.dm"
|
||||
#include "code\controllers\subsystem\economy.dm"
|
||||
|
||||
Reference in New Issue
Block a user