From ebe5cc916de1a38bbcb1259fd0699f771211ad99 Mon Sep 17 00:00:00 2001 From: PsiOmegaDelta Date: Sun, 2 Apr 2017 17:41:21 +0200 Subject: [PATCH] Port of @PsiOmegaDelta's Baystation12/Baystation12#16820 Ports tg's garbage collector subsystem and Destroy() returning qdel hints. --- code/ATMOSPHERICS/pipes.dm | 28 +- code/__defines/qdel.dm | 17 + code/_helpers/mobs.dm | 2 +- .../ProcessScheduler/core/process.dm | 2 +- code/controllers/Processes/garbage.dm | 254 ------------- code/controllers/Processes/machinery.dm | 6 +- code/controllers/Processes/mob.dm | 2 +- code/controllers/Processes/nanoui.dm | 2 +- code/controllers/Processes/obj.dm | 2 +- code/controllers/Processes/scheduler.dm | 4 +- code/controllers/failsafe.dm | 4 +- code/controllers/master.dm | 2 +- code/controllers/master_controller.dm | 6 +- code/controllers/subsystems/garbage.dm | 354 ++++++++++++++++++ code/datums/helper_datums/global_iterator.dm | 1 + code/datums/repositories/decls.dm | 2 +- code/datums/weakref.dm | 6 +- code/game/atoms_movable.dm | 12 +- .../endgame/supermatter_cascade/universe.dm | 1 - code/game/machinery/CableLayer.dm | 2 +- .../atmoalter/portable_atmospherics.dm | 11 +- code/game/machinery/doors/door.dm | 3 +- code/game/machinery/floor_light.dm | 2 +- code/game/machinery/pipe/construction.dm | 18 +- code/game/mecha/mecha.dm | 16 +- .../objects/items/devices/transfer_valve.dm | 2 +- .../objects/items/weapons/flamethrower.dm | 13 +- .../weapons/material/material_weapons.dm | 2 +- code/game/objects/structures.dm | 2 +- code/game/objects/structures/lattice.dm | 2 +- code/game/turfs/turf.dm | 1 + code/modules/admin/view_variables/helpers.dm | 7 - code/modules/assembly/signaler.dm | 2 +- code/modules/client/client defines.dm | 4 + code/modules/client/client procs.dm | 3 + .../hydroponics/spreading/spreading_growth.dm | 2 +- code/modules/lighting/lighting_overlay.dm | 2 +- .../mob/living/carbon/human/update_icons.dm | 2 +- .../living/silicon/robot/drone/drone_items.dm | 2 +- code/modules/mob/mob.dm | 1 + code/modules/mob/mob_grab.dm | 12 +- code/modules/organs/internal/brain.dm | 6 +- code/modules/reagents/Chemistry-Holder.dm | 2 +- code/modules/reagents/Chemistry-Machinery.dm | 2 +- code/modules/reagents/Chemistry-Reagents.dm | 2 +- .../reagent_containers/food/snacks.dm | 4 +- code/modules/shieldgen/emergency_shield.dm | 6 +- code/modules/tables/tables.dm | 2 +- polaris.dme | 3 +- 49 files changed, 479 insertions(+), 366 deletions(-) create mode 100644 code/__defines/qdel.dm delete mode 100644 code/controllers/Processes/garbage.dm create mode 100644 code/controllers/subsystems/garbage.dm diff --git a/code/ATMOSPHERICS/pipes.dm b/code/ATMOSPHERICS/pipes.dm index 105aaa8c8b..c23fc16555 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/qdel.dm b/code/__defines/qdel.dm new file mode 100644 index 0000000000..168a6adf0c --- /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 d7fd20ac77..8ac42dc0b9 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/controllers/ProcessScheduler/core/process.dm b/code/controllers/ProcessScheduler/core/process.dm index edcb8d703c..9f08241978 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 02f0411332..0000000000 --- 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 405615dc1f..1fd1f8ce45 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 8681803ac4..6b623e7730 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 49b9048c07..21d2f8e5f0 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 1010c8cc97..6032cbb541 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/scheduler.dm b/code/controllers/Processes/scheduler.dm index 276249bd55..fb9f85d7c4 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/failsafe.dm b/code/controllers/failsafe.dm index 98c6a5d89d..0589d5fa7f 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 981cf6d2bb..360b739bad 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 3075682438..c2d04d9c93 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/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm new file mode 100644 index 0000000000..e849b64be6 --- /dev/null +++ b/code/controllers/subsystems/garbage.dm @@ -0,0 +1,354 @@ +var/datum/controller/subsystem/garbage_collector/SSgarbage + +/datum/controller/subsystem/garbage_collector + 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_collector/New() + NEW_SS_GLOBAL(SSgarbage) + +/datum/controller/subsystem/garbage_collector/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_collector/fire() + HandleToBeQueued() + if(state == SS_RUNNING) + HandleQueue() + +//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_collector/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_collector/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]"]++ + var/time = world.timeofday + var/tick = world.tick_usage + var/ticktime = world.time + 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 then 1 second to delete (took [time/10] seconds to delete)") + message_admins("Error: [type]([refID]) took longer then 1 second to delete (took [time/10] seconds to delete).") + postpone(time/5) + break + ++delslasttick + ++totaldels + else + ++gcedlasttick + ++totalgcs + +/datum/controller/subsystem/garbage_collector/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_collector/proc/Queue(datum/A) + if (!istype(A) || (!isnull(A.gc_destroyed) && A.gc_destroyed >= 0)) + return + if (A.gc_destroyed == GC_QUEUED_FOR_HARD_DEL) + del(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 + +/datum/controller/subsystem/garbage_collector/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_collector/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(!D) + return +#ifdef TESTING + SSgarbage.qdel_list += D.type +#endif + if(!istype(D)) + log_debug("qdel() was passed [log_info_line(D)]. qdel() can only handle instances of (sub)type /datum.") + del(D) + else 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. + del(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/datums/helper_datums/global_iterator.dm b/code/datums/helper_datums/global_iterator.dm index 3bf80f06d0..d511b5d233 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 e87be74f53..61b24c21e9 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/weakref.dm b/code/datums/weakref.dm index 1a7d844ca8..348d73b0b2 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/game/atoms_movable.dm b/code/game/atoms_movable.dm index fe9f99b032..64ae0722f8 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 6f549d15f4..90ac43379f 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 2b4eae9a0d..2e10ed3b1b 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 b7bcc53e91..deee8ed76b 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/door.dm b/code/game/machinery/doors/door.dm index cf7f96d52d..240610358f 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 a134e68e3f..fdaf284f3f 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/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index 4abdb685d0..06228dd86f 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/mecha/mecha.dm b/code/game/mecha/mecha.dm index 09ff0075db..5d7d16a126 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/items/devices/transfer_valve.dm b/code/game/objects/items/devices/transfer_valve.dm index 88b53bd342..35697914ee 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 84bee72558..649b5677ca 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_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm index 31231964cb..c778ec07a9 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/structures.dm b/code/game/objects/structures.dm index 928246b2b5..eab315809f 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) diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm index ec4652901d..35b8571a11 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/turfs/turf.dm b/code/game/turfs/turf.dm index 3f2e0064f2..d34b210b99 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 67eefc018d..95945f82e0 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 169ab9b6a0..1273b004a4 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 b559652132..5e5a176128 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 d908df1b3d..d6f9375e98 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/hydroponics/spreading/spreading_growth.dm b/code/modules/hydroponics/spreading/spreading_growth.dm index fca9f3abaf..0dbe4c9eea 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_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 60c684fd86..6d912cd862 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -131,4 +131,4 @@ T.lighting_overlay = null T.luminosity = 1 - return ..() + . = ..() diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 8a2513a54a..9c372cf2e7 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/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index c7fb1d6728..7d5c270f9b 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/mob.dm b/code/modules/mob/mob.dm index 7574bb4c77..454b58a55c 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 80f68b979d..be114228de 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() diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 1390c89b29..e0809e32b5 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/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 49c1099184..60efe9893d 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 diff --git a/code/modules/reagents/Chemistry-Machinery.dm b/code/modules/reagents/Chemistry-Machinery.dm index 78b33ef36e..ccf7c62ac1 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 723db2ace7..f23e37f516 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/reagent_containers/food/snacks.dm b/code/modules/reagents/reagent_containers/food/snacks.dm index 3191442be8..ef07b03c4f 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 4872131429..d7ce2063b2 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 db445e9f50..cea2c93559 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/polaris.dme b/polaris.dme index 942ada13d4..9cb2df356d 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"