Files
Bubberstation/code/datums/datum.dm
LemonInTheDark 930c5e635e Moves "catch this var/flag" code from obj/init and datum/new into the types that use it (#69634)
* Optimizes away /obj/Initialize

We were spending like 0.15 seconds just checking for blueprints, obj
flags and network ids
All these things can just be applied where they're wanted, saves time

Oh and I replaced object flags with an emag injector. I'll give it a
sprite and name later I promise

* Requires a GenerateTag() call to set DF_USE_TAG, rather then doing a check in atom New

This is technically harder to use, but I don't really want people using
tags, and it saves 0.15 seconds

* Moves generatetag to /datum

* I am dumb

* Saves 0.5 seconds, makes init emissive blockers actually work

Ok so background. If an overlay is added with add_overlay, and not
"managed" somehow, it will effectively never be removed, because
nothing's tracking it.

Update_overlays uses the managed_overlays list/var (one of those) to do
this.
I'm gonna piggyback off this to make emissive overlays actually like,
respect overlay updates.

Oh and uh, I've saved maybe 0.5 seconds by caching the new emissive, and
not using add_overlay. There's a chance this will lead to overlay
corruption, but since we never readd the flattened, I think we'll be
safe

* Fixes plane not being set right, changes color logic too, since alpha will override past color sets

* Makes it actually work. also makes rand posters update appearance to clear away the overlay, since it shows on right click and looks bad

* Fixes blockers showing as emissives. It turns out alpha sets override the color list we use. Not sure why we pretend to support them

* Makes the injector support traits, adds an amazing sprite
2022-09-06 03:17:17 -07:00

276 lines
8.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
/// 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/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
/// Datum level flags
var/datum_flags = NONE
/// 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 REFERENCE_TRACKING
var/running_find_references
var/last_find_references = 0
#ifdef REFERENCE_TRACKING_DEBUG
///Stores info about where refs are found, used for sanity checks and testing
var/list/found_refs
#endif
#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][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.
var/list/timers = active_timers
active_timers = null
for(var/datum/timedevent/timer as anything in timers)
if (timer.spent && !(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)
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()
//END: ECS SHIT
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(!isdatum(returned))
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)
///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