diff --git a/docs/1.0/.buildinfo b/docs/1.0/.buildinfo
index f125147e08..6fc3451f70 100644
--- a/docs/1.0/.buildinfo
+++ b/docs/1.0/.buildinfo
@@ -1,4 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: 96cd01a646747ff4753f61aa6f956ce1
+config: 8cf66312f2f25f2121a051ec1d1b1577
tags: 645f666f9bcd5a90fca523b33c5a78b7
diff --git a/docs/1.0/Coding/Changelog.html b/docs/1.0/Coding/Changelog.html
index 7a9bcc3987..23652fb0c3 100644
--- a/docs/1.0/Coding/Changelog.html
+++ b/docs/1.0/Coding/Changelog.html
@@ -173,11 +173,19 @@
Main branch
-Feature: Attribute-support for saving/loading deques with maxlen= set.
-Contrib: Container typeclass with new commands for storing and retrieving
+
New Contrib: Container typeclass with new commands for storing and retrieving
things inside them (InspectorCaracal)
+Feature: Add TagCategoryProperty for setting categories with multiple tags
+as properties directly on objects. Complements TagProperty.
+Feature: Attribute-support for saving/loading deques with maxlen= set.
+Feature: Refactor to provide evennia.SESSION_HANDLER for easier overloading
+and less risks of circular import problems (Volund)
+Fix: Allow webclient’s goldenlayout UI (default) to understand msg
+cls kwarg for customizing the CSS class for every resulting div (friarzen)
Fix: The AttributeHandler.all() now actually accepts category= as
keyword arg, like our docs already claimed it should (Volund)
+Fix: TickerHandler store key updating was refactored, fixing an issue with
+updating intervals (InspectorCaracal)
Docs: New Beginner-Tutorial lessons for NPCs, Base-Combat Twitch-Combat and
Turnbased-combat (note that the Beginner tutorial is still WIP).
diff --git a/docs/1.0/Components/Tags.html b/docs/1.0/Components/Tags.html
index 25a30def32..512b28eba6 100644
--- a/docs/1.0/Components/Tags.html
+++ b/docs/1.0/Components/Tags.html
@@ -126,16 +126,24 @@
-
In code, using TagProperty (auto-assign tag to all instances of the class)
+
In code, using TagProperty or TagCategoryProperty
from evennia import DefaultObject
-from evennia import TagProperty
+from evennia import TagProperty, TagCategoryProperty
+
class Sword(DefaultObject):
+ # name of property is the tagkey, category as argument
can_be_wielded = TagProperty(category='combat')
has_sharp_edge = TagProperty(category='combat')
+
+ # name of property is the category, tag-keys are arguments
+ damage_type = TagCategoryProperty("piercing", "slashing")
+ crafting_element = TagCategory("blade", "hilt", "pommel")
+
Tags are short text lables one can ‘hang’ on objects in order to organize, group and quickly find out their properties. An Evennia entity can be tagged by any number of tags. They are more efficient than Attributes since on the database-side, Tags are shared between all objects with that particular tag. A tag does not carry a value in itself; it either sits on the entity
+You manage Tags using the TagHandler (.tags) on typeclassed entities. You can also assign Tags on the class level through the TagProperty (one tag, one category per line) or the TagCategoryProperty (one category, multiple tags per line). Both of these use the TagHandler under the hood, they are just convenient ways to add tags already when you define your class.
Above, the tags inform us that the Sword is both sharp and can be wielded. If that’s all they do, they could just be a normal Python flag. When tags become important is if there are a lot of objects with different combinations of tags. Maybe you have a magical spell that dulls all sharp-edged objects in the castle - whether sword, dagger, spear or kitchen knife! You can then just grab all objects with the has_sharp_edge tag.
Another example would be a weather script affecting all rooms tagged as outdoors or finding all characters tagged with belongs_to_fighter_guild.
In Evennia, Tags are technically also used to implement Aliases (alternative names for objects) and Permissions (simple strings for Locks to check for).
@@ -144,35 +152,23 @@ Another example would be a weather script affecting all rooms tagged as
Properties of Tags (and Aliases and Permissions)
Tags are unique. This means that there is only ever one Tag object with a given key and category.
-
-Not specifying a category (default) gives the tag a category of None, which is also considered a
-unique key + category combination.
-
-When Tags are assigned to game entities, these entities are actually sharing the same Tag. This
-means that Tags are not suitable for storing information about a single object - use an
+
+
Important
+
Not specifying a category (default) gives the tag a category of None, which is also considered a unique key + category combination. You cannot use TagCategoryProperty to set Tags with None categories, since the property name may not be None. Use the TagHandler (or TagProperty) for this.
+
+When Tags are assigned to game entities, these entities are actually sharing the same Tag. This means that Tags are not suitable for storing information about a single object - use an
Attribute for this instead. Tags are a lot more limited than Attributes but this also
makes them very quick to lookup in the database - this is the whole point.
Tags have the following properties, stored in the database:
key - the name of the Tag. This is the main property to search for when looking up a Tag.
-category - this category allows for retrieving only specific subsets of tags used for
-different purposes. You could have one category of tags for “zones”, another for “outdoor
-locations”, for example. If not given, the category will be None, which is also considered a
-separate, default, category.
-data - this is an optional text field with information about the tag. Remember that Tags are
-shared between entities, so this field cannot hold any object-specific information. Usually it would
-be used to hold info about the group of entities the Tag is tagging - possibly used for contextual
-help like a tool tip. It is not used by default.
+category - this category allows for retrieving only specific subsets of tags used for different purposes. You could have one category of tags for “zones”, another for “outdoor locations”, for example. If not given, the category will be None, which is also considered a separate, default, category.
+data - this is an optional text field with information about the tag. Remember that Tags are shared between entities, so this field cannot hold any object-specific information. Usually it would be used to hold info about the group of entities the Tag is tagging - possibly used for contextual help like a tool tip. It is not used by default.
-There are also two special properties. These should usually not need to be changed or set, it is
-used internally by Evennia to implement various other uses it makes of the Tag object:
+There are also two special properties. These should usually not need to be changed or set, it is used internally by Evennia to implement various other uses it makes of the Tag object:
-model - this holds a natural-key description of the model object that this tag deals with,
-on the form application.modelclass, for example objects.objectdb. It used by the TagHandler of
-each entity type for correctly storing the data behind the scenes.
-tagtype - this is a “top-level category” of sorts for the inbuilt children of Tags, namely
-Aliases and Permissions. The Taghandlers using this special field are especially intended to
-free up the category property for any use you desire.
+model - this holds a natural-key description of the model object that this tag deals with, on the form application.modelclass, for example objects.objectdb. It used by the TagHandler of each entity type for correctly storing the data behind the scenes.
+tagtype - this is a “top-level category” of sorts for the inbuilt children of Tags, namely Aliases and Permissions. The Taghandlers using this special field are especially intended to free up the category property for any use you desire.
all_aliases = boy.aliases.all()
-and so on. Similarly to how @tag works in-game, there is also the @perm command for assigning
-permissions and @alias command for aliases.
+and so on. Similarly to how tag works in-game, there is also the perm command for assigning permissions and @alias command for aliases.
diff --git a/docs/1.0/Contribs/Contribs-Overview.html b/docs/1.0/Contribs/Contribs-Overview.html
index 08eda7318c..da9e6bf36e 100644
--- a/docs/1.0/Contribs/Contribs-Overview.html
+++ b/docs/1.0/Contribs/Contribs-Overview.html
@@ -485,7 +485,7 @@ look of these clothes are appended to the character’s description when worn.
Installation
-Read the documentation - Browse the Code
+Read the documentation - Browse the Code
cooldowns
Contribution by owllex, 2021
diff --git a/docs/1.0/_modules/evennia.html b/docs/1.0/_modules/evennia.html
index 8e382fb2e3..cb59d5e5bb 100644
--- a/docs/1.0/_modules/evennia.html
+++ b/docs/1.0/_modules/evennia.html
@@ -128,6 +128,7 @@
# Properties
AttributeProperty = None
TagProperty = None
+TagCategoryProperty = None
# commands
Command = None
@@ -234,7 +235,7 @@
global GLOBAL_SCRIPTS, OPTION_CLASSES
global EvMenu, EvTable, EvForm, EvMore, EvEditor
global ANSIString
- global AttributeProperty, TagProperty
+ global AttributeProperty, TagProperty, TagCategoryProperty
# Parent typeclasses
# utilities
@@ -249,12 +250,7 @@
from .comms.models import ChannelDB, Msg
from .locks import lockfuncs
from .objects.models import ObjectDB
- from .objects.objects import (
- DefaultCharacter,
- DefaultExit,
- DefaultObject,
- DefaultRoom,
- )
+ from .objects.objects import DefaultCharacter, DefaultExit, DefaultObject, DefaultRoom
from .prototypes.spawner import spawn
from .scripts.models import ScriptDB
from .scripts.monitorhandler import MONITOR_HANDLER
@@ -263,7 +259,7 @@
from .scripts.tickerhandler import TICKER_HANDLER
from .server import signals
from .typeclasses.attributes import AttributeProperty
- from .typeclasses.tags import TagProperty
+ from .typeclasses.tags import TagCategoryProperty, TagProperty
from .utils import ansi, gametime, logger
from .utils.ansi import ANSIString
@@ -295,22 +291,23 @@
search_script,
search_tag,
)
-
from .utils.utils import class_from_module
+
if portal_mode:
# Set up the PortalSessionHandler
from evennia.server.portal import portalsessionhandler
+
portal_sess_handler_class = class_from_module(settings.PORTAL_SESSION_HANDLER_CLASS)
portalsessionhandler.PORTAL_SESSIONS = portal_sess_handler_class()
else:
# Create the ServerSesssionHandler
from evennia.server import sessionhandler
+
sess_handler_class = class_from_module(settings.SERVER_SESSION_HANDLER_CLASS)
sessionhandler.SESSIONS = sess_handler_class()
sessionhandler.SESSION_HANDLER = sessionhandler.SESSIONS
SESSION_HANDLER = sessionhandler.SESSIONS
-
# API containers
class _EvContainer(object):
diff --git a/docs/1.0/_modules/evennia/commands/default/system.html b/docs/1.0/_modules/evennia/commands/default/system.html
index b0db485b83..922c704410 100644
--- a/docs/1.0/_modules/evennia/commands/default/system.html
+++ b/docs/1.0/_modules/evennia/commands/default/system.html
@@ -91,10 +91,9 @@
import traceback
import django
+import evennia
import twisted
from django.conf import settings
-
-import evennia
from evennia.accounts.models import AccountDB
from evennia.scripts.taskhandler import TaskHandlerTask
from evennia.utils import gametime, logger, search, utils
@@ -264,7 +263,11 @@
if show_input:
for session in sessions:
- data = {"text": (f">>> {pycode}", {"type": "py_input"}), "options": {"raw": True, "highlight": True}}
+ data = {
+ # TODO: 'highlight' is not used yet
+ "text": (f">>> {pycode}", {"type": "py_input"}),
+ "options": {"raw": True, "highlight": True},
+ }
try:
caller.msg(session=session, **data)
except TypeError:
@@ -321,10 +324,16 @@
for session in sessions:
try:
- caller.msg((ret, {"type": "py_output"}), session=session, options={"raw": True, "client_raw": client_raw,
- "highlight": True})
+ caller.msg(
+ (ret, {"type": "py_output"}),
+ session=session,
+ options={"raw": True, "client_raw": client_raw, "highlight": True},
+ )
except TypeError:
- caller.msg((ret, {"type": "py_output"}), options={"raw": True, "client_raw": client_raw, "highlight": True})
+ caller.msg(
+ (ret, {"type": "py_output"}),
+ options={"raw": True, "client_raw": client_raw, "highlight": True},
+ )
def evennia_local_vars(caller):
@@ -1108,7 +1117,6 @@
# handle caller's request to manipulate a task(s)
if self.switches and self.lhs:
-
# find if the argument is a task id or function name
action_request = self.switches[0]
try:
@@ -1118,7 +1126,6 @@
# if the argument is a task id, proccess the action on a single task
if arg_is_id:
-
err_arg_msg = "Switch and task ID are required when manipulating a task."
task_comp_msg = "Task completed while processing request."
@@ -1183,7 +1190,6 @@
# the argument is not a task id, process the action on all task deferring the function
# specified as an argument
else:
-
name_match_found = False
arg_func_name = self.lhslist[0].lower()
diff --git a/docs/1.0/_modules/evennia/typeclasses/models.html b/docs/1.0/_modules/evennia/typeclasses/models.html
index 23439211ed..bfc03f018a 100644
--- a/docs/1.0/_modules/evennia/typeclasses/models.html
+++ b/docs/1.0/_modules/evennia/typeclasses/models.html
@@ -112,7 +112,6 @@
from django.urls import reverse
from django.utils.encoding import smart_str
from django.utils.text import slugify
-
from evennia.locks.lockhandler import LockHandler
from evennia.server.signals import SIGNAL_TYPED_OBJECT_POST_RENAME
from evennia.typeclasses import managers
@@ -128,6 +127,7 @@
AliasHandler,
PermissionHandler,
Tag,
+ TagCategoryProperty,
TagHandler,
TagProperty,
)
@@ -421,7 +421,7 @@
by fetching them once.
"""
for propkey, prop in self.__class__.__dict__.items():
- if isinstance(prop, (AttributeProperty, TagProperty)):
+ if isinstance(prop, (AttributeProperty, TagProperty, TagCategoryProperty)):
try:
getattr(self, propkey)
except Exception:
@@ -704,7 +704,8 @@
raise RuntimeError(
"Cannot use swap_typeclass on time-dependent "
"Script '%s'.\nStop and start a new Script of the "
- "right type instead." % self.key
+ "right type instead."
+ % self.key
)
self.typeclass_path = new_typeclass.path
diff --git a/docs/1.0/_modules/evennia/typeclasses/tags.html b/docs/1.0/_modules/evennia/typeclasses/tags.html
index 084d877ab0..62d020d187 100644
--- a/docs/1.0/_modules/evennia/typeclasses/tags.html
+++ b/docs/1.0/_modules/evennia/typeclasses/tags.html
@@ -91,7 +91,6 @@
from django.conf import settings
from django.db import models
-
from evennia.locks.lockfuncs import perm as perm_lockfunc
from evennia.utils.utils import make_iter, to_str
@@ -177,28 +176,31 @@
[docs]class TagProperty:
"""
-
Tag property descriptor. Allows for setting tags on an object as Django-like 'fields'
-
on the class level. Since Tags are almost always used for querying, Tags are always
-
created/assigned along with the object. Make sure the property/tagname does not collide
-
with an existing method/property on the class. If it does, you must use tags.add()
-
instead.
-
-
Note that while you _can_ check e.g. `obj.tagname,this will give an AttributeError
-
if the Tag is not set. Most often you want to use `obj.tags.get("tagname")` to check
-
if a tag is set on an object.
-
-
Example:
-
::
-
-
class Character(DefaultCharacter):
-
mytag = TagProperty() # category=None
-
mytag2 = TagProperty(category="tagcategory")
-
+
Tag Property.
"""
taghandler_name = "tags"
[docs] def __init__(self, category=None, data=None):
+
"""
+
Tag property descriptor. Allows for setting tags on an object as Django-like 'fields'
+
on the class level. Since Tags are almost always used for querying, Tags are always
+
created/assigned along with the object. Make sure the property/tagname does not collide
+
with an existing method/property on the class. If it does, you must use tags.add()
+
instead.
+
+
Note that while you _can_ check e.g. `obj.tagname,this will give an AttributeError
+
if the Tag is not set. Most often you want to use `obj.tags.get("tagname")` to check
+
if a tag is set on an object.
+
+
Example:
+
::
+
+
class Character(DefaultCharacter):
+
mytag = TagProperty() # category=None
+
mytag2 = TagProperty(category="tagcategory")
+
"""
+
self._category = category
self._data = data
self._key = ""
@@ -245,6 +247,129 @@
getattr(instance, self.taghandler_name).remove(key=self._key, category=self._category)
+[docs]class TagCategoryProperty:
+
"""
+
Tag Category Property.
+
+
"""
+
+
taghandler_name = "tags"
+
+
[docs] def __init__(self, *args):
+
"""
+
Assign a property for a Tag Category, with any number of Tag keys.
+
This is often more useful than the `TagProperty` since it's common to want to check which
+
tags of a particular category the object is a member of.
+
+
Args:
+
*args (str or callable): Tag keys to assign to this property, using the category given
+
by the name of the property. If a callable, it will be called without arguments
+
to return the tag key. It is not possible to set tag `data` this way (use the
+
Taghandler directly for that). Tag keys are not case sensitive.
+
+
Raises:
+
ValueError: If the input is not a valid tag key or tuple.
+
+
Notes:
+
It is not possible to set Tags with a `None` category using a `TagCategoryProperty` -
+
use `obj.tags.add()` instead.
+
+
Example:
+
::
+
+
class RogueCharacter(DefaultCharacter):
+
guild = TagProperty("thieves_guild", "merchant_guild")
+
+
"""
+
self._category = ""
+
self._tags = self._parse_tag_input(*args)
+
+
def _parse_tag_input(self, *args):
+
"""
+
Parse input to the property.
+
+
Args:
+
*args (str or callable): Tags, either as strings or `callable`, which should return
+
the tag key when called without arguments. Keys are not case sensitive.
+
+
Returns:
+
list: A list of tag keys.
+
+
"""
+
tags = []
+
for tagkey in args:
+
if callable(tagkey):
+
tagkey = tagkey()
+
tags.append((str(tagkey).lower()))
+
return tags
+
+
def __set_name__(self, cls, name):
+
"""
+
Called when descriptor is first assigned to the class (not the instance!).
+
It is called with the name of the field.
+
+
"""
+
self._category = name
+
+
def __get__(self, instance, owner):
+
"""
+
Called when accessing the tag as a property on the instance. Returns a list
+
of tags under the given category.
+
"""
+
taghandler = getattr(instance, self.taghandler_name)
+
+
tags = []
+
add_new = []
+
for tagkey in self._tags:
+
try:
+
tag = taghandler.get(
+
key=tagkey, category=self._category, return_list=False, raise_exception=True
+
)
+
except AttributeError:
+
add_new.append(tagkey)
+
else:
+
tags.append(tag)
+
if add_new:
+
for new_tag in add_new:
+
# we must remove this from the internal store or system will think it already
+
# existed when determining the sets in __set__
+
self._tags.remove(new_tag)
+
self.__set__(instance, *add_new)
+
+
return tags
+
+
def __set__(self, instance, *args):
+
"""
+
Assign a new set of tags to the category. This replaces the previous set of tags.
+
+
"""
+
taghandler = getattr(instance, self.taghandler_name)
+
+
old_tags = set(self._tags)
+
new_tags = set(self._parse_tag_input(*args))
+
+
# new_tags could be a sub/superset of old tags
+
removed_tags = old_tags - new_tags
+
added_tags = new_tags - old_tags
+
+
# remove tags
+
for tag in removed_tags:
+
taghandler.remove(key=tag, category=self._category)
+
+
# add new tags (won't re-add if obj already had it)
+
taghandler.batch_add(*[(tag, self._category) for tag in added_tags])
+
+
def __delete__(self, instance):
+
"""
+
Called when running `del` on the property. Will remove all tags of this
+
category from the object. Note that the tags will be readded on next fetch
+
unless the TagCategoryProperty is also removed in code!
+
+
"""
+
for tagkey in self.tags:
+
getattr(instance, self.taghandler_name).remove(key=self.tagkey, category=self._category)
+
+
[docs]class TagHandler(object):
"""
Generic tag-handler. Accessed via TypedObject.tags.
diff --git a/docs/1.0/_sources/Coding/Changelog.md.txt b/docs/1.0/_sources/Coding/Changelog.md.txt
index 962c25b7f8..3ba3f4a613 100644
--- a/docs/1.0/_sources/Coding/Changelog.md.txt
+++ b/docs/1.0/_sources/Coding/Changelog.md.txt
@@ -2,11 +2,19 @@
## Main branch
-- Feature: Attribute-support for saving/loading `deques` with `maxlen=` set.
-- Contrib: Container typeclass with new commands for storing and retrieving
+- New Contrib: `Container` typeclass with new commands for storing and retrieving
things inside them (InspectorCaracal)
+- Feature: Add `TagCategoryProperty` for setting categories with multiple tags
+ as properties directly on objects. Complements `TagProperty`.
+- Feature: Attribute-support for saving/loading `deques` with `maxlen=` set.
+- Feature: Refactor to provide `evennia.SESSION_HANDLER` for easier overloading
+ and less risks of circular import problems (Volund)
+- Fix: Allow webclient's goldenlayout UI (default) to understand `msg`
+ `cls` kwarg for customizing the CSS class for every resulting `div` (friarzen)
- Fix: The `AttributeHandler.all()` now actually accepts `category=` as
keyword arg, like our docs already claimed it should (Volund)
+- Fix: `TickerHandler` store key updating was refactored, fixing an issue with
+ updating intervals (InspectorCaracal)
- Docs: New Beginner-Tutorial lessons for NPCs, Base-Combat Twitch-Combat and
Turnbased-combat (note that the Beginner tutorial is still WIP).
diff --git a/docs/1.0/_sources/Components/Tags.md.txt b/docs/1.0/_sources/Components/Tags.md.txt
index f11e9d44b2..9aeecee161 100644
--- a/docs/1.0/_sources/Components/Tags.md.txt
+++ b/docs/1.0/_sources/Components/Tags.md.txt
@@ -12,18 +12,26 @@ obj.tags.get("mytag", category="foo")
```
```{code-block} python
-:caption: In code, using TagProperty (auto-assign tag to all instances of the class)
+:caption: In code, using TagProperty or TagCategoryProperty
from evennia import DefaultObject
-from evennia import TagProperty
+from evennia import TagProperty, TagCategoryProperty
+
class Sword(DefaultObject):
+ # name of property is the tagkey, category as argument
can_be_wielded = TagProperty(category='combat')
has_sharp_edge = TagProperty(category='combat')
+ # name of property is the category, tag-keys are arguments
+ damage_type = TagCategoryProperty("piercing", "slashing")
+ crafting_element = TagCategory("blade", "hilt", "pommel")
+
```
_Tags_ are short text lables one can 'hang' on objects in order to organize, group and quickly find out their properties. An Evennia entity can be tagged by any number of tags. They are more efficient than [Attributes](./Attributes.md) since on the database-side, Tags are _shared_ between all objects with that particular tag. A tag does not carry a value in itself; it either sits on the entity
+You manage Tags using the `TagHandler` (`.tags`) on typeclassed entities. You can also assign Tags on the class level through the `TagProperty` (one tag, one category per line) or the `TagCategoryProperty` (one category, multiple tags per line). Both of these use the `TagHandler` under the hood, they are just convenient ways to add tags already when you define your class.
+
Above, the tags inform us that the `Sword` is both sharp and can be wielded. If that's all they do, they could just be a normal Python flag. When tags become important is if there are a lot of objects with different combinations of tags. Maybe you have a magical spell that dulls _all_ sharp-edged objects in the castle - whether sword, dagger, spear or kitchen knife! You can then just grab all objects with the `has_sharp_edge` tag.
Another example would be a weather script affecting all rooms tagged as `outdoors` or finding all characters tagged with `belongs_to_fighter_guild`.
@@ -35,34 +43,24 @@ In Evennia, Tags are technically also used to implement `Aliases` (alternative n
Tags are *unique*. This means that there is only ever one Tag object with a given key and category.
-> Not specifying a category (default) gives the tag a category of `None`, which is also considered a
-unique key + category combination.
+```{important}
+Not specifying a category (default) gives the tag a category of `None`, which is also considered a unique key + category combination. You cannot use `TagCategoryProperty` to set Tags with `None` categories, since the property name may not be `None`. Use the `TagHandler` (or `TagProperty`) for this.
-When Tags are assigned to game entities, these entities are actually sharing the same Tag. This
-means that Tags are not suitable for storing information about a single object - use an
+```
+When Tags are assigned to game entities, these entities are actually sharing the same Tag. This means that Tags are not suitable for storing information about a single object - use an
[Attribute](./Attributes.md) for this instead. Tags are a lot more limited than Attributes but this also
makes them very quick to lookup in the database - this is the whole point.
Tags have the following properties, stored in the database:
- **key** - the name of the Tag. This is the main property to search for when looking up a Tag.
-- **category** - this category allows for retrieving only specific subsets of tags used for
-different purposes. You could have one category of tags for "zones", another for "outdoor
-locations", for example. If not given, the category will be `None`, which is also considered a
-separate, default, category.
-- **data** - this is an optional text field with information about the tag. Remember that Tags are
-shared between entities, so this field cannot hold any object-specific information. Usually it would
-be used to hold info about the group of entities the Tag is tagging - possibly used for contextual
-help like a tool tip. It is not used by default.
+- **category** - this category allows for retrieving only specific subsets of tags used for different purposes. You could have one category of tags for "zones", another for "outdoor locations", for example. If not given, the category will be `None`, which is also considered a separate, default, category.
+- **data** - this is an optional text field with information about the tag. Remember that Tags are shared between entities, so this field cannot hold any object-specific information. Usually it would be used to hold info about the group of entities the Tag is tagging - possibly used for contextual help like a tool tip. It is not used by default.
-There are also two special properties. These should usually not need to be changed or set, it is
-used internally by Evennia to implement various other uses it makes of the `Tag` object:
-- **model** - this holds a *natural-key* description of the model object that this tag deals with,
-on the form *application.modelclass*, for example `objects.objectdb`. It used by the TagHandler of
-each entity type for correctly storing the data behind the scenes.
-- **tagtype** - this is a "top-level category" of sorts for the inbuilt children of Tags, namely
-*Aliases* and *Permissions*. The Taghandlers using this special field are especially intended to
-free up the *category* property for any use you desire.
+There are also two special properties. These should usually not need to be changed or set, it is used internally by Evennia to implement various other uses it makes of the `Tag` object:
+
+- **model** - this holds a *natural-key* description of the model object that this tag deals with, on the form *application.modelclass*, for example `objects.objectdb`. It used by the TagHandler of each entity type for correctly storing the data behind the scenes.
+- **tagtype** - this is a "top-level category" of sorts for the inbuilt children of Tags, namely *Aliases* and *Permissions*. The Taghandlers using this special field are especially intended to free up the *category* property for any use you desire.
### Adding/Removing Tags
@@ -157,6 +155,5 @@ used in the same way as Tags above:
all_aliases = boy.aliases.all()
```
-and so on. Similarly to how `@tag` works in-game, there is also the `@perm` command for assigning
-permissions and `@alias` command for aliases.
+and so on. Similarly to how `tag` works in-game, there is also the `perm` command for assigning permissions and `@alias` command for aliases.
diff --git a/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.containers.md.txt b/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.containers.md.txt
new file mode 100644
index 0000000000..b4cdcfd0a2
--- /dev/null
+++ b/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.containers.md.txt
@@ -0,0 +1,10 @@
+```{eval-rst}
+evennia.contrib.game\_systems.containers.containers
+==========================================================
+
+.. automodule:: evennia.contrib.game_systems.containers.containers
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+```
\ No newline at end of file
diff --git a/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.md.txt b/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.md.txt
new file mode 100644
index 0000000000..69d3c7a5c5
--- /dev/null
+++ b/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.md.txt
@@ -0,0 +1,18 @@
+```{eval-rst}
+evennia.contrib.game\_systems.containers
+================================================
+
+.. automodule:: evennia.contrib.game_systems.containers
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+
+
+.. toctree::
+ :maxdepth: 6
+
+ evennia.contrib.game_systems.containers.containers
+ evennia.contrib.game_systems.containers.tests
+
+```
\ No newline at end of file
diff --git a/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.tests.md.txt b/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.tests.md.txt
new file mode 100644
index 0000000000..318a3d9669
--- /dev/null
+++ b/docs/1.0/_sources/api/evennia.contrib.game_systems.containers.tests.md.txt
@@ -0,0 +1,10 @@
+```{eval-rst}
+evennia.contrib.game\_systems.containers.tests
+=====================================================
+
+.. automodule:: evennia.contrib.game_systems.containers.tests
+ :members:
+ :undoc-members:
+ :show-inheritance:
+
+```
\ No newline at end of file
diff --git a/docs/1.0/_sources/api/evennia.contrib.game_systems.md.txt b/docs/1.0/_sources/api/evennia.contrib.game_systems.md.txt
index b321dd8abc..72bbb06be7 100644
--- a/docs/1.0/_sources/api/evennia.contrib.game_systems.md.txt
+++ b/docs/1.0/_sources/api/evennia.contrib.game_systems.md.txt
@@ -13,6 +13,7 @@ evennia.contrib.game\_systems
evennia.contrib.game_systems.barter
evennia.contrib.game_systems.clothing
+ evennia.contrib.game_systems.containers
evennia.contrib.game_systems.cooldowns
evennia.contrib.game_systems.crafting
evennia.contrib.game_systems.gendersub
diff --git a/docs/1.0/api/evennia-api.html b/docs/1.0/api/evennia-api.html
index 45184d719d..9484696fbe 100644
--- a/docs/1.0/api/evennia-api.html
+++ b/docs/1.0/api/evennia-api.html
@@ -237,6 +237,11 @@
evennia.contrib.game_systems.clothing.tests
+
evennia.contrib.game_systems.containers
+
evennia.contrib.game_systems.cooldowns