From 80ec1974577ec20d5bcb86724986d98e2b7e8045 Mon Sep 17 00:00:00 2001 From: Y0SH1M4S73R Date: Fri, 16 May 2025 22:49:57 -0400 Subject: [PATCH] Fixes USB ports/cables erroneously detaching when shuttles move (#91043) ## About The Pull Request When a shuttle moves, usb ports now wait for the shuttle to finish moving all of its contents to determine whether they should detach from whatever they are connected to. This PR also adds a new signal whose registered handlers can affect what gets moved when part of a shuttle moves, but until a handler for that signal actually needs to do so, that behavior only really matters in the sense that it is now exposed to lua scripting. --- .../signals_atom/signals_atom_movement.dm | 8 ++- code/datums/components/usb_port.dm | 45 ++++++++++++++ .../mobile_port/shuttle_move_callbacks.dm | 4 +- code/modules/wiremod/core/usb_cable.dm | 62 +++++++++---------- 4 files changed, 85 insertions(+), 34 deletions(-) diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm index 63ad7655be7..d468be8bf80 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_movement.dm @@ -13,7 +13,13 @@ #define COMSIG_ATOM_INTERCEPT_Z_FALL "movable_intercept_z_impact" ///signal sent out by an atom upon onZImpact : (turf/impacted_turf, levels) #define COMSIG_ATOM_ON_Z_IMPACT "movable_on_z_impact" -///Signal sent after an atom movable moves on shuttle. +/// From base of /atom/movable/beforeShuttleMove (turf/newT, direction, move_mode, /obj/docking_port/mobile/moving_dock) +#define COMSIG_ATOM_BEFORE_SHUTTLE_MOVE "movable_before_shuttle_move" + // Docking turf movement return values - return a combination of these to override the move_mode for the turf containing the atom + #define COMPONENT_MOVE_TURF MOVE_TURF + #define COMPONENT_MOVE_AREA MOVE_AREA + #define COMPONENT_MOVE_CONTENTS MOVE_CONTENTS +/// From base of /atom/movable/afterShuttleMove (turf/oldT) #define COMSIG_ATOM_AFTER_SHUTTLE_MOVE "movable_after_shuttle_move" ///called on a movable (NOT living) when it starts pulling (atom/movable/pulled, state, force) #define COMSIG_ATOM_START_PULL "movable_start_pull" diff --git a/code/datums/components/usb_port.dm b/code/datums/components/usb_port.dm index 051101fdbb4..c996475a239 100644 --- a/code/datums/components/usb_port.dm +++ b/code/datums/components/usb_port.dm @@ -1,5 +1,11 @@ +/// Range checking is being deferred because the component's parent is being moved by a shuttle +#define PARENT_DEFERRED (1<<0) +/// Range checking is being deferred because the circuit shell being tracked is being moved by a shuttle +#define PHYSICAL_OBJECT_DEFERRED (1<<1) + /// Opens up a USB port that can be connected to by circuits, creating registerable circuit components /datum/component/usb_port + dupe_mode = COMPONENT_DUPE_UNIQUE /// The component types to create when something plugs in var/list/circuit_component_types @@ -18,6 +24,9 @@ /// The current physical object that the beam is connected to and listens to. var/atom/movable/physical_object + /// Used to prevent range checking during shuttle movement, which moves atoms en-masse. + var/defer_range_checks = 0 + /datum/component/usb_port/Initialize(list/circuit_component_types) if (!isatom(parent)) return COMPONENT_INCOMPATIBLE @@ -48,6 +57,8 @@ /datum/component/usb_port/RegisterWithParent() RegisterSignal(parent, COMSIG_ATOM_USB_CABLE_TRY_ATTACH, PROC_REF(on_atom_usb_cable_try_attach)) RegisterSignal(parent, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(parent, COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, PROC_REF(before_parent_shuttle_move)) + RegisterSignal(parent, COMSIG_ATOM_AFTER_SHUTTLE_MOVE, PROC_REF(after_parent_shuttle_move)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(parent, COMSIG_MOVABLE_CIRCUIT_LOADED, PROC_REF(on_load)) @@ -58,6 +69,8 @@ UnregisterSignal(parent, list( COMSIG_ATOM_USB_CABLE_TRY_ATTACH, COMSIG_MOVABLE_MOVED, + COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, + COMSIG_ATOM_AFTER_SHUTTLE_MOVE, COMSIG_ATOM_EXAMINE, COMSIG_MOVABLE_CIRCUIT_LOADED, )) @@ -114,6 +127,8 @@ UnregisterSignal(physical_object, list( COMSIG_MOVABLE_MOVED, + COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, + COMSIG_ATOM_AFTER_SHUTTLE_MOVE, COMSIG_ATOM_EXAMINE, )) @@ -191,6 +206,8 @@ usb_cable_beam = atom_parent.Beam(new_physical_object, "usb_cable_beam", 'icons/obj/science/circuits.dmi') RegisterSignal(new_physical_object, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + RegisterSignal(new_physical_object, COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, PROC_REF(before_physical_object_shuttle_move)) + RegisterSignal(new_physical_object, COMSIG_ATOM_AFTER_SHUTTLE_MOVE, PROC_REF(after_physical_object_shuttle_move)) RegisterSignal(new_physical_object, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine_shell)) physical_object = new_physical_object @@ -205,6 +222,9 @@ if (isnull(attached_circuit)) return + if (defer_range_checks) + return + if (IN_GIVEN_RANGE(attached_circuit, parent, USB_CABLE_MAX_RANGE)) return @@ -245,3 +265,28 @@ usb_cable_ref = null QDEL_NULL(usb_cable_beam) + +/datum/component/usb_port/proc/before_parent_shuttle_move() + SIGNAL_HANDLER + + defer_range_checks |= PARENT_DEFERRED + +/datum/component/usb_port/proc/before_physical_object_shuttle_move() + SIGNAL_HANDLER + + defer_range_checks |= PHYSICAL_OBJECT_DEFERRED + +/datum/component/usb_port/proc/after_parent_shuttle_move() + SIGNAL_HANDLER + + defer_range_checks &= ~PARENT_DEFERRED + on_moved() + +/datum/component/usb_port/proc/after_physical_object_shuttle_move() + SIGNAL_HANDLER + + defer_range_checks &= ~PHYSICAL_OBJECT_DEFERRED + on_moved() + +#undef PARENT_DEFERRED +#undef PHYSICAL_OBJECT_DEFERRED diff --git a/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm b/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm index b8f23948bb7..5c812157c9a 100644 --- a/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm +++ b/code/modules/shuttle/mobile_port/shuttle_move_callbacks.dm @@ -99,7 +99,8 @@ All ShuttleMove procs go here // returns the new move_mode (based on the old) // WARNING: Do not leave turf contents in beforeShuttleMove or dock() will runtime /atom/movable/proc/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock) - return move_mode + SHOULD_CALL_PARENT(TRUE) + return SEND_SIGNAL(src, COMSIG_ATOM_BEFORE_SHUTTLE_MOVE, newT, rotation, move_mode, moving_dock) || move_mode /// Called on atoms to move the atom to the new location /atom/movable/proc/onShuttleMove(turf/newT, turf/oldT, list/movement_force, move_dir, obj/docking_port/stationary/old_dock, obj/docking_port/mobile/moving_dock) @@ -115,6 +116,7 @@ All ShuttleMove procs go here // Called on atoms after everything has been moved /atom/movable/proc/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) + SHOULD_CALL_PARENT(TRUE) SEND_SIGNAL(src, COMSIG_ATOM_AFTER_SHUTTLE_MOVE, oldT) if(light) update_light() diff --git a/code/modules/wiremod/core/usb_cable.dm b/code/modules/wiremod/core/usb_cable.dm index 62ba799b5b0..791ecab3735 100644 --- a/code/modules/wiremod/core/usb_cable.dm +++ b/code/modules/wiremod/core/usb_cable.dm @@ -14,6 +14,9 @@ /// The currently connected circuit var/obj/item/integrated_circuit/attached_circuit + /// Used to prevent range checking during shuttle movement, which moves atoms en-masse. + var/defer_range_checks = FALSE + /obj/item/usb_cable/Destroy() attached_circuit = null STOP_PROCESSING(SSobj, src) @@ -22,6 +25,12 @@ /obj/item/usb_cable/Initialize(mapload) . = ..() RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) + var/static/list/connections = list( + COMSIG_MOVABLE_MOVED = PROC_REF(on_moved), + COMSIG_ATOM_BEFORE_SHUTTLE_MOVE = PROC_REF(before_shuttle_move), + COMSIG_ATOM_AFTER_SHUTTLE_MOVE = PROC_REF(after_shuttle_move), + ) + AddComponent(/datum/component/connect_containers, src, connections) /obj/item/usb_cable/examine(mob/user) . = ..() @@ -29,13 +38,6 @@ if (!isnull(attached_circuit)) . += span_notice("It is attached to [attached_circuit.shell || attached_circuit].") -// Look, I'm not happy about this either, but moving an object doesn't call Moved if it's inside something else. -// There's good reason for this, but there's no element or similar yet to track it as far as I know. -// SSobj runs infrequently, this is only ran while there's an attached circuit, its performance cost is negligible. -/obj/item/usb_cable/process(seconds_per_tick) - if (!check_in_range()) - return PROCESS_KILL - /obj/item/usb_cable/pre_attack(atom/target, mob/living/user, list/modifiers) . = ..() if (.) @@ -47,7 +49,6 @@ var/signal_result = SEND_SIGNAL(target, COMSIG_ATOM_USB_CABLE_TRY_ATTACH, src, user) - var/last_attached_circuit = attached_circuit if (signal_result & COMSIG_USB_CABLE_CONNECTED_TO_CIRCUIT) if (isnull(attached_circuit)) CRASH("Producers of COMSIG_USB_CABLE_CONNECTED_TO_CIRCUIT must set attached_circuit") @@ -55,13 +56,6 @@ playsound(src, 'sound/machines/pda_button/pda_button1.ogg', 20, TRUE) - if (last_attached_circuit != attached_circuit) - if (!isnull(last_attached_circuit)) - unregister_circuit_signals(last_attached_circuit) - register_circuit_signals() - - START_PROCESSING(SSobj, src) - return TRUE if (signal_result & COMSIG_USB_CABLE_ATTACHED) @@ -86,34 +80,39 @@ user.visible_message(span_suicide("[user] is wrapping [src] around [user.p_their()] neck! It looks like [user.p_theyre()] trying to commit suicide!")) return OXYLOSS -/obj/item/usb_cable/proc/register_circuit_signals() - RegisterSignal(attached_circuit, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) - RegisterSignal(attached_circuit, COMSIG_QDELETING, PROC_REF(on_circuit_qdeling)) - RegisterSignal(attached_circuit.shell, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved)) - -/obj/item/usb_cable/proc/unregister_circuit_signals(obj/item/integrated_circuit/old_circuit) - UnregisterSignal(attached_circuit, list( - COMSIG_MOVABLE_MOVED, - COMSIG_QDELETING, - )) - - UnregisterSignal(attached_circuit.shell, COMSIG_MOVABLE_MOVED) - /obj/item/usb_cable/proc/on_moved() SIGNAL_HANDLER + if(defer_range_checks) + return + check_in_range() + +/obj/item/usb_cable/beforeShuttleMove(turf/newT, rotation, move_mode, obj/docking_port/mobile/moving_dock) + . = ..() + before_shuttle_move() + +/obj/item/usb_cable/proc/before_shuttle_move() + SIGNAL_HANDLER + + defer_range_checks = TRUE + +/obj/item/usb_cable/afterShuttleMove(turf/oldT, list/movement_force, shuttle_dir, shuttle_preferred_direction, move_dir, rotation) + . = ..() + after_shuttle_move() + +/obj/item/usb_cable/proc/after_shuttle_move() + SIGNAL_HANDLER + + defer_range_checks = FALSE check_in_range() /obj/item/usb_cable/proc/check_in_range() if (isnull(attached_circuit)) - STOP_PROCESSING(SSobj, src) return FALSE if (!IN_GIVEN_RANGE(attached_circuit, src, USB_CABLE_MAX_RANGE)) balloon_alert_to_viewers("detached, too far away") - unregister_circuit_signals(attached_circuit) attached_circuit = null - STOP_PROCESSING(SSobj, src) return FALSE return TRUE @@ -122,4 +121,3 @@ SIGNAL_HANDLER attached_circuit = null - STOP_PROCESSING(SSobj, src)