mirror of
https://github.com/yogstation13/Yogstation.git
synced 2025-02-26 09:04:50 +00:00
253 lines
7.5 KiB
Plaintext
253 lines
7.5 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
|
|
/// Datum level flags
|
|
var/datum_flags = NONE
|
|
|
|
/// A cached version of our \ref
|
|
/// The brunt of \ref costs are in creating entries in the string tree (a tree of immutable strings)
|
|
/// This avoids doing that more then once per datum by ensuring ref strings always have a reference to them after they're first pulled
|
|
var/cached_ref
|
|
|
|
/// 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
|
|
|
|
#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(TRUE)
|
|
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)
|
|
|
|
// Handle components & signals
|
|
var/list/dc = datum_components
|
|
if(dc)
|
|
var/all_components = dc[/datum/component]
|
|
if(length(all_components))
|
|
for(var/datum/component/component as anything in all_components)
|
|
qdel(component, FALSE, TRUE)
|
|
else
|
|
var/datum/component/C = all_components
|
|
qdel(C, FALSE, TRUE)
|
|
dc.Cut()
|
|
|
|
clear_signal_refs()
|
|
|
|
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 = 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])
|
|
|
|
#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")
|
|
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
|
|
|
|
/**
|
|
* 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)
|