mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-06-24 15:45:37 +01:00
21b4095dfd
Upstream 04/17/2026 fixes https://github.com/Bubberstation/Bubberstation/issues/5549 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: tgstation-ci[bot] <179393467+tgstation-ci[bot]@users.noreply.github.com> Co-authored-by: ArcaneMusic <41715314+ArcaneMusic@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com> Co-authored-by: rageguy505 <54517726+rageguy505@users.noreply.github.com> Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: Aliceee2ch <160794176+Aliceee2ch@users.noreply.github.com> Co-authored-by: Time-Green <7501474+Time-Green@users.noreply.github.com> Co-authored-by: Tsar-Salat <62388554+Tsar-Salat@users.noreply.github.com> Co-authored-by: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Co-authored-by: Maxipat <108554989+Maxipat112@users.noreply.github.com> Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: deltanedas <39013340+deltanedas@users.noreply.github.com> Co-authored-by: SimplyLogan <47579821+loganuk@users.noreply.github.com> Co-authored-by: loganuk <fakeemail123@aol.com> Co-authored-by: Leland Kemble <70413276+lelandkemble@users.noreply.github.com> Co-authored-by: FalloutFalcon <86381784+FalloutFalcon@users.noreply.github.com> Co-authored-by: Roxy <75404941+TealSeer@users.noreply.github.com> Co-authored-by: Lucy <lucy@absolucy.moe> Co-authored-by: siliconOpossum <138069572+siliconOpossum@users.noreply.github.com> Co-authored-by: Isratosh <Isratosh@hotmail.com> Co-authored-by: TheRyeGuyWhoWillNowDie <70169560+TheRyeGuyWhoWillNowDie@users.noreply.github.com> Co-authored-by: Neocloudy <88008002+Neocloudy@users.noreply.github.com> Co-authored-by: Alexander V. <volas@ya.ru> Co-authored-by: ElGitificador <168473461+ElGitificador@users.noreply.github.com> Co-authored-by: Twaticus <46540570+Twaticus@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Bloop <13398309+vinylspiders@users.noreply.github.com> Co-authored-by: Cameron Lennox <killer65311@gmail.com> Co-authored-by: Tim <timothymtorres@gmail.com> Co-authored-by: Iamgoofball <iamgoofball@gmail.com> Co-authored-by: Layzu666 <121319428+Layzu666@users.noreply.github.com> Co-authored-by: Arturlang <24881678+Arturlang@users.noreply.github.com> Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com> Co-authored-by: mrmanlikesbt <99309552+mrmanlikesbt@users.noreply.github.com> Co-authored-by: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Co-authored-by: John F. Kennedy <54908920+MacaroniCritter@users.noreply.github.com> Co-authored-by: Cursor <102828457+theselfish@users.noreply.github.com> Co-authored-by: Josh <josh.adam.powell@gmail.com> Co-authored-by: Josh Powell <josh.powell@softwire.com> Co-authored-by: Yobrocharlie <Charliemiller5617@gmail.com> Co-authored-by: Hardly3D <66234359+Hardly3D@users.noreply.github.com> Co-authored-by: shayoki <96078776+shayoki@users.noreply.github.com> Co-authored-by: LT3 <83487515+lessthnthree@users.noreply.github.com>
280 lines
9.7 KiB
Plaintext
280 lines
9.7 KiB
Plaintext
#ifdef REFERENCE_TRACKING
|
|
#define REFSEARCH_RECURSE_LIMIT 64
|
|
|
|
#ifdef FAST_REFERENCE_TRACKING
|
|
// typecache of types that almost certainly have no refs, and thus can be safely skipped when finding references
|
|
GLOBAL_ALIST_INIT(reftracker_skip_typecache, init_reftracker_skip_typecache())
|
|
// empty alist we swap with GLOB.reftracker_skip_typecache whenever someone calls toggle_fast_reftracking
|
|
GLOBAL_ALIST_EMPTY(reftracker_skip_typecache_b)
|
|
|
|
/proc/toggle_fast_reftracking()
|
|
var/alist/a = GLOB.reftracker_skip_typecache
|
|
var/alist/b = GLOB.reftracker_skip_typecache_b
|
|
GLOB.reftracker_skip_typecache = b
|
|
GLOB.reftracker_skip_typecache_b = a
|
|
|
|
/proc/init_reftracker_skip_typecache()
|
|
. = alist()
|
|
for(var/base_type in list(
|
|
/icon,
|
|
/matrix,
|
|
/regex,
|
|
/atom/movable/lighting_object, // only contains turf and MA refs
|
|
/atom/movable/mirage_holder,
|
|
/atom/movable/render_step/emissive_blocker,
|
|
/datum/armor,
|
|
/datum/asset_cache_item,
|
|
/datum/book_info,
|
|
/datum/card,
|
|
/datum/chat_payload,
|
|
/datum/comm_log_entry,
|
|
/datum/gas_mixture,
|
|
/datum/greyscale_layer,
|
|
/datum/icon_transformer,
|
|
/datum/instrument_key,
|
|
/datum/movespeed_modifier,
|
|
/datum/painting,
|
|
/datum/paper_input,
|
|
/datum/physiology,
|
|
/datum/plant_gene/reagent,
|
|
/datum/qdel_item,
|
|
/datum/stack_recipe,
|
|
/datum/tlv,
|
|
/datum/universal_icon,
|
|
/datum/weakref,
|
|
/datum/z_pillar,
|
|
/obj/effect/abstract/z_holder,
|
|
// stuff below isn't 100% guaranteed to be ref-free, but they're prolly not an issue
|
|
/turf/closed/mineral,
|
|
/turf/open/lava,
|
|
/turf/open/misc/asteroid,
|
|
/turf/open/openspace,
|
|
/turf/open/space,
|
|
/obj/structure/flora, // icebox and such has a LOT of these
|
|
/datum/chatmessage,
|
|
/datum/lighting_corner,
|
|
/datum/log_entry, // hopefully nobody's silly enough to accidentally pass a reference to these... right???
|
|
/datum/reagent/consumable/nutriment,
|
|
))
|
|
for(var/type in typesof(base_type))
|
|
.[type] = TRUE
|
|
#endif
|
|
|
|
/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.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
|
|
|
|
#ifdef FAST_REFERENCE_TRACKING
|
|
var/alist/skip_types = GLOB.reftracker_skip_typecache
|
|
#endif
|
|
|
|
for(var/datum/thing in world) //atoms (don't beleive its lies)
|
|
#ifdef FAST_REFERENCE_TRACKING
|
|
if(skip_types[thing.type])
|
|
continue
|
|
#endif
|
|
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
|
|
#ifdef FAST_REFERENCE_TRACKING
|
|
if(skip_types[thing.type])
|
|
continue
|
|
#endif
|
|
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
|
|
var/is_alist = isalist(potential_cache)
|
|
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_alist) && !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"])"
|