/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 || (timer.spent && !(timer.flags & TIMER_DELETE_ME))) continue qdel(timer) #ifdef REFERENCE_TRACKING #ifdef REFERENCE_TRACKING_DEBUG found_refs = null #endif #endif // Cleanup all events on non-turfs. These used to be procs, but have been removed to save proc overhead on 2.6 million destroy() calls if (!isturf(src)) if(GLOB.global_listen_count && GLOB.global_listen_count[src]) GLOB.global_listen_count -= src for(var/entry in GLOB.all_observable_events) var/singleton/observ/event = entry if(event.unregister_global(src)) log_debug("[event] - [src] was deleted while still registered to global events.") if(!(--GLOB.global_listen_count[src])) break if(GLOB.event_sources_count && GLOB.event_sources_count[src]) GLOB.event_sources_count -= src for(var/entry in GLOB.all_observable_events) var/singleton/observ/event = entry var/proc_owners = event.event_sources[src] if(proc_owners) for(var/proc_owner in proc_owners) if(event.unregister(src, proc_owner)) log_debug("[event] - [src] was deleted while still being listened to by [proc_owner].") if(!(--GLOB.event_sources_count[src])) break if(GLOB.event_listen_count && GLOB.event_listen_count[src]) GLOB.event_listen_count -= src for(var/entry in GLOB.all_observable_events) var/singleton/observ/event = entry for(var/event_source in event.event_sources) if(event.unregister(event_source, src)) log_debug("[event] - [src] was deleted while still listening to [event_source].") if(!(--GLOB.event_listen_count[src])) break // End of event cleanup //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) if (!component) continue qdel(component, FALSE) else var/datum/component/C = component_or_list qdel(C, FALSE) dc.Cut() // Signal cleanup. This originally was a proc from /tg/ that had this warning label asking me not to override it. /* ///Only override this if you know what you're doing. You do not know what you're doing ///This is a threat */ // Well proc overhead is a thing that exists when you're qdel'ing 2.6 million objects. And NOTHING overrides it or has any reason to override it. if(length(_listen_lookup)) for(var/sig in _listen_lookup) var/list/comps = _listen_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 = null for(var/target in _signal_procs) UnregisterSignal(target, _signal_procs[target]) //END: ECS SHIT return QDEL_HINT_QUEUE /** * 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])" : ""]"