From 0df0569321cb711c5983104dfaec0781e1caa660 Mon Sep 17 00:00:00 2001 From: SkyratBot <59378654+SkyratBot@users.noreply.github.com> Date: Sun, 5 Mar 2023 04:55:32 +0100 Subject: [PATCH] [MIRROR] Sourced Component Backend [MDB IGNORE] (#19665) * Sourced Component Backend (#73571) ## About The Pull Request Adds a backend for sourced components. which was requested by @ Fikou Also cleans up a little bit of the AddComponent logic ## Why It's Good For The Game Backend for fancy stuff and having a component from multiple sources --------- Signed-off-by: GitHub Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com> * Sourced Component Backend --------- Signed-off-by: GitHub Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Co-authored-by: Mothblocks <35135081+Mothblocks@ users.noreply.github.com> --- code/__DEFINES/dcs/flags.dm | 7 ++ code/__DEFINES/dcs/helpers.dm | 4 + code/datums/components/_component.dm | 144 ++++++++++++++++++--------- 3 files changed, 108 insertions(+), 47 deletions(-) diff --git a/code/__DEFINES/dcs/flags.dm b/code/__DEFINES/dcs/flags.dm index fef738e4111..3f3db0212b6 100644 --- a/code/__DEFINES/dcs/flags.dm +++ b/code/__DEFINES/dcs/flags.dm @@ -30,6 +30,13 @@ #define COMPONENT_DUPE_ALLOWED 1 /// new component is deleted #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...) + * Only the first args will be respected, and you should instead handle most of your logic in the on_source_added proc. + * Removing the last source will automatically remove the component from the parent. + */ +#define COMPONENT_DUPE_SOURCES 3 /// old component is given the initialization args of the new #define COMPONENT_DUPE_UNIQUE_PASSARGS 4 /// each component of the same type is consulted as to whether the duplicate should be allowed diff --git a/code/__DEFINES/dcs/helpers.dm b/code/__DEFINES/dcs/helpers.dm index 56ea5370c4a..a8dc3865702 100644 --- a/code/__DEFINES/dcs/helpers.dm +++ b/code/__DEFINES/dcs/helpers.dm @@ -18,5 +18,9 @@ /// A wrapper for _AddComponent that allows us to pretend we're using normal named 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)) diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm index 4a812d4f5e5..8cbc4efd329 100644 --- a/code/datums/components/_component.dm +++ b/code/datums/components/_component.dm @@ -38,6 +38,9 @@ */ var/can_transfer = FALSE + /// A lazy list of the sources for this component + var/list/sources + /** * Create a new component. * @@ -165,6 +168,27 @@ /datum/component/proc/UnregisterFromParent() return +/** + * Called when the component has a new source registered + */ +/datum/component/proc/on_source_add(source) + SHOULD_CALL_PARENT(TRUE) + if(dupe_mode != COMPONENT_DUPE_SOURCES) + CRASH("Component '[type]' does not use sources but has been given a source") + LAZYOR(sources, source) + +/** + * 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! + */ +/datum/component/proc/on_source_remove(source) + SHOULD_CALL_PARENT(TRUE) + if(dupe_mode != COMPONENT_DUPE_SOURCES) + CRASH("Component '[type]' does not use sources but is trying to remove a source") + LAZYREMOVE(sources, source) + if(!LAZYLEN(sources)) + qdel(src) + /** * Register to listen for a signal from the passed in target * @@ -411,74 +435,100 @@ * * Properly handles duplicate situations based on the `dupe_mode` var */ -/datum/proc/_AddComponent(list/raw_args) - var/new_type = raw_args[1] - var/datum/component/nt = new_type +/datum/proc/_AddComponent(list/raw_args, source) + var/original_type = raw_args[1] + var/datum/component/component_type = original_type if(QDELING(src)) - CRASH("Attempted to add a new component of type \[[nt]\] to a qdeleting parent of type \[[type]\]!") + CRASH("Attempted to add a new component of type \[[component_type]\] to a qdeleting parent of type \[[type]\]!") - var/dm = initial(nt.dupe_mode) - var/dt = initial(nt.dupe_type) + 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_comp - var/datum/component/new_comp + var/datum/component/old_component + var/datum/component/new_component - if(ispath(nt)) - if(nt == /datum/component) - CRASH("[nt] attempted instantiation!") + if(ispath(component_type)) + if(component_type == /datum/component) + CRASH("[component_type] attempted instantiation!") else - new_comp = nt - nt = new_comp.type + new_component = component_type + component_type = new_component.type raw_args[1] = src - - if(dm != COMPONENT_DUPE_ALLOWED && dm != COMPONENT_DUPE_SELECTIVE) - if(!dt) - old_comp = GetExactComponent(nt) + if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE) + if(!dupe_type) + old_component = GetExactComponent(component_type) else - old_comp = GetComponent(dt) - if(old_comp) - switch(dm) + old_component = GetComponent(dupe_type) + + if(old_component) + switch(dupe_mode) if(COMPONENT_DUPE_UNIQUE) - if(!new_comp) - new_comp = new nt(raw_args) - if(!QDELETED(new_comp)) - old_comp.InheritComponent(new_comp, TRUE) - QDEL_NULL(new_comp) + if(!new_component) + new_component = new component_type(raw_args) + if(!QDELETED(new_component)) + old_component.InheritComponent(new_component, TRUE) + QDEL_NULL(new_component) + if(COMPONENT_DUPE_HIGHLANDER) - if(!new_comp) - new_comp = new nt(raw_args) - if(!QDELETED(new_comp)) - new_comp.InheritComponent(old_comp, FALSE) - QDEL_NULL(old_comp) + if(!new_component) + new_component = new component_type(raw_args) + if(!QDELETED(new_component)) + new_component.InheritComponent(old_component, FALSE) + QDEL_NULL(old_component) + if(COMPONENT_DUPE_UNIQUE_PASSARGS) - if(!new_comp) + if(!new_component) var/list/arguments = raw_args.Copy(2) arguments.Insert(1, null, TRUE) - old_comp.InheritComponent(arglist(arguments)) + old_component.InheritComponent(arglist(arguments)) else - old_comp.InheritComponent(new_comp, TRUE) - 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(dm == COMPONENT_DUPE_SELECTIVE) + old_component.InheritComponent(new_component, TRUE) + + if(COMPONENT_DUPE_SOURCES) + if(source in old_component.sources) + return old_component // source already registered, no work to do + old_component.on_source_add(source) + + 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_comp + arguments[1] = new_component var/make_new_component = TRUE - for(var/datum/component/existing_component as anything in GetComponents(new_type)) + 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_comp) + QDEL_NULL(new_component) break - if(!new_comp && make_new_component) - new_comp = new nt(raw_args) - else if(!new_comp) - new_comp = new nt(raw_args) // Dupes are allowed, act like normal + if(!new_component && make_new_component) + new_component = new component_type(raw_args) + else if(!new_component) + new_component = new component_type(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 - SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_comp) - return new_comp - return old_comp + if(!old_component && !QDELETED(new_component)) // Nothing related to duplicate components happened and the new component is healthy + if(uses_sources) // make sure they have the source added if they use sources + new_component.on_source_add(source) + SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_component) + return new_component + + return old_component + +/** + * Removes a component source from this datum + */ +/datum/proc/RemoveComponentSource(source, datum/component/component_type) + if(ispath(component_type)) + component_type = GetExactComponent(component_type) + if(!component_type) + CRASH("Attempted to remove a null or non-existent component '[component_type]' from '[type]'") + component_type.on_source_remove(source) /** * Get existing component of type, or create it and return a reference to it