From d190233527b7e3c2ec5292daf3674b6962b006a4 Mon Sep 17 00:00:00 2001 From: VMSolidus Date: Tue, 7 Jul 2026 10:13:46 -0400 Subject: [PATCH] Various Incomplete Destroy Fixes (#22788) I've had extended logging for Incomplete Destroys for about 2 weeks now, so there's the promised PR that fixes each and every single poisoned destroy that was recorded in the past 2 weeks by the sentry logs. --- code/controllers/subsystems/spatial_gridmap.dm | 8 ++++++++ .../objects/structures/machinery/body_scanner.dm | 2 +- code/modules/admin/buildmode/advanced.dm | 9 +++++++++ code/modules/compass/compass_holder.dm | 10 ++++++++-- code/modules/heavy_vehicle/mech_life.dm | 1 + code/modules/heavy_vehicle/mecha.dm | 3 ++- code/modules/lighting/lighting_turf.dm | 2 +- code/modules/mob/living/carbon/carbon.dm | 14 ++++++++++++-- code/modules/overmap/contacts/_contacts.dm | 2 +- .../hellfirejag-incommplete-destroy-fixes.yml | 4 ++++ 10 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 html/changelogs/hellfirejag-incommplete-destroy-fixes.yml diff --git a/code/controllers/subsystems/spatial_gridmap.dm b/code/controllers/subsystems/spatial_gridmap.dm index 3d221e7288f..f2d408de8c8 100644 --- a/code/controllers/subsystems/spatial_gridmap.dm +++ b/code/controllers/subsystems/spatial_gridmap.dm @@ -365,10 +365,18 @@ SUBSYSTEM_DEF(spatial_grid) if(!initialized) return if(QDELETED(new_target)) + #ifdef TESTING CRASH("qdeleted or null target trying to enter the spatial grid!") + #else + return // Regular guard clause for production instead of forcing hard deletes. + #endif if(!target_turf || !new_target.spatial_grid_key) + #ifdef TESTING CRASH("null turf loc or a new_target that doesn't support it trying to enter the spatial grid!") + #else + return // Regular guard clause for production instead of forcing hard deletes. + #endif var/x_index = GET_SPATIAL_INDEX(target_turf.x) var/y_index = GET_SPATIAL_INDEX(target_turf.y) diff --git a/code/game/objects/structures/machinery/body_scanner.dm b/code/game/objects/structures/machinery/body_scanner.dm index d6d9a6be90c..895ef848b76 100644 --- a/code/game/objects/structures/machinery/body_scanner.dm +++ b/code/game/objects/structures/machinery/body_scanner.dm @@ -314,9 +314,9 @@ unlink_scanner() /obj/structure/machinery/body_scanconsole/proc/unlink_scanner() - connected = null UnregisterSignal(connected, COMSIG_QDELETING) update_icon() + connected = null /obj/structure/machinery/body_scanconsole/attack_ai(var/mob/user) if(!ai_can_interact(user)) diff --git a/code/modules/admin/buildmode/advanced.dm b/code/modules/admin/buildmode/advanced.dm index f72402356b4..e1d9344e93c 100644 --- a/code/modules/admin/buildmode/advanced.dm +++ b/code/modules/admin/buildmode/advanced.dm @@ -34,6 +34,15 @@ else to_chat(user, SPAN_NOTICE("Select a type to construct.")) else if(parameters["right"]) + if (isturf(A)) + // Delete exemption for Turfs, which under no circumstances are allowed to be qdel'ed directly. + // "Deleting" a turf instead attempts to change it to its base turf, + // which in most cases will either Plating, or Open Space. Open Space will simply return early instead. + var/turf/T = A + Log("Replaced turf - [log_info_line(T)]") + T.ChangeTurf(T.baseturf) + return + Log("Deleted - [log_info_line(A)]") qdel(A) else if((parameters["left"] && parameters["ctrl"]) || parameters["middle"]) diff --git a/code/modules/compass/compass_holder.dm b/code/modules/compass/compass_holder.dm index 3f623c92ced..9044beac115 100644 --- a/code/modules/compass/compass_holder.dm +++ b/code/modules/compass/compass_holder.dm @@ -61,8 +61,14 @@ rebuild_overlay_lists(TRUE) /obj/compass_holder/Destroy() - QDEL_LIST(compass_waypoints) - . = ..() + if (length(compass_waypoints)) + for (var/key, value in compass_waypoints) + // For reasons now known only to God, the key-value pairs here are allowed to be either String/Integer or String/Datum pairs. + // Make sure to thank Byond 516 for giving us what are basically dynamically typed hashmaps instead of statically typed key-value pairs. + if (isdatum(value)) + qdel(value) + compass_waypoints.Cut() + return ..() /obj/compass_holder/proc/get_heading() var/atom/A = loc?.loc // is there a get_holder_recursive() equivalent on Polaris? diff --git a/code/modules/heavy_vehicle/mech_life.dm b/code/modules/heavy_vehicle/mech_life.dm index 1a49326d6e1..e744e0d1720 100644 --- a/code/modules/heavy_vehicle/mech_life.dm +++ b/code/modules/heavy_vehicle/mech_life.dm @@ -132,6 +132,7 @@ eject(pilot, silent=1) if(remote_network && istype(pilot, /mob/living/simple_animal/spiderbot)) pilot.gib() + pilots.Remove(pilot) // Handle the rest of things. ..(gibbed, (gibbed ? "explodes!" : "grinds to a halt before collapsing!")) diff --git a/code/modules/heavy_vehicle/mecha.dm b/code/modules/heavy_vehicle/mecha.dm index cab3572e6fa..e15e46662e4 100644 --- a/code/modules/heavy_vehicle/mecha.dm +++ b/code/modules/heavy_vehicle/mecha.dm @@ -113,7 +113,8 @@ if(pilot.client) pilot.client.screen -= hud_elements pilot.client.images -= hud_elements - pilot.forceMove(get_turf(src)) + if (!QDELETED(pilot)) // Forcemove doesn't accept QDELETED inputs. + pilot.forceMove(get_turf(src)) pilots = null QDEL_LIST(hud_elements) diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index cc2b27c9035..efe9dc44055 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -75,7 +75,7 @@ return directional_opacity = NONE for(var/atom/movable/opacity_source as anything in opacity_sources) - if(opacity_source.atom_flags & ATOM_FLAG_CHECKS_BORDER) + if(opacity_source?.atom_flags & ATOM_FLAG_CHECKS_BORDER) directional_opacity |= opacity_source.dir else //If fulltile and opaque, then the whole tile blocks view, no need to continue checking. directional_opacity = ALL_CARDINALS diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index e7f39029e31..4187ca48a88 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -43,14 +43,24 @@ QDEL_NULL(handcuffed) QDEL_NULL(legcuffed) QDEL_NULL(op_stage) - chem_doses?.Cut() + + if (length(chem_doses)) + chem_doses.Cut() + // For whatever baffling reason this can potentially be a single reference instead of an alist. + else chem_doses = null + QDEL_NULL(bloodstr) QDEL_NULL(touching) QDEL_NULL(breathing) // Delete and null a direct list of references to our internal organs (such as brain, lungs, heart, etc). QDEL_LIST(internal_organs) + // Null an Associative list of String = Reference to the same organs. - internal_organs_by_name?.Cut() + if (length(internal_organs_by_name)) + internal_organs_by_name.Cut() + // For whatever baffling reason this can potentially be a single reference instead of an alist. + else internal_organs_by_name = null + QDEL_LIST(hallucinations) return ..() diff --git a/code/modules/overmap/contacts/_contacts.dm b/code/modules/overmap/contacts/_contacts.dm index 6902a92d088..70a77338efe 100644 --- a/code/modules/overmap/contacts/_contacts.dm +++ b/code/modules/overmap/contacts/_contacts.dm @@ -83,7 +83,7 @@ /datum/overmap_contact/Destroy() if(owner) // If we have a lock on what was lost, remove the lock from the targeting consoles - if(owner.connected.targeting == effect) + if(owner.connected && owner.connected.targeting == effect) for(var/obj/structure/machinery/computer/ship/targeting/console in owner.connected.consoles) owner.connected.detarget(effect, console) diff --git a/html/changelogs/hellfirejag-incommplete-destroy-fixes.yml b/html/changelogs/hellfirejag-incommplete-destroy-fixes.yml new file mode 100644 index 00000000000..29d5eb3b485 --- /dev/null +++ b/html/changelogs/hellfirejag-incommplete-destroy-fixes.yml @@ -0,0 +1,4 @@ +author: Hellfirejag +delete-after: True +changes: + - bugfix: "Fixed a variety of incomplete destroys which were causing a similar variety of bugs and lag spikes."