mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 02:34:00 +00:00
Port of @PsiOmegaDelta's Baystation12/Baystation12#16820
Ports tg's garbage collector subsystem and Destroy() returning qdel hints.
This commit is contained in:
@@ -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)
|
||||
|
||||
17
code/__defines/qdel.dm
Normal file
17
code/__defines/qdel.dm
Normal file
@@ -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)
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -49,7 +49,7 @@ datum/controller/game_controller/proc/setup_objects()
|
||||
admin_notice("<span class='danger'>Initializing objects</span>", R_DEBUG)
|
||||
sleep(-1)
|
||||
for(var/atom/movable/object in world)
|
||||
if(isnull(object.gcDestroyed))
|
||||
if(!QDELETED(object))
|
||||
object.initialize()
|
||||
|
||||
admin_notice("<span class='danger'>Initializing areas</span>", R_DEBUG)
|
||||
@@ -80,8 +80,8 @@ datum/controller/game_controller/proc/setup_objects()
|
||||
|
||||
admin_notice("<span class='danger'>Initializing turbolifts</span>", 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)
|
||||
|
||||
|
||||
354
code/controllers/subsystems/garbage.dm
Normal file
354
code/controllers/subsystems/garbage.dm
Normal file
@@ -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 = "<B>List of things that have been qdel()eted this round</B><BR><BR>"
|
||||
|
||||
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<BR>"
|
||||
|
||||
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
|
||||
@@ -155,4 +155,5 @@ Data storage vars:
|
||||
tag = null
|
||||
arg_list.Cut()
|
||||
stop()
|
||||
return QDEL_HINT_LETMELIVE
|
||||
//Do not call ..()
|
||||
|
||||
@@ -36,4 +36,4 @@
|
||||
|
||||
/decls/Destroy()
|
||||
crash_with("Prevented attempt to delete a decl instance: [log_info_line(src)]")
|
||||
return 1 // Prevents Decl destruction
|
||||
return QDEL_HINT_LETMELIVE // Prevents Decl destruction
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 << "<span class='sinister' style='font-size:22pt'>You are blinded by a brilliant flash of energy.</span>"
|
||||
|
||||
world << sound('sound/effects/cascade.ogg')
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 /////////
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
/obj/item/weapon/material/Destroy()
|
||||
processing_objects -= src
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/item/weapon/material/apply_hit_effect()
|
||||
..()
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/obj/structure/Destroy()
|
||||
if(parts)
|
||||
new parts(loc)
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
/obj/structure/attack_hand(mob/user)
|
||||
if(breakable)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
/turf/Destroy()
|
||||
turfs -= src
|
||||
..()
|
||||
return QDEL_HINT_IWILLGC
|
||||
|
||||
/turf/ex_act(severity)
|
||||
return 0
|
||||
|
||||
@@ -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 "<b>[src]</b>"
|
||||
/datum/proc/get_view_variables_header()
|
||||
return "<b>[src]</b>"
|
||||
|
||||
@@ -31,9 +27,6 @@
|
||||
</font>
|
||||
"}
|
||||
|
||||
// Same for these as for get_view_variables_header() above
|
||||
/client/proc/get_view_variables_options()
|
||||
return ""
|
||||
/datum/proc/get_view_variables_options()
|
||||
return ""
|
||||
|
||||
|
||||
@@ -187,4 +187,4 @@
|
||||
if(radio_controller)
|
||||
radio_controller.remove_object(src,frequency)
|
||||
frequency = 0
|
||||
..()
|
||||
. = ..()
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
/client
|
||||
//////////////////////
|
||||
//BLACK MAGIC THINGS//
|
||||
//////////////////////
|
||||
parent_type = /datum
|
||||
////////////////
|
||||
//ADMIN THINGS//
|
||||
////////////////
|
||||
|
||||
@@ -181,6 +181,9 @@
|
||||
clients -= src
|
||||
return ..()
|
||||
|
||||
/client/Destroy()
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
// here because it's similar to below
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -131,4 +131,4 @@
|
||||
T.lighting_overlay = null
|
||||
T.luminosity = 1
|
||||
|
||||
return ..()
|
||||
. = ..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
spellremove(src)
|
||||
ghostize()
|
||||
..()
|
||||
return QDEL_HINT_HARDDEL_NOW
|
||||
|
||||
/mob/proc/remove_screen_obj_references()
|
||||
hands = null
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
chemical_reagents_list[D.id] = D
|
||||
|
||||
/datum/reagents/Destroy()
|
||||
..()
|
||||
. = ..()
|
||||
if(chemistryProcess)
|
||||
chemistryProcess.active_holders -= src
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
. = ..()
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user