From 70d356506fe69636fee4c3756cc9b18ae4d238bc Mon Sep 17 00:00:00 2001 From: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Date: Thu, 20 Mar 2025 05:51:47 -0700 Subject: [PATCH] Fixes a bitrunning runtime & adds a component initialize result (#90099) ## About The Pull Request Noticed that bitrunning virtual spawners were runtiming if the server was already emagged. Basically, the guardrail component is added (virtual_entity), then immediately deleted because it's emagged, leading to a race condition in `JoinParent()` where parent is null. I still want to keep this "valid, but delete me" state for components, so I made `COMPONENT_REDUNDANT` (thx @LemonInTheDark). I can't say for certain because I couldn't repro, but this /probably/ fixes #89992 ## Why It's Good For The Game Fixes a runtime Allows devs to add components that execute an arbitrary amount of logic while still qdeling themselves due to some in-game incompatibility issue ## Changelog N/A --- code/__DEFINES/dcs/declarations.dm | 2 + code/datums/components/_component.dm | 12 +++++- .../components/toggle_attached_clothing.dm | 2 +- .../components/bitrunning_points.dm | 1 - .../bitrunning/components/virtual_entity.dm | 39 +++++++++++-------- code/modules/bitrunning/server/_parent.dm | 10 +++++ 6 files changed, 47 insertions(+), 19 deletions(-) diff --git a/code/__DEFINES/dcs/declarations.dm b/code/__DEFINES/dcs/declarations.dm index 04b31f4e185..95ad093f995 100644 --- a/code/__DEFINES/dcs/declarations.dm +++ b/code/__DEFINES/dcs/declarations.dm @@ -4,6 +4,8 @@ #define COMPONENT_INCOMPATIBLE 1 /// Returned in PostTransfer to prevent transfer, similar to `COMPONENT_INCOMPATIBLE` #define COMPONENT_NOTRANSFER 2 +/// Deletes the component silently. This is for valid, non-error cases where you still want to execute some of the component's logic. +#define COMPONENT_REDUNDANT 3 /// Return value to cancel attaching #define ELEMENT_INCOMPATIBLE 1 diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 9fc1e13e1b4..3cc0738e38d 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -43,11 +43,21 @@ /datum/component/New(list/raw_args) parent = raw_args[1] var/list/arguments = raw_args.Copy(2) - if(Initialize(arglist(arguments)) == COMPONENT_INCOMPATIBLE) + + var/result = Initialize(arglist(arguments)) + + if(result == COMPONENT_INCOMPATIBLE) stack_trace("Incompatible [type] assigned to a [parent.type]! args: [json_encode(arguments)]") qdel(src, TRUE, TRUE) return + if(result == COMPONENT_REDUNDANT) + qdel(src, TRUE, TRUE) + return + + if(QDELETED(src) || QDELETED(parent)) + CRASH("Component [type] was created with a deleted parent or was deleted itself before it could be added to a parent") + _JoinParent() /** diff --git a/code/datums/components/toggle_attached_clothing.dm b/code/datums/components/toggle_attached_clothing.dm index 8321119d85e..a1cde8cba63 100644 --- a/code/datums/components/toggle_attached_clothing.dm +++ b/code/datums/components/toggle_attached_clothing.dm @@ -48,7 +48,7 @@ if (!isitem(parent)) return COMPONENT_INCOMPATIBLE if (!deployable_type || !equipped_slot) - return COMPONENT_INCOMPATIBLE // Not strictly true but INITIALIZE_HINT_QDEL doesn't work from components + return COMPONENT_REDUNDANT src.deployable_type = deployable_type src.equipped_slot = equipped_slot src.destroy_on_removal = destroy_on_removal diff --git a/code/modules/bitrunning/components/bitrunning_points.dm b/code/modules/bitrunning/components/bitrunning_points.dm index ae20ec41fb4..b800d586e94 100644 --- a/code/modules/bitrunning/components/bitrunning_points.dm +++ b/code/modules/bitrunning/components/bitrunning_points.dm @@ -7,7 +7,6 @@ /datum/component/bitrunning_points/Initialize(datum/lazy_template/virtual_domain/domain) - . = ..() if(!isturf(parent)) return COMPONENT_INCOMPATIBLE diff --git a/code/modules/bitrunning/components/virtual_entity.dm b/code/modules/bitrunning/components/virtual_entity.dm index dba5e9db34e..52a259a6d04 100644 --- a/code/modules/bitrunning/components/virtual_entity.dm +++ b/code/modules/bitrunning/components/virtual_entity.dm @@ -1,19 +1,35 @@ /// Handles all special considerations for "virtual entities" such as bitrunning ghost roles or digital anomaly antagonists. /datum/component/virtual_entity - ///The cooldown for balloon alerts, so the player isn't spammed while trying to enter a restricted area. + /// The cooldown for balloon alerts, so the player isn't spammed while trying to enter a restricted area. COOLDOWN_DECLARE(OOB_cooldown) -/datum/component/virtual_entity/Initialize(obj/machinery/quantum_server) - . = ..() +/datum/component/virtual_entity/Initialize(obj/machinery/quantum_server) if(quantum_server.obj_flags & EMAGGED) - jailbreak_mobs() //This just sends a message and self-deletes, a bit messy but it works. - return + jailbreak_mobs() + return COMPONENT_REDUNDANT RegisterSignal(parent, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_parent_pre_move)) - RegisterSignal(quantum_server, COMSIG_ATOM_EMAG_ACT, PROC_REF(jailbreak_mobs)) + RegisterSignal(quantum_server, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emagged)) -///Prevents entry to a certain area if it has flags preventing virtual entities from entering. + +/// Self-destructs the component, allowing free-roam by all entities with this restriction. +/datum/component/virtual_entity/proc/jailbreak_mobs() + to_chat(parent, span_bolddanger("You shiver for a moment with a sense of clarity you haven't felt before.")) + to_chat(parent, span_notice("You could go anywhere, do anything! You could leave this simulation right now if you wanted!")) + to_chat(parent, span_danger("But be warned, quantum entanglement will interfere with any previous lives.")) + to_chat(parent, span_notice("You'll have just one chance to go nova, and there's no turning back.")) + + +/// Remove any restrictions AFTER the mob has spawned +/datum/component/virtual_entity/proc/on_emagged(datum/source) + SIGNAL_HANDLER + + jailbreak_mobs() + qdel(src) + + +/// Prevents entry to a certain area if it has flags preventing virtual entities from entering. /datum/component/virtual_entity/proc/on_parent_pre_move(atom/movable/source, atom/new_location) SIGNAL_HANDLER @@ -27,12 +43,3 @@ COOLDOWN_START(src, OOB_cooldown, 2 SECONDS) return COMPONENT_MOVABLE_BLOCK_PRE_MOVE -///Self-destructs the component, allowing free-roam by all entities with this restriction. -/datum/component/virtual_entity/proc/jailbreak_mobs() - SIGNAL_HANDLER - - to_chat(parent, span_bolddanger("You shiver for a moment with a sense of clarity you haven't felt before.")) - to_chat(parent, span_notice("You could go anywhere, do anything! You could leave this simulation right now if you wanted!")) - to_chat(parent, span_danger("But be warned, quantum entanglement will interfere with any previous lives.")) - to_chat(parent, span_notice("You'll have just one chance to go nova, and there's no turning back.")) - qdel(src) diff --git a/code/modules/bitrunning/server/_parent.dm b/code/modules/bitrunning/server/_parent.dm index f166a82c817..0f34826d1c8 100644 --- a/code/modules/bitrunning/server/_parent.dm +++ b/code/modules/bitrunning/server/_parent.dm @@ -49,12 +49,14 @@ /// Cooldown between being able to toggle broadcasting COOLDOWN_DECLARE(broadcast_toggle_cd) + /obj/machinery/quantum_server/post_machine_initialize() . = ..() RegisterSignals(src, list(COMSIG_MACHINERY_BROKEN, COMSIG_MACHINERY_POWER_LOST), PROC_REF(on_broken)) RegisterSignal(src, COMSIG_QDELETING, PROC_REF(on_delete)) + /obj/machinery/quantum_server/Destroy(force) . = ..() @@ -64,6 +66,7 @@ QDEL_NULL(exit_turfs) QDEL_NULL(generated_domain) + /obj/machinery/quantum_server/examine(mob/user) . = ..() @@ -88,6 +91,7 @@ if(isobserver(user) && (obj_flags & EMAGGED)) . += span_notice("Ominous warning lights are blinking red. This server has been tampered with.") + /obj/machinery/quantum_server/emag_act(mob/user, obj/item/card/emag/emag_card) . = ..() @@ -102,6 +106,7 @@ balloon_alert(user, "system jailbroken...") playsound(src, 'sound/effects/sparks/sparks1.ogg', 35, vary = TRUE) + /obj/machinery/quantum_server/update_appearance(updates) if(isnull(generated_domain) || !is_operational) set_light(l_on = FALSE) @@ -110,6 +115,7 @@ set_light(l_range = 2, l_power = 1.5, l_color = is_ready ? LIGHT_COLOR_BABY_BLUE : LIGHT_COLOR_FIRE, l_on = TRUE) return ..() + /obj/machinery/quantum_server/update_icon_state() if(isnull(generated_domain) || !is_operational) icon_state = base_icon_state @@ -118,6 +124,7 @@ icon_state = "[base_icon_state]_[is_ready ? "on" : "off"]" return ..() + /obj/machinery/quantum_server/attackby(obj/item/weapon, mob/user, params) . = ..() @@ -129,6 +136,7 @@ capacitor_coefficient = 0.1 points = 100 + /obj/machinery/quantum_server/crowbar_act(mob/living/user, obj/item/crowbar) . = ..() @@ -142,6 +150,7 @@ return TRUE return FALSE + /obj/machinery/quantum_server/screwdriver_act(mob/living/user, obj/item/screwdriver) . = ..() @@ -152,6 +161,7 @@ return TRUE return FALSE + /obj/machinery/quantum_server/RefreshParts() var/capacitor_rating = 1.15 var/datum/stock_part/capacitor/cap = locate() in component_parts