diff --git a/evennia/contrib/base_systems/components/README.md b/evennia/contrib/base_systems/components/README.md index 0a7d0d01e5..aa3391687c 100644 --- a/evennia/contrib/base_systems/components/README.md +++ b/evennia/contrib/base_systems/components/README.md @@ -30,12 +30,20 @@ class Character(ComponentHolderMixin, DefaultCharacter): # ... ``` -Components need to inherit the Component class directly and require a name. +Components need to inherit the Component class and require a unique name. +Components may inherit from other components but must specify another name. +You can assign the same 'slot' to both components to have alternative implementations. ```python from evennia.contrib.base_systems.components import Component + class Health(Component): name = "health" + + +class ItemHealth(Health): + name = "item_health" + slot = "health" ``` Components may define DBFields or NDBFields at the class level. @@ -103,7 +111,10 @@ character.components.add(vampirism) ... -vampirism_from_elsewhere = character.components.get("vampirism") +vampirism = character.components.get("vampirism") + +# Alternatively +vampirism = character.cmp.vampirism ``` Keep in mind that all components must be imported to be visible in the listing. @@ -128,6 +139,14 @@ from typeclasses.components import health ``` Both of the above examples will work. +## Known Issues + +Assigning mutable default values such as a list to a DBField will share it across instances. +To avoid this, you must set autocreate=True on the field, like this. +```python +health = DBField(default=[], autocreate=True) +``` + ## Full Example ```python from evennia.contrib.base_systems import components diff --git a/evennia/contrib/base_systems/components/component.py b/evennia/contrib/base_systems/components/component.py index 68bd50e492..d2ea83fac5 100644 --- a/evennia/contrib/base_systems/components/component.py +++ b/evennia/contrib/base_systems/components/component.py @@ -9,8 +9,17 @@ from evennia.contrib.base_systems.components import COMPONENT_LISTING, exception class BaseComponent(type): + """ + This is the metaclass for components, + responsible for registering components to the listing. + """ @classmethod def __new__(cls, *args): + """ + Every class that uses this metaclass will be registered + as a component in the Component Listing using its name. + All of them require a unique name. + """ new_type = super().__new__(*args) if new_type.__base__ == object: return new_type diff --git a/evennia/contrib/base_systems/components/dbfield.py b/evennia/contrib/base_systems/components/dbfield.py index 6eaf0eceef..709548acf3 100644 --- a/evennia/contrib/base_systems/components/dbfield.py +++ b/evennia/contrib/base_systems/components/dbfield.py @@ -26,17 +26,31 @@ class DBField(AttributeProperty): Called when descriptor is first assigned to the class. Args: - owner (object): The component classF on which this is set + owner (Component): The component classF on which this is set name (str): The name that was used to set the DBField. """ self._key = f"{owner.slot or owner.name}::{name}" owner.add_field(name, self) def at_added(self, component): + """ + Called when the parent component is added to a host. + + Args: + component (Component): The component instance being added. + """ + if self._autocreate: self.__get__(component, type(component)) def at_removed(self, component): + """ + Called when the parent component is removed from a host. + + Args: + component (Component): The component instance being removed. + """ + self.__delete__(component) @@ -52,18 +66,30 @@ class NDBField(NAttributeProperty): Called when descriptor is first assigned to the class. Args: - owner (object): The component class on which this is set + owner (Component): The component class on which this is set name (str): The name that was used to set the DBField. """ self._key = f"{owner.slot or owner.name}::{name}" owner.add_field(name, self) - def at_added(self, instance): - if self._autocreate: - self.__set__(instance, self._default) + def at_added(self, component): + """ + Called when the parent component is added to a host. - def at_removed(self, instance): - self.__delete__(instance) + Args: + component (Component): The component instance being added. + """ + if self._autocreate: + self.__set__(component, self._default) + + def at_removed(self, component): + """ + Called when the parent component is removed from a host. + + Args: + component (Component): The component instance being removed. + """ + self.__delete__(component) class TagField: @@ -124,8 +150,20 @@ class TagField: instance.host.tags.clear(category=self._category_key) def at_added(self, component): + """ + Called when the parent component is added to a host. + + Args: + component (Component): The component instance being added. + """ if self._default: self.__set__(component, self._default) def at_removed(self, component): + """ + Called when the parent component is removed from a host. + + Args: + component (Component): The component instance being removed. + """ self.__delete__(component) diff --git a/evennia/contrib/base_systems/components/listing.py b/evennia/contrib/base_systems/components/listing.py index 4ca51842d5..11647e9425 100644 --- a/evennia/contrib/base_systems/components/listing.py +++ b/evennia/contrib/base_systems/components/listing.py @@ -4,6 +4,11 @@ COMPONENT_LISTING = {} def get_component_class(name): + """ + Retrieves a component from the listing using a name + Args: + name (str): The unique name of the component + """ component_class = COMPONENT_LISTING.get(name) if component_class is None: message = (