diff --git a/code/_helpers/logging.dm b/code/_helpers/logging.dm index d264c05f4e..61857daf5f 100644 --- a/code/_helpers/logging.dm +++ b/code/_helpers/logging.dm @@ -253,29 +253,50 @@ /proc/key_name_admin(var/whom, var/include_name = 1) return key_name(whom, 1, include_name) -// Helper procs for building detailed log lines -/datum/proc/log_info_line() - return "[src] ([type])" - -/atom/log_info_line() - var/turf/t = get_turf(src) - if(istype(t)) - return "([t]) ([t.x],[t.y],[t.z]) ([t.type])" - else if(loc) - return "([loc]) (0,0,0) ([loc.type])" - else - return "(NULL) (0,0,0) (NULL)" - -/mob/log_info_line() - return "[..()] ([ckey])" - -/proc/log_info_line(var/datum/d) - if(!istype(d)) - return - return d.log_info_line() /mob/proc/simple_info_line() return "[key_name(src)] ([x],[y],[z])" + /client/proc/simple_info_line() return "[key_name(src)] ([mob.x],[mob.y],[mob.z])" + + +/proc/log_info_line(datum/thing) + if (isnull(thing)) + return "*null*" + if (islist(thing)) + var/list/result = list() + var/list/thing_list = thing + for (var/key in thing_list) + var/value = isnum(key) ? null : thing[key] + result += "[log_info_line(key)][value ? " - [log_info_line(value)]" : ""]" + return "\[[jointext(result, ", ")]\]" + if (!istype(thing)) + return json_encode(thing) + return thing.get_log_info_line() + + +/datum/proc/get_log_info_line() + return "[src] ([type])" + + +/weakref/get_log_info_line() + return "[ref_name] ([ref_type]) ([ref]) (WEAKREF)" + + +/area/get_log_info_line() + return "[..()] ([isnum(z) ? "[x],[y],[z]" : "0,0,0"])" + + +/turf/get_log_info_line() + return "[..()] ([x],[y],[z]) ([loc ? loc.type : "NULL"])" + + +/atom/movable/get_log_info_line() + var/turf/turf = get_turf(src) + return "[..()] ([turf ? turf : "NULL"]) ([turf ? "[turf.x],[turf.y],[turf.z]" : "0,0,0"]) ([turf ? turf.type : "NULL"])" + + +/mob/get_log_info_line() + return ckey ? "[..()] ([ckey])" : ..() diff --git a/code/controllers/subsystems/garbage.dm b/code/controllers/subsystems/garbage.dm index 273b1c99a3..86cd9caef1 100644 --- a/code/controllers/subsystems/garbage.dm +++ b/code/controllers/subsystems/garbage.dm @@ -273,71 +273,73 @@ SUBSYSTEM_DEF(garbage) // Should be treated as a replacement for the 'del' keyword. // Datums passed to this will be given a chance to clean up references to allow the GC to collect them. -/proc/qdel(datum/D, force=FALSE) - if(!istype(D)) - del(D) +/proc/qdel(datum/thing, force) + if (!thing) return - var/datum/qdel_item/I = SSgarbage.items[D.type] - if (!I) - I = SSgarbage.items[D.type] = new /datum/qdel_item(D.type) - I.qdels++ - - - if(isnull(D.gc_destroyed)) - if(SEND_SIGNAL(D, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted + if (!istype(thing)) + crash_with("qdel() can only handle /datum (sub)types, was passed: [log_info_line(thing)]") + del(thing) + return + var/datum/qdel_item/qdel_item = SSgarbage.items[thing.type] + if (!qdel_item) + qdel_item = new (thing.type) + SSgarbage.items[thing.type] = qdel_item + qdel_item.qdels++ + if (isnull(thing.gc_destroyed)) + if (SEND_SIGNAL(thing, COMSIG_PARENT_PREQDELETED, force)) // Give the components a chance to prevent their parent from being deleted return - D.gc_destroyed = GC_CURRENTLY_BEING_QDELETED + thing.gc_destroyed = GC_CURRENTLY_BEING_QDELETED var/start_time = world.time var/start_tick = world.tick_usage - SEND_SIGNAL(D, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy - var/hint = D.Destroy(force) // Let our friend know they're about to get fucked up. - if(world.time != start_time) - I.slept_destroy++ + SEND_SIGNAL(thing, COMSIG_PARENT_QDELETING, force) // Let the (remaining) components know about the result of Destroy + var/hint = thing.Destroy(force) // Let our friend know they're about to get fucked up. + if (world.time != start_time) + qdel_item.slept_destroy++ else - I.destroy_time += TICK_USAGE_TO_MS(start_tick) - if(!D) + qdel_item.destroy_time += TICK_USAGE_TO_MS(start_tick) + if (!thing) return - switch(hint) + switch (hint) if (QDEL_HINT_QUEUE) //qdel should queue the object for deletion. - SSgarbage.PreQueue(D) + SSgarbage.PreQueue(thing) if (QDEL_HINT_IWILLGC) - D.gc_destroyed = world.time + thing.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!) + thing.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 - #ifdef TESTING - if(!I.no_respect_force) - crash_with("[D.type] has been force deleted, but is \ +#ifdef TESTING + if(!qdel_item.no_respect_force) + crash_with("[thing.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.") - #endif - I.no_respect_force++ - - SSgarbage.PreQueue(D) +#endif + qdel_item.no_respect_force++ + SSgarbage.PreQueue(thing) 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) + SSgarbage.HardQueue(thing) if (QDEL_HINT_HARDDEL_NOW) //qdel should assume this object won't gc, and hard del it post haste. - SSgarbage.HardDelete(D) + SSgarbage.HardDelete(thing) if (QDEL_HINT_FINDREFERENCE)//qdel will, if TESTING is enabled, display all references to this object, then queue the object for deletion. - SSgarbage.PreQueue(D) + SSgarbage.PreQueue(thing) #ifdef TESTING - D.find_references() + thing.find_references() #endif else #ifdef TESTING - if(!I.no_hint) - 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.") + if (!qdel_item.no_hint) + crash_with("[thing.type] is not returning a qdel hint. It is being placed in the queue. Further instances of this type will also be queued.") #endif - I.no_hint++ - SSgarbage.PreQueue(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") + qdel_item.no_hint++ + SSgarbage.PreQueue(thing) + else if (thing.gc_destroyed == GC_CURRENTLY_BEING_QDELETED) + CRASH("[thing.type] destroy proc was called multiple times, likely due to a qdel loop in the Destroy logic") + #ifdef TESTING diff --git a/code/datums/weakref.dm b/code/datums/weakref.dm index 6c17c18bca..3c7903c21d 100644 --- a/code/datums/weakref.dm +++ b/code/datums/weakref.dm @@ -1,26 +1,30 @@ -//obtain a weak reference to a datum -/proc/weakref(datum/D) - if(!istype(D)) - return - if(QDELETED(D)) - return - if(!D.weakref) - D.weakref = new/weakref(D) - return D.weakref - /weakref var/ref + var/ref_name + var/ref_type -/weakref/New(datum/D) - ref = "\ref[D]" /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 QDEL_HINT_LETMELIVE + SHOULD_CALL_PARENT(FALSE) + return QDEL_HINT_IWILLGC + + +/weakref/New(datum/thing) + ref = "\ref[thing]" + ref_name = "[thing]" + ref_type = thing.type + /weakref/proc/resolve() - var/datum/D = locate(ref) - if(D && D.weakref == src) - return D - return null \ No newline at end of file + var/datum/thing = locate(ref) + if (thing && thing.weakref == src) + return thing + return null + + +/proc/weakref(datum/thing) + if (!istype(thing) || QDELING(thing)) + return + if (!thing.weakref) + thing.weakref = new /weakref (thing) + return thing.weakref diff --git a/code/unit_tests/subsystem_tests.dm b/code/unit_tests/subsystem_tests.dm index 2e1351f968..412ae23c5d 100644 --- a/code/unit_tests/subsystem_tests.dm +++ b/code/unit_tests/subsystem_tests.dm @@ -35,7 +35,7 @@ var/fail = FALSE for(var/atom/atom in world) if(!atom.initialized && !QDELETED(atom)) // Not ideal to skip over qdeleted atoms, but a lot of current code uses pre-init qdels - log_bad("Uninitialized atom: [atom.type] - [atom.log_info_line()]") + log_bad("Uninitialized atom: [atom.type] - [atom.get_log_info_line()]") fail = TRUE if(fail) fail("There were uninitialized atoms.")