DCS Update (#7843)

Co-authored-by: Raeschen <rycoop29@gmail.com>
This commit is contained in:
Selis
2024-03-01 17:45:33 +01:00
committed by GitHub
parent 38c1582f93
commit 18fbf65d97
26 changed files with 811 additions and 493 deletions

View File

@@ -1,4 +1,4 @@
/// Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type. /// Return this from `/datum/component/Initialize` or `/datum/component/OnTransfer` or `/datum/component/on_source_add` to have the component be deleted if it's applied to an incorrect type.
/// `parent` must not be modified if this is to be returned. /// `parent` must not be modified if this is to be returned.
/// This will be noted in the runtime logs /// This will be noted in the runtime logs
#define COMPONENT_INCOMPATIBLE 1 #define COMPONENT_INCOMPATIBLE 1
@@ -9,35 +9,56 @@
#define ELEMENT_INCOMPATIBLE 1 #define ELEMENT_INCOMPATIBLE 1
// /datum/element flags // /datum/element flags
/// Causes the detach proc to be called when the host object is being deleted /// Causes the detach proc to be called when the host object is being deleted.
#define ELEMENT_DETACH (1 << 0) /// Should only be used if you need to perform cleanup not related to the host object.
/// You do not need this if you are only unregistering signals, for instance.
/// You would need it if you are doing something like removing the target from a processing list.
#define ELEMENT_DETACH_ON_HOST_DESTROY (1 << 0)
/** /**
* Only elements created with the same arguments given after `id_arg_index` share an element instance * Only elements created with the same arguments given after `argument_hash_start_idx` share an element instance
* The arguments are the same when the text and number values are the same and all other values have the same ref * The arguments are the same when the text and number values are the same and all other values have the same ref
*/ */
#define ELEMENT_BESPOKE (1 << 1) #define ELEMENT_BESPOKE (1 << 1)
/// Causes all detach arguments to be passed to detach instead of only being used to identify the element /// Causes all detach arguments to be passed to detach instead of only being used to identify the element
/// When this is used your Detach proc should have the same signature as your Attach proc /// When this is used your Detach proc should have the same signature as your Attach proc
#define ELEMENT_COMPLEX_DETACH (1 << 2) #define ELEMENT_COMPLEX_DETACH (1 << 2)
/**
* Elements with this flag will have their datum lists arguments compared as is,
* without the contents being sorted alpha-numerically first.
* This is good for those elements where the position of the keys matter, like in the case of color matrices.
*/
#define ELEMENT_DONT_SORT_LIST_ARGS (1<<3)
/**
* Elements with this flag will be ignored by the dcs_check_list_arguments test.
* A good example is connect_loc, for which it's pratically undoable unless we force every signal proc to have a different name.
*/
#define ELEMENT_NO_LIST_UNIT_TEST (1<<4)
// How multiple components of the exact same type are handled in the same datum // How multiple components of the exact same type are handled in the same datum
/// old component is deleted (default) /// old component is deleted (default)
#define COMPONENT_DUPE_HIGHLANDER 0 #define COMPONENT_DUPE_HIGHLANDER 0
/// duplicates allowed /// duplicates allowed
#define COMPONENT_DUPE_ALLOWED 1 #define COMPONENT_DUPE_ALLOWED 1
/// new component is deleted /// new component is deleted
#define COMPONENT_DUPE_UNIQUE 2 #define COMPONENT_DUPE_UNIQUE 2
/**
* Component uses source tracking to manage adding and removal logic.
* Add a source/spawn to/the component by using AddComponentFrom(source, component_type, args...)
* Removing the last source will automatically remove the component from the parent.
* Arguments will be passed to on_source_add(source, args...); ensure that Initialize and on_source_add have the same signature.
*/
#define COMPONENT_DUPE_SOURCES 3
/// old component is given the initialization args of the new /// old component is given the initialization args of the new
#define COMPONENT_DUPE_UNIQUE_PASSARGS 4 #define COMPONENT_DUPE_UNIQUE_PASSARGS 4
/// each component of the same type is consulted as to whether the duplicate should be allowed /// each component of the same type is consulted as to whether the duplicate should be allowed
#define COMPONENT_DUPE_SELECTIVE 5 #define COMPONENT_DUPE_SELECTIVE 5
//Redirection component init flags //Redirection component init flags
#define REDIRECT_TRANSFER_WITH_TURF 1 #define REDIRECT_TRANSFER_WITH_TURF 1
//Arch //Arch
#define ARCH_PROB "probability" //Probability for each item #define ARCH_PROB "probability" //Probability for each item
#define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount #define ARCH_MAXDROP "max_drop_amount" //each item's max drop amount
//Ouch my toes! //Ouch my toes!
#define CALTROP_BYPASS_SHOES 1 #define CALTROP_BYPASS_SHOES 1
@@ -45,4 +66,4 @@
// Conflict element IDs // Conflict element IDs
#define CONFLICT_ELEMENT_CRUSHER "crusher" #define CONFLICT_ELEMENT_CRUSHER "crusher"
#define CONFLICT_ELEMENT_KA "kinetic_accelerator" #define CONFLICT_ELEMENT_KA "kinetic_accelerator"

View File

@@ -2,14 +2,14 @@
/// The datum hosting the signal is automaticaly added as the first argument /// The datum hosting the signal is automaticaly added as the first argument
/// Returns a bitfield gathered from all registered procs /// Returns a bitfield gathered from all registered procs
/// Arguments given here are packaged in a list and given to _SendSignal /// Arguments given here are packaged in a list and given to _SendSignal
#define SEND_SIGNAL(target, sigtype, arguments...) ( !target.comp_lookup || !target.comp_lookup[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) ) #define SEND_SIGNAL(target, sigtype, arguments...) ( !target._listen_lookup?[sigtype] ? NONE : target._SendSignal(sigtype, list(target, ##arguments)) )
#define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) ) #define SEND_GLOBAL_SIGNAL(sigtype, arguments...) ( SEND_SIGNAL(SSdcs, sigtype, ##arguments) )
/// Signifies that this proc is used to handle signals. /// Signifies that this proc is used to handle signals.
/// Every proc you pass to RegisterSignal must have this. /// Every proc you pass to RegisterSignal must have this.
//#define SIGNAL_HANDLER SHOULD_NOT_SLEEP(TRUE) // #define SIGNAL_HANDLER SHOULD_NOT_SLEEP(TRUE) FIXME: FIXME: Causing some big issues still
#define SIGNAL_HANDLER // Sigh #define SIGNAL_HANDLER
/// A wrapper for _AddElement that allows us to pretend we're using normal named arguments /// A wrapper for _AddElement that allows us to pretend we're using normal named arguments
#define AddElement(arguments...) _AddElement(list(##arguments)) #define AddElement(arguments...) _AddElement(list(##arguments))
@@ -18,3 +18,10 @@
/// A wrapper for _AddComponent that allows us to pretend we're using normal named arguments /// A wrapper for _AddComponent that allows us to pretend we're using normal named arguments
#define AddComponent(arguments...) _AddComponent(list(##arguments)) #define AddComponent(arguments...) _AddComponent(list(##arguments))
/// A wrapper for _AddComonent that passes in a source.
/// Necessary if dupe_mode is set to COMPONENT_DUPE_SOURCES.
#define AddComponentFrom(source, arguments...) _AddComponent(list(##arguments), source)
/// A wrapper for _LoadComponent that allows us to pretend we're using normal named arguments
#define LoadComponent(arguments...) _LoadComponent(list(##arguments))

View File

@@ -1,61 +1,3 @@
// trait accessor defines
#define ADD_TRAIT(target, trait, source) \
do { \
var/list/_L; \
if (!target.status_traits) { \
target.status_traits = list(); \
_L = target.status_traits; \
_L[trait] = list(source); \
} else { \
_L = target.status_traits; \
if (_L[trait]) { \
_L[trait] |= list(source); \
} else { \
_L[trait] = list(source); \
} \
} \
} while (0)
#define REMOVE_TRAIT(target, trait, sources) \
do { \
var/list/_L = target.status_traits; \
var/list/_S; \
if (sources && !islist(sources)) { \
_S = list(sources); \
} else { \
_S = sources\
}; \
if (_L && _L[trait]) { \
for (var/_T in _L[trait]) { \
if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \
_L[trait] -= _T \
} \
};\
if (!length(_L[trait])) { \
_L -= trait \
}; \
if (!length(_L)) { \
target.status_traits = null \
}; \
} \
} while (0)
#define REMOVE_TRAITS_NOT_IN(target, sources) \
do { \
var/list/_L = target.status_traits; \
var/list/_S = sources; \
if (_L) { \
for (var/_T in _L) { \
_L[_T] &= _S;\
if (!length(_L[_T])) { \
_L -= _T } \
};\
if (!length(_L)) { \
target.status_traits = null\
};\
}\
} while (0)
#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
/* /*
Remember to update _globalvars/traits.dm if you're adding/removing/renaming traits. Remember to update _globalvars/traits.dm if you're adding/removing/renaming traits.
*/ */
@@ -265,4 +207,4 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define SANGUIOSE_TRAIT "sanguiose" #define SANGUIOSE_TRAIT "sanguiose"
#define FROGENITE_TRAIT "frogenite" #define FROGENITE_TRAIT "frogenite"
#define FERVEATIUM_TRAIT "ferveatium" #define FERVEATIUM_TRAIT "ferveatium"
*/ */

View File

@@ -0,0 +1,63 @@
#define SIGNAL_ADDTRAIT(trait_ref) "addtrait [trait_ref]"
#define SIGNAL_REMOVETRAIT(trait_ref) "removetrait [trait_ref]"
// trait accessor defines
#define ADD_TRAIT(target, trait, source) \
do { \
var/list/_L; \
if (!target.status_traits) { \
target.status_traits = list(); \
_L = target.status_traits; \
_L[trait] = list(source); \
} else { \
_L = target.status_traits; \
if (_L[trait]) { \
_L[trait] |= list(source); \
} else { \
_L[trait] = list(source); \
} \
} \
} while (0)
#define REMOVE_TRAIT(target, trait, sources) \
do { \
var/list/_L = target.status_traits; \
var/list/_S; \
if (sources && !islist(sources)) { \
_S = list(sources); \
} else { \
_S = sources\
}; \
if (_L && _L[trait]) { \
for (var/_T in _L[trait]) { \
if ((!_S && (_T != ROUNDSTART_TRAIT)) || (_T in _S)) { \
_L[trait] -= _T \
} \
};\
if (!length(_L[trait])) { \
_L -= trait \
}; \
if (!length(_L)) { \
target.status_traits = null \
}; \
} \
} while (0)
#define REMOVE_TRAITS_NOT_IN(target, sources) \
do { \
var/list/_L = target.status_traits; \
var/list/_S = sources; \
if (_L) { \
for (var/_T in _L) { \
_L[_T] &= _S;\
if (!length(_L[_T])) { \
_L -= _T } \
};\
if (!length(_L)) { \
target.status_traits = null\
};\
}\
} while (0)
#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
#define HAS_TRAIT_FROM_ONLY(target, trait, source) (HAS_TRAIT(target, trait) && (source in target.status_traits[trait]) && (length(target.status_traits[trait]) == 1))
#define HAS_TRAIT_NOT_FROM(target, trait, source) (HAS_TRAIT(target, trait) && (length(target.status_traits[trait] - source) > 0))

View File

@@ -58,8 +58,8 @@
. = B[STAT_ENTRY_COUNT] - A[STAT_ENTRY_COUNT] . = B[STAT_ENTRY_COUNT] - A[STAT_ENTRY_COUNT]
/proc/cmp_typepaths_asc(A, B) /proc/cmp_typepaths_asc(A, B)
return sorttext("[B]","[A]") return sorttext("[B]","[A]")
/** /**
* Sorts crafting recipe requirements before the crafting recipe is inserted into GLOB.crafting_recipes * Sorts crafting recipe requirements before the crafting recipe is inserted into GLOB.crafting_recipes
* *
@@ -83,7 +83,7 @@
/proc/cmp_media_track_asc(datum/track/A, datum/track/B) /proc/cmp_media_track_asc(datum/track/A, datum/track/B)
var/genre_sort = sorttext(B.genre || "Uncategorized", A.genre || "Uncategorized") var/genre_sort = sorttext(B.genre || "Uncategorized", A.genre || "Uncategorized")
return genre_sort || sorttext(B.title, A.title) return genre_sort || sorttext(B.title, A.title)
///Filters have a numerical priority. ///Filters have a numerical priority.
/proc/cmp_filter_data_priority(list/A, list/B) /proc/cmp_filter_data_priority(list/A, list/B)
return A["priority"] - B["priority"] return A["priority"] - B["priority"]
@@ -97,4 +97,20 @@
return A.sort_hint == B.sort_hint ? sorttext("[B.name]","[A.name]") : A.sort_hint - B.sort_hint return A.sort_hint == B.sort_hint ? sorttext("[B.name]","[A.name]") : A.sort_hint - B.sort_hint
/proc/cmp_stored_item_name(datum/stored_item/A, datum/stored_item/B) /proc/cmp_stored_item_name(datum/stored_item/A, datum/stored_item/B)
return sorttext(B.item_name, A.item_name) return sorttext(B.item_name, A.item_name)
// CHOMPEdit Start - More text compares
/proc/cmp_embed_text_asc(a,b)
if(isdatum(a))
a = REF(a)
if(isdatum(b))
b = REF(b)
return sorttext("[b]", "[a]")
/proc/cmp_embed_text_dsc(a,b)
if(isdatum(a))
a = REF(a)
if(isdatum(b))
b = REF(b)
return sorttext("[a]", "[b]")
// CHOMPEdit End

View File

@@ -13,6 +13,7 @@ var/datum/controller/transfer_controller/transfer_controller
/datum/controller/transfer_controller/Destroy() /datum/controller/transfer_controller/Destroy()
STOP_PROCESSING(SSprocessing, src) STOP_PROCESSING(SSprocessing, src)
..()
/datum/controller/transfer_controller/process() /datum/controller/transfer_controller/process()
currenttick = currenttick + 1 currenttick = currenttick + 1

View File

@@ -5,10 +5,34 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
var/list/elements_by_type = list() var/list/elements_by_type = list()
/datum/controller/subsystem/processing/dcs/Recover() /**
comp_lookup = SSdcs.comp_lookup * A nested assoc list of bespoke element types (keys) and superlists containing all lists used as arguments (values).
* Inside the superlists, lists that've been sorted alphabetically are keys, while the original unsorted lists are values.
*
* e.g. list(
* /datum/element/first = list(list(A, B, C) = list(B, A, C), list(A, B) = list(A, B)),
* /datum/element/second = list(list(B, C) = list(C, B), list(D) = list(D)),
* )
*
* Used by the dcs_check_list_arguments unit test.
*/
var/list/arguments_that_are_lists_by_element = list()
/**
* An assoc list of list instances and their sorted counterparts.
*
* e.g. list(
* list(B, A, C) = list(A, B, C),
* list(C, B) = list(B, C),
* )
*
* Used to make sure each list instance is sorted no more than once, or the unit test won't work.
*/
var/list/sorted_arguments_that_are_lists = list()
/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments) /datum/controller/subsystem/processing/dcs/Recover()
_listen_lookup = SSdcs._listen_lookup
/datum/controller/subsystem/processing/dcs/proc/GetElement(list/arguments, init_element = TRUE)
var/datum/element/eletype = arguments[1] var/datum/element/eletype = arguments[1]
var/element_id = eletype var/element_id = eletype
@@ -16,10 +40,10 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
CRASH("Attempted to instantiate [eletype] as a /datum/element") CRASH("Attempted to instantiate [eletype] as a /datum/element")
if(initial(eletype.element_flags) & ELEMENT_BESPOKE) if(initial(eletype.element_flags) & ELEMENT_BESPOKE)
element_id = GetIdFromArguments(arguments) element_id = length(arguments) == 1 ? "[arguments[1]]" : GetIdFromArguments(arguments)
. = elements_by_type[element_id] . = elements_by_type[element_id]
if(.) if(. || !init_element)
return return
. = elements_by_type[element_id] = new eletype . = elements_by_type[element_id] = new eletype
@@ -31,24 +55,55 @@ PROCESSING_SUBSYSTEM_DEF(dcs)
**/ **/
/datum/controller/subsystem/processing/dcs/proc/GetIdFromArguments(list/arguments) /datum/controller/subsystem/processing/dcs/proc/GetIdFromArguments(list/arguments)
var/datum/element/eletype = arguments[1] var/datum/element/eletype = arguments[1]
var/list/fullid = list("[eletype]") var/list/fullid = list(eletype)
var/list/named_arguments = list() var/list/named_arguments
for(var/i in initial(eletype.id_arg_index) to length(arguments)) for(var/i in initial(eletype.argument_hash_start_idx) to length(arguments))
var/key = arguments[i] var/key = arguments[i]
var/value
if(istext(key))
value = arguments[key]
if(!(istext(key) || isnum(key)))
key = REF(key)
key = "[key]" // Key is stringified so numbers dont break things
if(!isnull(value))
if(!(istext(value) || isnum(value)))
value = REF(value)
named_arguments["[key]"] = value
else
fullid += "[key]"
if(length(named_arguments)) if(istext(key))
named_arguments = sortList(named_arguments) var/value = arguments[key]
if (isnull(value))
fullid += key
else
if (!istext(value) && !isnum(value))
//if(PERFORM_ALL_TESTS(dcs_check_list_arguments) && islist(value)) // Unit test stuff we dont have
//add_to_arguments_that_are_lists(value, eletype) // Unit test stuff we dont have
value = REF(value)
if (!named_arguments)
named_arguments = list()
named_arguments[key] = value
continue
if (isnum(key))
fullid += key
else
//if(PERFORM_ALL_TESTS(dcs_check_list_arguments) && islist(key)) // Unit test stuff we dont have
//add_to_arguments_that_are_lists(key, eletype) // Unit test stuff we dont have
fullid += REF(key)
if(named_arguments)
named_arguments = sortTim(named_arguments, GLOBAL_PROC_REF(cmp_text_asc))
fullid += named_arguments fullid += named_arguments
return list2params(fullid) return list2params(fullid)
/**
* Offloading the first half of the dcs_check_list_arguments here, which is populating the superlist
* with sublists that will be later compared with each other by the dcs_check_list_arguments unit test.
*/
/datum/controller/subsystem/processing/dcs/proc/add_to_arguments_that_are_lists(list/argument, datum/element/element_type)
if(initial(element_type.element_flags) & ELEMENT_NO_LIST_UNIT_TEST)
return
var/list/element_type_superlist = arguments_that_are_lists_by_element[element_type]
if(!element_type_superlist)
arguments_that_are_lists_by_element[element_type] = element_type_superlist = list()
var/list/sorted_argument = argument
if(!(initial(element_type.element_flags) & ELEMENT_DONT_SORT_LIST_ARGS))
sorted_argument = sorted_arguments_that_are_lists[argument]
if(!sorted_argument)
sorted_arguments_that_are_lists[argument] = sorted_argument = sortTim(argument.Copy(), GLOBAL_PROC_REF(cmp_embed_text_asc))
element_type_superlist[sorted_argument] = argument

View File

@@ -0,0 +1,55 @@
# Template file for your new component
See _component.dm for detailed explanations
```dm
/datum/component/mycomponent
//can_transfer = TRUE // Must have PostTransfer
//dupe_mode = COMPONENT_DUPE_ALLOWED // code/__DEFINES/dcs/flags.dm
var/myvar
/datum/component/mycomponent/Initialize(myargone, myargtwo)
if(myargone)
myvar = myargone
if(myargtwo)
send_to_playing_players(myargtwo)
/datum/component/mycomponent/RegisterWithParent()
RegisterSignal(parent, COMSIG_NOT_REAL, PROC_REF(signalproc)) // RegisterSignal can take a signal name by itself,
RegisterSignal(parent, list(COMSIG_NOT_REAL_EITHER, COMSIG_ALMOST_REAL), PROC_REF(otherproc)) // or a list of them to assign to the same proc
/datum/component/mycomponent/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_NOT_REAL) // UnregisterSignal has similar behavior
UnregisterSignal(parent, list( // But you can just include all registered signals in one call
COMSIG_NOT_REAL,
COMSIG_NOT_REAL_EITHER,
COMSIG_ALMOST_REAL,
))
/datum/component/mycomponent/proc/signalproc(datum/source)
SIGNAL_HANDLER
send_to_playing_players("[source] signaled [src]!")
/*
/datum/component/mycomponent/InheritComponent(datum/component/mycomponent/old, i_am_original, list/arguments)
myvar = old.myvar
if(i_am_original)
send_to_playing_players("No parent should have to bury their child")
*/
/*
/datum/component/mycomponent/PreTransfer()
send_to_playing_players("Goodbye [parent], I'm getting adopted")
/datum/component/mycomponent/PostTransfer()
send_to_playing_players("Hello my new parent, [parent]! It's nice to meet you!")
*/
/*
/datum/component/mycomponent/CheckDupeComponent(datum/mycomponent/new, myargone, myargtwo)
if(myargone == myvar)
return TRUE
*/
```

View File

@@ -4,6 +4,6 @@
Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SendSignal()` call. Now every component that want's to can also know about this happening. Loosely adapted from /vg/. This is an entity component system for adding behaviours to datums when inheritance doesn't quite cut it. By using signals and events instead of direct inheritance, you can inject behaviours without hacky overloads. It requires a different method of thinking, but is not hard to use correctly. If a behaviour can have application across more than one thing. Make it generic, make it a component. Atom/mob/obj event? Give it a signal, and forward it's arguments with a `SendSignal()` call. Now every component that want's to can also know about this happening.
See [this thread](https://tgstation13.org/phpBB/viewtopic.php?f=5&t=22674) for an introduction to the system as a whole. ### [HackMD page for an introduction to the system as a whole.](https://hackmd.io/@tgstation/SignalsComponentsElements)
### See/Define signals and their arguments in [__DEFINES\dcs\signals.dm](../../__DEFINES/dcs/signals.dm) ### See/Define signals and their arguments in [__DEFINES\components.dm](../../__DEFINES/components.dm)

View File

@@ -1,14 +1,14 @@
/** /**
* # Component * # Component
* *
* The component datum * The component datum
* *
* A component should be a single standalone unit * A component should be a single standalone unit
* of functionality, that works by receiving signals from it's parent * of functionality, that works by receiving signals from it's parent
* object to provide some single functionality (i.e a slippery component) * object to provide some single functionality (i.e a slippery component)
* that makes the object it's attached to cause people to slip over. * that makes the object it's attached to cause people to slip over.
* Useful when you want shared behaviour independent of type inheritance * Useful when you want shared behaviour independent of type inheritance
*/ */
/datum/component /datum/component
/** /**
* Defines how duplicate existing components are handled when added to a datum * Defines how duplicate existing components are handled when added to a datum
@@ -38,14 +38,17 @@
*/ */
var/can_transfer = FALSE var/can_transfer = FALSE
/// A lazy list of the sources for this component
var/list/sources
/** /**
* Create a new component. * Create a new component.
* *
* Additional arguments are passed to [Initialize()][/datum/component/proc/Initialize] * Additional arguments are passed to [Initialize()][/datum/component/proc/Initialize]
* *
* Arguments: * Arguments:
* * datum/P the parent datum this component reacts to signals from * * datum/P the parent datum this component reacts to signals from
*/ */
/datum/component/New(list/raw_args) /datum/component/New(list/raw_args)
parent = raw_args[1] parent = raw_args[1]
var/list/arguments = raw_args.Copy(2) var/list/arguments = raw_args.Copy(2)
@@ -57,50 +60,50 @@
_JoinParent(parent) _JoinParent(parent)
/** /**
* Called during component creation with the same arguments as in new excluding parent. * Called during component creation with the same arguments as in new excluding parent.
* *
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead * Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*/ */
/datum/component/proc/Initialize(...) /datum/component/proc/Initialize(...)
return return
/** /**
* Properly removes the component from `parent` and cleans up references * Properly removes the component from `parent` and cleans up references
* *
* Arguments: * Arguments:
* * force - makes it not check for and remove the component from the parent * * force - makes it not check for and remove the component from the parent
* * silent - deletes the component without sending a [COMSIG_COMPONENT_REMOVING] signal */
*/ /datum/component/Destroy(force = FALSE)
/datum/component/Destroy(force=FALSE, silent=FALSE) if(!parent)
if(!force && parent) return ..()
if(!force)
_RemoveFromParent() _RemoveFromParent()
if(parent && !silent) //CHOMPEdit SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
SEND_SIGNAL(parent, COMSIG_COMPONENT_REMOVING, src)
parent = null parent = null
return ..() return ..()
/** /**
* Internal proc to handle behaviour of components when joining a parent * Internal proc to handle behaviour of components when joining a parent
*/ */
/datum/component/proc/_JoinParent() /datum/component/proc/_JoinParent()
var/datum/P = parent var/datum/P = parent
//lazy init the parent's dc list //lazy init the parent's dc list
var/list/dc = P.datum_components var/list/dc = P._datum_components
if(!dc) if(!dc)
P.datum_components = dc = list() P._datum_components = dc = list()
//set up the typecache //set up the typecache
var/our_type = type var/our_type = type
for(var/I in _GetInverseTypeList(our_type)) for(var/I in _GetInverseTypeList(our_type))
var/test = dc[I] var/test = dc[I]
if(test) //already another component of this type here if(test) //already another component of this type here
var/list/components_of_type var/list/components_of_type
if(!length(test)) if(!length(test))
components_of_type = list(test) components_of_type = list(test)
dc[I] = components_of_type dc[I] = components_of_type
else else
components_of_type = test components_of_type = test
if(I == our_type) //exact match, take priority if(I == our_type) //exact match, take priority
var/inserted = FALSE var/inserted = FALSE
for(var/J in 1 to components_of_type.len) for(var/J in 1 to components_of_type.len)
var/datum/component/C = components_of_type[J] var/datum/component/C = components_of_type[J]
@@ -110,198 +113,126 @@
break break
if(!inserted) if(!inserted)
components_of_type += src components_of_type += src
else //indirect match, back of the line with ya else //indirect match, back of the line with ya
components_of_type += src components_of_type += src
else //only component of this type, no list else //only component of this type, no list
dc[I] = src dc[I] = src
RegisterWithParent() RegisterWithParent()
/** /**
* Internal proc to handle behaviour when being removed from a parent * Internal proc to handle behaviour when being removed from a parent
*/ */
/datum/component/proc/_RemoveFromParent() /datum/component/proc/_RemoveFromParent()
var/datum/P = parent var/datum/parent = src.parent
var/list/dc = P.datum_components var/list/parents_components = parent._datum_components
for(var/I in _GetInverseTypeList()) for(var/I in _GetInverseTypeList())
var/list/components_of_type = dc[I] var/list/components_of_type = parents_components[I]
if(length(components_of_type)) //
if(length(components_of_type)) //
var/list/subtracted = components_of_type - src var/list/subtracted = components_of_type - src
if(subtracted.len == 1) //only 1 guy left
dc[I] = subtracted[1] //make him special if(subtracted.len == 1) //only 1 guy left
parents_components[I] = subtracted[1] //make him special
else else
dc[I] = subtracted parents_components[I] = subtracted
else //just us
dc -= I else //just us
if(!dc.len) parents_components -= I
P.datum_components = null
if(!parents_components.len)
parent._datum_components = null
UnregisterFromParent() UnregisterFromParent()
/** /**
* Register the component with the parent object * Register the component with the parent object
* *
* Use this proc to register with your parent object * Use this proc to register with your parent object
* *
* Overridable proc that's called when added to a new parent * Overridable proc that's called when added to a new parent
*/ */
/datum/component/proc/RegisterWithParent() /datum/component/proc/RegisterWithParent()
return return
/** /**
* Unregister from our parent object * Unregister from our parent object
* *
* Use this proc to unregister from your parent object * Use this proc to unregister from your parent object
* *
* Overridable proc that's called when removed from a parent * Overridable proc that's called when removed from a parent
* * * *
*/ */
/datum/component/proc/UnregisterFromParent() /datum/component/proc/UnregisterFromParent()
return return
/** /**
* Register to listen for a signal from the passed in target * Called when the component has a new source registered.
* * Return COMPONENT_INCOMPATIBLE to signal that the source is incompatible and should not be added
* This sets up a listening relationship such that when the target object emits a signal */
* the source datum this proc is called upon, will recieve a callback to the given proctype /datum/component/proc/on_source_add(source, ...)
* Return values from procs registered must be a bitfield SHOULD_CALL_PARENT(TRUE)
* if(dupe_mode != COMPONENT_DUPE_SOURCES)
* Arguments: return COMPONENT_INCOMPATIBLE
* * datum/target The target to listen for signals from LAZYOR(sources, source)
* * sig_type_or_types Either a string signal name, or a list of signal names (strings)
* * proctype The proc to call back when the signal is emitted
* * override If a previous registration exists you must explicitly set this
*/
/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
return
var/list/procs = signal_procs
if(!procs)
signal_procs = procs = list()
if(!procs[target])
procs[target] = list()
var/list/lookup = target.comp_lookup
if(!lookup)
target.comp_lookup = lookup = list()
var/list/sig_types = islist(sig_type_or_types) ? sig_type_or_types : list(sig_type_or_types)
for(var/sig_type in sig_types)
if(!override && procs[target][sig_type])
var/trace_msg = "[sig_type] overridden. Use override = TRUE to suppress this warning."
if(isatom(target))
var/atom/A = target
trace_msg += " [A.x],[A.y],[A.z]"
stack_trace(trace_msg)
procs[target][sig_type] = proctype
if(!lookup[sig_type]) // Nothing has registered here yet
lookup[sig_type] = src
else if(lookup[sig_type] == src) // We already registered here
continue
else if(!length(lookup[sig_type])) // One other thing registered here
lookup[sig_type] = list(lookup[sig_type]=TRUE)
lookup[sig_type][src] = TRUE
else // Many other things have registered here
lookup[sig_type][src] = TRUE
signal_enabled = TRUE
//CHOMPAdd start
/// Registers multiple signals to the same proc.
/datum/proc/RegisterSignals(datum/target, list/signal_types, proctype, override = FALSE)
for (var/signal_type in signal_types)
RegisterSignal(target, signal_type, proctype, override)
//CHOMPAdd end
/** /**
* Stop listening to a given signal from target * Called when the component has a source removed.
* * You probably want to call parent after you do your logic because at the end of this we qdel if we have no sources remaining!
* Breaks the relationship between target and source datum, removing the callback when the signal fires */
* /datum/component/proc/on_source_remove(source)
* Doesn't care if a registration exists or not SHOULD_CALL_PARENT(TRUE)
* if(dupe_mode != COMPONENT_DUPE_SOURCES)
* Arguments: CRASH("Component '[type]' does not use sources but is trying to remove a source")
* * datum/target Datum to stop listening to signals from LAZYREMOVE(sources, source)
* * sig_type_or_types Signal string key or list of signal keys to stop listening to specifically if(!LAZYLEN(sources))
*/ qdel(src)
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
var/list/lookup = target?.comp_lookup
if(!signal_procs || !signal_procs[target] || !lookup)
return
if(!islist(sig_type_or_types))
sig_type_or_types = list(sig_type_or_types)
for(var/sig in sig_type_or_types)
if(!signal_procs[target][sig])
continue
switch(length(lookup[sig]))
if(2)
lookup[sig] = (lookup[sig]-src)[1]
if(1)
stack_trace("[target] ([target.type]) somehow has single length list inside comp_lookup")
if(src in lookup[sig])
lookup -= sig
if(!length(lookup))
target.comp_lookup = null
break
if(0)
lookup -= sig
if(!length(lookup))
target.comp_lookup = null
break
else
lookup[sig] -= src
signal_procs[target] -= sig_type_or_types
if(!signal_procs[target].len)
signal_procs -= target
/** /**
* Called on a component when a component of the same type was added to the same parent * Called on a component when a component of the same type was added to the same parent
* *
* See [/datum/component/var/dupe_mode] * See [/datum/component/var/dupe_mode]
* *
* `C`'s type will always be the same of the called component * `C`'s type will always be the same of the called component
*/ */
/datum/component/proc/InheritComponent(datum/component/C, i_am_original) /datum/component/proc/InheritComponent(datum/component/C, i_am_original)
return return
/** /**
* Called on a component when a component of the same type was added to the same parent with [COMPONENT_DUPE_SELECTIVE] * Called on a component when a component of the same type was added to the same parent with [COMPONENT_DUPE_SELECTIVE]
* *
* See [/datum/component/var/dupe_mode] * See [/datum/component/var/dupe_mode]
* *
* `C`'s type will always be the same of the called component * `C`'s type will always be the same of the called component
* *
* return TRUE if you are absorbing the component, otherwise FALSE if you are fine having it exist as a duplicate component * return TRUE if you are absorbing the component, otherwise FALSE if you are fine having it exist as a duplicate component
*/ */
/datum/component/proc/CheckDupeComponent(datum/component/C, ...) /datum/component/proc/CheckDupeComponent(datum/component/C, ...)
return return
/** /**
* Callback Just before this component is transferred * Callback Just before this component is transferred
* *
* Use this to do any special cleanup you might need to do before being deregged from an object * Use this to do any special cleanup you might need to do before being deregged from an object
*/ */
/datum/component/proc/PreTransfer() /datum/component/proc/PreTransfer()
return return
/** /**
* Callback Just after a component is transferred * Callback Just after a component is transferred
* *
* Use this to do any special setup you need to do after being moved to a new object * Use this to do any special setup you need to do after being moved to a new object
* *
* Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead * Do not call `qdel(src)` from this function, `return COMPONENT_INCOMPATIBLE` instead
*/ */
/datum/component/proc/PostTransfer() /datum/component/proc/PostTransfer()
return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it return COMPONENT_INCOMPATIBLE //Do not support transfer by default as you must properly support it
/** /**
* Internal proc to create a list of our type and all parent types * Internal proc to create a list of our type and all parent types
*/ */
/datum/component/proc/_GetInverseTypeList(our_type = type) /datum/component/proc/_GetInverseTypeList(our_type = type)
//we can do this one simple trick //we can do this one simple trick
. = list(our_type) . = list(our_type)
@@ -311,42 +242,20 @@
. += current_type . += current_type
current_type = type2parent(current_type) current_type = type2parent(current_type)
/**
* Internal proc to handle most all of the signaling procedure
*
* Will runtime if used on datums with an empty component list
*
* Use the [SEND_SIGNAL] define instead
*/
/datum/proc/_SendSignal(sigtype, list/arguments)
var/target = comp_lookup[sigtype]
if(!length(target))
var/datum/C = target
if(!C.signal_enabled)
return NONE
var/proctype = C.signal_procs[src][sigtype]
return NONE | CallAsync(C, proctype, arguments)
. = NONE
for(var/datum/C as anything in target)
if(!C.signal_enabled)
continue
var/proctype = C.signal_procs[src][sigtype]
. |= CallAsync(C, proctype, arguments)
// The type arg is casted so initial works, you shouldn't be passing a real instance into this // The type arg is casted so initial works, you shouldn't be passing a real instance into this
/** /**
* Return any component assigned to this datum of the given type * Return any component assigned to this datum of the given type
* *
* This will throw an error if it's possible to have more than one component of that type on the parent * This will throw an error if it's possible to have more than one component of that type on the parent
* *
* Arguments: * Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to * * datum/component/c_type The typepath of the component you want to get a reference to
*/ */
/datum/proc/GetComponent(datum/component/c_type) /datum/proc/GetComponent(datum/component/c_type)
RETURN_TYPE(c_type) RETURN_TYPE(c_type)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE) if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]") stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components var/list/dc = _datum_components
if(!dc) if(!dc)
return null return null
. = dc[c_type] . = dc[c_type]
@@ -355,18 +264,18 @@
// The type arg is casted so initial works, you shouldn't be passing a real instance into this // The type arg is casted so initial works, you shouldn't be passing a real instance into this
/** /**
* Return any component assigned to this datum of the exact given type * Return any component assigned to this datum of the exact given type
* *
* This will throw an error if it's possible to have more than one component of that type on the parent * This will throw an error if it's possible to have more than one component of that type on the parent
* *
* Arguments: * Arguments:
* * datum/component/c_type The typepath of the component you want to get a reference to * * datum/component/c_type The typepath of the component you want to get a reference to
*/ */
/datum/proc/GetExactComponent(datum/component/c_type) /datum/proc/GetExactComponent(datum/component/c_type)
RETURN_TYPE(c_type) RETURN_TYPE(c_type)
if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE) if(initial(c_type.dupe_mode) == COMPONENT_DUPE_ALLOWED || initial(c_type.dupe_mode) == COMPONENT_DUPE_SELECTIVE)
stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]") stack_trace("GetComponent was called to get a component of which multiple copies could be on an object. This can easily break and should be changed. Type: \[[c_type]\]")
var/list/dc = datum_components var/list/dc = _datum_components
if(!dc) if(!dc)
return null return null
var/datum/component/C = dc[c_type] var/datum/component/C = dc[c_type]
@@ -378,113 +287,154 @@
return null return null
/** /**
* Get all components of a given type that are attached to this datum * Get all components of a given type that are attached to this datum
* *
* Arguments: * Arguments:
* * c_type The component type path * * c_type The component type path
*/ */
/datum/proc/GetComponents(c_type) /datum/proc/GetComponents(c_type)
var/list/dc = datum_components var/list/components = _datum_components?[c_type]
if(!dc) if(!components)
return null return list()
. = dc[c_type] return islist(components) ? components : list(components)
if(!length(.))
return list(.)
/** /**
* Creates an instance of `new_type` in the datum and attaches to it as parent * Creates an instance of `new_type` in the datum and attaches to it as parent
* *
* Sends the [COMSIG_COMPONENT_ADDED] signal to the datum * Sends the [COMSIG_COMPONENT_ADDED] signal to the datum
* *
* Returns the component that was created. Or the old component in a dupe situation where [COMPONENT_DUPE_UNIQUE] was set * Returns the component that was created. Or the old component in a dupe situation where [COMPONENT_DUPE_UNIQUE] was set
* *
* If this tries to add a component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it * If this tries to add a component to an incompatible type, the component will be deleted and the result will be `null`. This is very unperformant, try not to do it
* *
* Properly handles duplicate situations based on the `dupe_mode` var * Properly handles duplicate situations based on the `dupe_mode` var
*/ */
/datum/proc/_AddComponent(list/raw_args) /datum/proc/_AddComponent(list/raw_args, source)
var/new_type = raw_args[1] var/original_type = raw_args[1]
var/datum/component/nt = new_type var/datum/component/component_type = original_type
var/dm = initial(nt.dupe_mode)
var/dt = initial(nt.dupe_type)
var/datum/component/old_comp if(QDELING(src))
var/datum/component/new_comp CRASH("Attempted to add a new component of type \[[component_type]\] to a qdeleting parent of type \[[type]\]!")
if(ispath(nt)) var/datum/component/new_component
if(nt == /datum/component)
CRASH("[nt] attempted instantiation!") if(!ispath(component_type, /datum/component))
else if(!istype(component_type, /datum/component))
new_comp = nt CRASH("Attempted to instantiate \[[component_type]\] as a component added to parent of type \[[type]\]!")
nt = new_comp.type else
new_component = component_type
component_type = new_component.type
else if(component_type == /datum/component)
CRASH("[component_type] attempted instantiation!")
var/dupe_mode = initial(component_type.dupe_mode)
var/dupe_type = initial(component_type.dupe_type)
var/uses_sources = (dupe_mode == COMPONENT_DUPE_SOURCES)
if(uses_sources && !source)
CRASH("Attempted to add a sourced component of type '[component_type]' to '[type]' without a source!")
else if(!uses_sources && source)
CRASH("Attempted to add a normal component of type '[component_type]' to '[type]' with a source!")
var/datum/component/old_component
raw_args[1] = src raw_args[1] = src
if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES)
if(dm != COMPONENT_DUPE_ALLOWED) if(!dupe_type)
if(!dt) old_component = GetExactComponent(component_type)
old_comp = GetExactComponent(nt)
else else
old_comp = GetComponent(dt) old_component = GetComponent(dupe_type)
if(old_comp)
switch(dm) if(old_component)
switch(dupe_mode)
if(COMPONENT_DUPE_UNIQUE) if(COMPONENT_DUPE_UNIQUE)
if(!new_comp) if(!new_component)
new_comp = new nt(raw_args) new_component = new component_type(raw_args)
if(!QDELETED(new_comp)) if(!QDELETED(new_component))
old_comp.InheritComponent(new_comp, TRUE) old_component.InheritComponent(new_component, TRUE)
QDEL_NULL(new_comp) QDEL_NULL(new_component)
if(COMPONENT_DUPE_HIGHLANDER) if(COMPONENT_DUPE_HIGHLANDER)
if(!new_comp) if(!new_component)
new_comp = new nt(raw_args) new_component = new component_type(raw_args)
if(!QDELETED(new_comp)) if(!QDELETED(new_component))
new_comp.InheritComponent(old_comp, FALSE) new_component.InheritComponent(old_component, FALSE)
QDEL_NULL(old_comp) QDEL_NULL(old_component)
if(COMPONENT_DUPE_UNIQUE_PASSARGS) if(COMPONENT_DUPE_UNIQUE_PASSARGS)
if(!new_comp) if(!new_component)
var/list/arguments = raw_args.Copy(2) var/list/arguments = raw_args.Copy(2)
arguments.Insert(1, null, TRUE) arguments.Insert(1, null, TRUE)
old_comp.InheritComponent(arglist(arguments)) old_component.InheritComponent(arglist(arguments))
else else
old_comp.InheritComponent(new_comp, TRUE) old_component.InheritComponent(new_component, TRUE)
if(COMPONENT_DUPE_SELECTIVE)
var/list/arguments = raw_args.Copy()
arguments[1] = new_comp
var/make_new_component = TRUE
for(var/datum/component/C as anything in GetComponents(new_type))
if(C.CheckDupeComponent(arglist(arguments)))
make_new_component = FALSE
QDEL_NULL(new_comp)
break
if(!new_comp && make_new_component)
new_comp = new nt(raw_args)
else if(!new_comp)
new_comp = new nt(raw_args) // There's a valid dupe mode but there's no old component, act like normal
else if(!new_comp)
new_comp = new nt(raw_args) // Dupes are allowed, act like normal
if(!old_comp && !QDELETED(new_comp)) // Nothing related to duplicate components happened and the new component is healthy if(COMPONENT_DUPE_SOURCES)
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp) if(source in old_component.sources)
return new_comp return old_component // source already registered, no work to do
return old_comp
if(old_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE)
stack_trace("incompatible source added to a [old_component.type]. Args: [json_encode(raw_args)]")
return null
else if(!new_component)
new_component = new component_type(raw_args) // There's a valid dupe mode but there's no old component, act like normal
else if(dupe_mode == COMPONENT_DUPE_SELECTIVE)
var/list/arguments = raw_args.Copy()
arguments[1] = new_component
var/make_new_component = TRUE
for(var/datum/component/existing_component as anything in GetComponents(original_type))
if(existing_component.CheckDupeComponent(arglist(arguments)))
make_new_component = FALSE
QDEL_NULL(new_component)
break
if(!new_component && make_new_component)
new_component = new component_type(raw_args)
else if(dupe_mode == COMPONENT_DUPE_SOURCES)
new_component = new component_type(raw_args)
if(new_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE)
stack_trace("incompatible source added to a [new_component.type]. Args: [json_encode(raw_args)]")
return null
else if(!new_component)
new_component = new component_type(raw_args) // Dupes are allowed, act like normal
if(!old_component && !QDELETED(new_component)) // Nothing related to duplicate components happened and the new component is healthy
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_component)
return new_component
return old_component
/** /**
* Get existing component of type, or create it and return a reference to it * Removes a component source from this datum
* */
* Use this if the item needs to exist at the time of this call, but may not have been created before now /datum/proc/RemoveComponentSource(source, datum/component/component_type)
* if(ispath(component_type))
* Arguments: component_type = GetExactComponent(component_type)
* * component_type The typepath of the component to create or return if(!component_type)
* * ... additional arguments to be passed when creating the component if it does not exist return
*/ component_type.on_source_remove(source)
/datum/proc/LoadComponent(component_type, ...)
. = GetComponent(component_type) /**
* Get existing component of type, or create it and return a reference to it
*
* Use this if the item needs to exist at the time of this call, but may not have been created before now
*
* Arguments:
* * component_type The typepath of the component to create or return
* * ... additional arguments to be passed when creating the component if it does not exist
*/
/datum/proc/_LoadComponent(list/arguments)
. = GetComponent(arguments[1])
if(!.) if(!.)
return _AddComponent(args) return _AddComponent(arguments)
/** /**
* Removes the component from parent, ends up with a null parent * Removes the component from parent, ends up with a null parent
*/ * Used as a helper proc by the component transfer proc, does not clean up the component like Destroy does
/datum/component/proc/RemoveComponent() */
/datum/component/proc/ClearFromParent()
if(!parent) if(!parent)
return return
var/datum/old_parent = parent var/datum/old_parent = parent
@@ -494,18 +444,18 @@
SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src) SEND_SIGNAL(old_parent, COMSIG_COMPONENT_REMOVING, src)
/** /**
* Transfer this component to another parent * Transfer this component to another parent
* *
* Component is taken from source datum * Component is taken from source datum
* *
* Arguments: * Arguments:
* * datum/component/target Target datum to transfer to * * datum/component/target Target datum to transfer to
*/ */
/datum/proc/TakeComponent(datum/component/target) /datum/proc/TakeComponent(datum/component/target)
if(!target || target.parent == src) if(!target || target.parent == src)
return return
if(target.parent) if(target.parent)
target.RemoveComponent() target.ClearFromParent()
target.parent = src target.parent = src
var/result = target.PostTransfer() var/result = target.PostTransfer()
switch(result) switch(result)
@@ -518,29 +468,30 @@
target._JoinParent() target._JoinParent()
/** /**
* Transfer all components to target * Transfer all components to target
* *
* All components from source datum are taken * All components from source datum are taken
* *
* Arguments: * Arguments:
* * /datum/target the target to move the components to * * /datum/target the target to move the components to
*/ */
/datum/proc/TransferComponents(datum/target) /datum/proc/TransferComponents(datum/target)
var/list/dc = datum_components var/list/dc = _datum_components
if(!dc) if(!dc)
return return
var/comps = dc[/datum/component] for(var/component_key in dc)
if(islist(comps)) var/component_or_list = dc[component_key]
for(var/datum/component/I in comps) if(islist(component_or_list))
if(I.can_transfer) for(var/datum/component/I in component_or_list)
target.TakeComponent(I) if(I.can_transfer)
else target.TakeComponent(I)
var/datum/component/C = comps else
if(C.can_transfer) var/datum/component/C = component_or_list
target.TakeComponent(comps) if(C.can_transfer)
target.TakeComponent(C)
/** /**
* Return the object that is the host of any UI's that this component has * Return the object that is the host of any UI's that this component has
*/ */
/datum/component/tgui_host() /datum/component/tgui_host()
return parent return parent

View File

@@ -27,17 +27,17 @@
/** /**
* Components attached to this datum * Components attached to this datum
* *
* Lazy associated list in the structure of `type:component/list of components` * Lazy associated list in the structure of `type -> component/list of components`
*/ */
var/list/datum_components var/list/_datum_components
/** /**
* Any datum registered to receive signals from this datum is in this list * 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` * Lazy associated list in the structure of `signal -> registree/list of registrees`
*/ */
var/list/comp_lookup var/list/_listen_lookup
var/list/list/signal_procs // List of lists /// Lazy associated list in the structure of `target -> list(signal -> proctype)` that are run when the datum receives that signal
var/signal_enabled = FALSE var/list/list/_signal_procs
/// Datum level flags /// Datum level flags
var/datum_flags = NONE var/datum_flags = NONE
@@ -72,32 +72,47 @@
// This should be overridden to remove all references pointing to the object being destroyed. // This should be overridden to remove all references pointing to the object being destroyed.
// Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE. // Return the appropriate QDEL_HINT; in most cases this is QDEL_HINT_QUEUE.
/datum/proc/Destroy(force=FALSE) /datum/proc/Destroy(force=FALSE)
SHOULD_CALL_PARENT(TRUE)
// SHOULD_NOT_SLEEP(TRUE) FIXME: Causing some big issues still
tag = null
weak_reference = null //ensure prompt GCing of weakref.
//clear timers if(active_timers)
var/list/timers = active_timers var/list/timers = active_timers
active_timers = null active_timers = null
for(var/datum/timedevent/timer as anything in timers) for(var/datum/timedevent/timer as anything in timers)
if (timer.spent) if (timer.spent)
continue continue
qdel(timer) qdel(timer)
weak_reference = null // Clear this reference to ensure it's kept for as brief duration as possible. #ifdef REFERENCE_TRACKING
#ifdef REFERENCE_TRACKING_DEBUG
found_refs = null
#endif
#endif
//BEGIN: ECS SHIT //BEGIN: ECS SHIT
signal_enabled = FALSE var/list/dc = _datum_components
var/list/dc = datum_components
if(dc) if(dc)
var/all_components = dc[/datum/component] for(var/component_key in dc)
if(length(all_components)) var/component_or_list = dc[component_key]
for(var/datum/component/C as anything in all_components) if(islist(component_or_list))
qdel(C, FALSE, TRUE) for(var/datum/component/component as anything in component_or_list)
else qdel(component, FALSE)
var/datum/component/C = all_components else
qdel(C, FALSE, TRUE) var/datum/component/C = component_or_list
qdel(C, FALSE)
dc.Cut() dc.Cut()
var/list/lookup = comp_lookup _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 = _listen_lookup
if(lookup) if(lookup)
for(var/sig in lookup) for(var/sig in lookup)
var/list/comps = lookup[sig] var/list/comps = lookup[sig]
@@ -107,15 +122,10 @@
else else
var/datum/component/comp = comps var/datum/component/comp = comps
comp.UnregisterSignal(src, sig) comp.UnregisterSignal(src, sig)
comp_lookup = lookup = null _listen_lookup = lookup = null
for(var/target in signal_procs) for(var/target in _signal_procs)
UnregisterSignal(target, signal_procs[target]) UnregisterSignal(target, _signal_procs[target])
//END: ECS SHIT
tag = null
SStgui.close_uis(src)
return QDEL_HINT_QUEUE
/** /**
* Callback called by a timer to end an associative-list-indexed cooldown. * Callback called by a timer to end an associative-list-indexed cooldown.

View File

@@ -0,0 +1,25 @@
# Template file for your new element
See _element.dm for detailed explanations
```dm
/datum/element/myelement
element_flags = ELEMENT_BESPOKE | ELEMENT_COMPLEX_DETACH | ELEMENT_DETACH_ON_HOST_DESTROY | ELEMENT_NOTAREALFLAG // code/__DEFINES/dcs/flags.dm
//argument_hash_start_idx = 2 // Use with ELEMENT_BESPOKE
var/list/myvar = list()
/datum/element/myelement/Attach(datum/target)
if(!ismovable(target))
return COMPONENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_MOVED, myproc)
to_chat(target, "Hey, you're in your element.")
/datum/element/myelement/Detach(datum/source)
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
to_chat(source, "You feel way out of your element.")
/datum/element/myelement/proc/myproc(datum/source)
SIGNAL_HANDLER
playsound(source, 'sound/effects/gong.ogg', 50, TRUE)
```

View File

@@ -10,11 +10,14 @@
/** /**
* The index of the first attach argument to consider for duplicate elements * The index of the first attach argument to consider for duplicate elements
* *
* All arguments from this index onwards (1 based) are hashed into the key to determine
* if this is a new unique element or one already exists
*
* Is only used when flags contains [ELEMENT_BESPOKE] * Is only used when flags contains [ELEMENT_BESPOKE]
* *
* This is infinity so you must explicitly set this * This is infinity so you must explicitly set this
*/ */
var/id_arg_index = INFINITY var/argument_hash_start_idx = INFINITY
/// Activates the functionality defined by the element on the given target datum /// Activates the functionality defined by the element on the given target datum
/datum/element/proc/Attach(datum/target) /datum/element/proc/Attach(datum/target)
@@ -22,19 +25,19 @@
if(type == /datum/element) if(type == /datum/element)
return ELEMENT_INCOMPATIBLE return ELEMENT_INCOMPATIBLE
SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src) SEND_SIGNAL(target, COMSIG_ELEMENT_ATTACH, src)
if(element_flags & ELEMENT_DETACH) if(element_flags & ELEMENT_DETACH_ON_HOST_DESTROY)
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(OnTargetDelete), override = TRUE) RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(OnTargetDelete), override = TRUE)
/datum/element/proc/OnTargetDelete(datum/source, force) /datum/element/proc/OnTargetDelete(datum/source)
SIGNAL_HANDLER SIGNAL_HANDLER
Detach(source) Detach(source)
/// Deactivates the functionality defines by the element on the given datum /// Deactivates the functionality defines by the element on the given datum
/datum/element/proc/Detach(datum/source, ...) /datum/element/proc/Detach(datum/source, ...)
SIGNAL_HANDLER SIGNAL_HANDLER
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src) SEND_SIGNAL(source, COMSIG_ELEMENT_DETACH, src)
SHOULD_CALL_PARENT(TRUE)
UnregisterSignal(source, COMSIG_PARENT_QDELETING) UnregisterSignal(source, COMSIG_PARENT_QDELETING)
/datum/element/Destroy(force) /datum/element/Destroy(force)
@@ -48,20 +51,53 @@
/// Finds the singleton for the element type given and attaches it to src /// Finds the singleton for the element type given and attaches it to src
/datum/proc/_AddElement(list/arguments) /datum/proc/_AddElement(list/arguments)
if(QDELING(src)) if(QDELING(src))
CRASH("We just tried to add an element to a qdeleted datum, something is fucked") var/datum/element/element_type = arguments[1]
stack_trace("We just tried to add the element [element_type] to a qdeleted datum, something is fucked")
return
var/datum/element/ele = SSdcs.GetElement(arguments) var/datum/element/ele = SSdcs.GetElement(arguments)
if(!ele) // We couldn't fetch the element, likely because it was not an element.
return // the crash message has already been sent
arguments[1] = src arguments[1] = src
if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE) if(ele.Attach(arglist(arguments)) == ELEMENT_INCOMPATIBLE)
CRASH("Incompatible [arguments[1]] assigned to a [type]! args: [json_encode(args)]") CRASH("Incompatible element [ele.type] was assigned to a [type]! args: [json_encode(args)]")
/** /**
* Finds the singleton for the element type given and detaches it from src * Finds the singleton for the element type given and detaches it from src
* You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE] * You only need additional arguments beyond the type if you're using [ELEMENT_BESPOKE]
*/ */
/datum/proc/_RemoveElement(list/arguments) /datum/proc/_RemoveElement(list/arguments)
var/datum/element/ele = SSdcs.GetElement(arguments) var/datum/element/ele = SSdcs.GetElement(arguments, FALSE)
if(!ele) // We couldn't fetch the element, likely because it didn't exist.
return
if(ele.element_flags & ELEMENT_COMPLEX_DETACH) if(ele.element_flags & ELEMENT_COMPLEX_DETACH)
arguments[1] = src arguments[1] = src
ele.Detach(arglist(arguments)) ele.Detach(arglist(arguments))
else else
ele.Detach(src) ele.Detach(src)
/**
* Used to manage (typically non_bespoke) elements with multiple sources through traits
* so we don't have to make them a components again.
* The element will be later removed once all trait sources are gone, there's no need of a
* "RemoveElementTrait" counterpart.
*/
/datum/proc/AddElementTrait(trait, source, datum/element/eletype, ...)
if(!ispath(eletype, /datum/element))
CRASH("AddElementTrait called, but [eletype] is not of a /datum/element path")
ADD_TRAIT(src, trait, source)
if(HAS_TRAIT_NOT_FROM(src, trait, source))
return
var/list/arguments = list(eletype)
/// 3 is the length of fixed args of this proc, any further one is passed down to AddElement.
if(length(args) > 3)
arguments += args.Copy(4)
/// We actually pass down a copy of the arguments since it's manipulated by the end of the proc.
_AddElement(arguments.Copy())
var/datum/ele = SSdcs.GetElement(arguments)
ele.RegisterSignal(src, SIGNAL_REMOVETRAIT(trait), TYPE_PROC_REF(/datum/element, _detach_on_trait_removed))
/datum/element/proc/_detach_on_trait_removed(datum/source, trait)
SIGNAL_HANDLER
Detach(source)
UnregisterSignal(source, SIGNAL_REMOVETRAIT(trait))

View File

@@ -2,8 +2,8 @@
* Simple conflict checking for getting number of conflicting things on someone with the same ID. * Simple conflict checking for getting number of conflicting things on someone with the same ID.
*/ */
/datum/element/conflict_checking /datum/element/conflict_checking
element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH element_flags = ELEMENT_BESPOKE | ELEMENT_DETACH_ON_HOST_DESTROY
id_arg_index = 1 argument_hash_start_idx = 1
/// we don't need to KNOW who has us, only our ID. /// we don't need to KNOW who has us, only our ID.
var/id var/id

View File

@@ -2,7 +2,7 @@
* Attached to movable atoms with opacity. Listens to them move and updates their old and new turf loc's opacity accordingly. * Attached to movable atoms with opacity. Listens to them move and updates their old and new turf loc's opacity accordingly.
*/ */
/datum/element/light_blocking /datum/element/light_blocking
element_flags = ELEMENT_DETACH element_flags = ELEMENT_DETACH_ON_HOST_DESTROY
/datum/element/light_blocking/Attach(datum/target) /datum/element/light_blocking/Attach(datum/target)

127
code/datums/signals.dm Normal file
View File

@@ -0,0 +1,127 @@
/**
* Register to listen for a signal from the passed in target
*
* This sets up a listening relationship such that when the target object emits a signal
* the source datum this proc is called upon, will receive a callback to the given proctype
* Use PROC_REF(procname), TYPE_PROC_REF(type,procname) or GLOBAL_PROC_REF(procname) macros to validate the passed in proc at compile time.
* PROC_REF for procs defined on current type or it's ancestors, TYPE_PROC_REF for procs defined on unrelated type and GLOBAL_PROC_REF for global procs.
* Return values from procs registered must be a bitfield
*
* Arguments:
* * datum/target The target to listen for signals from
* * signal_type A signal name
* * proctype The proc to call back when the signal is emitted
* * override If a previous registration exists you must explicitly set this
*/
/datum/proc/RegisterSignal(datum/target, signal_type, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
return
if (islist(signal_type))
var/static/list/known_failures = list()
var/list/signal_type_list = signal_type
var/message = "([target.type]) is registering [signal_type_list.Join(", ")] as a list, the older method. Change it to RegisterSignals."
if (!(message in known_failures))
known_failures[message] = TRUE
stack_trace("[target] [message]")
RegisterSignals(target, signal_type, proctype, override)
return
var/list/procs = (_signal_procs ||= list())
var/list/target_procs = (procs[target] ||= list())
var/list/lookup = (target._listen_lookup ||= list())
var/exists = target_procs[signal_type]
target_procs[signal_type] = proctype
if(exists)
if(!override)
var/override_message = "[signal_type] overridden. Use override = TRUE to suppress this warning.\nTarget: [target] ([target.type]) Proc: [proctype]"
//log_signal(override_message) // We don't have log_signal
log_world(override_message)
stack_trace(override_message)
return
var/list/looked_up = lookup[signal_type]
if(isnull(looked_up)) // Nothing has registered here yet
lookup[signal_type] = src
else if(!islist(looked_up)) // One other thing registered here
lookup[signal_type] = list(looked_up, src)
else // Many other things have registered here
looked_up += src
/// Registers multiple signals to the same proc.
/datum/proc/RegisterSignals(datum/target, list/signal_types, proctype, override = FALSE)
for (var/signal_type in signal_types)
RegisterSignal(target, signal_type, proctype, override)
/**
* Stop listening to a given signal from target
*
* Breaks the relationship between target and source datum, removing the callback when the signal fires
*
* Doesn't care if a registration exists or not
*
* Arguments:
* * datum/target Datum to stop listening to signals from
* * sig_typeor_types Signal string key or list of signal keys to stop listening to specifically
*/
/datum/proc/UnregisterSignal(datum/target, sig_type_or_types)
var/list/lookup = target._listen_lookup
if(!_signal_procs || !_signal_procs[target] || !lookup)
return
if(!islist(sig_type_or_types))
sig_type_or_types = list(sig_type_or_types)
for(var/sig in sig_type_or_types)
if(!_signal_procs[target][sig])
if(!istext(sig))
stack_trace("We're unregistering with something that isn't a valid signal \[[sig]\], you fucked up")
continue
switch(length(lookup[sig]))
if(2)
lookup[sig] = (lookup[sig]-src)[1]
if(1)
stack_trace("[target] ([target.type]) somehow has single length list inside _listen_lookup")
if(src in lookup[sig])
lookup -= sig
if(!length(lookup))
target._listen_lookup = null
break
if(0)
if(lookup[sig] != src)
continue
lookup -= sig
if(!length(lookup))
target._listen_lookup = null
break
else
lookup[sig] -= src
_signal_procs[target] -= sig_type_or_types
if(!_signal_procs[target].len)
_signal_procs -= target
/**
* Internal proc to handle most all of the signaling procedure
*
* Will runtime if used on datums with an empty lookup list
*
* Use the [SEND_SIGNAL] define instead
*/
/datum/proc/_SendSignal(sigtype, list/arguments)
var/target = _listen_lookup[sigtype]
if(!length(target))
var/datum/listening_datum = target
return NONE | call(listening_datum, listening_datum._signal_procs[src][sigtype])(arglist(arguments))
. = NONE
// This exists so that even if one of the signal receivers unregisters the signal,
// all the objects that are receiving the signal get the signal this final time.
// AKA: No you can't cancel the signal reception of another object by doing an unregister in the same signal.
var/list/queued_calls = list()
for(var/datum/listening_datum as anything in target)
queued_calls[listening_datum] = listening_datum._signal_procs[src][sigtype]
for(var/datum/listening_datum as anything in queued_calls)
. |= call(listening_datum, queued_calls[listening_datum])(arglist(arguments))

View File

@@ -49,6 +49,7 @@
/obj/machinery/exonet_node/Destroy() // CHOMPAdd: Just in case. /obj/machinery/exonet_node/Destroy() // CHOMPAdd: Just in case.
QDEL_NULL(soundloop) // CHOMPAdd: Exonet noises QDEL_NULL(soundloop) // CHOMPAdd: Exonet noises
return ..()
// Proc: update_icon() // Proc: update_icon()
// Parameters: None // Parameters: None

View File

@@ -869,6 +869,7 @@
/atom/movable/storage_slot/Destroy() /atom/movable/storage_slot/Destroy()
held_item = null held_item = null
..()
/// Has to be this way. The fact that the overlays will be constantly mutated by other storage means we can't wait. /// Has to be this way. The fact that the overlays will be constantly mutated by other storage means we can't wait.
/atom/movable/storage_slot/add_overlay(list/somethings) /atom/movable/storage_slot/add_overlay(list/somethings)

View File

@@ -70,7 +70,7 @@
// CHOMPStation Addition: Spaceman DMM Debugging // CHOMPStation Addition: Spaceman DMM Debugging
var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL") var/debug_server = world.GetConfig("env", "AUXTOOLS_DEBUG_DLL")
if (debug_server) if (debug_server)
call(debug_server, "auxtools_init")() LIBCALL(debug_server, "auxtools_init")()
enable_debugging() enable_debugging()
// CHOMPStation Add End // CHOMPStation Add End

View File

@@ -23,7 +23,7 @@ var/global/datum/global_init/init = new ()
debug_log = file("[log_path]-debug.log") debug_log = file("[log_path]-debug.log")
debug_log << "[log_end]\n[log_end]\nStarting up. [time_stamp()][log_end]\n---------------------[log_end]" debug_log << "[log_end]\n[log_end]\nStarting up. [time_stamp()][log_end]\n---------------------[log_end]"
*/ //VOREStation Removal End */ //VOREStation Removal End
decls_repository = new() decls_repository = new()
load_configuration() load_configuration()
makeDatumRefLists() makeDatumRefLists()
@@ -33,4 +33,5 @@ var/global/datum/global_init/init = new ()
/datum/global_init/Destroy() /datum/global_init/Destroy()
global.init = null global.init = null
..()
return 2 // QDEL_HINT_IWILLGC return 2 // QDEL_HINT_IWILLGC

View File

@@ -224,7 +224,8 @@
/mob/living/bot/secbot/resetTarget() /mob/living/bot/secbot/resetTarget()
..() ..()
UnregisterSignal(target, COMSIG_OBSERVER_MOVED) if(target)
UnregisterSignal(target, COMSIG_OBSERVER_MOVED)
awaiting_surrender = 0 awaiting_surrender = 0
attacked = FALSE attacked = FALSE
walk_to(src, 0) walk_to(src, 0)

View File

@@ -118,6 +118,7 @@
mind_ref = null mind_ref = null
limb_data.Cut() limb_data.Cut()
organ_data.Cut() organ_data.Cut()
..()
return QDEL_HINT_HARDDEL // For now at least there is no easy way to clear references to this in machines etc. return QDEL_HINT_HARDDEL // For now at least there is no easy way to clear references to this in machines etc.
/datum/transhuman/body_record/proc/init_from_mob(var/mob/living/carbon/human/M, var/add_to_db = 0, var/ckeylock = 0, var/database_key) /datum/transhuman/body_record/proc/init_from_mob(var/mob/living/carbon/human/M, var/add_to_db = 0, var/ckeylock = 0, var/database_key)

View File

@@ -51,7 +51,7 @@
. = ..() . = ..()
var/z = get_z(user) var/z = get_z(user)
var/list/map_levels = using_map.get_map_levels(z) var/list/map_levels = using_map.get_map_levels(z)
// TODO: Move these to a cache, similar to cameras // TODO: Move these to a cache, similar to cameras
var/alarms[0] var/alarms[0]
@@ -73,7 +73,7 @@
var/list/data = list() var/list/data = list()
var/z = get_z(user) var/z = get_z(user)
var/list/map_levels = using_map.get_map_levels(z) var/list/map_levels = using_map.get_map_levels(z)
data["map_levels"] = map_levels data["map_levels"] = map_levels
return data return data
@@ -106,10 +106,11 @@
/datum/tgui_state/air_alarm_remote/Destroy() /datum/tgui_state/air_alarm_remote/Destroy()
atmos_control = null atmos_control = null
air_alarm = null air_alarm = null
return ..()
/datum/tgui_module/atmos_control/ntos /datum/tgui_module/atmos_control/ntos
ntos = TRUE ntos = TRUE
/datum/tgui_module/atmos_control/robot /datum/tgui_module/atmos_control/robot
/datum/tgui_module/atmos_control/robot/tgui_state(mob/user) /datum/tgui_module/atmos_control/robot/tgui_state(mob/user)
return GLOB.tgui_self_state return GLOB.tgui_self_state

View File

@@ -16,7 +16,7 @@
/obj/machinery/artifact/Destroy() /obj/machinery/artifact/Destroy()
if(artifact_master) if(artifact_master)
var/datum/component/artifact_master/arti_mstr = artifact_master var/datum/component/artifact_master/arti_mstr = artifact_master
arti_mstr.RemoveComponent() arti_mstr.ClearFromParent()
artifact_master = null artifact_master = null
if(!QDELETED(arti_mstr)) if(!QDELETED(arti_mstr))
qdel(arti_mstr) qdel(arti_mstr)

View File

@@ -700,6 +700,7 @@
T.last_find = new_item T.last_find = new_item
qdel(src) qdel(src)
return
else if(talkative) else if(talkative)
src.talking_atom = new(src) src.talking_atom = new(src)
@@ -714,7 +715,7 @@
/obj/item/weapon/archaeological_find/Destroy() /obj/item/weapon/archaeological_find/Destroy()
if(src.is_anomalous()) if(src.is_anomalous())
var/datum/component/artifact_master/arti_mstr = GetComponent(/datum/component/artifact_master) var/datum/component/artifact_master/arti_mstr = GetComponent(/datum/component/artifact_master)
arti_mstr.RemoveComponent() arti_mstr.ClearFromParent()
if(!QDELETED(arti_mstr)) if(!QDELETED(arti_mstr))
qdel(arti_mstr) qdel(arti_mstr)

View File

@@ -125,6 +125,7 @@
#include "code\__defines\dcs\helpers.dm" #include "code\__defines\dcs\helpers.dm"
#include "code\__defines\dcs\signals.dm" #include "code\__defines\dcs\signals.dm"
#include "code\__defines\dcs\signals_ch.dm" #include "code\__defines\dcs\signals_ch.dm"
#include "code\__defines\traits\_traits.dm"
#include "code\_global_vars\_regexes.dm" #include "code\_global_vars\_regexes.dm"
#include "code\_global_vars\bitfields.dm" #include "code\_global_vars\bitfields.dm"
#include "code\_global_vars\logging_ch.dm" #include "code\_global_vars\logging_ch.dm"
@@ -375,6 +376,7 @@
#include "code\datums\progressbar.dm" #include "code\datums\progressbar.dm"
#include "code\datums\reference_tracking.dm" #include "code\datums\reference_tracking.dm"
#include "code\datums\riding.dm" #include "code\datums\riding.dm"
#include "code\datums\signals.dm"
#include "code\datums\soul_link.dm" #include "code\datums\soul_link.dm"
#include "code\datums\sun.dm" #include "code\datums\sun.dm"
#include "code\datums\weakrefs.dm" #include "code\datums\weakrefs.dm"