Added check to location setter that makes sure to stop a location-loop forming. This closes #454.

This commit is contained in:
Griatch 2014-02-15 21:05:23 +01:00
parent 471e1bbf9b
commit 393a3e5e73
2 changed files with 54 additions and 2 deletions

View file

@ -17,6 +17,7 @@ transparently through the decorating TypeClass.
import traceback import traceback
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from src.typeclasses.models import (TypedObject, TagHandler, NickHandler, from src.typeclasses.models import (TypedObject, TagHandler, NickHandler,
AliasHandler, AttributeHandler) AliasHandler, AttributeHandler)
@ -26,7 +27,8 @@ from src.commands.cmdsethandler import CmdSetHandler
from src.commands import cmdhandler from src.commands import cmdhandler
from src.scripts.scripthandler import ScriptHandler from src.scripts.scripthandler import ScriptHandler
from src.utils import logger from src.utils import logger
from src.utils.utils import make_iter, to_str, to_unicode, variable_from_module from src.utils.utils import (make_iter, to_str, to_unicode,
variable_from_module, dbref)
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
@ -180,6 +182,56 @@ class ObjectDB(TypedObject):
_GA(self, "save")() _GA(self, "save")()
cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del) cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del)
# location getsetter
def __location_get(self):
"Get location"
loc = _GA(_GA(self, "dbobj"), "db_location")
return _GA(loc, "typeclass") if loc else loc
def __location_set(self, location):
"Set location, checking for loops and allowing dbref"
if isinstance(location, (basestring, int)):
# allow setting of #dbref
dbid = dbref(location, reqhash=False)
if dbid:
try:
location = ObjectDB.objects.get(id=dbid)
except ObjectDoesNotExist:
# maybe it is just a name that happens to look like a dbid
pass
try:
def is_loc_loop(loc, depth=0):
"Recursively traverse target location, trying to catch a loop."
if depth > 10:
return
elif loc == self:
raise RuntimeError
elif loc == None:
raise RuntimeWarning
return is_loc_loop(_GA(_GA(loc, "dbobj"), "db_location"), depth + 1)
try:
is_loc_loop(location)
except RuntimeWarning:
pass
# actually set the field
_SA(_GA(self, "dbobj"), "db_location", _GA(location, "dbobj") if location else location)
_GA(_GA(self, "dbobj"), "save")(update_fields=["db_location"])
except RuntimeError:
errmsg = "Error: %s.location = %s creates a location loop." % (self.key, location)
logger.log_errmsg(errmsg)
raise RuntimeError(errmsg)
except Exception, e:
errmsg = "Error (%s): %s is not a valid location." % (str(e), location)
logger.log_errmsg(errmsg)
raise Exception(errmsg)
def __location_del(self):
"Cleably delete the location reference"
_SA(_GA(self, "dbobj"), "db_location", None)
_GA(_GA(self, "dbobj"), "save")(upate_fields=["db_location"])
location = property(__location_get, __location_set, __location_del)
class Meta: class Meta:
"Define Django meta options" "Define Django meta options"
verbose_name = "Object" verbose_name = "Object"

View file

@ -2,7 +2,7 @@
Django ID mapper Django ID mapper
Modified for Evennia by making sure that no model references Modified for Evennia by making sure that no model references
leave caching unexpectedly (no use if WeakRefs). leave caching unexpectedly (no use of WeakRefs).
Also adds cache_size() for monitoring the size of the cache. Also adds cache_size() for monitoring the size of the cache.
""" """