/** * The absolute base class for everything * * A datum instantiated has no physical world prescence, use an atom if you want something * that actually lives in the world * * Be very mindful about adding variables to this class, they are inherited by every single * thing in the entire game, and so you can easily cause memory usage to rise a lot with careless * use of variables at this level */ /datum /// Status traits attached to this datum. associative list of the form: list(trait name (string) = list(source1, source2, source3,...)) var/list/status_traits /// Datum level flags var/datum_flags = NONE /// A weak reference to another datum var/datum/weakref/weak_reference //* Datum Component System *// /** * Components attached to this datum * Lazy assoclist of type -> component reference or list of component references */ 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/comp_lookup /// Lazy associated list in the structure of `signals:proctype` that are run when the datum receives that signal var/list/list/datum/callback/signal_procs /// Is this datum capable of sending signals? var/signal_enabled = FALSE //* Cooldown System *// /* * 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 //* Subsystem - Garbage *// /** * 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 //* Subsystem - Timer *// /// Active timers with this datum as the target var/list/active_timers //* Subsystem - TGUI *// /// Open uis owned by this datum /// Lazy, since this case is semi rare var/list/datum/tgui/open_uis //* misc - filters *// /// List for handling persistent filters. var/list/filter_data /// An accursed beast of a list that contains our filters. Why? Because var/list/filters on atoms/images isn't actually a list /// but a snowflaked skinwalker pretending to be one, which doesn't support half the list procs/operations and the other half behaves weirdly /// so we cut down on filter creation and appearance update costs by editing *this* list, and then assigning ours to it var/list/filter_cache //* misc - reftracking *// #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 /** * 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 // -- CITADEL REMOVAL -- // This shouldn't be needed; weakref already has resolve() vs hard_resolve(). // We use weakref for GC tracing. // 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.timer_flags & TIMER_DELETE_ME)) continue qdel(timer) #ifdef REFERENCE_TRACKING #ifdef REFERENCE_TRACKING_DEBUG found_refs = null #endif #endif //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 /datum/proc/gc_trace_data() . = list() if(length(active_timers)) .["datum-active-timers"] = json_encode(active_timers) if(length(open_uis)) .["datum-open-uis"] = json_encode(open_uis) if(length(status_traits)) .["datum-status-traits"] = json_encode(status_traits) if(length(datum_components)) .["datum-components"] = json_encode(datum_components) if(length(comp_lookup)) .["datum-component-lookups"] = json_encode(comp_lookup) ///images are pretty generic, this should help a bit with tracking harddels related to them /image/gc_trace_data() . = list( "type" = "[type]", "refcount" = refcount(src), "icon" = "[icon]", "icon_state" = "[icon_state]", "loc" = loc ? "loc: [loc] ([COORD(loc)])" : "", ) ///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 = comp_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) comp_lookup = lookup = null for(var/target in signal_procs) UnregisterSignal(target, signal_procs[target]) //* Cooldowns *// /** * 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) //* Duplication *// /** * makes a clone of this datum */ /datum/proc/clone() return new type //* Serialization *// /** * serializes us to a list * note that *everything* will be trampled down to a number or text. * do not store raw types. * * do not serialize type with this; type should always be stored externally from data. */ /datum/proc/serialize() return list() /** * deserializes from a list * * @params * * data - json_decode()'d list. * * @return TRUE / FALSE success / failure */ /datum/proc/deserialize(list/data) return TRUE