Made scripts and typeclassed objects remember db_typeclass_path at all times - a temporarily faulty typeclass will no longer mess up things forever after. Refined and optimized the way typeclasses are cached and loaded, minimizing db hits. The default result when trying to create an object or script with a typeclass that is faulty/not found is now to fail. The previous way, to create an entity anyway using defaults was hard to debug and caused confusion. Resolves issue 175.
This commit is contained in:
parent
6cb2b8b745
commit
ddfd8120bb
10 changed files with 208 additions and 173 deletions
|
|
@ -19,21 +19,20 @@ class BodyFunctions(Script):
|
||||||
This class defines the script itself
|
This class defines the script itself
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
self.key = "bodyfunction"
|
self.key = "bodyfunction"
|
||||||
self.desc = "Adds various timed events to a character."
|
self.desc = "Adds various timed events to a character."
|
||||||
self.interval = 20 # seconds
|
self.interval = 20 # seconds
|
||||||
#self.repeats = 5 # repeat only a certain number of times
|
#self.repeats = 5 # repeat only a certain number of times
|
||||||
self.start_delay = True # wait self.interval until first call
|
self.start_delay = True # wait self.interval until first call
|
||||||
self.persistent = False
|
self.persistent = True
|
||||||
|
|
||||||
def at_repeat(self):
|
def at_repeat(self):
|
||||||
"""
|
"""
|
||||||
This gets called every self.interval seconds. We make
|
This gets called every self.interval seconds. We make
|
||||||
a random check here so as to only return 33% of the time.
|
a random check here so as to only return 33% of the time.
|
||||||
"""
|
"""
|
||||||
#)
|
if random.random() < 0.66:
|
||||||
if random.random() < 0.33:
|
|
||||||
# no message this time
|
# no message this time
|
||||||
return
|
return
|
||||||
rand = random.random()
|
rand = random.random()
|
||||||
|
|
|
||||||
|
|
@ -788,6 +788,7 @@ class ObjectDB(TypedObject):
|
||||||
objects to their respective home locations, as well as clean
|
objects to their respective home locations, as well as clean
|
||||||
up all exits to/from the object.
|
up all exits to/from the object.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if not self.at_object_delete():
|
if not self.at_object_delete():
|
||||||
# this is an extra pre-check
|
# this is an extra pre-check
|
||||||
# run before deletion mechanism
|
# run before deletion mechanism
|
||||||
|
|
|
||||||
|
|
@ -253,3 +253,15 @@ class ScriptDB(TypedObject):
|
||||||
default_typeclass_path = settings.DEFAULT_SCRIPT_TYPECLASS
|
default_typeclass_path = settings.DEFAULT_SCRIPT_TYPECLASS
|
||||||
except:
|
except:
|
||||||
default_typeclass_path = "src.scripts.scripts.DoNothing"
|
default_typeclass_path = "src.scripts.scripts.DoNothing"
|
||||||
|
|
||||||
|
def at_typeclass_error(self):
|
||||||
|
"""
|
||||||
|
If this is called, it means the typeclass has a critical
|
||||||
|
error and cannot even be loaded. We don't allow a script
|
||||||
|
to be created under those circumstances. Already created,
|
||||||
|
permanent scripts are set to already be active so they
|
||||||
|
won't get activated now (next reboot the bug might be fixed)
|
||||||
|
"""
|
||||||
|
# By setting is_active=True, we trick the script not to run "again".
|
||||||
|
self.is_active = True
|
||||||
|
return super(ScriptDB, self).at_typeclass_error()
|
||||||
|
|
|
||||||
|
|
@ -54,10 +54,10 @@ class ScriptHandler(object):
|
||||||
or a python path to such a class object.
|
or a python path to such a class object.
|
||||||
key - optional identifier for the script (often set in script definition)
|
key - optional identifier for the script (often set in script definition)
|
||||||
autostart - start the script upon adding it
|
autostart - start the script upon adding it
|
||||||
"""
|
"""
|
||||||
script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart)
|
script = create.create_script(scriptclass, key=key, obj=self.obj, autostart=autostart)
|
||||||
if not script:
|
if not script:
|
||||||
logger.log_errmsg("Script %s failed to be created/start." % scriptclass)
|
logger.log_errmsg("Script %s could not be created and/or started." % scriptclass)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,11 +378,11 @@ class Script(ScriptClass):
|
||||||
# Some useful default Script types used by Evennia.
|
# Some useful default Script types used by Evennia.
|
||||||
|
|
||||||
class DoNothing(Script):
|
class DoNothing(Script):
|
||||||
"An script that does nothing. Used as default."
|
"An script that does nothing. Used as default fallback."
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
"Setup the script"
|
"Setup the script"
|
||||||
self.key = "sys_do_nothing"
|
self.key = "sys_do_nothing"
|
||||||
self.desc = "This does nothing."
|
self.desc = "This is a placeholder script."
|
||||||
|
|
||||||
class CheckSessions(Script):
|
class CheckSessions(Script):
|
||||||
"Check sessions regularly."
|
"Check sessions regularly."
|
||||||
|
|
|
||||||
|
|
@ -52,14 +52,6 @@ PARENTS = {
|
||||||
"channel":"src.comms.models.Channel",
|
"channel":"src.comms.models.Channel",
|
||||||
"helpentry":"src.help.models.HelpEntry"}
|
"helpentry":"src.help.models.HelpEntry"}
|
||||||
|
|
||||||
# cached typeclasses for all typed models
|
|
||||||
TYPECLASS_CACHE = {}
|
|
||||||
|
|
||||||
def reset():
|
|
||||||
"Clean out the typeclass cache"
|
|
||||||
global TYPECLASS_CACHE
|
|
||||||
TYPECLASS_CACHE = {}
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Attributes
|
# Attributes
|
||||||
|
|
@ -426,6 +418,10 @@ class TypedObject(SharedMemoryModel):
|
||||||
# Database manager
|
# Database manager
|
||||||
objects = managers.TypedObjectManager()
|
objects = managers.TypedObjectManager()
|
||||||
|
|
||||||
|
# object cache and flags
|
||||||
|
cached_typeclass_path = ""
|
||||||
|
cached_typeclass = None
|
||||||
|
|
||||||
# lock handler self.locks
|
# lock handler self.locks
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"We must initialize the parent first - important!"
|
"We must initialize the parent first - important!"
|
||||||
|
|
@ -485,17 +481,22 @@ class TypedObject(SharedMemoryModel):
|
||||||
#@property
|
#@property
|
||||||
def typeclass_path_get(self):
|
def typeclass_path_get(self):
|
||||||
"Getter. Allows for value = self.typeclass_path"
|
"Getter. Allows for value = self.typeclass_path"
|
||||||
|
typeclass_path = object.__getattribute__(self, 'cached_typeclass_path')
|
||||||
|
if typeclass_path:
|
||||||
|
return typeclass_path
|
||||||
return self.db_typeclass_path
|
return self.db_typeclass_path
|
||||||
#@typeclass_path.setter
|
#@typeclass_path.setter
|
||||||
def typeclass_path_set(self, value):
|
def typeclass_path_set(self, value):
|
||||||
"Setter. Allows for self.typeclass_path = value"
|
"Setter. Allows for self.typeclass_path = value"
|
||||||
self.db_typeclass_path = value
|
self.db_typeclass_path = value
|
||||||
self.save()
|
self.save()
|
||||||
|
object.__setattr__(self, "cached_typeclass_path", value)
|
||||||
#@typeclass_path.deleter
|
#@typeclass_path.deleter
|
||||||
def typeclass_path_del(self):
|
def typeclass_path_del(self):
|
||||||
"Deleter. Allows for del self.typeclass_path"
|
"Deleter. Allows for del self.typeclass_path"
|
||||||
self.db_typeclass_path = None
|
self.db_typeclass_path = ""
|
||||||
self.save()
|
self.save()
|
||||||
|
self.cached_typeclass_path = ""
|
||||||
typeclass_path = property(typeclass_path_get, typeclass_path_set, typeclass_path_del)
|
typeclass_path = property(typeclass_path_get, typeclass_path_set, typeclass_path_del)
|
||||||
|
|
||||||
# date_created property
|
# date_created property
|
||||||
|
|
@ -613,96 +614,61 @@ class TypedObject(SharedMemoryModel):
|
||||||
it allows for extending the Typed object for all different
|
it allows for extending the Typed object for all different
|
||||||
types of objects that the game needs. This property
|
types of objects that the game needs. This property
|
||||||
handles loading and initialization of the typeclass on the fly.
|
handles loading and initialization of the typeclass on the fly.
|
||||||
"""
|
|
||||||
|
|
||||||
def errmsg(message):
|
|
||||||
"""
|
|
||||||
Helper function to display error.
|
|
||||||
"""
|
|
||||||
infochan = None
|
|
||||||
cmessage = message
|
|
||||||
try:
|
|
||||||
from src.comms.models import Channel
|
|
||||||
infochan = settings.CHANNEL_MUDINFO
|
|
||||||
infochan = Channel.objects.get_channel(infochan[0])
|
|
||||||
if infochan:
|
|
||||||
cname = infochan.key
|
|
||||||
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n')])
|
|
||||||
infochan.msg(message)
|
|
||||||
logger.log_errmsg(cmessage)
|
|
||||||
else:
|
|
||||||
# no mudinfo channel is found. Log instead.
|
|
||||||
cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
|
|
||||||
logger.log_errmsg(cmessage)
|
|
||||||
except Exception, e:
|
|
||||||
if ServerConfig.objects.conf("server_starting_mode"):
|
|
||||||
print cmessage
|
|
||||||
else:
|
|
||||||
logger.log_trace(cmessage)
|
|
||||||
|
|
||||||
path = object.__getattribute__(self, 'db_typeclass_path')
|
Note: The liberal use of object.__getattribute__ and __setattr__ (instead
|
||||||
#print "typeclass_loading:", id(self), path
|
of normal dot notation) is due to optimization: it avoids calling
|
||||||
|
the custom self.__getattribute__ more than necessary.
|
||||||
|
"""
|
||||||
|
|
||||||
|
path = object.__getattribute__(self, "cached_typeclass_path")
|
||||||
|
if not path:
|
||||||
|
path = object.__getattribute__(self, 'db_typeclass_path')
|
||||||
|
typeclass = object.__getattribute__(self, "cached_typeclass")
|
||||||
|
try:
|
||||||
|
if typeclass and object.__getattribute__(typeclass, "path") == path:
|
||||||
|
return typeclass
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
errstring = ""
|
errstring = ""
|
||||||
if not path:
|
if not path:
|
||||||
# this means we should get the default obj
|
# this means we should get the default obj without giving errors.
|
||||||
# without giving errors.
|
return object.__getattribute__(self, "get_default_typeclass")(cache=True, silent=True, save=True)
|
||||||
defpath = object.__getattribute__(self, 'default_typeclass_path')
|
|
||||||
typeclass = object.__getattribute__(self, '_path_import')(defpath)
|
|
||||||
#typeclass = self._path_import(defpath)
|
|
||||||
else:
|
else:
|
||||||
# handle loading/importing of typeclasses, searching all paths.
|
# handle loading/importing of typeclasses, searching all paths.
|
||||||
# (self.typeclss_paths is a shortcut to settings.TYPECLASS_*_PATH
|
# (self.typeclass_paths is a shortcut to settings.TYPECLASS_*_PATHS
|
||||||
# where '*' is either OBJECT, SCRIPT or PLAYER depending on the typed
|
# where '*' is either OBJECT, SCRIPT or PLAYER depending on the typed
|
||||||
# object).
|
# entities).
|
||||||
typeclass_paths = [path] + ["%s.%s" % (prefix, path) for prefix in self.typeclass_paths]
|
typeclass_paths = [path] + ["%s.%s" % (prefix, path) for prefix in object.__getattribute__(self, 'typeclass_paths')]
|
||||||
|
|
||||||
for tpath in typeclass_paths:
|
for tpath in typeclass_paths:
|
||||||
# try to find any matches to the typeclass path, in all possible permutations..
|
|
||||||
typeclass = TYPECLASS_CACHE.get(tpath, None)
|
# try to import and analyze the result
|
||||||
if typeclass:
|
|
||||||
# we've imported this before. We're done.
|
|
||||||
return typeclass
|
|
||||||
# not in cache. Try to import anew.
|
|
||||||
typeclass = object.__getattribute__(self, "_path_import")(tpath)
|
typeclass = object.__getattribute__(self, "_path_import")(tpath)
|
||||||
if callable(typeclass):
|
if callable(typeclass):
|
||||||
# don't return yet, we must cache this further down.
|
# we succeeded to import. Cache and return.
|
||||||
errstring = ""
|
object.__setattr__(self, 'db_typeclass_path', tpath)
|
||||||
break # leave test loop
|
object.__getattribute__(self, 'save')()
|
||||||
|
object.__setattr__(self, "cached_typeclass_path", tpath)
|
||||||
|
object.__setattr__(self, "cached_typeclass", typeclass)
|
||||||
|
return typeclass
|
||||||
elif hasattr(typeclass, '__file__'):
|
elif hasattr(typeclass, '__file__'):
|
||||||
errstring += "\n%s seems to be just the path to a module. You need" % tpath
|
errstring += "\n%s seems to be just the path to a module. You need" % tpath
|
||||||
errstring += " to specify the actual typeclass name inside the module too."
|
errstring += " to specify the actual typeclass name inside the module too."
|
||||||
else:
|
else:
|
||||||
errstring += "\n%s" % typeclass # this will hold an error message.
|
errstring += "\n%s" % typeclass # this will hold a growing error message.
|
||||||
|
# If we reach this point we couldn't import any typeclasses. Return default. It's up to the calling
|
||||||
if not callable(typeclass):
|
# method to use e.g. self.is_typeclass() to detect that the result is not the one asked for.
|
||||||
# Still not a valid import. Fallback to default.
|
object.__getattribute__(self, "_display_errmsg")(errstring)
|
||||||
# Note that we don't save to this changed path! Fix the typeclass
|
return object.__getattribute__(self, "get_default_typeclass")(cache=False, silent=False, save=False)
|
||||||
# definition instead.
|
|
||||||
defpath = object.__getattribute__(self, "default_typeclass_path")
|
|
||||||
errstring += "\n\nUsing Default class '%s'." % defpath
|
|
||||||
typeclass = object.__getattribute__(self, "_path_import")(defpath)
|
|
||||||
errmsg(errstring)
|
|
||||||
if not callable(typeclass):
|
|
||||||
# if typeclass still doesn't exist at this point, we're in trouble.
|
|
||||||
# fall back to hardcoded core class which is wrong for e.g. scripts/players etc.
|
|
||||||
errstring = " %s\n%s" % (typeclass, errstring)
|
|
||||||
errstring += " Default class '%s' failed to load." % defpath
|
|
||||||
defpath = "src.objects.objects.Object"
|
|
||||||
errstring += "\n Using Evennia's default class '%s'." % defpath
|
|
||||||
typeclass = object.__getattribute__(self, "_path_import")(defpath)
|
|
||||||
errmsg(errstring)
|
|
||||||
else:
|
|
||||||
TYPECLASS_CACHE[path] = typeclass
|
|
||||||
return typeclass
|
|
||||||
|
|
||||||
#@typeclass.deleter
|
#@typeclass.deleter
|
||||||
def typeclass_del(self):
|
def typeclass_del(self):
|
||||||
"Deleter. Allows for del self.typeclass (don't allow deletion)"
|
"Deleter. Disallow 'del self.typeclass'"
|
||||||
raise Exception("The typeclass property should never be deleted!")
|
raise Exception("The typeclass property should never be deleted, only changed in-place!")
|
||||||
|
|
||||||
# typeclass property
|
# typeclass property
|
||||||
typeclass = property(typeclass_get, fdel=typeclass_del)
|
typeclass = property(typeclass_get, fdel=typeclass_del)
|
||||||
|
|
||||||
|
|
||||||
def _path_import(self, path):
|
def _path_import(self, path):
|
||||||
"""
|
"""
|
||||||
|
|
@ -731,38 +697,95 @@ class TypedObject(SharedMemoryModel):
|
||||||
errstring = "No class '%s' was found in module '%s'."
|
errstring = "No class '%s' was found in module '%s'."
|
||||||
errstring = errstring % (class_name, modpath)
|
errstring = errstring % (class_name, modpath)
|
||||||
except Exception:
|
except Exception:
|
||||||
trc = traceback.format_exc()
|
trc = traceback.format_exc()
|
||||||
errstring = "\n%sException importing '%s'." % (trc, path)
|
errstring = "\n%sException importing '%s'." % (trc, path)
|
||||||
# return the error.
|
# return the error.
|
||||||
return errstring
|
return errstring
|
||||||
|
|
||||||
|
def _display_errmsg(self, message):
|
||||||
|
"""
|
||||||
|
Helper function to display error.
|
||||||
|
"""
|
||||||
|
infochan = None
|
||||||
|
cmessage = message
|
||||||
|
try:
|
||||||
|
from src.comms.models import Channel
|
||||||
|
infochan = settings.CHANNEL_MUDINFO
|
||||||
|
infochan = Channel.objects.get_channel(infochan[0])
|
||||||
|
if infochan:
|
||||||
|
cname = infochan.key
|
||||||
|
cmessage = "\n".join(["[%s]: %s" % (cname, line) for line in message.split('\n')])
|
||||||
|
infochan.msg(message)
|
||||||
|
else:
|
||||||
|
# no mudinfo channel is found. Log instead.
|
||||||
|
cmessage = "\n".join(["[NO MUDINFO CHANNEL]: %s" % line for line in message.split('\n')])
|
||||||
|
logger.log_errmsg(cmessage)
|
||||||
|
except Exception, e:
|
||||||
|
if ServerConfig.objects.conf("server_starting_mode"):
|
||||||
|
print cmessage
|
||||||
|
else:
|
||||||
|
logger.log_trace(cmessage)
|
||||||
|
|
||||||
def is_typeclass(self, other_typeclass, exact=False):
|
def get_default_typeclass(self, cache=False, silent=False, save=False):
|
||||||
|
"""
|
||||||
|
This is called when a typeclass fails to
|
||||||
|
load for whatever reason.
|
||||||
|
Overload this in different entities.
|
||||||
|
|
||||||
|
Default operation is to load a default typeclass.
|
||||||
|
"""
|
||||||
|
defpath = object.__getattribute__(self, "default_typeclass_path")
|
||||||
|
typeclass = object.__getattribute__(self, "_path_import")(defpath)
|
||||||
|
# if not silent:
|
||||||
|
# #errstring = "\n\nUsing Default class '%s'." % defpath
|
||||||
|
# object.__getattribute__(self, "_display_errmsg")(errstring)
|
||||||
|
|
||||||
|
if not callable(typeclass):
|
||||||
|
# if typeclass still doesn't exist at this point, we're in trouble.
|
||||||
|
# fall back to hardcoded core class which is wrong for e.g. scripts/players etc.
|
||||||
|
failpath = defpath
|
||||||
|
defpath = "src.objects.objects.Object"
|
||||||
|
typeclass = object.__getattribute__(self, "_path_import")(defpath)
|
||||||
|
if not silent:
|
||||||
|
errstring += " %s\n%s" % (typeclass, errstring)
|
||||||
|
errstring += " Default class '%s' failed to load." % failpath
|
||||||
|
errstring += "\n Using Evennia's default class '%s'." % defpath
|
||||||
|
object.__getattribute__(self, "_display_errmsg")(errstring)
|
||||||
|
if not callable(typeclass):
|
||||||
|
# if this is still giving an error, Evennia is wrongly configured or buggy
|
||||||
|
raise Exception("CRITICAL ERROR: The final fallback typeclass %s cannot load!!" % defpath)
|
||||||
|
if save:
|
||||||
|
object.__setattr__(self, 'db_typeclass_path', defpath)
|
||||||
|
object.__getattribute__(self, 'save')()
|
||||||
|
if cache:
|
||||||
|
object.__setattr__(self, "cached_typeclass_path", defpath)
|
||||||
|
object.__setattr__(self, "cached_typeclass", typeclass)
|
||||||
|
return typeclass
|
||||||
|
|
||||||
|
def is_typeclass(self, typeclass, exact=False):
|
||||||
"""
|
"""
|
||||||
Returns true if this object has this type
|
Returns true if this object has this type
|
||||||
OR has a typeclass which is an subclass of
|
OR has a typeclass which is an subclass of
|
||||||
the given typeclass.
|
the given typeclass.
|
||||||
|
|
||||||
other_typeclass - can be a class object or the
|
typeclass - can be a class object or the
|
||||||
python path to such an object.
|
python path to such an object to match against.
|
||||||
|
|
||||||
exact - returns true only if the object's
|
exact - returns true only if the object's
|
||||||
type is exactly this typeclass, ignoring
|
type is exactly this typeclass, ignoring
|
||||||
parents.
|
parents.
|
||||||
"""
|
"""
|
||||||
if callable(other_typeclass):
|
try:
|
||||||
# this is an actual class object. Get the path to it.
|
typeclass = typeclass.path
|
||||||
cls = other_typeclass.__class__
|
except AttributeError:
|
||||||
other_typeclass = "%s.%s" % (cls.__module__, cls.__name__)
|
pass
|
||||||
if not other_typeclass:
|
if exact:
|
||||||
return False
|
current_path = object.__getattribute__(self, "cached_typeclass_path")
|
||||||
if self.db_typeclass_path == other_typeclass:
|
return typeclass and current_path == typeclass
|
||||||
return True
|
else:
|
||||||
if not exact:
|
# check parent chain
|
||||||
# check the parent chain.
|
|
||||||
return any([cls for cls in self.typeclass.mro()
|
return any([cls for cls in self.typeclass.mro()
|
||||||
if other_typeclass == "%s.%s" % (cls.__module__,
|
if "%s.%s" % (cls.__module__, cls.__name__) == typeclass])
|
||||||
cls.__name__)])
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Object manipulation methods
|
# Object manipulation methods
|
||||||
|
|
@ -826,8 +849,6 @@ class TypedObject(SharedMemoryModel):
|
||||||
new_typeclass.basetype_setup()
|
new_typeclass.basetype_setup()
|
||||||
new_typeclass.at_object_creation()
|
new_typeclass.at_object_creation()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Attribute handler methods
|
# Attribute handler methods
|
||||||
#
|
#
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ from django.conf import settings
|
||||||
# to *in-game* safety (if you can edit typeclasses you have
|
# to *in-game* safety (if you can edit typeclasses you have
|
||||||
# full access anyway), so no protection against changing
|
# full access anyway), so no protection against changing
|
||||||
# e.g. 'locks' or 'permissions' should go here.
|
# e.g. 'locks' or 'permissions' should go here.
|
||||||
PROTECTED = ['id', 'dbobj', 'db', 'objects', 'typeclass',
|
PROTECTED = ['id', 'dbobj', 'db', 'ndb', 'objects', 'typeclass',
|
||||||
'attr', 'save', 'delete']
|
'attr', 'save', 'delete']
|
||||||
|
|
||||||
# If this is true, all non-protected property assignments
|
# If this is true, all non-protected property assignments
|
||||||
|
|
@ -70,8 +70,8 @@ class TypeClass(object):
|
||||||
o = dbobj.object_class(dbobj) : this is used when dbobj.object_class is already set.
|
o = dbobj.object_class(dbobj) : this is used when dbobj.object_class is already set.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# typecheck of dbobj - we can't allow it to be added here unless
|
# typecheck of dbobj - we can't allow it to be added here
|
||||||
# unless it's really a TypedObject.
|
# unless it's really a TypedObject.
|
||||||
dbobj_cls = object.__getattribute__(dbobj, '__class__')
|
dbobj_cls = object.__getattribute__(dbobj, '__class__')
|
||||||
dbobj_mro = object.__getattribute__(dbobj_cls, '__mro__')
|
dbobj_mro = object.__getattribute__(dbobj_cls, '__mro__')
|
||||||
if not any('src.typeclasses.models.TypedObject'
|
if not any('src.typeclasses.models.TypedObject'
|
||||||
|
|
@ -83,16 +83,13 @@ class TypeClass(object):
|
||||||
# store the needed things on the typeclass
|
# store the needed things on the typeclass
|
||||||
object.__setattr__(self, '_protected_attrs', PROTECTED)
|
object.__setattr__(self, '_protected_attrs', PROTECTED)
|
||||||
|
|
||||||
# sync the database object to this typeclass.
|
# # sync the database object to this typeclass.
|
||||||
cls = object.__getattribute__(self, '__class__')
|
# cls = object.__getattribute__(self, '__class__')
|
||||||
db_typeclass_path = "%s.%s" % (object.__getattribute__(cls, '__module__'),
|
# db_typeclass_path = "%s.%s" % (object.__getattribute__(cls, '__module__'),
|
||||||
object.__getattribute__(cls, '__name__'))
|
# object.__getattribute__(cls, '__name__'))
|
||||||
if not object.__getattribute__(dbobj, "db_typeclass_path") == db_typeclass_path:
|
# if not object.__getattribute__(dbobj, "db_typeclass_path") == db_typeclass_path:
|
||||||
object.__setattr__(dbobj, "db_typeclass_path", db_typeclass_path)
|
# object.__setattr__(dbobj, "db_typeclass_path", db_typeclass_path)
|
||||||
object.__getattribute__(dbobj, "save")()
|
# object.__getattribute__(dbobj, "save")()
|
||||||
|
|
||||||
# (The inheriting typed object classes often extend this __init__ to
|
|
||||||
# add handlers etc.)
|
|
||||||
|
|
||||||
def __getattribute__(self, propname):
|
def __getattribute__(self, propname):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,9 @@ Models covered:
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from src.utils import logger, utils
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.utils.utils import is_iter, has_parent
|
from src.utils import logger, utils, idmapper
|
||||||
|
from src.utils.utils import is_iter, has_parent, inherits_from
|
||||||
|
|
||||||
#
|
#
|
||||||
# Game Object creation
|
# Game Object creation
|
||||||
|
|
@ -45,30 +46,28 @@ def create_object(typeclass, key=None, location=None,
|
||||||
# deferred import to avoid loops
|
# deferred import to avoid loops
|
||||||
from src.objects.objects import Object
|
from src.objects.objects import Object
|
||||||
from src.objects.models import ObjectDB
|
from src.objects.models import ObjectDB
|
||||||
#print "in create_object", typeclass
|
|
||||||
if isinstance(typeclass, ObjectDB):
|
if isinstance(typeclass, ObjectDB):
|
||||||
# this is already an objectdb instance!
|
# this is already an objectdb instance, extract its typeclass
|
||||||
new_db_object = typeclass
|
typeclass = new_db_object.typeclass.path
|
||||||
typeclass = new_db_object.typeclass
|
elif isinstance(typeclass, Object) or utils.inherits_from(typeclass, Object):
|
||||||
elif isinstance(typeclass, Object):
|
# this is already an object typeclass, extract its path
|
||||||
# this is already an object typeclass!
|
typeclass = typeclass.path
|
||||||
new_db_object = typeclass.dbobj
|
|
||||||
typeclass = typeclass.__class__
|
# create new database object
|
||||||
else:
|
new_db_object = ObjectDB()
|
||||||
# create database object
|
|
||||||
new_db_object = ObjectDB()
|
# assign the typeclass
|
||||||
#new_db_object = ObjectDB()
|
typeclass = utils.to_unicode(typeclass)
|
||||||
if not callable(typeclass):
|
new_db_object.typeclass_path = typeclass
|
||||||
# this means typeclass might be a path. If not,
|
# this will either load the typeclass or the default one
|
||||||
# the type mechanism will automatically assign
|
typeclass = new_db_object.typeclass
|
||||||
# the BASE_OBJECT_TYPE from settings.
|
|
||||||
if typeclass:
|
if not object.__getattribute__(new_db_object, "is_typeclass")(typeclass, exact=True):
|
||||||
typeclass = utils.to_unicode(typeclass)
|
# this will fail if we gave a typeclass as input and it still gave us a default
|
||||||
new_db_object.typeclass_path = typeclass
|
SharedMemoryModel.delete(new_db_object)
|
||||||
new_db_object.save()
|
return None
|
||||||
# this will either load the typeclass or the default one
|
|
||||||
typeclass = new_db_object.typeclass
|
|
||||||
new_db_object.save()
|
|
||||||
# the name/key is often set later in the typeclass. This
|
# the name/key is often set later in the typeclass. This
|
||||||
# is set here as a failsafe.
|
# is set here as a failsafe.
|
||||||
if key:
|
if key:
|
||||||
|
|
@ -142,33 +141,37 @@ def create_script(typeclass, key=None, obj=None, locks=None, autostart=True):
|
||||||
See src.scripts.manager for methods to manipulate existing
|
See src.scripts.manager for methods to manipulate existing
|
||||||
scripts in the database.
|
scripts in the database.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# deferred import to avoid loops.
|
# deferred import to avoid loops.
|
||||||
from src.scripts.scripts import Script
|
from src.scripts.scripts import Script
|
||||||
#print "in create_script", typeclass
|
#print "in create_script", typeclass
|
||||||
from src.scripts.models import ScriptDB
|
from src.scripts.models import ScriptDB
|
||||||
|
|
||||||
if isinstance(typeclass, ScriptDB):
|
if isinstance(typeclass, ScriptDB):
|
||||||
#print "this is already a scriptdb instance!"
|
# this is already an objectdb instance, extract its typeclass
|
||||||
new_db_object = typeclass
|
typeclass = new_db_object.typeclass.path
|
||||||
typeclass = new_db_object.typeclass
|
elif isinstance(typeclass, Script) or utils.inherits_from(typeclass, Script):
|
||||||
elif isinstance(typeclass, Script):
|
# this is already an object typeclass, extract its path
|
||||||
#print "this is already an object typeclass!", typeclass, typeclass.__class__
|
typeclass = typeclass.path
|
||||||
new_db_object = typeclass.dbobj
|
# create new database object
|
||||||
typeclass = typeclass.__class__
|
new_db_object = ScriptDB()
|
||||||
else:
|
|
||||||
# create a new instance.
|
# assign the typeclass
|
||||||
new_db_object = ScriptDB()
|
typeclass = utils.to_unicode(typeclass)
|
||||||
#new_db_object = ScriptDB()
|
new_db_object.typeclass_path = typeclass
|
||||||
if not callable(typeclass):
|
# this will either load the typeclass or the default one
|
||||||
# try to load this in case it's a path
|
typeclass = new_db_object.typeclass
|
||||||
if typeclass:
|
|
||||||
typeclass = utils.to_unicode(typeclass)
|
if not object.__getattribute__(new_db_object, "is_typeclass")(typeclass, exact=True):
|
||||||
new_db_object.db_typeclass_path = typeclass
|
# this can happen if the default was loaded (due to
|
||||||
new_db_object.save()
|
# inability to load given typeclass), which we
|
||||||
# this will load either the typeclass or the default one
|
# don't accept during creation.
|
||||||
typeclass = new_db_object.typeclass
|
SharedMemoryModel.delete(new_db_object)
|
||||||
new_db_object.save()
|
return None
|
||||||
|
|
||||||
# the typeclass is initialized
|
# the typeclass is initialized
|
||||||
new_script = typeclass(new_db_object)
|
new_script = typeclass(new_db_object)
|
||||||
|
|
||||||
# store variables on the typeclass (which means
|
# store variables on the typeclass (which means
|
||||||
# it's actually transparently stored on the db object)
|
# it's actually transparently stored on the db object)
|
||||||
|
|
||||||
|
|
@ -388,8 +391,8 @@ def create_player(name, email, password,
|
||||||
new_user = User.objects.create_user(name, email, password)
|
new_user = User.objects.create_user(name, email, password)
|
||||||
|
|
||||||
# create the associated Player for this User, and tie them together
|
# create the associated Player for this User, and tie them together
|
||||||
new_player = PlayerDB(db_key=name, user=new_user)
|
new_player = PlayerDB(db_key=name, user=new_user)
|
||||||
new_player.save()
|
new_player.save()
|
||||||
|
|
||||||
new_player.basetype_setup() # setup the basic locks and cmdset
|
new_player.basetype_setup() # setup the basic locks and cmdset
|
||||||
# call hook method (may override default permissions)
|
# call hook method (may override default permissions)
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,11 @@ class SharedMemoryModel(Model):
|
||||||
"""
|
"""
|
||||||
if instance._get_pk_val() is not None:
|
if instance._get_pk_val() is not None:
|
||||||
cls.__instance_cache__[instance._get_pk_val()] = instance
|
cls.__instance_cache__[instance._get_pk_val()] = instance
|
||||||
if hasattr(instance, 'at_cache') and callable(instance.at_cache):
|
try:
|
||||||
instance.at_cache()
|
object.__getattribute__(instance, "at_cache")()
|
||||||
|
except (TypeError, AttributeError):
|
||||||
|
pass
|
||||||
|
|
||||||
#key = "%s-%s" % (cls, instance.pk)
|
#key = "%s-%s" % (cls, instance.pk)
|
||||||
#TCACHE[key] = instance
|
#TCACHE[key] = instance
|
||||||
#print "cached: %s (%s: %s) (total cached: %s)" % (instance, cls.__name__, len(cls.__instance_cache__), len(TCACHE))
|
#print "cached: %s (%s: %s) (total cached: %s)" % (instance, cls.__name__, len(cls.__instance_cache__), len(TCACHE))
|
||||||
|
|
|
||||||
|
|
@ -122,8 +122,7 @@ def reload_modules():
|
||||||
else:
|
else:
|
||||||
cemit_info(" No modules reloaded.")
|
cemit_info(" No modules reloaded.")
|
||||||
|
|
||||||
# clean out cache dictionary of typeclasses, exits and channels
|
# clean out cache dictionary of typeclasses, exits and channels
|
||||||
typeclassmodels.reset()
|
|
||||||
channelhandler.CHANNELHANDLER.update()
|
channelhandler.CHANNELHANDLER.update()
|
||||||
|
|
||||||
# run through all objects in database, forcing re-caching.
|
# run through all objects in database, forcing re-caching.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue