/** * 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 /** * 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 //Time when this object was destroyed. /// Active timers with this datum as the target var/list/active_timers /// Status traits attached to this datum 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/comp_lookup //it used to be for looking up components which had registered a signal but now anything can register /// 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? /// Set to true when a signal has been registered var/signal_enabled = FALSE /// Datum level flags var/datum_flags = NONE /// A weak reference to another datum var/datum/weakref/weak_reference #ifdef TESTING var/running_find_references var/last_find_references = 0 #endif #ifdef DATUMVAR_DEBUGGING_MODE var/list/cached_vars #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 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, ...) tag = null datum_flags &= ~DF_USE_TAG //In case something tries to REF us weak_reference = null //ensure prompt GCing of weakref. var/list/timers = active_timers active_timers = null for(var/thing in timers) var/datum/timedevent/timer = thing if (timer.spent) continue qdel(timer) //BEGIN: ECS SHIT signal_enabled = FALSE var/list/dc = datum_components if(dc) var/all_components = dc[/datum/component] if(length(all_components)) for(var/I in all_components) var/datum/component/C = I qdel(C, FALSE, TRUE) else var/datum/component/C = all_components qdel(C, FALSE, TRUE) dc.Cut() var/list/lookup = comp_lookup if(lookup) for(var/sig in lookup) var/list/comps = lookup[sig] if(length(comps)) for(var/i in comps) var/datum/component/comp = i 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]) //END: ECS SHIT SSsounds.free_datum_channels(src) return QDEL_HINT_QUEUE #ifdef DATUMVAR_DEBUGGING_MODE /datum/proc/save_vars() cached_vars = list() for(var/i in vars) if(i == "cached_vars") continue cached_vars[i] = vars[i] /datum/proc/check_changed_vars() . = list() for(var/i in vars) if(i == "cached_vars") continue if(cached_vars[i] != vars[i]) .[i] = list(cached_vars[i], vars[i]) /datum/proc/txt_changed_vars() var/list/l = check_changed_vars() var/t = "[src]([REF(src)]) changed vars:" for(var/i in l) t += "\"[i]\" \[[l[i][1]]\] --> \[[l[i][2]]\] " t += "." /datum/proc/to_chat_check_changed_vars(target = world) to_chat(target, txt_changed_vars()) #endif //Return a LIST for serialize_datum to encode! Not the actual json! /datum/proc/serialize_list(list/options) CRASH("Attempted to serialize datum [src] of type [type] without serialize_list being implemented!") //Accepts a LIST from deserialize_datum. Should return src or another datum. /datum/proc/deserialize_list(json, list/options) CRASH("Attempted to deserialize datum [src] of type [type] without deserialize_list being implemented!") //Serializes into JSON. Does not encode type. /datum/proc/serialize_json(list/options) . = serialize_list(options) if(!islist(.)) . = null else . = json_encode(.) //Deserializes from JSON. Does not parse type. /datum/proc/deserialize_json(list/input, list/options) var/list/jsonlist = json_decode(input) . = deserialize_list(jsonlist) if(!istype(., /datum)) . = null /proc/json_serialize_datum(datum/D, list/options) if(!istype(D)) return var/list/jsonlist = D.serialize_list(options) if(islist(jsonlist)) jsonlist["DATUM_TYPE"] = D.type return json_encode(jsonlist) /proc/json_deserialize_datum(list/jsonlist, list/options, target_type, strict_target_type = FALSE) if(!islist(jsonlist)) if(!istext(jsonlist)) CRASH("Invalid JSON") jsonlist = json_decode(jsonlist) if(!islist(jsonlist)) CRASH("Invalid JSON") if(!jsonlist["DATUM_TYPE"]) return if(!ispath(jsonlist["DATUM_TYPE"])) if(!istext(jsonlist["DATUM_TYPE"])) return jsonlist["DATUM_TYPE"] = text2path(jsonlist["DATUM_TYPE"]) if(!ispath(jsonlist["DATUM_TYPE"])) return if(target_type) if(!ispath(target_type)) return if(strict_target_type) if(target_type != jsonlist["DATUM_TYPE"]) return else if(!ispath(jsonlist["DATUM_TYPE"], target_type)) return var/typeofdatum = jsonlist["DATUM_TYPE"] //BYOND won't directly read if this is just put in the line below, and will instead runtime because it thinks you're trying to make a new list? var/datum/D = new typeofdatum var/datum/returned = D.deserialize_list(jsonlist, options) if(!istype(returned, /datum)) qdel(D) else return returned