diff --git a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
index 2ccfe0f2e87..ea604bc996a 100644
--- a/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/binary_atmos_base.dm
@@ -42,7 +42,7 @@ obj/machinery/atmospherics/binary
return null
Destroy()
- loc = null
+ . = ..()
if(node1)
node1.disconnect(src)
@@ -54,8 +54,6 @@ obj/machinery/atmospherics/binary
node1 = null
node2 = null
- ..()
-
initialize()
if(node1 && node2) return
diff --git a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm
index 5768def1ec1..d0417e14dd2 100644
--- a/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm
+++ b/code/ATMOSPHERICS/components/binary_devices/pipeturbine.dm
@@ -36,7 +36,7 @@
initialize_directions = NORTH|SOUTH
Destroy()
- loc = null
+ . = ..()
if(node1)
node1.disconnect(src)
@@ -48,8 +48,6 @@
node1 = null
node2 = null
- ..()
-
process()
..()
if(anchored && !(stat&BROKEN))
diff --git a/code/ATMOSPHERICS/components/omni_devices/filter.dm b/code/ATMOSPHERICS/components/omni_devices/filter.dm
index b2966ef4314..feb1b5cb93c 100644
--- a/code/ATMOSPHERICS/components/omni_devices/filter.dm
+++ b/code/ATMOSPHERICS/components/omni_devices/filter.dm
@@ -28,7 +28,7 @@
input = null
output = null
filters.Cut()
- ..()
+ return ..()
/obj/machinery/atmospherics/omni/filter/sort_ports()
for(var/datum/omni_port/P in ports)
diff --git a/code/ATMOSPHERICS/components/portables_connector.dm b/code/ATMOSPHERICS/components/portables_connector.dm
index 4f6473fccf3..9668f7a5a3e 100644
--- a/code/ATMOSPHERICS/components/portables_connector.dm
+++ b/code/ATMOSPHERICS/components/portables_connector.dm
@@ -61,7 +61,7 @@
return null
/obj/machinery/atmospherics/portables_connector/Destroy()
- loc = null
+ . = ..()
if(connected_device)
connected_device.disconnect()
@@ -72,8 +72,6 @@
node = null
- ..()
-
/obj/machinery/atmospherics/portables_connector/initialize()
if(node) return
diff --git a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
index fa066d978f7..717540db69d 100644
--- a/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
+++ b/code/ATMOSPHERICS/components/trinary_devices/trinary_base.dm
@@ -51,7 +51,7 @@ obj/machinery/atmospherics/trinary
return null
Destroy()
- loc = null
+ . = ..()
if(node1)
node1.disconnect(src)
@@ -67,8 +67,6 @@ obj/machinery/atmospherics/trinary
node2 = null
node3 = null
- ..()
-
initialize()
if(node1 && node2 && node3) return
diff --git a/code/ATMOSPHERICS/components/tvalve.dm b/code/ATMOSPHERICS/components/tvalve.dm
index f93970a93fd..a7deb70abaa 100644
--- a/code/ATMOSPHERICS/components/tvalve.dm
+++ b/code/ATMOSPHERICS/components/tvalve.dm
@@ -100,7 +100,7 @@
return null
/obj/machinery/atmospherics/tvalve/Destroy()
- loc = null
+ . = ..()
if(node1)
node1.disconnect(src)
@@ -116,8 +116,6 @@
node2 = null
node3 = null
- ..()
-
/obj/machinery/atmospherics/tvalve/proc/go_to_side()
if(state) return 0
diff --git a/code/ATMOSPHERICS/components/unary/unary_base.dm b/code/ATMOSPHERICS/components/unary/unary_base.dm
index 20d37b309c7..0372483c796 100644
--- a/code/ATMOSPHERICS/components/unary/unary_base.dm
+++ b/code/ATMOSPHERICS/components/unary/unary_base.dm
@@ -31,7 +31,7 @@
return null
Destroy()
- loc = null
+ . = ..()
if(node)
node.disconnect(src)
@@ -39,8 +39,6 @@
node = null
- ..()
-
initialize()
if(node) return
diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm
index 00bc7e57728..c6121dea31a 100644
--- a/code/ATMOSPHERICS/components/unary/vent_pump.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm
@@ -80,7 +80,10 @@
/obj/machinery/atmospherics/unary/vent_pump/Destroy()
unregister_radio(src, frequency)
- ..()
+ if(initial_loc)
+ initial_loc.air_vent_info -= id_tag
+ initial_loc.air_vent_names -= id_tag
+ return ..()
/obj/machinery/atmospherics/unary/vent_pump/high_volume
name = "Large Air Vent"
@@ -414,13 +417,6 @@
new /obj/item/pipe(loc, make_from=src)
qdel(src)
-/obj/machinery/atmospherics/unary/vent_pump/Destroy()
- if(initial_loc)
- initial_loc.air_vent_info -= id_tag
- initial_loc.air_vent_names -= id_tag
- ..()
- return
-
#undef DEFAULT_PRESSURE_DELTA
#undef EXTERNAL_PRESSURE_BOUND
diff --git a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
index 06d1bce4373..3fccbb50f33 100644
--- a/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
+++ b/code/ATMOSPHERICS/components/unary/vent_scrubber.dm
@@ -44,8 +44,10 @@
/obj/machinery/atmospherics/unary/vent_scrubber/Destroy()
unregister_radio(src, frequency)
- ..()
-
+ if(initial_loc)
+ initial_loc.air_scrub_info -= id_tag
+ initial_loc.air_scrub_names -= id_tag
+ return ..()
/obj/machinery/atmospherics/unary/vent_scrubber/update_icon(var/safety = 0)
if(!check_icon_cache())
@@ -285,10 +287,3 @@
user << "A small gauge in the corner reads [round(last_flow_rate, 0.1)] L/s; [round(last_power_draw)] W"
else
user << "You are too far away to read the gauge."
-
-/obj/machinery/atmospherics/unary/vent_scrubber/Destroy()
- if(initial_loc)
- initial_loc.air_scrub_info -= id_tag
- initial_loc.air_scrub_names -= id_tag
- ..()
- return
diff --git a/code/ATMOSPHERICS/components/valve.dm b/code/ATMOSPHERICS/components/valve.dm
index 3cd77597b39..01e6e977bbc 100644
--- a/code/ATMOSPHERICS/components/valve.dm
+++ b/code/ATMOSPHERICS/components/valve.dm
@@ -72,7 +72,7 @@
return null
/obj/machinery/atmospherics/valve/Destroy()
- loc = null
+ . = ..()
if(node1)
node1.disconnect(src)
@@ -84,8 +84,6 @@
node1 = null
node2 = null
- ..()
-
/obj/machinery/atmospherics/valve/proc/open()
if(open) return 0
diff --git a/code/ATMOSPHERICS/datum_pipe_network.dm b/code/ATMOSPHERICS/datum_pipe_network.dm
index 7fe586733ca..84414613428 100644
--- a/code/ATMOSPHERICS/datum_pipe_network.dm
+++ b/code/ATMOSPHERICS/datum_pipe_network.dm
@@ -16,6 +16,15 @@ datum/pipe_network
..()
+ Destroy()
+ pipe_networks -= src
+ for(var/datum/pipeline/line_member in line_members)
+ line_member.network = null
+ for(var/obj/machinery/atmospherics/normal_member in normal_members)
+ normal_member.reassign_network(src, null)
+ gases.Cut() // Do not qdel the gases, we don't own them
+ return ..()
+
proc/process()
//Equalize gases amongst pipe if called for
if(update)
diff --git a/code/ATMOSPHERICS/datum_pipeline.dm b/code/ATMOSPHERICS/datum_pipeline.dm
index 44c7cd25905..0115c85c3dd 100644
--- a/code/ATMOSPHERICS/datum_pipeline.dm
+++ b/code/ATMOSPHERICS/datum_pipeline.dm
@@ -10,14 +10,14 @@ datum/pipeline
var/alert_pressure = 0
Destroy()
- if(network)
- qdel(network)
+ qdel_null(network)
if(air && air.volume)
temporarily_store_air()
- qdel(air)
+ for(var/obj/machinery/atmospherics/pipe/P in members)
+ P.parent = null
- ..()
+ . = ..()
proc/process()//This use to be called called from the pipe networks
diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm
index 105aaa8c8b8..c23fc165552 100644
--- a/code/ATMOSPHERICS/pipes.dm
+++ b/code/ATMOSPHERICS/pipes.dm
@@ -63,11 +63,11 @@
return parent.return_network(reference)
/obj/machinery/atmospherics/pipe/Destroy()
- qdel(parent)
+ qdel_null(parent)
if(air_temporary)
loc.assume_air(air_temporary)
- ..()
+ . = ..()
/obj/machinery/atmospherics/pipe/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
if (istype(src, /obj/machinery/atmospherics/pipe/tank))
@@ -227,10 +227,12 @@
/obj/machinery/atmospherics/pipe/simple/Destroy()
if(node1)
node1.disconnect(src)
+ node1 = null
if(node2)
node2.disconnect(src)
+ node1 = null
- ..()
+ . = ..()
/obj/machinery/atmospherics/pipe/simple/pipeline_expansion()
return list(node1, node2)
@@ -461,12 +463,15 @@
/obj/machinery/atmospherics/pipe/manifold/Destroy()
if(node1)
node1.disconnect(src)
+ node1 = null
if(node2)
node2.disconnect(src)
+ node2 = null
if(node3)
node3.disconnect(src)
+ node3 = null
- ..()
+ . = ..()
/obj/machinery/atmospherics/pipe/manifold/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
@@ -705,14 +710,18 @@
/obj/machinery/atmospherics/pipe/manifold4w/Destroy()
if(node1)
node1.disconnect(src)
+ node1 = null
if(node2)
node2.disconnect(src)
+ node2 = null
if(node3)
node3.disconnect(src)
+ node3 = null
if(node4)
node4.disconnect(src)
+ node4 = null
- ..()
+ . = ..()
/obj/machinery/atmospherics/pipe/manifold4w/disconnect(obj/machinery/atmospherics/reference)
if(reference == node1)
@@ -967,8 +976,9 @@
/obj/machinery/atmospherics/pipe/cap/Destroy()
if(node)
node.disconnect(src)
+ node = null
- ..()
+ . = ..()
/obj/machinery/atmospherics/pipe/cap/disconnect(obj/machinery/atmospherics/reference)
if(reference == node)
@@ -1081,8 +1091,9 @@
/obj/machinery/atmospherics/pipe/tank/Destroy()
if(node1)
node1.disconnect(src)
+ node1 = null
- ..()
+ . = ..()
/obj/machinery/atmospherics/pipe/tank/pipeline_expansion()
return list(node1)
@@ -1251,8 +1262,9 @@
/obj/machinery/atmospherics/pipe/vent/Destroy()
if(node1)
node1.disconnect(src)
+ node1 = null
- ..()
+ . = ..()
/obj/machinery/atmospherics/pipe/vent/pipeline_expansion()
return list(node1)
diff --git a/code/__defines/MC.dm b/code/__defines/MC.dm
index 90724a2f984..08b8d223ec7 100644
--- a/code/__defines/MC.dm
+++ b/code/__defines/MC.dm
@@ -51,3 +51,11 @@
#define SS_PAUSED 3 //paused by mc_tick_check
#define SS_SLEEPING 4 //fire() slept.
#define SS_PAUSING 5 //in the middle of pausing
+
+// Standard way to define a global subsystem, keep boilerplate organized here!
+#define SUBSYSTEM_DEF(X) var/datum/controller/subsystem/##X/SS##X;\
+/datum/controller/subsystem/##X/New(){\
+ NEW_SS_GLOBAL(SS##X);\
+ PreInit();\
+}\
+/datum/controller/subsystem/##X
diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm
new file mode 100644
index 00000000000..168a6adf0c3
--- /dev/null
+++ b/code/__defines/qdel.dm
@@ -0,0 +1,17 @@
+//defines that give qdel hints. these can be given as a return in destory() or by calling
+
+#define QDEL_HINT_QUEUE 0 //qdel should queue the object for deletion.
+#define QDEL_HINT_LETMELIVE 1 //qdel should let the object live after calling destory.
+#define QDEL_HINT_IWILLGC 2 //functionally the same as the above. qdel should assume the object will gc on its own, and not check it.
+#define QDEL_HINT_HARDDEL 3 //qdel should assume this object won't gc, and queue a hard delete using a hard reference.
+#define QDEL_HINT_HARDDEL_NOW 4 //qdel should assume this object won't gc, and hard del it post haste.
+#define QDEL_HINT_FINDREFERENCE 5 //functionally identical to QDEL_HINT_QUEUE if TESTING is not enabled in _compiler_options.dm.
+ //if TESTING is enabled, qdel will call this object's find_references() verb.
+//defines for the gc_destroyed var
+
+#define GC_QUEUED_FOR_QUEUING -1
+#define GC_QUEUED_FOR_HARD_DEL -2
+#define GC_CURRENTLY_BEING_QDELETED -3
+
+#define QDELETED(X) (!X || X.gc_destroyed)
+#define QDESTROYING(X) (!X || X.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
diff --git a/code/_helpers/mobs.dm b/code/_helpers/mobs.dm
index d7fd20ac770..8ac42dc0b92 100644
--- a/code/_helpers/mobs.dm
+++ b/code/_helpers/mobs.dm
@@ -228,7 +228,7 @@ Proc for attack log creation, because really why not
. = 0
break
- if(target_loc && (!target || target_loc != target.loc))
+ if(target_loc && (QDELETED(target) || target_loc != target.loc))
. = 0
break
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index 2b75414a7e9..63bac6e134d 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -1294,6 +1294,13 @@ var/mob/dview/dview_mob = new
else
living_mob_list -= src
+/mob/dview/Destroy(var/force)
+ crash_with("Attempt to delete the dview_mob: [log_info_line(src)]")
+ if (!force)
+ return QDEL_HINT_LETMELIVE
+ global.dview_mob = new
+ return ..()
+
// call to generate a stack trace and print to runtime logs
/proc/crash_with(msg)
CRASH(msg)
diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm
index 2081e011b35..d29ef9dd967 100644
--- a/code/_onclick/hud/ability_screen_objects.dm
+++ b/code/_onclick/hud/ability_screen_objects.dm
@@ -20,7 +20,7 @@
message_admins("ERROR: ability_master's New() was not given an owner argument. This is a bug.")
/obj/screen/movable/ability_master/Destroy()
- ..()
+ . = ..()
//Get rid of the ability objects.
remove_all_abilities()
ability_objects.Cut()
diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm
index 9781e9c236d..7e03151fd66 100644
--- a/code/_onclick/hud/action.dm
+++ b/code/_onclick/hud/action.dm
@@ -30,6 +30,9 @@
/datum/action/Destroy()
if(owner)
Remove(owner)
+ target = null
+ qdel_null(button)
+ return ..()
/datum/action/proc/Grant(mob/living/T)
if(owner)
@@ -45,7 +48,7 @@
if(button)
if(T.client)
T.client.screen -= button
- del(button)
+ qdel_null(button)
T.actions.Remove(src)
T.update_action_buttons()
owner = null
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 45c6f7e277c..0f6990f6812 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -155,7 +155,7 @@ datum/hud/New(mob/owner)
..()
/datum/hud/Destroy()
- ..()
+ . = ..()
grab_intent = null
hurt_intent = null
disarm_intent = null
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index e55323fb3a2..35ef300978c 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -46,7 +46,7 @@
var/obj/item/owner
/obj/screen/item_action/Destroy()
- ..()
+ . = ..()
owner = null
/obj/screen/item_action/Click()
diff --git a/code/_onclick/hud/spell_screen_objects.dm b/code/_onclick/hud/spell_screen_objects.dm
index a599381730a..e79db287132 100644
--- a/code/_onclick/hud/spell_screen_objects.dm
+++ b/code/_onclick/hud/spell_screen_objects.dm
@@ -13,7 +13,7 @@
var/mob/spell_holder
/obj/screen/movable/spell_master/Destroy()
- ..()
+ . = ..()
for(var/obj/screen/spell/spells in spell_objects)
spells.spellmaster = null
spell_objects.Cut()
@@ -156,7 +156,7 @@
var/icon/last_charged_icon
/obj/screen/spell/Destroy()
- ..()
+ . = ..()
spell = null
last_charged_icon = null
if(spellmaster)
diff --git a/code/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm
index edcb8d703c6..9f08241978d 100644
--- a/code/controllers/ProcessScheduler/core/process.dm
+++ b/code/controllers/ProcessScheduler/core/process.dm
@@ -387,6 +387,6 @@
exceptions[eid] = 0
/datum/controller/process/proc/catchBadType(var/datum/caught)
- if(isnull(caught) || !istype(caught) || !isnull(caught.gcDestroyed))
+ if(isnull(caught) || !istype(caught) || QDELETED(caught))
return // Only bother with types we can identify and that don't belong
catchException("Type [caught.type] does not belong in process' queue")
diff --git a/code/controllers/Processes/garbage.dm b/code/controllers/Processes/garbage.dm
deleted file mode 100644
index 02f04113320..00000000000
--- a/code/controllers/Processes/garbage.dm
+++ /dev/null
@@ -1,254 +0,0 @@
-// The time a datum was destroyed by the GC, or null if it hasn't been
-/datum/var/gcDestroyed
-
-#define GC_COLLECTIONS_PER_RUN 300
-#define GC_COLLECTION_TIMEOUT (30 SECONDS)
-#define GC_FORCE_DEL_PER_RUN 30
-
-var/datum/controller/process/garbage_collector/garbage_collector
-var/list/delayed_garbage = list()
-
-/datum/controller/process/garbage_collector
- var/garbage_collect = 1 // Whether or not to actually do work
- var/total_dels = 0 // number of total del()'s
- var/tick_dels = 0 // number of del()'s we've done this tick
- var/soft_dels = 0
- var/hard_dels = 0 // number of hard dels in total
- var/list/destroyed = list() // list of refID's of things that should be garbage collected
- // refID's are associated with the time at which they time out and need to be manually del()
- // we do this so we aren't constantly locating them and preventing them from being gc'd
-
- var/list/logging = list() // list of all types that have failed to GC associated with the number of times that's happened.
- // the types are stored as strings
-
-/datum/controller/process/garbage_collector/setup()
- name = "garbage"
- schedule_interval = 5 SECONDS
- start_delay = 3
-
- if(!garbage_collector)
- garbage_collector = src
-
- for(var/garbage in delayed_garbage)
- qdel(garbage)
- delayed_garbage.Cut()
- delayed_garbage = null
-
-#ifdef GC_FINDREF
-world/loop_checks = 0
-#endif
-
-/datum/controller/process/garbage_collector/doWork()
- if(!garbage_collect)
- return
-
- tick_dels = 0
- var/time_to_kill = world.time - GC_COLLECTION_TIMEOUT
- var/checkRemain = GC_COLLECTIONS_PER_RUN
- var/remaining_force_dels = GC_FORCE_DEL_PER_RUN
-
-
- while(destroyed.len && --checkRemain >= 0)
- if(remaining_force_dels <= 0)
- #ifdef GC_DEBUG
- testing("GC: Reached max force dels per tick [dels] vs [maxDels]")
- #endif
- break // Server's already pretty pounded, everything else can wait 2 seconds
- var/refID = destroyed[1]
- var/GCd_at_time = destroyed[refID]
- if(GCd_at_time > time_to_kill)
- #ifdef GC_DEBUG
- testing("GC: [refID] not old enough, breaking at [world.time] for [GCd_at_time - time_to_kill] deciseconds until [GCd_at_time + collection_timeout]")
- #endif
- break // Everything else is newer, skip them
- var/datum/A = locate(refID)
- #ifdef GC_DEBUG
- testing("GC: [refID] old enough to test: GCd_at_time: [GCd_at_time] time_to_kill: [time_to_kill] current: [world.time]")
- #endif
- if(A && A.gcDestroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
- // Something's still referring to the qdel'd object. Kill it.
- testing("GC: -- \ref[A] | [A.type] was unable to be GC'd and was deleted --")
- logging["[A.type]"]++
- del(A)
-
- hard_dels++
- remaining_force_dels--
- else
- #ifdef GC_DEBUG
- testing("GC: [refID] properly GC'd at [world.time] with timeout [GCd_at_time]")
- #endif
- soft_dels++
- tick_dels++
- total_dels++
- destroyed.Cut(1, 2)
- SCHECK
-
-#undef GC_FORCE_DEL_PER_TICK
-#undef GC_COLLECTION_TIMEOUT
-#undef GC_COLLECTIONS_PER_TICK
-
-#ifdef GC_FINDREF
-/datum/controller/process/garbage_collector/proc/LookForRefs(var/datum/D, var/list/targ)
- . = 0
- for(var/V in D.vars)
- if(V == "contents")
- continue
- if(istype(D.vars[V], /atom))
- var/atom/A = D.vars[V]
- if(A in targ)
- testing("GC: [A] | [A.type] referenced by [D] | [D.type], var [V]")
- . += 1
- else if(islist(D.vars[V]))
- . += LookForListRefs(D.vars[V], targ, D, V)
-
-/datum/controller/process/garbage_collector/proc/LookForListRefs(var/list/L, var/list/targ, var/datum/D, var/V)
- . = 0
- for(var/F in L)
- if(istype(F, /atom))
- var/atom/A = F
- if(A in targ)
- testing("GC: [A] | [A.type] referenced by [D] | [D.type], list [V]")
- . += 1
- if(islist(F))
- . += LookForListRefs(F, targ, D, "[F] in list [V]")
-#endif
-
-/datum/controller/process/garbage_collector/proc/AddTrash(datum/A)
- if(!istype(A) || !isnull(A.gcDestroyed))
- return
- #ifdef GC_DEBUG
- testing("GC: AddTrash(\ref[A] - [A.type])")
- #endif
- A.gcDestroyed = world.time
- destroyed -= "\ref[A]" // Removing any previous references that were GC'd so that the current object will be at the end of the list.
- destroyed["\ref[A]"] = world.time
-
-/datum/controller/process/garbage_collector/statProcess()
- ..()
- stat(null, "[garbage_collect ? "On" : "Off"], [destroyed.len] queued")
- stat(null, "Dels: [total_dels], [soft_dels] soft, [hard_dels] hard, [tick_dels] last run")
-
-
-// Tests if an atom has been deleted.
-/proc/deleted(atom/A)
- return !A || !isnull(A.gcDestroyed)
-
-// Should be treated as a replacement for the 'del' keyword.
-// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
-/proc/qdel(var/datum/A)
- if(!A)
- return
- if(!istype(A))
- warning("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
- crash_with("qdel() passed object of type [A.type]. qdel() can only handle /datum types.")
- del(A)
- if(garbage_collector)
- garbage_collector.total_dels++
- garbage_collector.hard_dels++
- else if(isnull(A.gcDestroyed))
- // Let our friend know they're about to get collected
- . = !A.Destroy()
- if(. && A)
- A.finalize_qdel()
-
-/datum/proc/finalize_qdel()
- del(src)
-
-/atom/finalize_qdel()
- if(garbage_collector)
- garbage_collector.AddTrash(src)
- else
- delayed_garbage |= src
-
-/icon/finalize_qdel()
- del(src)
-
-/image/finalize_qdel()
- del(src)
-
-/mob/finalize_qdel()
- del(src)
-
-/turf/finalize_qdel()
- del(src)
-
-// Default implementation of clean-up code.
-// This should be overridden to remove all references pointing to the object being destroyed.
-// Return true if the the GC controller should allow the object to continue existing.
-/datum/proc/Destroy()
- nanomanager.close_uis(src)
- tag = null
- return
-
-#ifdef TESTING
-/client/var/running_find_references
-
-/mob/verb/create_thing()
- set category = "Debug"
- set name = "Create Thing"
-
- var/path = input("Enter path")
- var/atom/thing = new path(loc)
- thing.find_references()
-
-/atom/verb/find_references()
- set category = "Debug"
- set name = "Find References"
- set background = 1
- set src in world
-
- if(!usr || !usr.client)
- return
-
- if(usr.client.running_find_references)
- testing("CANCELLED search for references to a [usr.client.running_find_references].")
- usr.client.running_find_references = null
- return
-
- if(alert("Running this will create a lot of lag until it finishes. You can cancel it by running it again. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
- return
-
- // Remove this object from the list of things to be auto-deleted.
- if(garbage_collector)
- garbage_collector.destroyed -= "\ref[src]"
-
- usr.client.running_find_references = type
- testing("Beginning search for references to a [type].")
- var/list/things = list()
- for(var/client/thing)
- things += thing
- for(var/datum/thing)
- things += thing
- for(var/atom/thing)
- things += thing
- testing("Collected list of things in search for references to a [type]. ([things.len] Thing\s)")
- for(var/datum/thing in things)
- if(!usr.client.running_find_references) return
- for(var/varname in thing.vars)
- var/variable = thing.vars[varname]
- if(variable == src)
- testing("Found [src.type] \ref[src] in [thing.type]'s [varname] var.")
- else if(islist(variable))
- if(src in variable)
- testing("Found [src.type] \ref[src] in [thing.type]'s [varname] list var.")
- testing("Completed search for references to a [type].")
- usr.client.running_find_references = null
-
-/client/verb/purge_all_destroyed_objects()
- set category = "Debug"
- if(garbage_collector)
- while(garbage_collector.destroyed.len)
- var/datum/o = locate(garbage_collector.destroyed[1])
- if(istype(o) && o.gcDestroyed)
- del(o)
- garbage_collector.dels++
- garbage_collector.destroyed.Cut(1, 2)
-#endif
-
-#ifdef GC_DEBUG
-#undef GC_DEBUG
-#endif
-
-#ifdef GC_FINDREF
-#undef GC_FINDREF
-#endif
\ No newline at end of file
diff --git a/code/controllers/Processes/machinery.dm b/code/controllers/Processes/machinery.dm
index 405615dc1fe..1fd1f8ce456 100644
--- a/code/controllers/Processes/machinery.dm
+++ b/code/controllers/Processes/machinery.dm
@@ -20,7 +20,7 @@
/datum/controller/process/machinery/proc/internal_process_machinery()
for(last_object in machines)
var/obj/machinery/M = last_object
- if(M && !M.gcDestroyed)
+ if(M && !QDELETED(M))
if(M.process() == PROCESS_KILL)
//M.inMachineList = 0 We don't use this debugging function
machines.Remove(M)
@@ -34,7 +34,7 @@
/datum/controller/process/machinery/proc/internal_process_power()
for(last_object in powernets)
var/datum/powernet/powerNetwork = last_object
- if(istype(powerNetwork) && isnull(powerNetwork.gcDestroyed))
+ if(istype(powerNetwork) && !QDELETED(powerNetwork))
powerNetwork.reset()
SCHECK
continue
@@ -52,7 +52,7 @@
/datum/controller/process/machinery/proc/internal_process_pipenets()
for(last_object in pipe_networks)
var/datum/pipe_network/pipeNetwork = last_object
- if(istype(pipeNetwork) && isnull(pipeNetwork.gcDestroyed))
+ if(istype(pipeNetwork) && !QDELETED(pipeNetwork))
pipeNetwork.process()
SCHECK
continue
diff --git a/code/controllers/Processes/mob.dm b/code/controllers/Processes/mob.dm
index 8681803ac44..6b623e77305 100644
--- a/code/controllers/Processes/mob.dm
+++ b/code/controllers/Processes/mob.dm
@@ -14,7 +14,7 @@
/datum/controller/process/mob/doWork()
for(last_object in mob_list)
var/mob/M = last_object
- if(M && isnull(M.gcDestroyed))
+ if(M && !QDELETED(M))
try
M.Life()
catch(var/exception/e)
diff --git a/code/controllers/Processes/nanoui.dm b/code/controllers/Processes/nanoui.dm
index 49b9048c078..21d2f8e5f01 100644
--- a/code/controllers/Processes/nanoui.dm
+++ b/code/controllers/Processes/nanoui.dm
@@ -9,7 +9,7 @@
/datum/controller/process/nanoui/doWork()
for(last_object in nanomanager.processing_uis)
var/datum/nanoui/NUI = last_object
- if(istype(NUI) && isnull(NUI.gcDestroyed))
+ if(istype(NUI) && !QDELETED(NUI))
try
NUI.process()
catch(var/exception/e)
diff --git a/code/controllers/Processes/obj.dm b/code/controllers/Processes/obj.dm
index 1010c8cc977..6032cbb5412 100644
--- a/code/controllers/Processes/obj.dm
+++ b/code/controllers/Processes/obj.dm
@@ -11,7 +11,7 @@
/datum/controller/process/obj/doWork()
for(last_object in processing_objects)
var/datum/O = last_object
- if(O && isnull(O.gcDestroyed))
+ if(!QDELETED(O))
try
O:process()
catch(var/exception/e)
diff --git a/code/controllers/Processes/radiation.dm b/code/controllers/Processes/radiation.dm
index 192acf5d5cb..71d9c60233e 100644
--- a/code/controllers/Processes/radiation.dm
+++ b/code/controllers/Processes/radiation.dm
@@ -15,10 +15,10 @@
/datum/controller/process/radiation/proc/sources_decay()
var/list/sources = linked.sources
for(var/thing in sources)
- if(deleted(thing))
- sources.Remove(thing)
- continue
var/datum/radiation_source/S = thing
+ if(QDELETED(S))
+ sources.Remove(S)
+ continue
if(S.decay)
S.update_rad_power(S.rad_power - config.radiation_decay_rate)
if(S.rad_power <= config.radiation_lower_limit)
@@ -29,22 +29,22 @@
/datum/controller/process/radiation/proc/cache_expires()
var/list/resistance_cache = linked.resistance_cache
for(var/thing in resistance_cache)
- if(deleted(thing))
- resistance_cache.Remove(thing)
- continue
var/turf/T = thing
+ if(QDELETED(T))
+ resistance_cache.Remove(T)
+ continue
if((length(T.contents) + 1) != resistance_cache[T])
resistance_cache.Remove(T) // If its stale REMOVE it! It will get added if its needed.
SCHECK
- // Step 3 - Registered irradiatable things are checked for radiation
+// Step 3 - Registered irradiatable things are checked for radiation
/datum/controller/process/radiation/proc/irradiate_targets()
var/list/registered_listeners = living_mob_list // For now just use this. Nothing else is interested anyway.
if(length(linked.sources) > 0)
for(var/thing in registered_listeners)
- if(deleted(thing))
- continue
var/atom/A = thing
+ if(QDELETED(A))
+ continue
var/turf/T = get_turf(thing)
var/rads = linked.get_rads_at_turf(T)
if(rads)
diff --git a/code/controllers/Processes/scheduler.dm b/code/controllers/Processes/scheduler.dm
index 276249bd558..fb9f85d7c46 100644
--- a/code/controllers/Processes/scheduler.dm
+++ b/code/controllers/Processes/scheduler.dm
@@ -30,8 +30,8 @@
// Note: We won't be informed about tasks being destroyed, but this is the best we can do.
/datum/controller/process/scheduler/copyStateFrom(var/datum/controller/process/scheduler/target)
scheduled_tasks = list()
- for(var/st in target.scheduled_tasks)
- if(!deleted(st) && istype(st, /datum/scheduled_task))
+ for(var/datum/scheduled_task/st in target.scheduled_tasks)
+ if(!QDELETED(st) && istype(st))
schedule(st)
scheduler = src
diff --git a/code/controllers/communications.dm b/code/controllers/communications.dm
index 796fb0b6649..332ab9af527 100644
--- a/code/controllers/communications.dm
+++ b/code/controllers/communications.dm
@@ -314,7 +314,6 @@ var/global/datum/controller/radio/radio_controller
devices_line -= null
if (devices_line.len==0)
devices -= devices_filter
- del(devices_line)
/datum/signal
var/obj/source
diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm
index 98c6a5d89d6..0589d5fa7ff 100644
--- a/code/controllers/failsafe.dm
+++ b/code/controllers/failsafe.dm
@@ -33,13 +33,13 @@ var/datum/controller/failsafe/Failsafe
/datum/controller/failsafe/Initialize()
set waitfor = 0
Failsafe.Loop()
- if(!deleted(src))
+ if(!QDELETED(src))
qdel(src) //when Loop() returns, we delete ourselves and let the mc recreate us
/datum/controller/failsafe/Destroy()
running = FALSE
..()
- // return QDEL_HINT_HARDDEL_NOW // TODO - Once we port garbage.dm
+ return QDEL_HINT_HARDDEL_NOW
/datum/controller/failsafe/proc/Loop()
while(running)
diff --git a/code/controllers/master.dm b/code/controllers/master.dm
index 981cf6d2bb8..360b739badf 100644
--- a/code/controllers/master.dm
+++ b/code/controllers/master.dm
@@ -66,7 +66,7 @@ var/CURRENT_TICKLIMIT = TICK_LIMIT_RUNNING
/datum/controller/master/Destroy()
..()
// Tell qdel() to Del() this object.
- // return QDEL_HINT_HARDDEL_NOW // TODO - Once we port garbage.dm
+ return QDEL_HINT_HARDDEL_NOW
/datum/controller/master/Shutdown()
processing = FALSE
diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm
index 3075682438d..c2d04d9c939 100644
--- a/code/controllers/master_controller.dm
+++ b/code/controllers/master_controller.dm
@@ -49,7 +49,7 @@ datum/controller/game_controller/proc/setup_objects()
admin_notice("Initializing objects", R_DEBUG)
sleep(-1)
for(var/atom/movable/object in world)
- if(isnull(object.gcDestroyed))
+ if(!QDELETED(object))
object.initialize()
admin_notice("Initializing areas", R_DEBUG)
@@ -80,8 +80,8 @@ datum/controller/game_controller/proc/setup_objects()
admin_notice("Initializing turbolifts", R_DEBUG)
for(var/thing in turbolifts)
- if(!deleted(thing))
- var/obj/turbolift_map_holder/lift = thing
+ var/obj/turbolift_map_holder/lift = thing
+ if(!QDELETED(lift))
lift.initialize()
sleep(-1)
diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm
index ce9558ca4f5..ff81fdc23e2 100644
--- a/code/controllers/subsystem.dm
+++ b/code/controllers/subsystem.dm
@@ -27,8 +27,15 @@
var/datum/controller/subsystem/queue_next
var/datum/controller/subsystem/queue_prev
-// Used to initialize the subsystem BEFORE the map has loaded
+//Do not override
/datum/controller/subsystem/New()
+ return
+
+// Used to initialize the subsystem BEFORE the map has loaded
+// Called AFTER Recover if that is called
+// Prefer to use Initialize if possible
+/datum/controller/subsystem/proc/PreInit()
+ return
//This is used so the mc knows when the subsystem sleeps. do not override.
/datum/controller/subsystem/proc/ignite(resumed = 0)
diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm
new file mode 100644
index 00000000000..b07337e8035
--- /dev/null
+++ b/code/controllers/subsystems/garbage.dm
@@ -0,0 +1,377 @@
+//
+// Garbage Collector Subsystem - Implements qdel() and the GC queue
+//
+SUBSYSTEM_DEF(garbage)
+ name = "Garbage"
+ priority = 15
+ wait = 5
+ flags = SS_FIRE_IN_LOBBY|SS_POST_FIRE_TIMING|SS_BACKGROUND|SS_NO_INIT
+
+ var/collection_timeout = 3000// deciseconds to wait to let running procs finish before we just say fuck it and force del() the object
+ var/delslasttick = 0 // number of del()'s we've done this tick
+ var/gcedlasttick = 0 // number of things that gc'ed last tick
+ var/totaldels = 0
+ var/totalgcs = 0
+
+ var/highest_del_time = 0
+ var/highest_del_tickusage = 0
+
+ var/list/queue = list() // list of refID's of things that should be garbage collected
+ // refID's are associated with the time at which they time out and need to be manually del()
+ // we do this so we aren't constantly locating them and preventing them from being gc'd
+
+ var/list/tobequeued = list() //We store the references of things to be added to the queue seperately so we can spread out GC overhead over a few ticks
+
+ var/list/didntgc = list() // list of all types that have failed to GC associated with the number of times that's happened.
+ // the types are stored as strings
+ var/list/sleptDestroy = list() //Same as above but these are paths that slept during their Destroy call
+
+ var/list/noqdelhint = list()// list of all types that do not return a QDEL_HINT
+ // all types that did not respect qdel(A, force=TRUE) and returned one
+ // of the immortality qdel hints
+ var/list/noforcerespect = list()
+
+#ifdef TESTING
+ var/list/qdel_list = list() // list of all types that have been qdel()eted
+#endif
+
+/datum/controller/subsystem/garbage/stat_entry(msg)
+ msg += "Q:[queue.len]|D:[delslasttick]|G:[gcedlasttick]|"
+ msg += "GR:"
+ if (!(delslasttick+gcedlasttick))
+ msg += "n/a|"
+ else
+ msg += "[round((gcedlasttick/(delslasttick+gcedlasttick))*100, 0.01)]%|"
+
+ msg += "TD:[totaldels]|TG:[totalgcs]|"
+ if (!(totaldels+totalgcs))
+ msg += "n/a|"
+ else
+ msg += "TGR:[round((totalgcs/(totaldels+totalgcs))*100, 0.01)]%"
+ ..(msg)
+
+/datum/controller/subsystem/garbage/Shutdown()
+ //Adds the del() log to world.log in a format condensable by the runtime condenser found in tools
+ if(didntgc.len || sleptDestroy.len)
+ var/list/dellog = list()
+ for(var/path in didntgc)
+ dellog += "Path : [path] \n"
+ dellog += "Failures : [didntgc[path]] \n"
+ if(path in sleptDestroy)
+ dellog += "Sleeps : [sleptDestroy[path]] \n"
+ sleptDestroy -= path
+ for(var/path in sleptDestroy)
+ dellog += "Path : [path] \n"
+ dellog += "Sleeps : [sleptDestroy[path]] \n"
+ log_misc(dellog.Join())
+
+/datum/controller/subsystem/garbage/fire()
+ HandleToBeQueued()
+ if(state == SS_RUNNING)
+ HandleQueue()
+
+ if (state == SS_PAUSED) //make us wait again before the next run.
+ state = SS_RUNNING
+
+//If you see this proc high on the profile, what you are really seeing is the garbage collection/soft delete overhead in byond.
+//Don't attempt to optimize, not worth the effort.
+/datum/controller/subsystem/garbage/proc/HandleToBeQueued()
+ var/list/tobequeued = src.tobequeued
+ var/starttime = world.time
+ var/starttimeofday = world.timeofday
+ while(tobequeued.len && starttime == world.time && starttimeofday == world.timeofday)
+ if (MC_TICK_CHECK)
+ break
+ var/ref = tobequeued[1]
+ Queue(ref)
+ tobequeued.Cut(1, 2)
+
+/datum/controller/subsystem/garbage/proc/HandleQueue()
+ delslasttick = 0
+ gcedlasttick = 0
+ var/time_to_kill = world.time - collection_timeout // Anything qdel() but not GC'd BEFORE this time needs to be manually del()
+ var/list/queue = src.queue
+ var/starttime = world.time
+ var/starttimeofday = world.timeofday
+ while(queue.len && starttime == world.time && starttimeofday == world.timeofday)
+ if (MC_TICK_CHECK)
+ break
+ var/refID = queue[1]
+ if (!refID)
+ queue.Cut(1, 2)
+ continue
+
+ var/GCd_at_time = queue[refID]
+ if(GCd_at_time > time_to_kill)
+ break // Everything else is newer, skip them
+ queue.Cut(1, 2)
+ var/datum/A
+ A = locate(refID)
+ if (A && A.gc_destroyed == GCd_at_time) // So if something else coincidently gets the same ref, it's not deleted by mistake
+ #ifdef GC_FAILURE_HARD_LOOKUP
+ A.find_references()
+ #endif
+
+ // Something's still referring to the qdel'd object. Kill it.
+ var/type = A.type
+ testing("GC: -- \ref[A] | [type] was unable to be GC'd and was deleted --")
+ didntgc["[type]"]++
+
+ HardDelete(A)
+
+ ++delslasttick
+ ++totaldels
+ else
+ ++gcedlasttick
+ ++totalgcs
+
+/datum/controller/subsystem/garbage/proc/QueueForQueuing(datum/A)
+ if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+ tobequeued += A
+ A.gc_destroyed = GC_QUEUED_FOR_QUEUING
+
+/datum/controller/subsystem/garbage/proc/Queue(datum/A)
+ if (isnull(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0))
+ return
+ if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL)
+ HardDelete(A)
+ return
+ var/gctime = world.time
+ var/refid = "\ref[A]"
+
+ A.gc_destroyed = gctime
+
+ if (queue[refid])
+ queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
+
+ queue[refid] = gctime
+
+//this is purely to seperate things profile wise.
+/datum/controller/subsystem/garbage/proc/HardDelete(datum/A)
+ var/time = world.timeofday
+ var/tick = world.tick_usage
+ var/ticktime = world.time
+
+ var/type = A.type
+ var/refID = "\ref[A]"
+
+ del(A)
+
+ tick = (world.tick_usage-tick+((world.time-ticktime)/world.tick_lag*100))
+ if (tick > highest_del_tickusage)
+ highest_del_tickusage = tick
+ time = world.timeofday - time
+ if (!time && TICK_DELTA_TO_MS(tick) > 1)
+ time = TICK_DELTA_TO_MS(tick)/100
+ if (time > highest_del_time)
+ highest_del_time = time
+ if (time > 10)
+ log_game("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete)")
+ message_admins("Error: [type]([refID]) took longer than 1 second to delete (took [time/10] seconds to delete).")
+ postpone(time/5)
+
+/datum/controller/subsystem/garbage/proc/HardQueue(datum/A)
+ if (istype(A) && A.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+ tobequeued += A
+ A.gc_destroyed = GC_QUEUED_FOR_HARD_DEL
+
+/datum/controller/subsystem/garbage/Recover()
+ if (istype(SSgarbage.queue))
+ queue |= SSgarbage.queue
+ if (istype(SSgarbage.tobequeued))
+ tobequeued |= SSgarbage.tobequeued
+
+// Should be treated as a replacement for the 'del' keyword.
+// Datums passed to this will be given a chance to clean up references to allow the GC to collect them.
+/proc/qdel(datum/D, force=FALSE)
+ if(!istype(D))
+ del(D)
+ return
+#ifdef TESTING
+ SSgarbage.qdel_list += D.type
+#endif
+ if(isnull(D.gc_destroyed))
+ D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED
+ var/start_time = world.time
+ var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up.
+ if(world.time != start_time)
+ SSgarbage.sleptDestroy[D.type]++
+ if(!D)
+ return
+ switch(hint)
+ if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion.
+ SSgarbage.QueueForQueuing(D)
+ if (QDEL_HINT_IWILLGC)
+ D.gc_destroyed = world.time
+ return
+ if (QDEL_HINT_LETMELIVE) //qdel should let the object live after calling destory.
+ if(!force)
+ D.gc_destroyed = null //clear the gc variable (important!)
+ return
+ // Returning LETMELIVE after being told to force destroy
+ // indicates the objects Destroy() does not respect force
+ if(!SSgarbage.noforcerespect[D.type])
+ SSgarbage.noforcerespect[D.type] = D.type
+ crash_with("[D.type] has been force deleted, but is \
+ returning an immortal QDEL_HINT, indicating it does \
+ not respect the force flag for qdel(). It has been \
+ placed in the queue, further instances of this type \
+ will also be queued.")
+ SSgarbage.QueueForQueuing(D)
+ if (QDEL_HINT_HARDDEL) //qdel should assume this object won't gc, and queue a hard delete using a hard reference to save time from the locate()
+ SSgarbage.HardQueue(D)
+ if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste.
+ SSgarbage.HardDelete(D)
+ if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion.
+ SSgarbage.QueueForQueuing(D)
+ #ifdef TESTING
+ D.find_references()
+ #endif
+ else
+ if(!SSgarbage.noqdelhint[D.type])
+ SSgarbage.noqdelhint[D.type] = D.type
+ crash_with("[D.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.")
+ SSgarbage.QueueForQueuing(D)
+ else if(D.gc_destroyed == GC_CURRENTLY_BEING_QDELETED)
+ CRASH("[D.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic")
+
+// Default implementation of clean-up code.
+// This should be overridden to remove all references pointing to the object being destroyed.
+// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
+/datum/proc/Destroy(force=FALSE)
+ tag = null
+ nanomanager.close_uis(src)
+ return QDEL_HINT_QUEUE
+
+/datum/var/gc_destroyed //Time when this object was destroyed.
+
+#ifdef TESTING
+/datum/var/running_find_references
+/datum/var/last_find_references = 0
+
+/datum/verb/find_refs()
+ set category = "Debug"
+ set name = "Find References"
+ set background = 1
+ set src in world
+
+ find_references(FALSE)
+
+/datum/proc/find_references(skip_alert)
+ running_find_references = type
+ if(usr && usr.client)
+ if(usr.client.running_find_references)
+ testing("CANCELLED search for references to a [usr.client.running_find_references].")
+ usr.client.running_find_references = null
+ running_find_references = null
+ //restart the garbage collector
+ SSgarbage.can_fire = 1
+ SSgarbage.next_fire = world.time + world.tick_lag
+ return
+
+ if(!skip_alert)
+ if(alert("Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", "Yes", "No") == "No")
+ running_find_references = null
+ return
+
+ //this keeps the garbage collector from failing to collect objects being searched for in here
+ SSgarbage.can_fire = 0
+
+ if(usr && usr.client)
+ usr.client.running_find_references = type
+
+ testing("Beginning search for references to a [type].")
+ last_find_references = world.time
+ find_references_in_globals()
+ for(var/datum/thing in world)
+ DoSearchVar(thing, "WorldRef: [thing]")
+ testing("Completed search for references to a [type].")
+ if(usr && usr.client)
+ usr.client.running_find_references = null
+ running_find_references = null
+
+ //restart the garbage collector
+ SSgarbage.can_fire = 1
+ SSgarbage.next_fire = world.time + world.tick_lag
+
+/client/verb/purge_all_destroyed_objects()
+ set category = "Debug"
+ if(SSgarbage)
+ while(SSgarbage.queue.len)
+ var/datum/o = locate(SSgarbage.queue[1])
+ if(istype(o) && o.gc_destroyed)
+ del(o)
+ SSgarbage.totaldels++
+ SSgarbage.queue.Cut(1, 2)
+
+/datum/verb/qdel_then_find_references()
+ set category = "Debug"
+ set name = "qdel() then Find References"
+ set background = 1
+ set src in world
+
+ qdel(src)
+ if(!running_find_references)
+ find_references(TRUE)
+
+/client/verb/show_qdeleted()
+ set category = "Debug"
+ set name = "Show qdel() Log"
+ set desc = "Render the qdel() log and display it"
+
+ var/dat = "List of things that have been qdel()eted this round
"
+
+ var/tmplist = list()
+ for(var/elem in SSgarbage.qdel_list)
+ if(!(elem in tmplist))
+ tmplist[elem] = 0
+ tmplist[elem]++
+
+ for(var/path in tmplist)
+ dat += "[path] - [tmplist[path]] times
"
+
+ usr << browse(dat, "window=qdeletedlog")
+
+/datum/proc/DoSearchVar(X, Xname)
+ if(usr && usr.client && !usr.client.running_find_references) return
+ if(istype(X, /datum))
+ var/datum/D = X
+ if(D.last_find_references == last_find_references)
+ return
+ D.last_find_references = last_find_references
+ for(var/V in D.vars)
+ for(var/varname in D.vars)
+ var/variable = D.vars[varname]
+ if(variable == src)
+ testing("Found [src.type] \ref[src] in [D.type]'s [varname] var. [Xname]")
+ else if(islist(variable))
+ if(src in variable)
+ testing("Found [src.type] \ref[src] in [D.type]'s [varname] list var. Global: [Xname]")
+#ifdef GC_FAILURE_HARD_LOOKUP
+ for(var/I in variable)
+ DoSearchVar(I, TRUE)
+ else
+ DoSearchVar(variable, "[Xname]: [varname]")
+#endif
+ else if(islist(X))
+ if(src in X)
+ testing("Found [src.type] \ref[src] in list [Xname].")
+#ifdef GC_FAILURE_HARD_LOOKUP
+ for(var/I in X)
+ DoSearchVar(I, Xname + ": list")
+#else
+ CHECK_TICK
+#endif
+
+//if find_references isn't working for some datum
+//update this list using tools/GenerateGlobalVarAccess
+/datum/proc/find_references_in_globals()
+ // TODO - Impement Global Variable Access
+ // for(var/global_var in _all_globals)
+ // DoSearchVar(readglobal(global_var), "Global: [global_var]")
+#endif
+
+
+/image/Destroy()
+ ..()
+ return QDEL_HINT_HARDDEL_NOW
diff --git a/code/controllers/voting.dm b/code/controllers/voting.dm
index aa06984f692..ba1bb23c33a 100644
--- a/code/controllers/voting.dm
+++ b/code/controllers/voting.dm
@@ -23,9 +23,15 @@ var/global/list/round_voters = list() // Keeps track of the individuals voting f
/datum/controller/vote/New()
if(vote != src)
if(istype(vote))
- del(vote)
+ Recover()
+ qdel(vote)
vote = src
+/datum/controller/vote/Destroy()
+ ..()
+ // Tell qdel() to Del() this object.
+ return QDEL_HINT_HARDDEL_NOW
+
/datum/controller/vote/proc/process() //called by master_controller
if(mode)
// No more change mode votes after the game has started.
diff --git a/code/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm
index 3bf80f06d0d..d511b5d2337 100644
--- a/code/datums/helper_datums/global_iterator.dm
+++ b/code/datums/helper_datums/global_iterator.dm
@@ -155,4 +155,5 @@ Data storage vars:
tag = null
arg_list.Cut()
stop()
+ return QDEL_HINT_LETMELIVE
//Do not call ..()
diff --git a/code/datums/repositories/decls.dm b/code/datums/repositories/decls.dm
index e87be74f531..61b24c21e9e 100644
--- a/code/datums/repositories/decls.dm
+++ b/code/datums/repositories/decls.dm
@@ -36,4 +36,4 @@
/decls/Destroy()
crash_with("Prevented attempt to delete a decl instance: [log_info_line(src)]")
- return 1 // Prevents Decl destruction
\ No newline at end of file
+ return QDEL_HINT_LETMELIVE // Prevents Decl destruction
diff --git a/code/datums/repositories/radiation.dm b/code/datums/repositories/radiation.dm
index 0245bb801cf..00c35324580 100644
--- a/code/datums/repositories/radiation.dm
+++ b/code/datums/repositories/radiation.dm
@@ -22,14 +22,6 @@ var/global/repository/radiation/radiation_repository = new()
src.source_turf = null
. = ..()
-// TEMPORARY HACK - hard del()'ing sources is too expensive! Until we implement qdel() hints we need to override behavior here
-/datum/radiation_source/finalize_qdel()
- if(garbage_collector)
- garbage_collector.AddTrash(src)
- else
- delayed_garbage |= src
-// TEMPORARY HACK END
-
/datum/radiation_source/proc/update_rad_power(var/new_power = null)
if(new_power != null && new_power != rad_power)
rad_power = new_power
diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm
index 1a7d844ca87..348d73b0b23 100644
--- a/code/datums/weakref.dm
+++ b/code/datums/weakref.dm
@@ -7,7 +7,9 @@
//obtain a weak reference to a datum
/proc/weakref(datum/D)
- if(D.gcDestroyed)
+ if(!istype(D))
+ return
+ if(QDELETED(D))
return
if(!D.weakref)
D.weakref = new/weakref(D)
@@ -22,7 +24,7 @@
/weakref/Destroy()
// A weakref datum should not be manually destroyed as it is a shared resource,
// rather it should be automatically collected by the BYOND GC when all references are gone.
- return 0
+ return QDEL_HINT_LETMELIVE
/weakref/proc/resolve()
var/datum/D = locate(ref)
diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm
index 066c357a22e..c1ee225dce5 100644
--- a/code/datums/wires/wires.dm
+++ b/code/datums/wires/wires.dm
@@ -48,6 +48,7 @@ var/list/wireColours = list("red", "blue", "green", "darkred", "orange", "brown"
/datum/wires/Destroy()
holder = null
+ signallers.Cut()
return ..()
/datum/wires/proc/GenerateWires()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index fe9f99b0328..64ae0722f83 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -23,16 +23,6 @@
if(auto_init && ticker && ticker.current_state == GAME_STATE_PLAYING)
initialize()
-/atom/movable/Del()
- if(isnull(gcDestroyed) && loc)
- testing("GC: -- [type] was deleted via del() rather than qdel() --")
- crash_with("GC: -- [type] was deleted via del() rather than qdel() --") // stick a stack trace in the runtime logs
-// else if(isnull(gcDestroyed))
-// testing("GC: [type] was deleted via GC without qdel()") //Not really a huge issue but from now on, please qdel()
-// else
-// testing("GC: [type] was deleted via GC with qdel()")
- ..()
-
/atom/movable/Destroy()
. = ..()
if(reagents)
@@ -53,7 +43,7 @@
pulledby = null
/atom/movable/proc/initialize()
- if(!isnull(gcDestroyed))
+ if(QDELETED(src))
crash_with("GC: -- [type] had initialize() called after qdel() --")
/atom/movable/Bump(var/atom/A, yes)
diff --git a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
index 6f549d15f4e..90ac43379fa 100644
--- a/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
+++ b/code/game/gamemodes/endgame/supermatter_cascade/universe.dm
@@ -37,7 +37,6 @@ var/global/universe_has_ended = 0
// Apply changes when entering state
/datum/universal_state/supermatter_cascade/OnEnter()
set background = 1
- garbage_collector.garbage_collect = 0
world << "You are blinded by a brilliant flash of energy."
world << sound('sound/effects/cascade.ogg')
diff --git a/code/game/machinery/CableLayer.dm b/code/game/machinery/CableLayer.dm
index 2b4eae9a0d3..2e10ed3b1b0 100644
--- a/code/game/machinery/CableLayer.dm
+++ b/code/game/machinery/CableLayer.dm
@@ -73,7 +73,7 @@
visible_message("A red light flashes on \the [src].")
return
cable.use(amount)
- if(deleted(cable))
+ if(QDELETED(cable))
cable = null
return 1
diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm
index b7bcc53e91e..deee8ed76b9 100644
--- a/code/game/machinery/atmoalter/portable_atmospherics.dm
+++ b/code/game/machinery/atmoalter/portable_atmospherics.dm
@@ -21,9 +21,9 @@
return 1
/obj/machinery/portable_atmospherics/Destroy()
- qdel(air_contents)
- qdel(holding)
- ..()
+ qdel_null(air_contents)
+ qdel_null(holding)
+ . = ..()
/obj/machinery/portable_atmospherics/initialize()
. = ..()
@@ -40,11 +40,6 @@
else
update_icon()
-/obj/machinery/portable_atmospherics/Destroy()
- qdel(air_contents)
-
- ..()
-
/obj/machinery/portable_atmospherics/proc/StandardAirMix()
return list(
"oxygen" = O2STANDARD * MolesForPressure(),
diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm
index 805bb7dba9b..ba8aa85b2c7 100644
--- a/code/game/machinery/doors/airlock_control.dm
+++ b/code/game/machinery/doors/airlock_control.dm
@@ -145,7 +145,7 @@ obj/machinery/door/airlock/New()
obj/machinery/door/airlock/Destroy()
if(frequency && radio_controller)
radio_controller.remove_object(src,frequency)
- ..()
+ return ..()
obj/machinery/airlock_sensor
icon = 'icons/obj/airlock_machines.dmi'
@@ -220,7 +220,7 @@ obj/machinery/airlock_sensor/New()
obj/machinery/airlock_sensor/Destroy()
if(radio_controller)
radio_controller.remove_object(src,frequency)
- ..()
+ return ..()
obj/machinery/airlock_sensor/airlock_interior
command = "cycle_interior"
@@ -292,7 +292,7 @@ obj/machinery/access_button/New()
obj/machinery/access_button/Destroy()
if(radio_controller)
radio_controller.remove_object(src, frequency)
- ..()
+ return ..()
obj/machinery/access_button/airlock_interior
frequency = 1379
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index cf7f96d52da..240610358fb 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -73,8 +73,7 @@
/obj/machinery/door/Destroy()
density = 0
update_nearby_tiles()
- ..()
- return
+ . = ..()
/obj/machinery/door/process()
if(close_door_at && world.time >= close_door_at)
diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm
index a134e68e3fe..fdaf284f3f2 100644
--- a/code/game/machinery/floor_light.dm
+++ b/code/game/machinery/floor_light.dm
@@ -150,7 +150,7 @@ var/list/floor_light_cache = list()
var/area/A = get_area(src)
if(A)
on = 0
- ..()
+ . = ..()
/obj/machinery/floor_light/cultify()
default_light_colour = "#FF0000"
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index 158b718e833..e0156196d6a 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -202,7 +202,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
/obj/machinery/hologram/holopad/Destroy()
for (var/mob/living/silicon/ai/master in masters)
clear_holo(master)
- ..()
+ return ..()
/*
Holographic project of everything else.
diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm
index 4abdb685d05..06228dd86f8 100644
--- a/code/game/machinery/pipe/construction.dm
+++ b/code/game/machinery/pipe/construction.dm
@@ -485,7 +485,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
- if (deleted(P))
+ if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -504,7 +504,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
- if (deleted(P))
+ if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -523,7 +523,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
- if (deleted(P))
+ if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -542,7 +542,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
- if (deleted(P))
+ if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -559,7 +559,7 @@ Buildable meters
P.initialize_directions = pipe_dir //this var it's used to know if the pipe is bent or not
P.initialize_directions_he = pipe_dir
P.initialize()
- if (deleted(P))
+ if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
@@ -594,7 +594,7 @@ Buildable meters
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
M.initialize()
- if (deleted(M))
+ if (QDELETED(M))
usr << pipefailtext
return 1
M.build_network()
@@ -663,7 +663,7 @@ Buildable meters
var/turf/T = M.loc
M.level = !T.is_plating() ? 2 : 1
M.initialize()
- if (deleted(M))
+ if (QDELETED(M))
usr << pipefailtext
return 1
M.build_network()
@@ -740,7 +740,7 @@ Buildable meters
P.initialize_directions = src.get_pdir()
P.initialize_directions_he = src.get_hdir()
P.initialize()
- if (deleted(P))
+ if (QDELETED(P))
usr << pipefailtext //"There's nothing to connect this pipe to! (with how the pipe code works, at least one end needs to be connected to something, otherwise the game deletes the segment)"
return 1
P.build_network()
@@ -922,7 +922,7 @@ Buildable meters
var/turf/T = P.loc
P.level = !T.is_plating() ? 2 : 1
P.initialize()
- if (deleted(P))
+ if (QDELETED(P))
usr << pipefailtext
return 1
P.build_network()
diff --git a/code/game/machinery/pipe/pipelayer.dm b/code/game/machinery/pipe/pipelayer.dm
index 40726825a19..c960879e540 100644
--- a/code/game/machinery/pipe/pipelayer.dm
+++ b/code/game/machinery/pipe/pipelayer.dm
@@ -23,9 +23,8 @@
update_icon()
/obj/machinery/pipelayer/Destroy()
- qdel(W)
- W = null
- ..()
+ qdel_null(W)
+ . = ..()
/obj/machinery/pipelayer/RefreshParts()
var/mb_rating = 0
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 09ff0075dbf..5d7d16a1261 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -140,18 +140,14 @@
cell = null
internal_tank = null
- qdel(pr_int_temp_processor)
- qdel(pr_inertial_movement)
- qdel(pr_give_air)
- qdel(pr_internal_damage)
- qdel(spark_system)
- pr_int_temp_processor = null
- pr_give_air = null
- pr_internal_damage = null
- spark_system = null
+ qdel_null(pr_int_temp_processor)
+ qdel_null(pr_inertial_movement)
+ qdel_null(pr_give_air)
+ qdel_null(pr_internal_damage)
+ qdel_null(spark_system)
mechas_list -= src //global mech list
- ..()
+ . = ..()
////////////////////////
////// Helpers /////////
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 8dfe6270308..c963a902bb4 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -21,11 +21,6 @@
if(can_buckle && istype(M))
user_buckle_mob(M, user)
-//Cleanup
-/obj/Del()
- unbuckle_mob()
- return ..()
-
/obj/Destroy()
unbuckle_mob()
return ..()
diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm
index 2343871b95a..4c930d23cf2 100644
--- a/code/game/objects/effects/effect_system.dm
+++ b/code/game/objects/effects/effect_system.dm
@@ -13,11 +13,6 @@ would spawn and follow the beaker, even if it is carried or thrown.
unacidable = 1//So effect are not targeted by alien acid.
pass_flags = PASSTABLE | PASSGRILLE
-/obj/effect/Destroy()
- if(reagents)
- reagents.delete()
- return ..()
-
/datum/effect/effect/system
var/number = 3
var/cardinals = 0
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 88f69d80111..7f1d83cc4a8 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -1470,8 +1470,12 @@ var/global/list/obj/item/device/pda/PDAs = list()
/obj/item/device/pda/Destroy()
PDAs -= src
if (src.id && prob(90)) //IDs are kept in 90% of the cases
- src.id.loc = get_turf(src.loc)
- ..()
+ src.id.forceMove(get_turf(src.loc))
+ else
+ qdel_null(src.id)
+ qdel_null(src.cartridge)
+ qdel_null(src.pai)
+ return ..()
/obj/item/device/pda/clown/Crossed(AM as mob|obj) //Clown PDA is slippery.
if (istype(AM, /mob/living))
diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm
index 2c4368736e8..c53ba712db4 100644
--- a/code/game/objects/items/devices/PDA/cart.dm
+++ b/code/game/objects/items/devices/PDA/cart.dm
@@ -78,6 +78,10 @@ var/list/civilian_cartridges = list(
var/message2
var/list/stored_data = list()
+/obj/item/weapon/cartridge/Destroy()
+ qdel_null(radio)
+ return ..()
+
/obj/item/weapon/cartridge/engineering
name = "\improper Power-ON cartridge"
icon_state = "cart-e"
@@ -164,10 +168,6 @@ var/list/civilian_cartridges = list(
radio = new /obj/item/radio/integrated/signal(src)
..()
-/obj/item/weapon/cartridge/signal/Destroy()
- qdel(radio)
- ..()
-
/obj/item/weapon/cartridge/quartermaster
name = "\improper Space Parts & Space Vendors cartridge"
desc = "Perfect for the Quartermaster on the go!"
diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm
index 5df911cba83..6f8a4210be4 100644
--- a/code/game/objects/items/devices/communicator/communicator.dm
+++ b/code/game/objects/items/devices/communicator/communicator.dm
@@ -803,7 +803,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
if(exonet)
exonet.remove_address()
exonet = null
- ..()
+ return ..()
// Proc: update_icon()
// Parameters: None
diff --git a/code/game/objects/items/devices/radio/intercom.dm b/code/game/objects/items/devices/radio/intercom.dm
index 8e9a72d2038..106d1448344 100644
--- a/code/game/objects/items/devices/radio/intercom.dm
+++ b/code/game/objects/items/devices/radio/intercom.dm
@@ -96,7 +96,7 @@
/obj/item/device/radio/intercom/Destroy()
processing_objects -= src
- ..()
+ return ..()
/obj/item/device/radio/intercom/attack_ai(mob/user as mob)
src.add_fingerprint(user)
diff --git a/code/game/objects/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm
index 88b53bd342d..35697914ee7 100644
--- a/code/game/objects/items/devices/transfer_valve.dm
+++ b/code/game/objects/items/devices/transfer_valve.dm
@@ -169,7 +169,7 @@
valve_open = 0
- if(deleted(tank_one) || deleted(tank_two))
+ if(QDELETED(tank_one) || QDELETED(tank_two))
return
var/ratio1 = tank_one.air_contents.volume/tank_two.air_contents.volume
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index 84bee725588..649b5677cad 100644
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -27,15 +27,10 @@
/obj/item/weapon/flamethrower/Destroy()
- if(weldtool)
- qdel(weldtool)
- if(igniter)
- qdel(igniter)
- if(ptank)
- qdel(ptank)
- ..()
- return
-
+ qdel_null(weldtool)
+ qdel_null(igniter)
+ qdel_null(ptank)
+ . = ..()
/obj/item/weapon/flamethrower/process()
if(!lit)
diff --git a/code/game/objects/items/weapons/material/material_armor.dm b/code/game/objects/items/weapons/material/material_armor.dm
index 1041fb5d1fd..990ae376591 100644
--- a/code/game/objects/items/weapons/material/material_armor.dm
+++ b/code/game/objects/items/weapons/material/material_armor.dm
@@ -42,7 +42,7 @@ Protectiveness | Armor %
/obj/item/clothing/Destroy()
processing_objects -= src
- ..()
+ return ..()
/obj/item/clothing/get_material()
return material
diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm
index 31231964cbe..c778ec07a99 100644
--- a/code/game/objects/items/weapons/material/material_weapons.dm
+++ b/code/game/objects/items/weapons/material/material_weapons.dm
@@ -71,7 +71,7 @@
/obj/item/weapon/material/Destroy()
processing_objects -= src
- ..()
+ . = ..()
/obj/item/weapon/material/apply_hit_effect()
..()
diff --git a/code/game/objects/items/weapons/mop_deploy.dm b/code/game/objects/items/weapons/mop_deploy.dm
index 8baab875285..30a6991a531 100644
--- a/code/game/objects/items/weapons/mop_deploy.dm
+++ b/code/game/objects/items/weapons/mop_deploy.dm
@@ -26,7 +26,7 @@
T.dirt = 0
for(var/obj/effect/O in src)
if(istype(O,/obj/effect/rune) || istype(O,/obj/effect/decal/cleanable) || istype(O,/obj/effect/overlay))
- del(O)
+ qdel(O)
/* //Reagent code changed at some point and the below doesn't work. To be fixed later.
source.reagents.reaction(src, TOUCH, 10) //10 is the multiplier for the reaction effect. probably needed to wet the floor properly.
source.reagents.remove_any(1) //reaction() doesn't use up the reagents
@@ -47,16 +47,16 @@
return
..()
-/obj/item/weapon/mop_deploy/Del()
+/obj/item/weapon/mop_deploy/Destroy()
processing_objects -= src
- ..()
+ . = ..()
/obj/item/weapon/mop_deploy/attack_self(mob/user as mob)
user.drop_from_inventory(src)
- spawn(1) if(src) del(src)
+ spawn(1) if(!QDELETED(src)) qdel(src)
/obj/item/weapon/mop_deploy/dropped()
- spawn(1) if(src) del(src)
+ spawn(1) if(!QDELETED(src)) qdel(src)
/obj/item/weapon/mop_deploy/process()
if(!creator || loc != creator || !creator.item_is_in_hands(src))
@@ -71,4 +71,4 @@
host.pinned -= src
host.embedded -= src
host.drop_from_inventory(src)
- spawn(1) if(src) del(src)
\ No newline at end of file
+ spawn(1) if(!QDELETED(src)) qdel(src)
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/internal.dm b/code/game/objects/items/weapons/storage/internal.dm
index 8d1898fe4b2..1f1a5f6963e 100644
--- a/code/game/objects/items/weapons/storage/internal.dm
+++ b/code/game/objects/items/weapons/storage/internal.dm
@@ -12,7 +12,7 @@
/obj/item/weapon/storage/internal/Destroy()
master_item = null
- ..()
+ . = ..()
/obj/item/weapon/storage/internal/attack_hand()
return //make sure this is never picked up
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 3846a44d2b3..db55e95b44d 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -38,15 +38,15 @@
/obj/item/weapon/storage/Destroy()
close_all()
- qdel(boxes)
- qdel(src.storage_start)
- qdel(src.storage_continue)
- qdel(src.storage_end)
- qdel(src.stored_start)
- qdel(src.stored_continue)
- qdel(src.stored_end)
- qdel(closer)
- ..()
+ qdel_null(boxes)
+ qdel_null(src.storage_start)
+ qdel_null(src.storage_continue)
+ qdel_null(src.storage_end)
+ qdel_null(src.stored_start)
+ qdel_null(src.stored_continue)
+ qdel_null(src.stored_end)
+ qdel_null(closer)
+ . = ..()
/obj/item/weapon/storage/MouseDrop(obj/over_object as obj)
if(!canremove)
diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm
index 57a5cf06a45..b8daa05d7f3 100644
--- a/code/game/objects/items/weapons/tanks/tanks.dm
+++ b/code/game/objects/items/weapons/tanks/tanks.dm
@@ -44,8 +44,7 @@ var/list/global/tank_gauge_cache = list()
return
/obj/item/weapon/tank/Destroy()
- if(air_contents)
- qdel(air_contents)
+ qdel_null(air_contents)
processing_objects.Remove(src)
@@ -53,7 +52,7 @@ var/list/global/tank_gauge_cache = list()
var/obj/item/device/transfer_valve/TTV = loc
TTV.remove_tank(src)
- ..()
+ return ..()
/obj/item/weapon/tank/examine(mob/user)
. = ..(user, 0)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 928246b2b5c..b41743799bf 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -10,7 +10,7 @@
/obj/structure/Destroy()
if(parts)
new parts(loc)
- ..()
+ . = ..()
/obj/structure/attack_hand(mob/user)
if(breakable)
@@ -49,9 +49,6 @@
if(climbable)
verbs += /obj/structure/proc/climb_on
-/obj/structure/Destroy()
- ..()
-
/obj/structure/proc/climb_on()
set name = "Climb structure"
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index ec4652901d4..35b8571a114 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -39,7 +39,7 @@
spawn(1)
if(istype(O)) // If we built a new floor with the lattice, the open turf won't exist anymore.
O.update() // This lattice may be supporting things on top of it. If it's being deleted, they need to fall down.
- ..()
+ . = ..()
/obj/structure/lattice/ex_act(severity)
switch(severity)
diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm
index 4c36a7ac150..ffddffe022a 100644
--- a/code/game/objects/structures/window.dm
+++ b/code/game/objects/structures/window.dm
@@ -355,12 +355,9 @@
density = 0
update_nearby_tiles()
var/turf/location = loc
- loc = null
+ . = ..()
for(var/obj/structure/window/W in orange(location, 1))
W.update_icon()
- loc = location
- ..()
-
/obj/structure/window/Move()
var/ini_dir = dir
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 3f2e0064f22..d34b210b997 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -47,6 +47,7 @@
/turf/Destroy()
turfs -= src
..()
+ return QDEL_HINT_IWILLGC
/turf/ex_act(severity)
return 0
diff --git a/code/modules/admin/view_variables/helpers.dm b/code/modules/admin/view_variables/helpers.dm
index 67eefc018de..95945f82e00 100644
--- a/code/modules/admin/view_variables/helpers.dm
+++ b/code/modules/admin/view_variables/helpers.dm
@@ -1,8 +1,4 @@
-// Keep these two together, they *must* be defined on both
-// If /client ever becomes /datum/client or similar, they can be merged
-/client/proc/get_view_variables_header()
- return "[src]"
/datum/proc/get_view_variables_header()
return "[src]"
@@ -31,9 +27,6 @@
"}
-// Same for these as for get_view_variables_header() above
-/client/proc/get_view_variables_options()
- return ""
/datum/proc/get_view_variables_options()
return ""
diff --git a/code/modules/assembly/signaler.dm b/code/modules/assembly/signaler.dm
index 169ab9b6a02..1273b004a43 100644
--- a/code/modules/assembly/signaler.dm
+++ b/code/modules/assembly/signaler.dm
@@ -187,4 +187,4 @@
if(radio_controller)
radio_controller.remove_object(src,frequency)
frequency = 0
- ..()
+ . = ..()
diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm
index b5596521328..5e5a1761285 100644
--- a/code/modules/client/client defines.dm
+++ b/code/modules/client/client defines.dm
@@ -1,4 +1,8 @@
/client
+ //////////////////////
+ //BLACK MAGIC THINGS//
+ //////////////////////
+ parent_type = /datum
////////////////
//ADMIN THINGS//
////////////////
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index d908df1b3d9..d6f9375e984 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -181,6 +181,9 @@
clients -= src
return ..()
+/client/Destroy()
+ ..()
+ return QDEL_HINT_HARDDEL_NOW
// here because it's similar to below
diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm
index cec738479ee..583f491d5e6 100644
--- a/code/modules/clothing/spacesuits/rig/modules/utility.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm
@@ -477,7 +477,7 @@
for(var/obj/item/weapon/mop_deploy/blade in M.contents)
M.drop_from_inventory(blade)
- del(blade)
+ qdel(blade)
//Space Cleaner Launcher
@@ -520,7 +520,7 @@
user << "You slot \the [input_device] into the suit module."
user.drop_from_inventory(input_device)
- del(input_device)
+ qdel(input_device)
accepted_item.charges++
return 1
diff --git a/code/modules/hydroponics/spreading/spreading_growth.dm b/code/modules/hydroponics/spreading/spreading_growth.dm
index fca9f3abafe..0dbe4c9eead 100644
--- a/code/modules/hydroponics/spreading/spreading_growth.dm
+++ b/code/modules/hydroponics/spreading/spreading_growth.dm
@@ -116,7 +116,7 @@
var/obj/effect/plant/child = new(get_turf(src),seed,parent)
spawn(1) // This should do a little bit of animation.
- if(deleted(child))
+ if(QDELETED(child))
return
//move out to the destination
diff --git a/code/modules/lighting/lighting_corner.dm b/code/modules/lighting/lighting_corner.dm
index 63ce1232535..088c53e5216 100644
--- a/code/modules/lighting/lighting_corner.dm
+++ b/code/modules/lighting/lighting_corner.dm
@@ -131,3 +131,9 @@
/datum/lighting_corner/dummy/New()
return
+
+/datum/lighting_corner/Destroy(var/force)
+ if (!force)
+ return QDEL_HINT_LETMELIVE
+ crash_with("Who decided to force qdel() a lighting corner? Why did you do this?")
+ return ..()
diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm
index 60c684fd862..690eba93264 100644
--- a/code/modules/lighting/lighting_overlay.dm
+++ b/code/modules/lighting/lighting_overlay.dm
@@ -39,7 +39,7 @@
log_debug("A lighting overlay realised its loc was NOT a turf (actual loc: [loc][loc ? ", " + loc.type : "null"]) in update_overlay() and got qdel'ed!")
else
log_debug("A lighting overlay realised it was in nullspace in update_overlay() and got pooled!")
- qdel(src)
+ qdel(src, force=TRUE)
return
// To the future coder who sees this and thinks
@@ -121,14 +121,17 @@
/atom/movable/lighting_overlay/throw_at()
return 0
-/atom/movable/lighting_overlay/Destroy()
- total_lighting_overlays--
- global.lighting_update_overlays -= src
- global.lighting_update_overlays_old -= src
+/atom/movable/lighting_overlay/Destroy(var/force)
+ if (force)
+ total_lighting_overlays--
+ global.lighting_update_overlays -= src
+ global.lighting_update_overlays_old -= src
- var/turf/T = loc
- if(istype(T))
- T.lighting_overlay = null
- T.luminosity = 1
+ var/turf/T = loc
+ if(istype(T))
+ T.lighting_overlay = null
+ T.luminosity = 1
- return ..()
+ return ..()
+ else
+ return QDEL_HINT_LETMELIVE
diff --git a/code/modules/mob/freelook/ai/eye.dm b/code/modules/mob/freelook/ai/eye.dm
index 62f0d931520..a1c28a4af34 100644
--- a/code/modules/mob/freelook/ai/eye.dm
+++ b/code/modules/mob/freelook/ai/eye.dm
@@ -59,7 +59,7 @@
/mob/living/silicon/ai/Destroy()
destroy_eyeobj()
- ..()
+ return ..()
/atom/proc/move_camera_by_click()
if(istype(usr, /mob/living/silicon/ai))
diff --git a/code/modules/mob/freelook/ai/update_triggers.dm b/code/modules/mob/freelook/ai/update_triggers.dm
index f4fea0aa58f..5c21c6784da 100644
--- a/code/modules/mob/freelook/ai/update_triggers.dm
+++ b/code/modules/mob/freelook/ai/update_triggers.dm
@@ -59,7 +59,7 @@
/obj/machinery/camera/Destroy()
clear_all_networks()
cameranet.cameras -= src
- ..()
+ return ..()
// Mobs
/mob/living/silicon/ai/rejuvenate()
diff --git a/code/modules/mob/freelook/eye.dm b/code/modules/mob/freelook/eye.dm
index 0c38ad8033c..ff8681d128a 100644
--- a/code/modules/mob/freelook/eye.dm
+++ b/code/modules/mob/freelook/eye.dm
@@ -38,7 +38,11 @@ mob/observer/eye/Destroy()
qdel(ghostimage)
ghostimage = null
updateallghostimages()
- ..()
+ if(owner)
+ if(owner.eyeobj == src)
+ owner.eyeobj = null
+ owner = null
+ . = ..()
/mob/observer/eye/Move(n, direct)
if(owner == src)
diff --git a/code/modules/mob/freelook/mask/update_triggers.dm b/code/modules/mob/freelook/mask/update_triggers.dm
index 11423671640..1dd520e7f1d 100644
--- a/code/modules/mob/freelook/mask/update_triggers.dm
+++ b/code/modules/mob/freelook/mask/update_triggers.dm
@@ -25,7 +25,7 @@
/mob/living/Destroy()
cultnet.updateVisibility(src, 0)
- ..()
+ return ..()
/mob/living/rejuvenate()
var/was_dead = stat == DEAD
diff --git a/code/modules/mob/freelook/update_triggers.dm b/code/modules/mob/freelook/update_triggers.dm
index 8408083ba8b..482025426eb 100644
--- a/code/modules/mob/freelook/update_triggers.dm
+++ b/code/modules/mob/freelook/update_triggers.dm
@@ -15,7 +15,7 @@
/turf/simulated/Destroy()
updateVisibility(src)
- ..()
+ return ..()
/turf/simulated/New()
..()
@@ -26,7 +26,7 @@
/obj/structure/Destroy()
updateVisibility(src)
- ..()
+ return ..()
/obj/structure/New()
..()
diff --git a/code/modules/mob/freelook/visualnet.dm b/code/modules/mob/freelook/visualnet.dm
index 24ad252eda1..0d53d1e48a5 100644
--- a/code/modules/mob/freelook/visualnet.dm
+++ b/code/modules/mob/freelook/visualnet.dm
@@ -14,7 +14,7 @@
/datum/visualnet/Destroy()
visual_nets -= src
- ..()
+ return ..()
// Checks if a chunk has been Generated in x, y, z.
/datum/visualnet/proc/chunkGenerated(x, y, z)
diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm
index 1998e7fe650..5d0796f77c4 100644
--- a/code/modules/mob/holder.dm
+++ b/code/modules/mob/holder.dm
@@ -27,7 +27,7 @@ var/list/holder_mob_icon_cache = list()
/obj/item/weapon/holder/Destroy()
processing_objects.Remove(src)
- ..()
+ return ..()
/obj/item/weapon/holder/process()
update_state()
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 8a2513a54ab..9c372cf2e7d 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -443,7 +443,7 @@ var/global/list/damage_icon_parts = list()
//For legacy support.
/mob/living/carbon/human/regenerate_icons()
..()
- if(transforming) return
+ if(transforming || QDELETED(src)) return
update_mutations(0)
update_body(0)
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 8583521f854..a22011e8abf 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -215,20 +215,15 @@ var/list/ai_verbs_hidden = list( // For why this exists, refer to https://xkcd.c
/mob/living/silicon/ai/Destroy()
ai_list -= src
- qdel(eyeobj)
- eyeobj = null
-
- qdel(psupply)
- psupply = null
-
- qdel(aiMulti)
- aiMulti = null
-
- qdel(aiRadio)
- aiRadio = null
-
- qdel(aiCamera)
- aiCamera = null
+ qdel_null(announcement)
+ qdel_null(eyeobj)
+ qdel_null(psupply)
+ qdel_null(aiPDA)
+ qdel_null(aiCommunicator)
+ qdel_null(aiMulti)
+ qdel_null(aiRadio)
+ qdel_null(aiCamera)
+ hack = null
return ..()
diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
index c7fb1d67284..7d5c270f9b8 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm
@@ -184,7 +184,7 @@
wrapped.force = 0.0
wrapped.attack(M,user)
M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe.
- if(deleted(wrapped) || wrapped.loc != src.loc)
+ if(QDELETED(wrapped) || wrapped.loc != src.loc)
wrapped = null
return 1
return 0
diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm
index 40b18525282..b74b7d6cf79 100644
--- a/code/modules/mob/living/silicon/silicon.dm
+++ b/code/modules/mob/living/silicon/silicon.dm
@@ -41,7 +41,7 @@
silicon_mob_list -= src
for(var/datum/alarm_handler/AH in alarm_manager.all_handlers)
AH.unregister_alarm(src)
- ..()
+ return ..()
/mob/living/silicon/proc/init_id()
if(idcard)
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 3ad6260813a..0e121e1481d 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -195,7 +195,7 @@
friends.Cut() //This one is not
walk_list.Cut()
languages.Cut()
- ..()
+ return ..()
//Client attached
/mob/living/simple_animal/Login()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 7574bb4c774..454b58a55c1 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -14,6 +14,7 @@
spellremove(src)
ghostize()
..()
+ return QDEL_HINT_HARDDEL_NOW
/mob/proc/remove_screen_obj_references()
hands = null
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 80f68b979d5..890ff2e8732 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -83,7 +83,9 @@
//This makes sure that the grab screen object is displayed in the correct hand.
-/obj/item/weapon/grab/proc/synch()
+/obj/item/weapon/grab/proc/synch() //why is this needed?
+ if(QDELETED(src))
+ return
if(affecting)
if(assailant.r_hand == src)
hud.screen_loc = ui_rhand
@@ -91,7 +93,7 @@
hud.screen_loc = ui_lhand
/obj/item/weapon/grab/process()
- if(gcDestroyed) // GC is trying to delete us, we'll kill our processing so we can cleanly GC
+ if(QDELETED(src)) // GC is trying to delete us, we'll kill our processing so we can cleanly GC
return PROCESS_KILL
confirm()
@@ -221,6 +223,8 @@
animate(affecting, pixel_x =-shift, pixel_y = 0, 5, 1, LINEAR_EASING)
/obj/item/weapon/grab/proc/s_click(obj/screen/S)
+ if(QDELETED(src))
+ return
if(!affecting)
return
if(state == GRAB_UPGRADING)
@@ -291,6 +295,8 @@
return 1
/obj/item/weapon/grab/attack(mob/M, mob/living/user)
+ if(QDELETED(src))
+ return
if(!affecting)
return
if(world.time < (last_action + 20))
@@ -336,7 +342,7 @@
/obj/item/weapon/grab/dropped()
loc = null
- if(!destroying)
+ if(!QDELETED(src))
qdel(src)
/obj/item/weapon/grab/proc/reset_kill_state()
@@ -387,9 +393,6 @@
/obj/item/weapon/grab/proc/size_difference(mob/A, mob/B)
return mob_size_difference(A.mob_size, B.mob_size)
-/obj/item/weapon/grab
- var/destroying = 0
-
/obj/item/weapon/grab/Destroy()
animate(affecting, pixel_x = 0, pixel_y = 0, 4, 1, LINEAR_EASING)
affecting.layer = 4
@@ -402,5 +405,4 @@
assailant = null
qdel(hud)
hud = null
- destroying = 1 // stops us calling qdel(src) on dropped()
- ..()
+ return ..()
diff --git a/code/modules/mob/modifiers.dm b/code/modules/mob/modifiers.dm
index f67ffc374b6..0b39c9e2754 100644
--- a/code/modules/mob/modifiers.dm
+++ b/code/modules/mob/modifiers.dm
@@ -73,7 +73,7 @@
/mob/living/Destroy()
remove_all_modifiers(TRUE)
- ..()
+ return ..()
// Called by Life().
/mob/living/proc/handle_modifiers()
diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm
index 1390c89b291..e0809e32b59 100644
--- a/code/modules/organs/internal/brain.dm
+++ b/code/modules/organs/internal/brain.dm
@@ -67,10 +67,8 @@
brainmob.client.screen.len = null //clear the hud
/obj/item/organ/internal/brain/Destroy()
- if(brainmob)
- qdel(brainmob)
- brainmob = null
- ..()
+ qdel_null(brainmob)
+ . = ..()
/obj/item/organ/internal/brain/proc/transfer_identity(var/mob/living/carbon/H)
diff --git a/code/modules/power/breaker_box.dm b/code/modules/power/breaker_box.dm
index e133f82e6e2..2b10a9017d0 100644
--- a/code/modules/power/breaker_box.dm
+++ b/code/modules/power/breaker_box.dm
@@ -20,7 +20,7 @@
var/update_locked = 0
/obj/machinery/power/breakerbox/Destroy()
- ..()
+ . = ..()
for(var/datum/nano_module/rcon/R in world)
R.FindDevices()
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index 2d53277590e..ebfa5330864 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -108,7 +108,7 @@ var/list/possible_cable_coil_colours = list(
if(powernet)
cut_cable_from_powernet() // update the powernets
cable_list -= src //remove it from global cable list
- ..() // then go ahead and delete the cable
+ return ..() // then go ahead and delete the cable
// Ghost examining the cable -> tells him the power
/obj/structure/cable/attack_ghost(mob/user)
diff --git a/code/modules/power/grid_checker.dm b/code/modules/power/grid_checker.dm
index 4d42d4ca3f2..b8a123e02fa 100644
--- a/code/modules/power/grid_checker.dm
+++ b/code/modules/power/grid_checker.dm
@@ -28,7 +28,7 @@
/obj/machinery/power/grid_checker/Destroy()
qdel(wires)
wires = null
- ..()
+ return ..()
/obj/machinery/power/grid_checker/update_icon()
if(power_failing)
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 1f4f8ec7563..0609e18041d 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -248,7 +248,7 @@
if(A)
on = 0
// A.update_lights()
- ..()
+ return ..()
/obj/machinery/light/update_icon()
diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm
index 13213c2e7e3..76320db4796 100644
--- a/code/modules/power/power.dm
+++ b/code/modules/power/power.dm
@@ -19,7 +19,7 @@
disconnect_from_network()
disconnect_terminal()
- ..()
+ return ..()
///////////////////////////////
// General procedures
diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm
index 47a9c08923e..53e3dc29c98 100644
--- a/code/modules/power/singularity/collector.dm
+++ b/code/modules/power/singularity/collector.dm
@@ -23,7 +23,7 @@ var/global/list/rad_collectors = list()
/obj/machinery/power/rad_collector/Destroy()
rad_collectors -= src
- ..()
+ return ..()
/obj/machinery/power/rad_collector/process()
//so that we don't zero out the meter if the SM is processed first.
diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm
index 49c1099184c..d6347113790 100644
--- a/code/modules/reagents/Chemistry-Holder.dm
+++ b/code/modules/reagents/Chemistry-Holder.dm
@@ -23,7 +23,7 @@
chemical_reagents_list[D.id] = D
/datum/reagents/Destroy()
- ..()
+ . = ..()
if(chemistryProcess)
chemistryProcess.active_holders -= src
@@ -79,12 +79,6 @@
total_volume += R.volume
return
-/datum/reagents/proc/delete()
- for(var/datum/reagent/R in reagent_list)
- R.holder = null
- if(my_atom)
- my_atom.reagents = null
-
/datum/reagents/proc/handle_reactions()
if(chemistryProcess)
chemistryProcess.mark_for_update(src)
diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm
index 78b33ef36ee..ccf7c62ac1e 100644
--- a/code/modules/reagents/Chemistry-Machinery.dm
+++ b/code/modules/reagents/Chemistry-Machinery.dm
@@ -488,7 +488,7 @@
var/amount_to_take = max(0,min(stack.amount,round(remaining_volume/REAGENTS_PER_SHEET)))
if(amount_to_take)
stack.use(amount_to_take)
- if(deleted(stack))
+ if(QDELETED(stack))
holdingitems -= stack
beaker.reagents.add_reagent(sheet_reagents[stack.type], (amount_to_take*REAGENTS_PER_SHEET))
continue
diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm
index 723db2ace77..f23e37f5167 100644
--- a/code/modules/reagents/Chemistry-Reagents.dm
+++ b/code/modules/reagents/Chemistry-Reagents.dm
@@ -118,8 +118,8 @@
return null
/datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references
- ..()
holder = null
+ . = ..()
/* DEPRECATED - TODO: REMOVE EVERYWHERE */
diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
index 12540c2d049..b6c45475cc3 100644
--- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
+++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Toxins.dm
@@ -205,7 +205,7 @@
if(holder && holder.my_atom && ismob(holder.my_atom))
var/mob/M = holder.my_atom
M.status_flags &= ~FAKEDEATH
- ..()
+ return ..()
/datum/reagent/toxin/fertilizer //Reagents used for plant fertilizers.
name = "fertilizer"
diff --git a/code/modules/reagents/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm
index 3191442be80..ef07b03c4f5 100644
--- a/code/modules/reagents/reagent_containers/food/snacks.dm
+++ b/code/modules/reagents/reagent_containers/food/snacks.dm
@@ -210,8 +210,8 @@
/obj/item/weapon/reagent_containers/food/snacks/Destroy()
if(contents)
for(var/atom/movable/something in contents)
- something.loc = get_turf(src)
- ..()
+ something.dropInto(loc)
+ . = ..()
////////////////////////////////////////////////////////////////////////////////
/// FOOD END
diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm
index 48721314294..d7ce2063b25 100644
--- a/code/modules/shieldgen/emergency_shield.dm
+++ b/code/modules/shieldgen/emergency_shield.dm
@@ -55,7 +55,7 @@
check_failure()
set_opacity(1)
- spawn(20) if(!deleted(src)) set_opacity(0)
+ spawn(20) if(!QDELETED(src)) set_opacity(0)
..()
@@ -64,7 +64,7 @@
..()
check_failure()
set_opacity(1)
- spawn(20) if(!deleted(src)) set_opacity(0)
+ spawn(20) if(!QDELETED(src)) set_opacity(0)
/obj/machinery/shield/ex_act(severity)
switch(severity)
@@ -114,7 +114,7 @@
//The shield becomes dense to absorb the blow.. purely asthetic.
set_opacity(1)
- spawn(20) if(!deleted(src)) set_opacity(0)
+ spawn(20) if(!QDELETED(src)) set_opacity(0)
..()
return
diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm
index db445e9f509..cea2c935594 100644
--- a/code/modules/tables/tables.dm
+++ b/code/modules/tables/tables.dm
@@ -79,7 +79,7 @@
update_connections(1) // Update tables around us to ignore us (material=null forces no connections)
for(var/obj/structure/table/T in oview(src, 1))
T.update_icon()
- ..()
+ . = ..()
/obj/structure/table/examine(mob/user)
. = ..()
diff --git a/code/modules/tables/update_triggers.dm b/code/modules/tables/update_triggers.dm
index a90e3eeb8a2..b7c087f9c0f 100644
--- a/code/modules/tables/update_triggers.dm
+++ b/code/modules/tables/update_triggers.dm
@@ -6,12 +6,10 @@
/obj/structure/window/Destroy()
var/oldloc = loc
- loc=null
+ . = ..()
for(var/obj/structure/table/T in view(oldloc, 1))
T.update_connections()
T.update_icon()
- loc=oldloc
- ..()
/obj/structure/window/Move()
var/oldloc = loc
diff --git a/code/world.dm b/code/world.dm
index 8d51f84b47b..a11d875c51f 100644
--- a/code/world.dm
+++ b/code/world.dm
@@ -26,7 +26,8 @@ var/global/datum/global_init/init = new ()
qdel(src) //we're done
/datum/global_init/Destroy()
- return 1
+ global.init = null
+ return 2 // QDEL_HINT_IWILLGC
/world
mob = /mob/new_player
diff --git a/maps/RandomZLevels/stationCollision.dm b/maps/RandomZLevels/stationCollision.dm
index fa20f440ec8..0ebf12f169a 100644
--- a/maps/RandomZLevels/stationCollision.dm
+++ b/maps/RandomZLevels/stationCollision.dm
@@ -54,7 +54,7 @@
B.item_state = "melted"
new /obj/item/weapon/paper/sc_safehint_paper_bible(B)
new /obj/item/weapon/pen(B)
- del(src)
+ qdel(src)
/*
* Guns - I'm making these specifically so that I dont spawn a pile of fully loaded weapons on the map.
@@ -189,7 +189,7 @@ var/sc_safecode5 = "[rand(0,9)]"
else if(istype(A,/obj/))
var/obj/O = A
O.ex_act(1.0)
- if(O) del(O)
+ if(O) qdel(O)
else if(isturf(A))
var/turf/T = A
if(T.intact)
diff --git a/maps/RandomZLevels/wildwest.dm b/maps/RandomZLevels/wildwest.dm
index b3ae83b9d7b..51ac498fb24 100644
--- a/maps/RandomZLevels/wildwest.dm
+++ b/maps/RandomZLevels/wildwest.dm
@@ -133,7 +133,7 @@
s.start()
explosion(mob, 1, 0, 0, 0)
spawn(0)
- del(src)
+ qdel(src)
/obj/effect/meatgrinder
name = "Meat Grinder"
diff --git a/maps/~map_system/maps.dm b/maps/~map_system/maps.dm
index 1a575b3578b..65797ebafaa 100644
--- a/maps/~map_system/maps.dm
+++ b/maps/~map_system/maps.dm
@@ -161,5 +161,10 @@ var/list/all_maps = list()
if(transit_chance)
map.accessible_z_levels["[z]"] = transit_chance
-/datum/map_z_level/Destroy()
- return TRUE // No.
+/datum/map_z_level/Destroy(var/force)
+ crash_with("Attempt to delete a map_z_level instance [log_info_line(src)]")
+ if(!force)
+ return QDEL_HINT_LETMELIVE // No.
+ if (using_map.zlevels["[z]"] == src)
+ using_map.zlevels -= "[z]"
+ return ..()
diff --git a/polaris.dme b/polaris.dme
index fc2c4fef681..3b49b9566ba 100644
--- a/polaris.dme
+++ b/polaris.dme
@@ -37,6 +37,7 @@
#include "code\__defines\mobs.dm"
#include "code\__defines\planets.dm"
#include "code\__defines\process_scheduler.dm"
+#include "code\__defines\qdel.dm"
#include "code\__defines\research.dm"
#include "code\__defines\species_languages.dm"
#include "code\__defines\targeting.dm"
@@ -152,7 +153,6 @@
#include "code\controllers\Processes\emergencyShuttle.dm"
#include "code\controllers\Processes\event.dm"
#include "code\controllers\Processes\game_master.dm"
-#include "code\controllers\Processes\garbage.dm"
#include "code\controllers\Processes\inactivity.dm"
#include "code\controllers\Processes\lighting.dm"
#include "code\controllers\Processes\machinery.dm"
@@ -170,6 +170,7 @@
#include "code\controllers\Processes\vote.dm"
#include "code\controllers\ProcessScheduler\core\process.dm"
#include "code\controllers\ProcessScheduler\core\processScheduler.dm"
+#include "code\controllers\subsystems\garbage.dm"
#include "code\datums\ai_law_sets.dm"
#include "code\datums\ai_laws.dm"
#include "code\datums\browser.dm"