Source code for entities.dictionary

# ../entities/dictionary.py

"""Provides helper class to store entity instances."""

# ============================================================================
# >> IMPORTS
# ============================================================================
# Source.Python Imports
#   Core
from core import AutoUnload
#   Filters
from filters.entities import EntityIter
#   Entities
from entities.entity import Entity
from entities.helpers import index_from_inthandle
#   Listeners
from listeners import on_networked_entity_created_listener_manager
from listeners import on_networked_entity_deleted_listener_manager


# ============================================================================
# >> ALL DECLARATION
# ============================================================================
__all__ = ('EntityDictionary',
           'SyncedEntityDictionary',
           )


# ============================================================================
# >> CLASSES
# ============================================================================
[docs]class EntityDictionary(AutoUnload, dict): """Helper class used to store entity instances."""
[docs] def __init__(self, factory=Entity, *args, **kwargs): """Initializes the dictionary. :param callable factory: Factory class or function used to create missing instances. Set to `None` to disable this feature. Factory signature: index, *args, **kwargs :param tuple args: Arguments passed to the factory class or function. :param dict kwargs: Keyword arguments passed to the factory class or function. """ # Store the given entity class... self._factory = factory # Store given arguments/keywords self._args = args self._kwargs = kwargs # Register our networked entity deletion listener... on_networked_entity_deleted_listener_manager.register_listener( self._on_networked_entity_deleted) # Initialize the dictionary... super().__init__()
def __missing__(self, index): """Called when an instance is requested but missing. :param int index: The index of the entity instance requested. :raises KeyError: If the auto-construction of missing instances is disabled or the factory class or function fails to return an instance. """ # Get the factory factory = self._factory # Let's mimic dict's behaviour if the factory is set to None if factory is None: raise KeyError(index) # For uniformity reasons, ensure we only raise a KeyError try: instance = factory(index, *self._args, **self._kwargs) except Exception as e: raise KeyError(e).with_traceback(e.__traceback__) from None # Only cache entities that are not marked for deletion. # This is required, because if someone request an entity instance # after we invalidated our cache but before the engine processed # the deletion we would now have an invalid instance in the cache. if (not isinstance(instance, Entity) or not instance.is_marked_for_deletion()): self[index] = instance return instance def __delitem__(self, index): """Removes the given index from the dictionary. :param int index: The index of the entity instance being removed. """ # Remove the given index from the dictionary... try: super().__delitem__(index) except KeyError: pass
[docs] def from_inthandle(self, inthandle): """Returns an entity instance from an inthandle. :param int inthandle: The inthandle. :rtype: Entity """ return self[index_from_inthandle(inthandle)]
[docs] def on_automatically_removed(self, index): """Called when an index is automatically removed. :param int index: The index of the entity instance being removed. """
def _on_networked_entity_deleted(self, entity): """Internal networked entity deletion callback. :param Entity entity: The networked entity being removed. """ # Get the index of the entity index = entity.index # No need to go further if there is no object associated to this index if index not in self: return try: # Call the deletion callback for the index... self.on_automatically_removed(index) finally: # Remove the index from the dictionary... super().__delitem__(index) def _unload_instance(self): """Unregister our networked entity deletion listener.""" on_networked_entity_deleted_listener_manager.unregister_listener( self._on_networked_entity_deleted)
[docs]class SyncedEntityDictionary(EntityDictionary): """Helper class used to keep entity instances synced with the game."""
[docs] def __init__( self, factory=Entity, iterator=EntityIter(), *args, **kwargs): """Initializes the dictionary. :param callable factory: Factory class or function. Factory signature: index, *args, **kwargs :param iterable iterator: Iterator used to generates instances on initialization and resync. :param tuple args: Arguments passed to the factory class or function. :param dict kwargs: Keyword arguments passed to the factory class or function. :raise ValueError: If the factory is set to None. """ if factory is None: raise ValueError( 'Factory cannot be None for synced dictionaries.') # Initialize the dictionary super().__init__(factory, *args, **kwargs) # Store the given iterator and resync the dictionary self._iterator = iterator self.resync() # Register our networked entity creation listener on_networked_entity_created_listener_manager.register_listener( self._on_networked_entity_created)
def __missing__(self, index): """Raises a KeyError, because creation of missing instances is not allowed for synced dictionaries. """ raise KeyError(index)
[docs] def resync(self): """Resync the dictionary with the game.""" # Clear the dictionary self.clear() # Loop through all entities and add them to the dictionary for entity in self._iterator: self._on_networked_entity_created(entity)
[docs] def on_automatically_created(self, index): """Called when an index is automatically added. :param int index: The index of the entity instance being added. """
def _on_networked_entity_created(self, entity): """Internal networked entity creation callback. :param Entity entity: The networked entity being created. """ # Validate the entity if not self._iterator._is_valid(entity): return # Get the index of the entity index = entity.index # Add the index to the dictionary super().__missing__(index) # Call the creation callback for the index self.on_automatically_created(index) def _unload_instance(self): """Unregister our networked entity creation listener.""" on_networked_entity_created_listener_manager.unregister_listener( self._on_networked_entity_created) # Unload the dictionary super()._unload_instance()