Skip to content

Added entity instances caching. #291

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 26, 2019
Merged
68 changes: 66 additions & 2 deletions addons/source-python/packages/source-python/entities/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
from collections import defaultdict
# Contextlib
from contextlib import suppress
# WeakRef
from weakref import WeakSet

# Source.Python Imports
# Core
Expand Down Expand Up @@ -45,6 +47,7 @@
from filters.weapons import WeaponClassIter
# Listeners
from listeners import OnEntityDeleted
from listeners import on_entity_deleted_listener_manager
from listeners.tick import Delay
# Mathlib
from mathlib import NULL_VECTOR
Expand All @@ -66,11 +69,58 @@
# Get a dictionary to store the delays
_entity_delays = defaultdict(set)

# Get a set to store the registered entity classes
_entity_classes = WeakSet()


# =============================================================================
# >> CLASSES
# =============================================================================
class Entity(BaseEntity):
class _EntityCaching(BaseEntity.__class__):
"""Metaclass used to cache entity instances."""

def __init__(cls, classname, bases, attributes):
"""Initializes the class."""
# New instances of this class will be cached in that dictionary
cls._cache = {}

# Add the class to the registered classes
_entity_classes.add(cls)

def __call__(cls, index, caching=True):
"""Called when a new instance of this class is requested.

:param int index:
The index of the entity instance requested.
:param bool caching:
Whether to lookup the cache for an existing instance or not.
"""
# Let's first lookup for a cached instance
if caching:
obj = cls._cache.get(index, None)
if obj is not None:
return obj

# Nothing in cache, let's create a new instance
obj = super().__call__(index)

# Let's cache the new instance we just created
if caching:
cls._cache[index] = obj

# We are done, let's return the instance
return obj

@property
def cache(cls):
"""Returns the cached instances of this class.

:rtype: dict
"""
return cls._cache


class Entity(BaseEntity, metaclass=_EntityCaching):
"""Class used to interact directly with entities.

Beside the standard way of doing stuff via methods and properties this
Expand All @@ -82,13 +132,23 @@ class also provides dynamic attributes that depend on the entity that is
2. :attr:`inputs`
3. :attr:`outputs`
4. :attr:`keyvalues`

:var cache:
A read-only attribute that returns a dictionary containing the cached
instances of this class.

.. note::
This is not an instance property, so it can only be
accessed through the class itself.
"""

def __init__(self, index):
def __init__(self, index, caching=True):
"""Initialize the Entity instance.

:param int index:
The entity index to wrap.
:param bool caching:
Whether to lookup the cache for an existing instance or not.
"""
# Initialize the object
super().__init__(index)
Expand Down Expand Up @@ -1031,6 +1091,10 @@ def _on_entity_deleted(base_entity):
# Get the index of the entity...
index = base_entity.index

# Cleanup the cache
for cls in _entity_classes:
cls.cache.pop(index, None)

# Was no delay registered for this entity?
if index not in _entity_delays:
return
Expand Down
4 changes: 3 additions & 1 deletion addons/source-python/packages/source-python/players/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,13 @@
class Player(PlayerMixin, Entity):
"""Class used to interact directly with players."""

def __init__(self, index):
def __init__(self, index, caching=True):
"""Initialize the object.

:param int index:
A valid player index.
:param bool caching:
Whether to lookup the cache for an existing instance or not.
:raise ValueError:
Raised if the index is invalid.
"""
Expand Down
4 changes: 3 additions & 1 deletion addons/source-python/packages/source-python/weapons/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@
class Weapon(WeaponMixin, Entity):
"""Allows easy usage of the weapon's attributes."""

def __init__(self, index):
def __init__(self, index, caching=True):
"""Initialize the object.

:param int index:
A valid weapon index.
:param bool caching:
Whether to lookup the cache for an existing instance or not.
:raise ValueError:
Raised if the index is invalid.
"""
Expand Down