Source code for effects.templates

# ../effects/templates.py

"""Base templates for effect classes."""

# ============================================================================
# >> IMPORTS
# ============================================================================
# Source.Python Imports
#   Core
from core import GameConfigObj
#   Effects
from _effects._base import BaseTempEntity
#   Entities
from entities.classes import _supported_property_types
from entities.classes import server_classes
from entities.props import SendPropType
#   Memory
from memory import TYPE_SIZES
from memory import get_size
from memory.helpers import Type
from memory.hooks import HookType
from memory.manager import manager
#   Paths
from paths import SP_DATA_PATH

# Site-Packages Imports
#   ConfigObj
from configobj import Section


# =============================================================================
# >> ALL DECLARATION
# =============================================================================
__all__ = ('TempEntityTemplate',
           'TempEntityTemplates',
           'temp_entity_templates',
           )


# ============================================================================
# >> CLASSES
# ============================================================================
[docs]class TempEntityTemplate(BaseTempEntity): """A temp entity template."""
[docs] def __init__(self, temp_entity): """Initialize the instance. :param BaseTempEntity temp_entity: The base entity instance to copy the base from. """ # Store the size of the temp entity... self._size = get_size(BaseTempEntity) # Get the aliases... self._aliases = GameConfigObj( SP_DATA_PATH / 'effects' / temp_entity.server_class.name + '.ini') # Get a dictionary to store the properties... self._properties = dict() # Add the properties to the dictionary... self._add_properties(temp_entity.server_class.table) # Loop through all properties... for prop in temp_entity.server_class.table: # Is the current prop a base class? if prop.name != 'baseclass': # Pass to the next property... continue # Add the current table to the properties... self._add_properties(prop.data_table) # Get a list to store our hooks... self._hooks = list() # Initialize the base class... super()._copy_base(temp_entity, self.size)
def _add_properties(self, send_table): """Parse the given send table and add all properties to the dictionary. :param SendTable send_table: The send table instance to parse. """ # Loop through all properties... for name, prop, offset in server_classes._find_properties(send_table): # Is the current prop an array? if prop.type == SendPropType.ARRAY: # Loop through all aliases... for alias, data in self.aliases.items(): # Is the current alias matching the prop name? if isinstance(data, Section) and data['name'] == name: # Set the name of the type... type_name = data['type'] # Get the size of the array... size = self._get_type_size(data['type']) * prop.length # No need to continue looping... break # Was no size retrieved? else: # We don't want to add that property... continue # Otherwise, is the type supported? elif prop.type in _supported_property_types: # Set the name of the type... type_name = _supported_property_types[ prop.type] # Get the size of the type... size = self._get_type_size(type_name) # Otherwise... else: # We don't want to add that property... continue # Add the offset to the size... size += offset # Is the size larger than our actual stored size? if size > self.size: # Update the size... self._size = size # Add the property... self._properties[name] = (prop, offset, type_name) @staticmethod def _get_type_size(type_name): """Helper method returning the size of the given type. :param str type_name: The name of the type. """ # Is the type native? if Type.is_native(type_name): # Return the size of the type... return TYPE_SIZES[type_name.upper()] # Otherwise... else: # Get the size of the type... return get_size(manager.get_class(type_name)) # Raise an exception... raise ValueError('"{}" is not a supported type.'.format(type_name))
[docs] def add_hook(self, callback): """Register a hook for this temp entity. :param function callback: The callback function to register. """ # Is the given callback not callable? if not callable(callback): raise TypeError('The given callback is not callable.') # Is the callback already registered? if callback in self.hooks: raise ValueError('The given callback is already registered.') # Register the hook... self.hooks.append(callback)
[docs] def remove_hook(self, callback): """Unregister a hook for this temp entity. :param function callback: The callback function to unregister. """ # Raise an exception if the given callback isn't registered... if callback not in self.hooks: raise ValueError('The given callback is not registered.') # Unregister the hook... self.hooks.remove(callback)
[docs] def handle_hook(self, temp_entity, recipient_filter): """Call the registered callbacks. :param TempEntity temp_entity: The TempEntity instance. :param RecipientFilter recipient_filter: The RecipientFilter instance. :rtype: bool """ # Set the default return value to None... return_value = None # Loop through all registered hooks for this temp entity... for callback in self.hooks: # Call the callback and store the value it returned... returned_value = callback(temp_entity, recipient_filter) # Did the callback return anything? if returned_value is not None: # Yes, so override the return value... return_value = returned_value # Return the return value... return return_value
@property def aliases(self): """Return the aliases of the temp entity. :rtype: GameConfigObj """ return self._aliases @property def hooks(self): """Return the registered hooks for this temp entity. :rtype: dict """ return self._hooks @property def properties(self): """Return the properties data of the temp entity. :rtype: dict """ return self._properties @property def size(self): """Return the size of the temp entity instance. :rtype: int """ return self._size
[docs]class TempEntityTemplates(dict): """Container class used to store the temp entity templates instances.""" def __missing__(self, temp_entity_name): """Called when a temp entity template is requested but missing. :param str temp_entity_name: The name of the temp entity template requested. :raise NameError: Raised if the given name is not a valid temp entity name. """ # Import this here to fix a cyclic import from effects import _first_temp_entity # Get the first temp entity in the chain... temp_entity = _first_temp_entity # Loop as long as we have a valid instance... while temp_entity: # Are the names matching? if temp_entity.name == temp_entity_name: # No need to continue searching... break # Pass to the next temp entity in the chain... temp_entity = temp_entity.next # Did we reach the last temp entity in the chain? if temp_entity is None: # If so, raise an exception.... raise NameError('"{}" is not a valid temp entity name.'.format( temp_entity_name)) # Get the temp entity template... temp_entity_template = self[temp_entity_name] = TempEntityTemplate( temp_entity) # Return the temp entity template... return temp_entity_template
# ============================================================================ # >> GLOBAL VARIABLES # ============================================================================ # Get a dictionary to store the temp entity templates... temp_entity_templates = TempEntityTemplates()