mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-30 11:32:20 +00:00
Timers, signals, and components need to know when the thing they're attached to is getting destroyed or bad things happen. Since any datum can make use of these every destroy needs to call parent.
212 lines
6.2 KiB
Plaintext
212 lines
6.2 KiB
Plaintext
/**
|
|
* 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
|
|
|
|
/// Active timers with this datum as the target
|
|
var/list/active_timers
|
|
/// Components attached to this datum
|
|
var/list/datum_components
|
|
/// Status traits attached to this datum
|
|
var/list/status_traits
|
|
/// Any datum registered to receive signals from this datum is in this list
|
|
var/list/comp_lookup
|
|
/// List of callbacks for signal procs
|
|
var/list/list/datum/callback/signal_procs
|
|
/// Is this datum capable of sending signals?
|
|
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
|
|
|
|
/**
|
|
* Called when a href for this datum is clicked
|
|
*
|
|
* Sends a COMSIG_TOPIC signal
|
|
*/
|
|
/datum/Topic(href, href_list[])
|
|
..()
|
|
SEND_SIGNAL(src, COMSIG_TOPIC, usr, href_list)
|
|
|
|
/**
|
|
* 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, ...)
|
|
SHOULD_CALL_PARENT(1)
|
|
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
|
|
|
|
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
|
|
|
|
///Convert a datum into a json blob
|
|
/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)
|
|
|
|
/// Convert a list of json to datum
|
|
/proc/json_deserialize_datum(list/jsonlist, list/options, target_type, strict_target_type = FALSE)
|
|
if(!islist(jsonlist))
|
|
if(!istext(jsonlist))
|
|
CRASH("Invalid JSON")
|
|
return
|
|
jsonlist = json_decode(jsonlist)
|
|
if(!islist(jsonlist))
|
|
CRASH("Invalid JSON")
|
|
return
|
|
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
|