From af85f098cd515a70938be97e6cc8f51abd6d4c1f Mon Sep 17 00:00:00 2001 From: Kashargul <144968721+Kashargul@users.noreply.github.com> Date: Wed, 5 Feb 2025 19:53:53 +0100 Subject: [PATCH] bunch of misc fixes (#17031) * bunch of misc fixes * . * more refs also new ref tracking from chomp * oups * eh --- code/ATMOSPHERICS/pipes/pipe_base.dm | 8 +- code/__defines/_compile_options.dm | 5 + code/_helpers/game.dm | 2 - code/controllers/subsystems/machines.dm | 4 +- code/controllers/subsystems/plants.dm | 3 +- code/datums/reference_tracking_new.dm | 212 ++++++++++++++++++ code/game/atoms_movable.dm | 11 + .../stool_bed_chair_nest/wheelchair.dm | 2 +- code/modules/ai/ai_holder_combat.dm | 2 +- .../hydroponics/spreading/spreading.dm | 1 - code/modules/mob/living/carbon/alien/life.dm | 1 - code/modules/mob/living/silicon/robot/life.dm | 1 - code/modules/mob/mob_planes_vr.dm | 4 + code/modules/vore/resizing/crackers.dm | 2 +- 14 files changed, 243 insertions(+), 15 deletions(-) create mode 100644 code/datums/reference_tracking_new.dm diff --git a/code/ATMOSPHERICS/pipes/pipe_base.dm b/code/ATMOSPHERICS/pipes/pipe_base.dm index 98577539099..e384751cc21 100644 --- a/code/ATMOSPHERICS/pipes/pipe_base.dm +++ b/code/ATMOSPHERICS/pipes/pipe_base.dm @@ -71,28 +71,28 @@ return 1 /obj/machinery/atmospherics/pipe/return_air() - if(!parent) + if(!QDELETED(src) || !parent) parent = new /datum/pipeline() parent.build_pipeline(src) return parent.air /obj/machinery/atmospherics/pipe/build_network() - if(!parent) + if(!QDELETED(src) || !parent) parent = new /datum/pipeline() parent.build_pipeline(src) return parent.return_network() /obj/machinery/atmospherics/pipe/network_expand(datum/pipe_network/new_network, obj/machinery/atmospherics/pipe/reference) - if(!parent) + if(!QDELETED(src) || !parent) parent = new /datum/pipeline() parent.build_pipeline(src) return parent.network_expand(new_network, reference) /obj/machinery/atmospherics/pipe/return_network(obj/machinery/atmospherics/reference) - if(!parent) + if(!QDELETED(src) || !parent) parent = new /datum/pipeline() parent.build_pipeline(src) diff --git a/code/__defines/_compile_options.dm b/code/__defines/_compile_options.dm index ffee05a3e58..5cf3db3ed3a 100644 --- a/code/__defines/_compile_options.dm +++ b/code/__defines/_compile_options.dm @@ -39,6 +39,11 @@ ///Used for doing dry runs of the reference finder, to test for feature completeness //#define REFERENCE_TRACKING_DEBUG +#ifdef FIND_REF_NO_CHECK_TICK +/world + loop_checks = FALSE +#endif + ///Run a lookup on things hard deleting by default. //#define GC_FAILURE_HARD_LOOKUP #ifdef GC_FAILURE_HARD_LOOKUP diff --git a/code/_helpers/game.dm b/code/_helpers/game.dm index 80d66f555da..e484e8d9b3a 100644 --- a/code/_helpers/game.dm +++ b/code/_helpers/game.dm @@ -221,8 +221,6 @@ /proc/get_mobs_in_radio_ranges(var/list/obj/item/radio/radios) - set background = 1 - . = list() // Returns a list of mobs who can hear any of the radios given in @radios var/list/speaker_coverage = list() diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm index 70f913be523..5fc3e67f948 100644 --- a/code/controllers/subsystems/machines.dm +++ b/code/controllers/subsystems/machines.dm @@ -105,7 +105,7 @@ SUBSYSTEM_DEF(machines) while(current_run.len) var/datum/pipe_network/PN = current_run[current_run.len] current_run.len-- - if(!PN) + if(!PN || QDELETED(PN)) networks.Remove(PN) DISABLE_BITFIELD(PN?.datum_flags, DF_ISPROCESSING) else @@ -122,7 +122,7 @@ SUBSYSTEM_DEF(machines) while(current_run.len) var/obj/machinery/M = current_run[current_run.len] current_run.len-- - if(!M || (M.process(wait) == PROCESS_KILL)) + if(!istype(M) || QDELETED(M) || (M.process(wait) == PROCESS_KILL)) processing_machines.Remove(M) DISABLE_BITFIELD(M?.datum_flags, DF_ISPROCESSING) if(MC_TICK_CHECK) diff --git a/code/controllers/subsystems/plants.dm b/code/controllers/subsystems/plants.dm index 0743f1e262e..3f278da4221 100644 --- a/code/controllers/subsystems/plants.dm +++ b/code/controllers/subsystems/plants.dm @@ -139,7 +139,8 @@ SUBSYSTEM_DEF(plants) return /datum/controller/subsystem/plants/proc/add_plant(var/obj/effect/plant/plant) - processing |= plant + if(!QDELETED(plant)) + processing |= plant /datum/controller/subsystem/plants/proc/remove_plant(var/obj/effect/plant/plant) processing -= plant diff --git a/code/datums/reference_tracking_new.dm b/code/datums/reference_tracking_new.dm new file mode 100644 index 00000000000..56200a7d4e8 --- /dev/null +++ b/code/datums/reference_tracking_new.dm @@ -0,0 +1,212 @@ +// Drop in replacement for reference tracking +#ifdef REFERENCE_TRACKING +#define REFSEARCH_RECURSE_LIMIT 64 + +/datum + var/references_to_clear + +/datum/proc/find_references(references_to_clear = INFINITY) + if(usr?.client) + if(tgui_alert(usr,"Running this will lock everything up for about 5 minutes. Would you like to begin the search?", "Find References", list("Yes", "No")) != "Yes") + return + + src.references_to_clear = references_to_clear + //this keeps the garbage collector from failing to collect objects being searched for in here + SSgarbage.can_fire = FALSE + + _search_references() + //restart the garbage collector + SSgarbage.can_fire = TRUE + SSgarbage.next_fire = world.time + world.tick_lag + //SSgarbage.update_nextfire(reset_time = TRUE) + +/datum/proc/_search_references() + log_reftracker("Beginning search for references to a [type], looking for [references_to_clear] refs.") + + var/starting_time = world.time + //Time to search the whole game for our ref + DoSearchVar(GLOB, "GLOB", starting_time) //globals + log_reftracker("Finished searching globals") + if(src.references_to_clear == 0) + return + + //Yes we do actually need to do this. The searcher refuses to read weird lists + //And global.vars is a really weird list + var/global_vars = list() + for(var/key in global.vars) + global_vars[key] = global.vars[key] + + DoSearchVar(global_vars, "Native Global", starting_time) + log_reftracker("Finished searching native globals") + if(src.references_to_clear == 0) + return + + for(var/datum/thing in world) //atoms (don't beleive its lies) + DoSearchVar(thing, "World -> [thing.type]", starting_time) + if(src.references_to_clear == 0) + break + log_reftracker("Finished searching atoms") + if(src.references_to_clear == 0) + return + + for(var/datum/thing) //datums + DoSearchVar(thing, "Datums -> [thing.type]", starting_time) + if(src.references_to_clear == 0) + break + log_reftracker("Finished searching datums") + if(src.references_to_clear == 0) + return + + //Warning, attempting to search clients like this will cause crashes if done on live. Watch yourself +#ifndef REFERENCE_DOING_IT_LIVE + for(var/client/thing) //clients + DoSearchVar(thing, "Clients -> [thing.type]", starting_time) + if(src.references_to_clear == 0) + break + log_reftracker("Finished searching clients") + if(src.references_to_clear == 0) + return +#endif + + log_reftracker("Completed search for references to a [type].") + +/datum/proc/DoSearchVar(potential_container, container_name, search_time, recursion_count, is_special_list) + if(recursion_count >= REFSEARCH_RECURSE_LIMIT) + log_reftracker("Recursion limit reached. [container_name]") + return + + if(references_to_clear == 0) + return + + //Check each time you go down a layer. This makes it a bit slow, but it won't effect the rest of the game at all + #ifndef FIND_REF_NO_CHECK_TICK + CHECK_TICK + #endif + + if(isdatum(potential_container)) + var/datum/datum_container = potential_container + if(datum_container.last_find_references == search_time) + return + + datum_container.last_find_references = search_time + var/list/vars_list = datum_container.vars + + var/is_atom = FALSE + var/is_area = FALSE + if(isatom(datum_container)) + is_atom = TRUE + if(isarea(datum_container)) + is_area = TRUE + for(var/varname in vars_list) + var/variable = vars_list[varname] + if(islist(variable)) + //Fun fact, vis_locs don't count for references + if(varname == "vars" || (is_atom && (varname == "vis_locs" || varname == "overlays" || varname == "underlays" || varname == "filters" || varname == "verbs" || (is_area && varname == "contents")))) + continue + // We do this after the varname check to avoid area contents (reading it incures a world loop's worth of cost) + if(!length(variable)) + continue + DoSearchVar(variable,\ + "[container_name] [datum_container.ref_search_details()] -> [varname] (list)",\ + search_time,\ + recursion_count + 1,\ + /*is_special_list = */ is_atom && (varname == "contents" || varname == "vis_contents" || varname == "locs")) + else if(variable == src) + #ifdef REFERENCE_TRACKING_DEBUG + if(SSgarbage.should_save_refs) + if(!found_refs) + found_refs = list() + found_refs[varname] = TRUE + continue //End early, don't want these logging + else + log_reftracker("Found [type] [text_ref(src)] in [datum_container.type]'s [datum_container.ref_search_details()] [varname] var. [container_name]") + #else + log_reftracker("Found [type] [text_ref(src)] in [datum_container.type]'s [datum_container.ref_search_details()] [varname] var. [container_name]") + #endif + references_to_clear -= 1 + if(references_to_clear == 0) + log_reftracker("All references to [type] [text_ref(src)] found, exiting.") + return + continue + + else if(islist(potential_container)) + var/list/potential_cache = potential_container + for(var/element_in_list in potential_cache) + //Check normal sublists + if(islist(element_in_list)) + if(length(element_in_list)) + DoSearchVar(element_in_list, "[container_name] -> [element_in_list] (list)", search_time, recursion_count + 1) + //Check normal entrys + else if(element_in_list == src) + #ifdef REFERENCE_TRACKING_DEBUG + if(SSgarbage.should_save_refs) + if(!found_refs) + found_refs = list() + found_refs[potential_cache] = TRUE + continue + else + log_reftracker("Found [type] [text_ref(src)] in list [container_name].") + #else + log_reftracker("Found [type] [text_ref(src)] in list [container_name].") + #endif + + // This is dumb as hell I'm sorry + // I don't want the garbage subsystem to count as a ref for the purposes of this number + // If we find all other refs before it I want to early exit, and if we don't I want to keep searching past it + var/ignore_ref = FALSE + var/list/queues = SSgarbage.queues + for(var/list/queue in queues) + if(potential_cache in queue) + ignore_ref = TRUE + break + if(ignore_ref) + log_reftracker("[container_name] does not count as a ref for our count") + else + references_to_clear -= 1 + if(references_to_clear == 0) + log_reftracker("All references to [type] [text_ref(src)] found, exiting.") + return + + if(!isnum(element_in_list) && !is_special_list) + // This exists to catch an error that throws when we access a special list + // is_special_list is a hint, it can be wrong + try + var/assoc_val = potential_cache[element_in_list] + //Check assoc sublists + if(islist(assoc_val)) + if(length(assoc_val)) + DoSearchVar(potential_container[element_in_list], "[container_name]\[[element_in_list]\] -> [assoc_val] (list)", search_time, recursion_count + 1) + //Check assoc entry + else if(assoc_val == src) + #ifdef REFERENCE_TRACKING_DEBUG + if(SSgarbage.should_save_refs) + if(!found_refs) + found_refs = list() + found_refs[potential_cache] = TRUE + continue + else + log_reftracker("Found [type] [text_ref(src)] in list [container_name]\[[element_in_list]\]") + #else + log_reftracker("Found [type] [text_ref(src)] in list [container_name]\[[element_in_list]\]") + #endif + references_to_clear -= 1 + if(references_to_clear == 0) + log_reftracker("All references to [type] [text_ref(src)] found, exiting.") + return + catch + // So if it goes wrong we kill it + is_special_list = TRUE + log_reftracker("Curiosity: [container_name] lead to an error when acessing [element_in_list], what is it?") + +#undef REFSEARCH_RECURSE_LIMIT +#endif + +// Kept outside the ifdef so overrides are easy to implement + +/// Return info about us for reference searching purposes +/// Will be logged as a representation of this datum if it's a part of a search chain +/datum/proc/ref_search_details() + return text_ref(src) + +/datum/callback/ref_search_details() + return "[text_ref(src)] (obj: [object] proc: [delegate] args: [json_encode(arguments)] user: [user?.resolve() || "null"])" diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 2a3d12e4d1d..0702312544e 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -43,6 +43,7 @@ render_target = ref(src) em_block = new(src, render_target) add_overlay(list(em_block), TRUE) + RegisterSignal(em_block, COMSIG_PARENT_QDELETING, PROC_REF(emblocker_gc)) if(opacity) AddElement(/datum/element/light_blocking) if(icon_scale_x != DEFAULT_ICON_SCALE_X || icon_scale_y != DEFAULT_ICON_SCALE_Y || icon_rotation != DEFAULT_ICON_ROTATION) @@ -56,6 +57,10 @@ AddComponent(/datum/component/overlay_lighting, is_directional = TRUE, starts_on = light_on) /atom/movable/Destroy() + if(em_block) + cut_overlay(em_block) + UnregisterSignal(em_block, COMSIG_PARENT_QDELETING) + QDEL_NULL(em_block) . = ..() for(var/atom/movable/AM in contents) qdel(AM) @@ -640,3 +645,9 @@ /atom/movable/proc/get_cell() return + +/atom/movable/proc/emblocker_gc(var/datum/source) + UnregisterSignal(source, COMSIG_PARENT_QDELETING) + cut_overlay(source) + if(em_block == source) + em_block = null diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index 8d56aa70d4e..4b1703de78e 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -14,7 +14,7 @@ var/max_mob_buckle_size = MOB_LARGE /obj/structure/bed/chair/wheelchair/Initialize(var/newloc, var/new_material, var/new_padding_material) - ..() + . = ..() update_icon() /obj/structure/bed/chair/wheelchair/motor diff --git a/code/modules/ai/ai_holder_combat.dm b/code/modules/ai/ai_holder_combat.dm index e21d8de5b28..26c4d8c0074 100644 --- a/code/modules/ai/ai_holder_combat.dm +++ b/code/modules/ai/ai_holder_combat.dm @@ -189,7 +189,7 @@ /datum/ai_holder/proc/walk_to_target() ai_log("walk_to_target() : Entering.", AI_LOG_DEBUG) // Make sure we can still chase/attack them. - if(!target || !can_attack(target)) + if(QDELETED(target) || !can_attack(target)) ai_log("walk_to_target() : Lost target.", AI_LOG_INFO) lose_target() return diff --git a/code/modules/hydroponics/spreading/spreading.dm b/code/modules/hydroponics/spreading/spreading.dm index c3ca8e77a15..bd8be781016 100644 --- a/code/modules/hydroponics/spreading/spreading.dm +++ b/code/modules/hydroponics/spreading/spreading.dm @@ -205,7 +205,6 @@ density = FALSE /obj/effect/plant/proc/calc_dir() - set background = 1 var/turf/T = get_turf(src) if(!istype(T)) return diff --git a/code/modules/mob/living/carbon/alien/life.dm b/code/modules/mob/living/carbon/alien/life.dm index e474a33d720..cad1b37865a 100644 --- a/code/modules/mob/living/carbon/alien/life.dm +++ b/code/modules/mob/living/carbon/alien/life.dm @@ -2,7 +2,6 @@ /mob/living/carbon/alien/Life() set invisibility = 0 - set background = 1 if (transforming) return if(!loc) return diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 7b8f5a5319b..ee51a8592c0 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -1,6 +1,5 @@ /mob/living/silicon/robot/Life() set invisibility = 0 - set background = 1 if (transforming) return diff --git a/code/modules/mob/mob_planes_vr.dm b/code/modules/mob/mob_planes_vr.dm index 9928180070b..521ad9a7977 100644 --- a/code/modules/mob/mob_planes_vr.dm +++ b/code/modules/mob/mob_planes_vr.dm @@ -42,3 +42,7 @@ entopic_users -= my_mob if(my_mob.client) my_mob.client.images -= entopic_images + +/mob/Destroy() + . = ..() + entopic_users -= src diff --git a/code/modules/vore/resizing/crackers.dm b/code/modules/vore/resizing/crackers.dm index 6ce47ec6c38..5d02bdc4dc0 100644 --- a/code/modules/vore/resizing/crackers.dm +++ b/code/modules/vore/resizing/crackers.dm @@ -128,7 +128,7 @@ var/style = pick(styles) icon_state = style item_state = style - ..() + . = ..() /obj/item/cracker/shrinking name = "shrinking bluespace cracker"