Files
Aurora.3/code/datums/datum.dm
Wildkins 84d3a447ab Lag War Day 2: TGUI performance improvements (#21848)
<img width="707" height="659" alt="image"
src="https://github.com/user-attachments/assets/6c8db0bf-dec6-40f2-86e8-120531fa5693"
/>

the traitor uplink is causing a genuine hostage situation to the server
by simply being open due to the frankly insane amount of data that it
builds and ships every single UI process tick (which is very often!!!)

i've never been ragebaited by a single .dm file before so hard in my
entire developer career so I simply neutered it into using static data
and hit the commit button. it might break, if it does we're burning it
to the ground and remaking it. genuinely heinous crimes going on there

In addition I'm porting:

* [Fix several SHOULD_NOT_SLEEP hits in SStgui.update_uis()
tgstation/tgstation#75411](https://github.com/tgstation/tgstation/pull/75411)
* [Moves ui references from the tgui subsystem to datums themselves
tgstation/tgstation#76215](https://github.com/tgstation/tgstation/pull/76215)
* [Fixes some spurious runtimes in SStgui procs
tgstation/tgstation#76251](https://github.com/tgstation/tgstation/pull/76251)
* [Fixes tgui_open_uis tracking
tgstation/tgstation#77101](https://github.com/tgstation/tgstation/pull/77101)

moving ui references to the datums themselves is a pretty nice
performance boost because otherwise we have to iterate across all open
uis every time a list input closes which sucks! the rest are just fixes
to that original PR.

thank you for your attention to this matter
2026-02-14 02:17:23 +00:00

208 lines
6.6 KiB
Plaintext

/datum
/**
* Tick count time when this object was destroyed.
*
* If this is non zero then the object has been garbage collected and is awaiting either
* a hard del by the GC subsystme, or to be autocollected (if it has no references)
*/
var/gc_destroyed
/// Open uis owned by this datum
/// Lazy, since this case is semi rare
var/list/open_uis
var/tmp/list/active_timers
/// Active timers with this datum as the target
var/list/_active_timers
/// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...))
var/list/status_traits
/**
* Components attached to this datum
*
* Lazy associated list in the structure of `type -> component/list of components`
*/
var/list/_datum_components
/**
* Any datum registered to receive signals from this datum is in this list
*
* Lazy associated list in the structure of `signal -> registree/list of registrees`
*/
var/list/_listen_lookup
/// Lazy associated list in the structure of `target -> list(signal -> proctype)` that are run when the datum receives that signal
var/list/list/_signal_procs
/// Datum level flags
var/datum_flags = NONE
/**
* If set, a path at/above this one that expects not to be instantiated
*
* This is a `typepath`
*
* Do not instantiate a datum that has the path set as its abstract_type, this indicates
* that the datum is abstract and is not meant to be spawned/used directly
*/
var/abstract_type
/// A weak reference to another datum
var/datum/weakref/weak_reference
/*
* Lazy associative list of currently active cooldowns.
*
* cooldowns [ COOLDOWN_INDEX ] = add_timer()
* add_timer() returns the truthy value of -1 when not stoppable, and else a truthy numeric index
*/
var/list/cooldowns
/// Used to avoid unnecessary refstring creation in Destroy().
var/tmp/has_state_machine = FALSE
#ifdef REFERENCE_TRACKING
/// When was this datum last touched by a reftracker?
/// If this value doesn't match with the start of the search
/// We know this datum has never been seen before, and we should check it
var/last_find_references = 0
/// How many references we're trying to find when searching
var/references_to_clear = 0
#ifdef REFERENCE_TRACKING_DEBUG
///Stores info about where refs are found, used for sanity checks and testing
var/list/found_refs
#endif
#endif
// If we have called dump_harddel_info already. Used to avoid duped calls (since we call it immediately in some cases on failure to process)
// Create and destroy is weird and I wanna cover my bases
var/harddel_deets_dumped = FALSE
/**
* Default implementation of clean-up code.
*
* This should be overridden to remove all references pointing to the object being destroyed, if
* you do override it, make sure to call the parent and return it's return value by default
*
* Return an appropriate [QDEL_HINT][QDEL_HINT_QUEUE] to modify handling of your deletion;
* in most cases this is [QDEL_HINT_QUEUE].
*
* The base case is responsible for doing the following
* * Erasing timers pointing to this datum
* * Erasing compenents on this datum
* * Notifying datums listening to signals from this datum that we are going away
*
* Returns [QDEL_HINT_QUEUE]
*/
/datum/proc/Destroy(force=FALSE)
SHOULD_CALL_PARENT(TRUE)
SHOULD_NOT_SLEEP(TRUE)
tag = null
datum_flags &= ~DF_USE_TAG //In case something tries to REF us
weak_reference = null //ensure prompt GCing of weakref.
if(_active_timers)
var/list/timers = _active_timers
_active_timers = null
for(var/datum/timedevent/timer as anything in timers)
if (timer.spent && !(timer.flags & TIMER_DELETE_ME))
continue
qdel(timer)
#ifdef REFERENCE_TRACKING
#ifdef REFERENCE_TRACKING_DEBUG
found_refs = null
#endif
#endif
if (!isturf(src))
cleanup_events(src)
//BEGIN: ECS SHIT
var/list/dc = _datum_components
if(dc)
for(var/component_key in dc)
var/component_or_list = dc[component_key]
if(islist(component_or_list))
for(var/datum/component/component as anything in component_or_list)
qdel(component, FALSE)
else
var/datum/component/C = component_or_list
qdel(C, FALSE)
dc.Cut()
_clear_signal_refs()
//END: ECS SHIT
return QDEL_HINT_QUEUE
///Only override this if you know what you're doing. You do not know what you're doing
///This is a threat
/datum/proc/_clear_signal_refs()
var/list/lookup = _listen_lookup
if(lookup)
for(var/sig in lookup)
var/list/comps = lookup[sig]
if(length(comps))
for(var/datum/component/comp as anything in comps)
comp.UnregisterSignal(src, sig)
else
var/datum/component/comp = comps
comp.UnregisterSignal(src, sig)
_listen_lookup = lookup = null
for(var/target in _signal_procs)
UnregisterSignal(target, _signal_procs[target])
/**
* Callback called by a timer to end an associative-list-indexed cooldown.
*
* Arguments:
* * source - datum storing the cooldown
* * index - string index storing the cooldown on the cooldowns associative list
*
* This sends a signal reporting the cooldown end.
*/
/proc/end_cooldown(datum/source, index)
if(QDELETED(source))
return
SEND_SIGNAL(source, COMSIG_CD_STOP(index))
TIMER_COOLDOWN_END(source, index)
/**
* Proc used by stoppable timers to end a cooldown before the time has ran out.
*
* Arguments:
* * source - datum storing the cooldown
* * index - string index storing the cooldown on the cooldowns associative list
*
* This sends a signal reporting the cooldown end, passing the time left as an argument.
*/
/proc/reset_cooldown(datum/source, index)
if(QDELETED(source))
return
SEND_SIGNAL(source, COMSIG_CD_RESET(index), S_TIMER_COOLDOWN_TIMELEFT(source, index))
TIMER_COOLDOWN_END(source, index)
///Generate a tag for this /datum, if it implements one
///Should be called as early as possible, best would be in New, to avoid weakref mistargets
///Really just don't use this, you don't need it, global lists will do just fine MOST of the time
///We really only use it for mobs to make id'ing people easier
/datum/proc/GenerateTag()
datum_flags |= DF_USE_TAG
/// Return text from this proc to provide extra context to hard deletes that happen to it
/// Optional, you should use this for cases where replication is difficult and extra context is required
/// Can be called more then once per object, use harddel_deets_dumped to avoid duplicate calls (I am so sorry)
/datum/proc/dump_harddel_info()
return
///images are pretty generic, this should help a bit with tracking harddels related to them
/image/dump_harddel_info()
if(harddel_deets_dumped)
return
harddel_deets_dumped = TRUE
return "Image icon: [icon] - icon_state: [icon_state] [loc ? "loc: [loc] ([loc.x],[loc.y],[loc.z])" : ""]"