mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Implemented contents_cache handler for a speed boost for many situations, as per #620.
This commit is contained in:
parent
06fe2e5a9c
commit
b94bb17576
6 changed files with 118 additions and 10 deletions
|
|
@ -171,7 +171,7 @@ def get_and_merge_cmdsets(caller, session, player, obj,
|
|||
# Gather all cmdsets stored on objects in the room and
|
||||
# also in the caller's inventory and the location itself
|
||||
local_objlist = yield (location.contents_get(exclude=obj) +
|
||||
obj.contents + [location])
|
||||
obj.contents_get() + [location])
|
||||
local_objlist = [o for o in local_objlist if not o._is_deleted]
|
||||
for lobj in local_objlist:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -171,8 +171,7 @@ class ObjectDBManager(TypedObjectManager):
|
|||
@returns_typeclass_list
|
||||
def get_contents(self, location, excludeobj=None):
|
||||
"""
|
||||
Get all objects that has a location
|
||||
set to this one.
|
||||
Get all objects that has a location set to this one.
|
||||
|
||||
excludeobj - one or more object keys to exclude from the match
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -20,9 +20,82 @@ from django.core.exceptions import ObjectDoesNotExist
|
|||
from evennia.typeclasses.models import TypedObject
|
||||
from evennia.objects.manager import ObjectDBManager
|
||||
from evennia.utils import logger
|
||||
from evennia.utils.utils import (make_iter, dbref)
|
||||
from evennia.utils.utils import (make_iter, dbref, lazy_property)
|
||||
|
||||
|
||||
class ContentsHandler(object):
|
||||
"""
|
||||
Handles and caches the contents of an object
|
||||
to avoid excessive lookups (this is done very
|
||||
often due to cmdhandler needing to look for
|
||||
object-cmdsets). It is stored on the 'contents_cache'
|
||||
property of the ObjectDB.
|
||||
"""
|
||||
def __init__(self, obj):
|
||||
"""
|
||||
Sets up the contents handler.
|
||||
|
||||
Args:
|
||||
obj (Object): The object on which the
|
||||
handler is defined
|
||||
|
||||
"""
|
||||
self.obj = obj
|
||||
self._cache = {}
|
||||
self.init()
|
||||
|
||||
def init(self):
|
||||
"""
|
||||
Re-initialize the content cache
|
||||
|
||||
"""
|
||||
self._cache.update(dict((obj.pk, obj) for obj in
|
||||
ObjectDB.objects.filter(db_location=self.obj)))
|
||||
|
||||
def get(self, exclude=None):
|
||||
"""
|
||||
Return the contents of the cache.
|
||||
|
||||
Args:
|
||||
exclude (Object or list of Object): object(s) to ignore
|
||||
|
||||
Returns:
|
||||
objects (list): the Objects inside this location
|
||||
|
||||
"""
|
||||
if exclude:
|
||||
exclude = [excl.pk for excl in make_iter(exclude)]
|
||||
return [obj for key, obj in self._cache.items() if key not in exclude]
|
||||
return self._cache.values()
|
||||
|
||||
def add(self, obj):
|
||||
"""
|
||||
Add a new object to this location
|
||||
|
||||
Args:
|
||||
obj (Object): object to add
|
||||
|
||||
"""
|
||||
self._cache[obj.pk] = obj
|
||||
|
||||
def remove(self, obj):
|
||||
"""
|
||||
Remove object from this location
|
||||
|
||||
Args:
|
||||
obj (Object): object to remove
|
||||
|
||||
"""
|
||||
self._cache.pop(obj.pk, None)
|
||||
|
||||
def clear(self):
|
||||
"""
|
||||
Clear the contents cache and re-initialize
|
||||
|
||||
"""
|
||||
self._cache = {}
|
||||
self._init()
|
||||
|
||||
#------------------------------------------------------------
|
||||
#
|
||||
# ObjectDB
|
||||
|
|
@ -105,6 +178,10 @@ class ObjectDB(TypedObject):
|
|||
# Database manager
|
||||
objects = ObjectDBManager()
|
||||
|
||||
@lazy_property
|
||||
def contents_cache(self):
|
||||
return ContentsHandler(self)
|
||||
|
||||
# cmdset_storage property handling
|
||||
def __cmdset_storage_get(self):
|
||||
"getter"
|
||||
|
|
@ -152,9 +229,27 @@ class ObjectDB(TypedObject):
|
|||
is_loc_loop(location)
|
||||
except RuntimeWarning:
|
||||
pass
|
||||
# actually set the field
|
||||
|
||||
# if we get to this point we are ready to change location
|
||||
|
||||
old_location = self.db_location
|
||||
|
||||
# this is checked in _db_db_location_post_save below
|
||||
self._safe_contents_update = True
|
||||
|
||||
# actually set the field (this will error if location is invalid)
|
||||
self.db_location = location
|
||||
self.save(update_fields=["db_location"])
|
||||
|
||||
# remove the safe flag
|
||||
del self._safe_contents_update
|
||||
|
||||
# update the contents cache
|
||||
if old_location:
|
||||
old_location.contents_cache.remove(self)
|
||||
if self.db_location:
|
||||
self.db_location.contents_cache.add(self)
|
||||
|
||||
except RuntimeError:
|
||||
errmsg = "Error: %s.location = %s creates a location loop." % (self.key, location)
|
||||
logger.log_errmsg(errmsg)
|
||||
|
|
@ -170,6 +265,20 @@ class ObjectDB(TypedObject):
|
|||
self.save(update_fields=["db_location"])
|
||||
location = property(__location_get, __location_set, __location_del)
|
||||
|
||||
def _db_location_post_save(self):
|
||||
"""
|
||||
This is called automatically after the location field was saved,
|
||||
no matter how. It checks for a variable _safe_contents_update to
|
||||
know if the save was triggered via the proper handler or not.
|
||||
|
||||
Since we cannot know at this point was old_location was, we
|
||||
trigger a full-on contents_cache update here.
|
||||
|
||||
"""
|
||||
if not hasattr(self, "_safe_contents_update"):
|
||||
logger.log_warn("db_location direct save triggered contents_cache.init() for all objects!")
|
||||
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
||||
|
||||
class Meta:
|
||||
"Define Django meta options"
|
||||
verbose_name = "Object"
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ class DefaultObject(ObjectDB):
|
|||
|
||||
exclude is one or more objects to not return
|
||||
"""
|
||||
return ObjectDB.objects.get_contents(self, excludeobj=exclude)
|
||||
return self.contents_cache.get(exclude=exclude)
|
||||
contents = property(contents_get)
|
||||
|
||||
|
||||
|
|
@ -709,7 +709,6 @@ class DefaultObject(ObjectDB):
|
|||
location or to default home.
|
||||
"""
|
||||
# Gather up everything that thinks this is its location.
|
||||
objs = ObjectDB.objects.filter(db_location=self)
|
||||
default_home_id = int(settings.DEFAULT_HOME.lstrip("#"))
|
||||
try:
|
||||
default_home = ObjectDB.objects.get(id=default_home_id)
|
||||
|
|
@ -721,7 +720,7 @@ class DefaultObject(ObjectDB):
|
|||
log_errmsg(string % default_home_id)
|
||||
default_home = None
|
||||
|
||||
for obj in objs:
|
||||
for obj in self.contents:
|
||||
home = obj.home
|
||||
# Obviously, we can't send it back to here.
|
||||
if not home or (home and home.dbid == self.dbid):
|
||||
|
|
@ -824,6 +823,7 @@ class DefaultObject(ObjectDB):
|
|||
self.attributes.clear()
|
||||
self.nicks.clear()
|
||||
self.aliases.clear()
|
||||
self.location = None # this updates contents_cache for our location
|
||||
|
||||
# Perform the deletion of the object
|
||||
super(ObjectDB, self).delete()
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class PortalSessionHandler(SessionHandler):
|
|||
if not self.portal.amp_protocol:
|
||||
# if amp is not yet ready (usually because the server is
|
||||
# booting up), try again a little later
|
||||
reactor.CallLater(0.5, self.connect, session)
|
||||
reactor.callLater(0.5, self.connect, session)
|
||||
return
|
||||
|
||||
# sync with server-side
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ CHANCE_OF_ACTION = 0.5
|
|||
# Chance of a currently unlogged-in dummy performing its login
|
||||
# action every tick. This emulates not all players logging in
|
||||
# at exactly the same time.
|
||||
CHANCE_OF_LOGIN = 1.0#0.5
|
||||
CHANCE_OF_LOGIN = 1.0
|
||||
|
||||
# Which telnet port to connect to. If set to None, uses the first
|
||||
# default telnet port of the running server.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue