Made a new version of create_object function, and made Objects creatable using o=Object().
This commit is contained in:
parent
969b947ba0
commit
24764743ff
8 changed files with 858 additions and 1173 deletions
|
|
@ -7,6 +7,5 @@ Also, the initiated object manager is available as src.objects.manager.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
#from src.objects.objects import *
|
#from src.objects.objects import *
|
||||||
from src.objects.models import ObjectDB
|
#from src.objects.models import ObjectDB
|
||||||
|
#manager = ObjectDB.objects
|
||||||
manager = ObjectDB.objects
|
|
||||||
|
|
|
||||||
|
|
@ -14,81 +14,14 @@ the database object. Like everything else, they can be accessed
|
||||||
transparently through the decorating TypeClass.
|
transparently through the decorating TypeClass.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
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 django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
||||||
from src.typeclasses.models import TypedObject, NickHandler
|
from src.typeclasses.models import TypedObject
|
||||||
from src.objects.manager import ObjectDBManager
|
from src.objects.manager import ObjectDBManager
|
||||||
from src.players.models import PlayerDB
|
|
||||||
from src.commands.cmdsethandler import CmdSetHandler
|
|
||||||
from src.commands import cmdhandler
|
|
||||||
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, lazy_property,
|
from src.utils.utils import (make_iter, dbref)
|
||||||
variable_from_module, dbref)
|
|
||||||
|
|
||||||
MULTISESSION_MODE = settings.MULTISESSION_MODE
|
|
||||||
from django.utils.translation import ugettext as _
|
|
||||||
|
|
||||||
#__all__ = ("ObjectDB", )
|
|
||||||
|
|
||||||
_ScriptDB = None
|
|
||||||
_AT_SEARCH_RESULT = variable_from_module(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
|
|
||||||
_SESSIONS = None
|
|
||||||
|
|
||||||
_GA = object.__getattribute__
|
|
||||||
_SA = object.__setattr__
|
|
||||||
_DA = object.__delattr__
|
|
||||||
|
|
||||||
# the sessid_max is based on the length of the db_sessid csv field (excluding commas)
|
|
||||||
_SESSID_MAX = 16 if MULTISESSION_MODE in (1, 3) else 1
|
|
||||||
|
|
||||||
class SessidHandler(object):
|
|
||||||
"""
|
|
||||||
Handles the get/setting of the sessid
|
|
||||||
comma-separated integer field
|
|
||||||
"""
|
|
||||||
def __init__(self, obj):
|
|
||||||
self.obj = obj
|
|
||||||
self._cache = set()
|
|
||||||
self._recache()
|
|
||||||
|
|
||||||
def _recache(self):
|
|
||||||
self._cache = list(set(int(val) for val in (_GA(self.obj, "db_sessid") or "").split(",") if val))
|
|
||||||
|
|
||||||
def get(self):
|
|
||||||
"Returns a list of one or more session ids"
|
|
||||||
return self._cache
|
|
||||||
|
|
||||||
def add(self, sessid):
|
|
||||||
"Add sessid to handler"
|
|
||||||
_cache = self._cache
|
|
||||||
if sessid not in _cache:
|
|
||||||
if len(_cache) >= _SESSID_MAX:
|
|
||||||
return
|
|
||||||
_cache.append(sessid)
|
|
||||||
_SA(self.obj, "db_sessid", ",".join(str(val) for val in _cache))
|
|
||||||
_GA(self.obj, "save")(update_fields=["db_sessid"])
|
|
||||||
|
|
||||||
def remove(self, sessid):
|
|
||||||
"Remove sessid from handler"
|
|
||||||
_cache = self._cache
|
|
||||||
if sessid in _cache:
|
|
||||||
_cache.remove(sessid)
|
|
||||||
_SA(self.obj, "db_sessid", ",".join(str(val) for val in _cache))
|
|
||||||
_GA(self.obj, "save")(update_fields=["db_sessid"])
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
"Clear sessids"
|
|
||||||
self._cache = []
|
|
||||||
_SA(self.obj, "db_sessid", None)
|
|
||||||
_GA(self.obj, "save")(update_fields=["db_sessid"])
|
|
||||||
|
|
||||||
def count(self):
|
|
||||||
"Return amount of sessions connected"
|
|
||||||
return len(self._cache)
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -173,27 +106,7 @@ class ObjectDB(TypedObject):
|
||||||
# Database manager
|
# Database manager
|
||||||
objects = ObjectDBManager()
|
objects = ObjectDBManager()
|
||||||
|
|
||||||
# caches for quick lookups of typeclass loading.
|
# field-related field-related properties
|
||||||
_typeclass_paths = settings.OBJECT_TYPECLASS_PATHS
|
|
||||||
_default_typeclass_path = settings.BASE_OBJECT_TYPECLASS or "src.objects.objects.Object"
|
|
||||||
|
|
||||||
# lazy-load handlers
|
|
||||||
@lazy_property
|
|
||||||
def cmdset(self):
|
|
||||||
return CmdSetHandler(self, True)
|
|
||||||
|
|
||||||
@lazy_property
|
|
||||||
def scripts(self):
|
|
||||||
return ScriptHandler(self)
|
|
||||||
|
|
||||||
@lazy_property
|
|
||||||
def nicks(self):
|
|
||||||
return NickHandler(self)
|
|
||||||
|
|
||||||
@lazy_property
|
|
||||||
def sessid(self):
|
|
||||||
return SessidHandler(self)
|
|
||||||
|
|
||||||
def _at_db_player_postsave(self):
|
def _at_db_player_postsave(self):
|
||||||
"""
|
"""
|
||||||
This hook is called automatically after the player field is saved.
|
This hook is called automatically after the player field is saved.
|
||||||
|
|
@ -201,30 +114,21 @@ class ObjectDB(TypedObject):
|
||||||
# we need to re-cache this for superusers to bypass.
|
# we need to re-cache this for superusers to bypass.
|
||||||
self.locks.cache_lock_bypass(self)
|
self.locks.cache_lock_bypass(self)
|
||||||
|
|
||||||
# cmdset_storage property. We use a custom wrapper to manage this. This also
|
# cmdset_storage property handling
|
||||||
# seems very sensitive to caching, so leaving it be for now. /Griatch
|
|
||||||
#@property
|
|
||||||
def __cmdset_storage_get(self):
|
def __cmdset_storage_get(self):
|
||||||
"""
|
"getter"
|
||||||
Getter. Allows for value = self.name.
|
storage = self.db_cmdset_storage
|
||||||
Returns a list of cmdset_storage.
|
|
||||||
"""
|
|
||||||
storage = _GA(self, "db_cmdset_storage")
|
|
||||||
# we need to check so storage is not None
|
|
||||||
return [path.strip() for path in storage.split(',')] if storage else []
|
return [path.strip() for path in storage.split(',')] if storage else []
|
||||||
#@cmdset_storage.setter
|
|
||||||
def __cmdset_storage_set(self, value):
|
def __cmdset_storage_set(self, value):
|
||||||
"""
|
"setter"
|
||||||
Setter. Allows for self.name = value.
|
self.db_cmdset_storage = ",".join(str(val).strip() for val in make_iter(value))
|
||||||
Stores as a comma-separated string.
|
self.save(update_fields=["db_cmdset_storage"])
|
||||||
"""
|
|
||||||
_SA(self, "db_cmdset_storage", ",".join(str(val).strip() for val in make_iter(value)))
|
|
||||||
_GA(self, "save")()
|
|
||||||
#@cmdset_storage.deleter
|
|
||||||
def __cmdset_storage_del(self):
|
def __cmdset_storage_del(self):
|
||||||
"Deleter. Allows for del self.name"
|
"deleter"
|
||||||
_SA(self, "db_cmdset_storage", None)
|
self.db_cmdset_storage = None
|
||||||
_GA(self, "save")()
|
self.save(update_fields=["db_cmdset_storage"])
|
||||||
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
|
# location getsetter
|
||||||
|
|
@ -280,567 +184,3 @@ class ObjectDB(TypedObject):
|
||||||
verbose_name = "Object"
|
verbose_name = "Object"
|
||||||
verbose_name_plural = "Objects"
|
verbose_name_plural = "Objects"
|
||||||
|
|
||||||
#
|
|
||||||
# ObjectDB class access methods/properties
|
|
||||||
#
|
|
||||||
|
|
||||||
#@property
|
|
||||||
def __sessions_get(self):
|
|
||||||
"""
|
|
||||||
Retrieve sessions connected to this object.
|
|
||||||
"""
|
|
||||||
# if the player is not connected, this will simply be an empty list.
|
|
||||||
if _GA(self, "db_player"):
|
|
||||||
return _GA(_GA(self, "db_player"), "get_all_sessions")()
|
|
||||||
return []
|
|
||||||
sessions = property(__sessions_get)
|
|
||||||
|
|
||||||
#@property
|
|
||||||
def __has_player_get(self):
|
|
||||||
"""
|
|
||||||
Convenience function for checking if an active player is
|
|
||||||
currently connected to this object
|
|
||||||
"""
|
|
||||||
return any(_GA(self, "sessions"))
|
|
||||||
has_player = property(__has_player_get)
|
|
||||||
is_player = property(__has_player_get)
|
|
||||||
|
|
||||||
#@property
|
|
||||||
def __is_superuser_get(self):
|
|
||||||
"Check if user has a player, and if so, if it is a superuser."
|
|
||||||
return (_GA(self, "db_player") and _GA(_GA(self, "db_player"), "is_superuser")
|
|
||||||
and not _GA(_GA(self, "db_player"), "attributes").get("_quell"))
|
|
||||||
is_superuser = property(__is_superuser_get)
|
|
||||||
|
|
||||||
# contents
|
|
||||||
|
|
||||||
def contents_get(self, exclude=None):
|
|
||||||
"""
|
|
||||||
Returns the contents of this object, i.e. all
|
|
||||||
objects that has this object set as its location.
|
|
||||||
This should be publically available.
|
|
||||||
|
|
||||||
exclude is one or more objects to not return
|
|
||||||
"""
|
|
||||||
if exclude:
|
|
||||||
return ObjectDB.objects.get_contents(self, excludeobj=make_iter(exclude))
|
|
||||||
return ObjectDB.objects.get_contents(self)
|
|
||||||
contents = property(contents_get)
|
|
||||||
|
|
||||||
#@property
|
|
||||||
def __exits_get(self):
|
|
||||||
"""
|
|
||||||
Returns all exits from this object, i.e. all objects
|
|
||||||
at this location having the property destination != None.
|
|
||||||
"""
|
|
||||||
return [exi for exi in _GA(self, "contents")
|
|
||||||
if exi.destination]
|
|
||||||
exits = property(__exits_get)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Main Search method
|
|
||||||
#
|
|
||||||
|
|
||||||
def search(self, searchdata,
|
|
||||||
global_search=False,
|
|
||||||
use_nicks=True, # should this default to off?
|
|
||||||
typeclass=None,
|
|
||||||
location=None,
|
|
||||||
attribute_name=None,
|
|
||||||
quiet=False,
|
|
||||||
exact=False):
|
|
||||||
"""
|
|
||||||
Returns the typeclass of an Object matching a search string/condition
|
|
||||||
|
|
||||||
Perform a standard object search in the database, handling
|
|
||||||
multiple results and lack thereof gracefully. By default, only
|
|
||||||
objects in self's current location or inventory is searched.
|
|
||||||
Note: to find Players, use eg. ev.player_search.
|
|
||||||
|
|
||||||
Inputs:
|
|
||||||
|
|
||||||
searchdata (str or obj): Primary search criterion. Will be matched
|
|
||||||
against object.key (with object.aliases second) unless
|
|
||||||
the keyword attribute_name specifies otherwise.
|
|
||||||
Special strings:
|
|
||||||
#<num> - search by unique dbref. This is always
|
|
||||||
a global search.
|
|
||||||
me,self - self-reference to this object
|
|
||||||
<num>-<string> - can be used to differentiate
|
|
||||||
between multiple same-named matches
|
|
||||||
global_search (bool): Search all objects globally. This is overruled
|
|
||||||
by "location" keyword.
|
|
||||||
use_nicks (bool): Use nickname-replace (nicktype "object") on the
|
|
||||||
search string
|
|
||||||
typeclass (str or Typeclass, or list of either): Limit search only
|
|
||||||
to Objects with this typeclass. May be a list of typeclasses
|
|
||||||
for a broader search.
|
|
||||||
location (Object): Specify a location to search, if different from the
|
|
||||||
self's given location plus its contents. This can also
|
|
||||||
be a list of locations.
|
|
||||||
attribute_name (str): Define which property to search. If set, no
|
|
||||||
key+alias search will be performed. This can be used to
|
|
||||||
search database fields (db_ will be automatically
|
|
||||||
appended), and if that fails, it will try to return
|
|
||||||
objects having Attributes with this name and value
|
|
||||||
equal to searchdata. A special use is to search for
|
|
||||||
"key" here if you want to do a key-search without
|
|
||||||
including aliases.
|
|
||||||
quiet (bool) - don't display default error messages - this tells the
|
|
||||||
search method that the user wants to handle all errors
|
|
||||||
themselves. It also changes the return value type, see
|
|
||||||
below.
|
|
||||||
exact (bool) - if unset (default) - prefers to match to beginning of
|
|
||||||
string rather than not matching at all. If set, requires
|
|
||||||
exact mathing of entire string.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
quiet=False (default):
|
|
||||||
no match or multimatch:
|
|
||||||
auto-echoes errors to self.msg, then returns None
|
|
||||||
(results are handled by settings.SEARCH_AT_RESULT
|
|
||||||
and settings.SEARCH_AT_MULTIMATCH_INPUT)
|
|
||||||
match:
|
|
||||||
a unique object match
|
|
||||||
quiet=True:
|
|
||||||
returns a list of 0, 1 or more matches
|
|
||||||
|
|
||||||
"""
|
|
||||||
is_string = isinstance(searchdata, basestring)
|
|
||||||
|
|
||||||
if use_nicks:
|
|
||||||
# do nick-replacement on search
|
|
||||||
searchdata = self.nicks.nickreplace(searchdata, categories=("object", "player"), include_player=True)
|
|
||||||
|
|
||||||
candidates=None
|
|
||||||
if(global_search or (is_string and searchdata.startswith("#") and
|
|
||||||
len(searchdata) > 1 and searchdata[1:].isdigit())):
|
|
||||||
# only allow exact matching if searching the entire database
|
|
||||||
# or unique #dbrefs
|
|
||||||
exact = True
|
|
||||||
elif location:
|
|
||||||
# location(s) were given
|
|
||||||
candidates = []
|
|
||||||
for obj in make_iter(location):
|
|
||||||
candidates.extend(obj.contents)
|
|
||||||
else:
|
|
||||||
# local search. Candidates are self.contents, self.location
|
|
||||||
# and self.location.contents
|
|
||||||
location = self.location
|
|
||||||
candidates = self.contents
|
|
||||||
if location:
|
|
||||||
candidates = candidates + [location] + location.contents
|
|
||||||
else:
|
|
||||||
# normally we are included in location.contents
|
|
||||||
candidates.append(self)
|
|
||||||
|
|
||||||
results = ObjectDB.objects.object_search(searchdata,
|
|
||||||
attribute_name=attribute_name,
|
|
||||||
typeclass=typeclass,
|
|
||||||
candidates=candidates,
|
|
||||||
exact=exact)
|
|
||||||
if quiet:
|
|
||||||
return results
|
|
||||||
return _AT_SEARCH_RESULT(self, searchdata, results, global_search)
|
|
||||||
|
|
||||||
def search_player(self, searchdata, quiet=False):
|
|
||||||
"""
|
|
||||||
Simple shortcut wrapper to search for players, not characters.
|
|
||||||
|
|
||||||
searchdata - search criterion - the key or dbref of the player
|
|
||||||
to search for. If this is "here" or "me", search
|
|
||||||
for the player connected to this object.
|
|
||||||
quiet - return the results as a list rather than echo eventual
|
|
||||||
standard error messages.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
quiet=False (default):
|
|
||||||
no match or multimatch:
|
|
||||||
auto-echoes errors to self.msg, then returns None
|
|
||||||
(results are handled by settings.SEARCH_AT_RESULT
|
|
||||||
and settings.SEARCH_AT_MULTIMATCH_INPUT)
|
|
||||||
match:
|
|
||||||
a unique player match
|
|
||||||
quiet=True:
|
|
||||||
no match or multimatch:
|
|
||||||
returns None or list of multi-matches
|
|
||||||
match:
|
|
||||||
a unique object match
|
|
||||||
"""
|
|
||||||
results = PlayerDB.objects.player_search(searchdata)
|
|
||||||
if quiet:
|
|
||||||
return results
|
|
||||||
return _AT_SEARCH_RESULT(self, searchdata, results, global_search=True)
|
|
||||||
|
|
||||||
#
|
|
||||||
# Execution/action methods
|
|
||||||
#
|
|
||||||
|
|
||||||
def execute_cmd(self, raw_string, sessid=None, **kwargs):
|
|
||||||
"""
|
|
||||||
Do something as this object. This method is a copy of the execute_
|
|
||||||
cmd method on the session. This is never called normally, it's only
|
|
||||||
used when wanting specifically to let an object be the caller of a
|
|
||||||
command. It makes use of nicks of eventual connected players as well.
|
|
||||||
|
|
||||||
Argument:
|
|
||||||
raw_string (string) - raw command input
|
|
||||||
sessid (int) - optional session id to return results to
|
|
||||||
**kwargs - other keyword arguments will be added to the found command
|
|
||||||
object instace as variables before it executes. This is
|
|
||||||
unused by default Evennia but may be used to set flags and
|
|
||||||
change operating paramaters for commands at run-time.
|
|
||||||
|
|
||||||
Returns Deferred - this is an asynchronous Twisted object that will
|
|
||||||
not fire until the command has actually finished executing. To
|
|
||||||
overload this one needs to attach callback functions to it, with
|
|
||||||
addCallback(function). This function will be called with an
|
|
||||||
eventual return value from the command execution.
|
|
||||||
|
|
||||||
This return is not used at all by Evennia by default, but might
|
|
||||||
be useful for coders intending to implement some sort of nested
|
|
||||||
command structure.
|
|
||||||
"""
|
|
||||||
# nick replacement - we require full-word matching.
|
|
||||||
|
|
||||||
# do text encoding conversion
|
|
||||||
raw_string = to_unicode(raw_string)
|
|
||||||
raw_string = self.nicks.nickreplace(raw_string,
|
|
||||||
categories=("inputline", "channel"), include_player=True)
|
|
||||||
return cmdhandler.cmdhandler(self, raw_string, callertype="object", sessid=sessid, **kwargs)
|
|
||||||
|
|
||||||
def msg(self, text=None, from_obj=None, sessid=0, **kwargs):
|
|
||||||
"""
|
|
||||||
Emits something to a session attached to the object.
|
|
||||||
|
|
||||||
message (str): The message to send
|
|
||||||
from_obj (obj): object that is sending.
|
|
||||||
data (object): an optional data object that may or may not
|
|
||||||
be used by the protocol.
|
|
||||||
sessid (int): sessid to relay to, if any.
|
|
||||||
If set to 0 (default), use either from_obj.sessid (if set) or self.sessid automatically
|
|
||||||
If None, echo to all connected sessions
|
|
||||||
|
|
||||||
When this message is called, from_obj.at_msg_send and self.at_msg_receive are called.
|
|
||||||
|
|
||||||
"""
|
|
||||||
global _SESSIONS
|
|
||||||
if not _SESSIONS:
|
|
||||||
from src.server.sessionhandler import SESSIONS as _SESSIONS
|
|
||||||
|
|
||||||
text = to_str(text, force_string=True) if text else ""
|
|
||||||
|
|
||||||
if "data" in kwargs:
|
|
||||||
# deprecation warning
|
|
||||||
logger.log_depmsg("ObjectDB.msg(): 'data'-dict keyword is deprecated. Use **kwargs instead.")
|
|
||||||
data = kwargs.pop("data")
|
|
||||||
if isinstance(data, dict):
|
|
||||||
kwargs.update(data)
|
|
||||||
|
|
||||||
if from_obj:
|
|
||||||
# call hook
|
|
||||||
try:
|
|
||||||
from_obj.at_msg_send(text=text, to_obj=self, **kwargs)
|
|
||||||
except Exception:
|
|
||||||
logger.log_trace()
|
|
||||||
try:
|
|
||||||
if not self.at_msg_receive(text=text, **kwargs):
|
|
||||||
# if at_msg_receive returns false, we abort message to this object
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
logger.log_trace()
|
|
||||||
|
|
||||||
sessions = _SESSIONS.session_from_sessid([sessid] if sessid else make_iter(_GA(self, "sessid").get()))
|
|
||||||
for session in sessions:
|
|
||||||
session.msg(text=text, **kwargs)
|
|
||||||
|
|
||||||
def msg_contents(self, message, exclude=None, from_obj=None, **kwargs):
|
|
||||||
"""
|
|
||||||
Emits something to all objects inside an object.
|
|
||||||
|
|
||||||
exclude is a list of objects not to send to. See self.msg() for
|
|
||||||
more info.
|
|
||||||
"""
|
|
||||||
contents = _GA(self, "contents")
|
|
||||||
if exclude:
|
|
||||||
exclude = make_iter(exclude)
|
|
||||||
contents = [obj for obj in contents if obj not in exclude]
|
|
||||||
for obj in contents:
|
|
||||||
obj.msg(message, from_obj=from_obj, **kwargs)
|
|
||||||
|
|
||||||
def move_to(self, destination, quiet=False,
|
|
||||||
emit_to_obj=None, use_destination=True, to_none=False):
|
|
||||||
"""
|
|
||||||
Moves this object to a new location.
|
|
||||||
|
|
||||||
Moves this object to a new location. Note that if <destination> is an
|
|
||||||
exit object (i.e. it has "destination"!=None), the move_to will
|
|
||||||
happen to this destination and -not- into the exit object itself, unless
|
|
||||||
use_destination=False. Note that no lock checks are done by this
|
|
||||||
function, such things are assumed to have been handled before calling
|
|
||||||
move_to.
|
|
||||||
|
|
||||||
destination: (Object) Reference to the object to move to. This
|
|
||||||
can also be an exit object, in which case the destination
|
|
||||||
property is used as destination.
|
|
||||||
quiet: (bool) If true, don't emit left/arrived messages.
|
|
||||||
emit_to_obj: (Object) object to receive error messages
|
|
||||||
use_destination (bool): Default is for objects to use the "destination"
|
|
||||||
property of destinations as the target to move to.
|
|
||||||
Turning off this keyword allows objects to move
|
|
||||||
"inside" exit objects.
|
|
||||||
to_none - allow destination to be None. Note that no hooks are run when
|
|
||||||
moving to a None location. If you want to run hooks,
|
|
||||||
run them manually (and make sure they can manage None
|
|
||||||
locations).
|
|
||||||
|
|
||||||
Returns True/False depending on if there were problems with the move.
|
|
||||||
This method may also return various error messages to the
|
|
||||||
emit_to_obj.
|
|
||||||
"""
|
|
||||||
def logerr(string=""):
|
|
||||||
trc = traceback.format_exc()
|
|
||||||
errstring = "%s%s" % (trc, string)
|
|
||||||
logger.log_trace()
|
|
||||||
_GA(self, "msg")(errstring)
|
|
||||||
|
|
||||||
errtxt = _("Couldn't perform move ('%s'). Contact an admin.")
|
|
||||||
if not emit_to_obj:
|
|
||||||
emit_to_obj = self
|
|
||||||
|
|
||||||
if not destination:
|
|
||||||
if to_none:
|
|
||||||
# immediately move to None. There can be no hooks called since
|
|
||||||
# there is no destination to call them with.
|
|
||||||
self.location = None
|
|
||||||
return True
|
|
||||||
emit_to_obj.msg(_("The destination doesn't exist."))
|
|
||||||
return
|
|
||||||
if destination.destination and use_destination:
|
|
||||||
# traverse exits
|
|
||||||
destination = destination.destination
|
|
||||||
|
|
||||||
# Before the move, call eventual pre-commands.
|
|
||||||
try:
|
|
||||||
if not self.at_before_move(destination):
|
|
||||||
return
|
|
||||||
except Exception:
|
|
||||||
logerr(errtxt % "at_before_move()")
|
|
||||||
#emit_to_obj.msg(errtxt % "at_before_move()")
|
|
||||||
#logger.log_trace()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Save the old location
|
|
||||||
source_location = _GA(self, "location")
|
|
||||||
if not source_location:
|
|
||||||
# there was some error in placing this room.
|
|
||||||
# we have to set one or we won't be able to continue
|
|
||||||
if _GA(self, "home"):
|
|
||||||
source_location = _GA(self, "home")
|
|
||||||
else:
|
|
||||||
default_home = ObjectDB.objects.get_id(settings.DEFAULT_HOME)
|
|
||||||
source_location = default_home
|
|
||||||
|
|
||||||
# Call hook on source location
|
|
||||||
try:
|
|
||||||
source_location.at_object_leave(self, destination)
|
|
||||||
except Exception:
|
|
||||||
logerr(errtxt % "at_object_leave()")
|
|
||||||
#emit_to_obj.msg(errtxt % "at_object_leave()")
|
|
||||||
#logger.log_trace()
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not quiet:
|
|
||||||
#tell the old room we are leaving
|
|
||||||
try:
|
|
||||||
self.announce_move_from(destination)
|
|
||||||
except Exception:
|
|
||||||
logerr(errtxt % "at_announce_move()")
|
|
||||||
#emit_to_obj.msg(errtxt % "at_announce_move()" )
|
|
||||||
#logger.log_trace()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Perform move
|
|
||||||
try:
|
|
||||||
#print "move_to location:", destination
|
|
||||||
_SA(self, "location", destination)
|
|
||||||
except Exception:
|
|
||||||
emit_to_obj.msg(errtxt % "location change")
|
|
||||||
logger.log_trace()
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not quiet:
|
|
||||||
# Tell the new room we are there.
|
|
||||||
try:
|
|
||||||
self.announce_move_to(source_location)
|
|
||||||
except Exception:
|
|
||||||
logerr(errtxt % "announce_move_to()")
|
|
||||||
#emit_to_obj.msg(errtxt % "announce_move_to()")
|
|
||||||
#logger.log_trace()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Perform eventual extra commands on the receiving location
|
|
||||||
# (the object has already arrived at this point)
|
|
||||||
try:
|
|
||||||
destination.at_object_receive(self, source_location)
|
|
||||||
except Exception:
|
|
||||||
logerr(errtxt % "at_object_receive()")
|
|
||||||
#emit_to_obj.msg(errtxt % "at_object_receive()")
|
|
||||||
#logger.log_trace()
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Execute eventual extra commands on this object after moving it
|
|
||||||
# (usually calling 'look')
|
|
||||||
try:
|
|
||||||
self.at_after_move(source_location)
|
|
||||||
except Exception:
|
|
||||||
logerr(errtxt % "at_after_move")
|
|
||||||
#emit_to_obj.msg(errtxt % "at_after_move()")
|
|
||||||
#logger.log_trace()
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
#
|
|
||||||
# Object Swap, Delete and Cleanup methods
|
|
||||||
#
|
|
||||||
|
|
||||||
def clear_exits(self):
|
|
||||||
"""
|
|
||||||
Destroys all of the exits and any exits pointing to this
|
|
||||||
object as a destination.
|
|
||||||
"""
|
|
||||||
for out_exit in [exi for exi in ObjectDB.objects.get_contents(self) if exi.db_destination]:
|
|
||||||
out_exit.delete()
|
|
||||||
for in_exit in ObjectDB.objects.filter(db_destination=self):
|
|
||||||
in_exit.delete()
|
|
||||||
|
|
||||||
def clear_contents(self):
|
|
||||||
"""
|
|
||||||
Moves all objects (players/things) to their home
|
|
||||||
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)
|
|
||||||
if default_home.dbid == _GA(self, "dbid"):
|
|
||||||
# we are deleting default home!
|
|
||||||
default_home = None
|
|
||||||
except Exception:
|
|
||||||
string = _("Could not find default home '(#%d)'.")
|
|
||||||
logger.log_errmsg(string % default_home_id)
|
|
||||||
default_home = None
|
|
||||||
|
|
||||||
for obj in objs:
|
|
||||||
home = obj.home
|
|
||||||
# Obviously, we can't send it back to here.
|
|
||||||
if not home or (home and home.dbid == _GA(self, "dbid")):
|
|
||||||
obj.home = default_home
|
|
||||||
home = default_home
|
|
||||||
|
|
||||||
# If for some reason it's still None...
|
|
||||||
if not home:
|
|
||||||
string = "Missing default home, '%s(#%d)' "
|
|
||||||
string += "now has a null location."
|
|
||||||
obj.location = None
|
|
||||||
obj.msg(_("Something went wrong! You are dumped into nowhere. Contact an admin."))
|
|
||||||
logger.log_errmsg(string % (obj.name, obj.dbid))
|
|
||||||
return
|
|
||||||
|
|
||||||
if obj.has_player:
|
|
||||||
if home:
|
|
||||||
string = "Your current location has ceased to exist,"
|
|
||||||
string += " moving you to %s(#%d)."
|
|
||||||
obj.msg(_(string) % (home.name, home.dbid))
|
|
||||||
else:
|
|
||||||
# Famous last words: The player should never see this.
|
|
||||||
string = "This place should not exist ... contact an admin."
|
|
||||||
obj.msg(_(string))
|
|
||||||
obj.move_to(home)
|
|
||||||
|
|
||||||
def copy(self, new_key=None):
|
|
||||||
"""
|
|
||||||
Makes an identical copy of this object. If you want to customize the
|
|
||||||
copy by changing some settings, use ObjectDB.object.copy_object()
|
|
||||||
directly.
|
|
||||||
|
|
||||||
new_key (string) - new key/name of copied object. If new_key is not
|
|
||||||
specified, the copy will be named <old_key>_copy
|
|
||||||
by default.
|
|
||||||
Returns: Object (copy of this one)
|
|
||||||
"""
|
|
||||||
def find_clone_key():
|
|
||||||
"""
|
|
||||||
Append 01, 02 etc to obj.key. Checks next higher number in the
|
|
||||||
same location, then adds the next number available
|
|
||||||
|
|
||||||
returns the new clone name on the form keyXX
|
|
||||||
"""
|
|
||||||
key = _GA(self, "key")
|
|
||||||
num = 1
|
|
||||||
for obj in (obj for obj in self.location.contents
|
|
||||||
if obj.key.startswith(key) and
|
|
||||||
obj.key.lstrip(key).isdigit()):
|
|
||||||
num += 1
|
|
||||||
return "%s%03i" % (key, num)
|
|
||||||
new_key = new_key or find_clone_key()
|
|
||||||
return ObjectDB.objects.copy_object(self, new_key=new_key)
|
|
||||||
|
|
||||||
delete_iter = 0
|
|
||||||
def delete(self):
|
|
||||||
"""
|
|
||||||
Deletes this object.
|
|
||||||
Before deletion, this method makes sure to move all contained
|
|
||||||
objects to their respective home locations, as well as clean
|
|
||||||
up all exits to/from the object.
|
|
||||||
"""
|
|
||||||
global _ScriptDB
|
|
||||||
if not _ScriptDB:
|
|
||||||
from src.scripts.models import ScriptDB as _ScriptDB
|
|
||||||
|
|
||||||
if _GA(self, "delete_iter") > 0:
|
|
||||||
# make sure to only call delete once on this object
|
|
||||||
# (avoid recursive loops)
|
|
||||||
return False
|
|
||||||
|
|
||||||
if not self.at_object_delete():
|
|
||||||
# this is an extra pre-check
|
|
||||||
# run before deletion mechanism
|
|
||||||
# is kicked into gear.
|
|
||||||
_SA(self, "delete_iter", 0)
|
|
||||||
return False
|
|
||||||
|
|
||||||
self.delete_iter += 1
|
|
||||||
|
|
||||||
# See if we need to kick the player off.
|
|
||||||
|
|
||||||
for session in _GA(self, "sessions"):
|
|
||||||
session.msg(_("Your character %s has been destroyed.") % _GA(self, "key"))
|
|
||||||
# no need to disconnect, Player just jumps to OOC mode.
|
|
||||||
# sever the connection (important!)
|
|
||||||
if _GA(self, 'player'):
|
|
||||||
_SA(_GA(self, "player"), "character", None)
|
|
||||||
_SA(self, "player", None)
|
|
||||||
|
|
||||||
for script in _ScriptDB.objects.get_all_scripts_on_obj(self):
|
|
||||||
script.stop()
|
|
||||||
#for script in _GA(self, "scripts").all():
|
|
||||||
# script.stop()
|
|
||||||
|
|
||||||
# if self.player:
|
|
||||||
# self.player.user.is_active = False
|
|
||||||
# self.player.user.save(
|
|
||||||
|
|
||||||
# Destroy any exits to and from this room, if any
|
|
||||||
_GA(self, "clear_exits")()
|
|
||||||
# Clear out any non-exit objects located within the object
|
|
||||||
_GA(self, "clear_contents")()
|
|
||||||
_GA(self, "attributes").clear()
|
|
||||||
_GA(self, "nicks").clear()
|
|
||||||
_GA(self, "aliases").clear()
|
|
||||||
|
|
||||||
# Perform the deletion of the object
|
|
||||||
super(ObjectDB, self).delete()
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,7 +5,7 @@ Player that are controlled by the server.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.players.player import Player
|
from src.players.player import DefaultPlayer
|
||||||
from src.scripts.scripts import Script
|
from src.scripts.scripts import Script
|
||||||
from src.commands.command import Command
|
from src.commands.command import Command
|
||||||
from src.commands.cmdset import CmdSet
|
from src.commands.cmdset import CmdSet
|
||||||
|
|
@ -87,7 +87,7 @@ class BotCmdSet(CmdSet):
|
||||||
|
|
||||||
# Bot base class
|
# Bot base class
|
||||||
|
|
||||||
class Bot(Player):
|
class Bot(DefaultPlayer):
|
||||||
"""
|
"""
|
||||||
A Bot will start itself when the server
|
A Bot will start itself when the server
|
||||||
starts (it will generally not do so
|
starts (it will generally not do so
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,10 @@ Everything starts at handle_setup()
|
||||||
import django
|
import django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
from src.utils import create
|
from src.utils import create
|
||||||
from django.utils.translation import ugettext as _
|
from src.utils.utils import class_from_module
|
||||||
|
|
||||||
|
|
||||||
def create_config_values():
|
def create_config_values():
|
||||||
"""
|
"""
|
||||||
|
|
@ -26,10 +26,10 @@ def get_god_player():
|
||||||
"""
|
"""
|
||||||
Creates the god user.
|
Creates the god user.
|
||||||
"""
|
"""
|
||||||
PlayerDB = get_user_model()
|
Player = class_from_module(settings.BASE_PLAYER_TYPECLASS)
|
||||||
try:
|
try:
|
||||||
god_player = PlayerDB.objects.get(id=1)
|
god_player = Player.objects.get(id=1)
|
||||||
except PlayerDB.DoesNotExist:
|
except Player.DoesNotExist:
|
||||||
txt = "\n\nNo superuser exists yet. The superuser is the 'owner'"
|
txt = "\n\nNo superuser exists yet. The superuser is the 'owner'"
|
||||||
txt += "\account on the Evennia server. Create a new superuser using"
|
txt += "\account on the Evennia server. Create a new superuser using"
|
||||||
txt += "\nthe command"
|
txt += "\nthe command"
|
||||||
|
|
@ -78,6 +78,7 @@ def create_objects():
|
||||||
|
|
||||||
god_character.save()
|
god_character.save()
|
||||||
god_player.attributes.add("_first_login", True)
|
god_player.attributes.add("_first_login", True)
|
||||||
|
print god_character
|
||||||
god_player.attributes.add("_last_puppet", god_character)
|
god_player.attributes.add("_last_puppet", god_character)
|
||||||
god_player.db._playable_characters.append(god_character)
|
god_player.db._playable_characters.append(god_character)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,9 @@ import sys
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
import weakref
|
import weakref
|
||||||
from importlib import import_module
|
|
||||||
|
from django.db.models import signals
|
||||||
|
from django.dispatch import dispatcher
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
|
|
@ -38,6 +40,7 @@ from django.conf import settings
|
||||||
from django.utils.encoding import smart_str
|
from django.utils.encoding import smart_str
|
||||||
|
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
|
from src.utils.idmapper.base import SharedMemoryModelBase
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache
|
from src.server.caches import get_prop_cache, set_prop_cache
|
||||||
#from src.server.caches import set_attr_cache
|
#from src.server.caches import set_attr_cache
|
||||||
|
|
||||||
|
|
@ -48,7 +51,8 @@ from src.locks.lockhandler import LockHandler
|
||||||
#from src.utils import logger
|
#from src.utils import logger
|
||||||
from django.db.models.base import ModelBase
|
from django.db.models.base import ModelBase
|
||||||
from src.utils.utils import (
|
from src.utils.utils import (
|
||||||
make_iter, is_iter, to_str, inherits_from, lazy_property)
|
make_iter, is_iter, to_str, inherits_from, lazy_property,
|
||||||
|
class_from_module)
|
||||||
from src.utils.dbserialize import to_pickle, from_pickle
|
from src.utils.dbserialize import to_pickle, from_pickle
|
||||||
from src.utils.picklefield import PickledObjectField
|
from src.utils.picklefield import PickledObjectField
|
||||||
|
|
||||||
|
|
@ -56,6 +60,8 @@ __all__ = ("Attribute", "TypeNick", "TypedObject")
|
||||||
|
|
||||||
TICKER_HANDLER = None
|
TICKER_HANDLER = None
|
||||||
|
|
||||||
|
_DB_REGEX = re.compile(r"db$")
|
||||||
|
|
||||||
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
_PERMISSION_HIERARCHY = [p.lower() for p in settings.PERMISSION_HIERARCHY]
|
||||||
_TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE
|
_TYPECLASS_AGGRESSIVE_CACHE = settings.TYPECLASS_AGGRESSIVE_CACHE
|
||||||
|
|
||||||
|
|
@ -757,7 +763,16 @@ from django.apps.config import MODELS_MODULE_NAME
|
||||||
from django.db.models.fields.related import OneToOneField
|
from django.db.models.fields.related import OneToOneField
|
||||||
#/ django patch imports
|
#/ django patch imports
|
||||||
|
|
||||||
from src.utils.idmapper.base import SharedMemoryModelBase
|
|
||||||
|
# signal receivers. Assigned in __new__
|
||||||
|
def post_save(sender, instance, created, **kwargs):
|
||||||
|
"""
|
||||||
|
Is called Receive a signal just after the object is saved.
|
||||||
|
"""
|
||||||
|
if created:
|
||||||
|
instance.at_instance_creation()
|
||||||
|
#TODO - put OOB handler here?
|
||||||
|
|
||||||
|
|
||||||
class TypeclassBase(SharedMemoryModelBase):
|
class TypeclassBase(SharedMemoryModelBase):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1013,10 +1028,14 @@ class TypeclassBase(SharedMemoryModelBase):
|
||||||
|
|
||||||
new_class._prepare()
|
new_class._prepare()
|
||||||
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
|
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
|
||||||
return new_class
|
|
||||||
|
#return new_class
|
||||||
|
|
||||||
# /patch end
|
# /patch end
|
||||||
|
|
||||||
|
# attach signal
|
||||||
|
signals.post_save.connect(post_save, sender=new_class)
|
||||||
|
return new_class
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
@ -1082,22 +1101,14 @@ class TypedObject(SharedMemoryModel):
|
||||||
|
|
||||||
# typeclass mechanism
|
# typeclass mechanism
|
||||||
|
|
||||||
def _import_class(self, path):
|
|
||||||
path, clsname = path.rsplit(".", 1)
|
|
||||||
mod = import_module(path)
|
|
||||||
try:
|
|
||||||
return getattr(mod, clsname)
|
|
||||||
except AttributeError:
|
|
||||||
raise AttributeError("module '%s' has no attribute '%s'" % (path, clsname))
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
typeclass_path = kwargs.pop("typeclass", None)
|
typeclass_path = kwargs.pop("typeclass", None)
|
||||||
super(TypedObject, self).__init__(*args, **kwargs)
|
super(TypedObject, self).__init__(*args, **kwargs)
|
||||||
if typeclass_path:
|
if typeclass_path:
|
||||||
self.__class__ = self._import_class(typeclass_path)
|
self.__class__ = class_from_module(typeclass_path)
|
||||||
self.db_typclass_path = typeclass_path
|
self.db_typclass_path = typeclass_path
|
||||||
elif self.db_typeclass_path:
|
elif self.db_typeclass_path:
|
||||||
self.__class__ = self._import_class(self.db_typeclass_path)
|
self.__class__ = class_from_module(self.db_typeclass_path)
|
||||||
else:
|
else:
|
||||||
self.db_typeclass_path = "%s.%s" % (self.__module__, self.__class__.__name__)
|
self.db_typeclass_path = "%s.%s" % (self.__module__, self.__class__.__name__)
|
||||||
# important to put this at the end since _meta is based on the set __class__
|
# important to put this at the end since _meta is based on the set __class__
|
||||||
|
|
@ -1372,12 +1383,12 @@ class TypedObject(SharedMemoryModel):
|
||||||
self._is_deleted = True
|
self._is_deleted = True
|
||||||
super(TypedObject, self).delete()
|
super(TypedObject, self).delete()
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
#def save(self, *args, **kwargs):
|
||||||
"Block saving non-proxy typeclassed objects"
|
# "Block saving non-proxy typeclassed objects"
|
||||||
if not self._meta.proxy:
|
# if not self._meta.proxy:
|
||||||
raise RuntimeError("Don't create instances of %s, "
|
# raise RuntimeError("Don't create instances of %s, "
|
||||||
"use its child typeclasses instead." % self.__class__.__name__)
|
# "use its child typeclasses instead." % self.__class__.__name__)
|
||||||
super(TypedObject, self).save(*args, **kwargs)
|
# super(TypedObject, self).save(*args, **kwargs)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Memory management
|
# Memory management
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,12 @@ from django.conf import settings
|
||||||
from django.db import IntegrityError
|
from django.db import IntegrityError
|
||||||
from src.utils.idmapper.models import SharedMemoryModel
|
from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.utils import utils, logger
|
from src.utils import utils, logger
|
||||||
from src.utils.utils import make_iter
|
from src.utils.utils import make_iter, class_from_module, dbid_to_obj
|
||||||
|
|
||||||
# delayed imports
|
# delayed imports
|
||||||
_User = None
|
_User = None
|
||||||
_Object = None
|
|
||||||
_ObjectDB = None
|
_ObjectDB = None
|
||||||
|
_Object = None
|
||||||
_Script = None
|
_Script = None
|
||||||
_ScriptDB = None
|
_ScriptDB = None
|
||||||
_HelpEntry = None
|
_HelpEntry = None
|
||||||
|
|
@ -58,11 +58,7 @@ def handle_dbref(inp, objclass, raise_errors=True):
|
||||||
objects.
|
objects.
|
||||||
"""
|
"""
|
||||||
if not (isinstance(inp, basestring) and inp.startswith("#")):
|
if not (isinstance(inp, basestring) and inp.startswith("#")):
|
||||||
try:
|
return inp
|
||||||
return inp.dbobj
|
|
||||||
except AttributeError:
|
|
||||||
return inp
|
|
||||||
|
|
||||||
# a string, analyze it
|
# a string, analyze it
|
||||||
inp = inp.lstrip('#')
|
inp = inp.lstrip('#')
|
||||||
try:
|
try:
|
||||||
|
|
@ -87,119 +83,54 @@ def create_object(typeclass=None, key=None, location=None,
|
||||||
home=None, permissions=None, locks=None,
|
home=None, permissions=None, locks=None,
|
||||||
aliases=None, destination=None, report_to=None, nohome=False):
|
aliases=None, destination=None, report_to=None, nohome=False):
|
||||||
"""
|
"""
|
||||||
Create a new in-game object. Any game object is a combination
|
|
||||||
of a database object that stores data persistently to
|
|
||||||
the database, and a typeclass, which on-the-fly 'decorates'
|
|
||||||
the database object into whataver different type of object
|
|
||||||
it is supposed to be in the game.
|
|
||||||
|
|
||||||
See src.objects.managers for methods to manipulate existing objects
|
Create a new in-game object.
|
||||||
in the database. src.objects.objects holds the base typeclasses
|
|
||||||
and src.objects.models hold the database model.
|
|
||||||
|
|
||||||
report_to is an optional object for reporting errors to in string form.
|
keywords:
|
||||||
If report_to is not set, errors will be raised as en Exception
|
typeclass - class or python path to a typeclass
|
||||||
containing the error message. If set, this method will return
|
key - name of the new object. If not set, a name of #dbref will be set.
|
||||||
None upon errors.
|
home - obj or #dbref to use as the object's home location
|
||||||
nohome - this allows the creation of objects without a default home location;
|
permissions - a comma-separated string of permissions
|
||||||
this only used when creating the default location itself or during unittests
|
locks - one or more lockstrings, separated by semicolons
|
||||||
|
aliases - a list of alternative keys
|
||||||
|
destination - obj or #dbref to use as an Exit's target
|
||||||
|
|
||||||
|
nohome - this allows the creation of objects without a default home location;
|
||||||
|
only used when creating the default location itself or during unittests
|
||||||
"""
|
"""
|
||||||
global _Object, _ObjectDB
|
typeclass = typeclass if typeclass else settings.BASE_OBJECT_TYPECLASS
|
||||||
if not _Object:
|
|
||||||
from src.objects.objects import Object as _Object
|
|
||||||
if not _ObjectDB:
|
|
||||||
from src.objects.models import ObjectDB as _ObjectDB
|
|
||||||
|
|
||||||
# input validation
|
if isinstance(typeclass, basestring):
|
||||||
|
# a path is given. Load the actual typeclass
|
||||||
|
typeclass = class_from_module(typeclass, settings.OBJECT_TYPECLASS_PATHS)
|
||||||
|
|
||||||
if not typeclass:
|
# Setup input for the create command. We use ObjectDB as baseclass here
|
||||||
typeclass = settings.BASE_OBJECT_TYPECLASS
|
# to give us maximum freedom (the typeclasses will load
|
||||||
elif isinstance(typeclass, _ObjectDB):
|
# correctly when each object is recovered).
|
||||||
# this is already an objectdb instance, extract its typeclass
|
|
||||||
typeclass = typeclass.typeclass.path
|
|
||||||
elif isinstance(typeclass, _Object) or utils.inherits_from(typeclass, _Object):
|
|
||||||
# this is already an object typeclass, extract its path
|
|
||||||
typeclass = typeclass.path
|
|
||||||
typeclass = utils.to_unicode(typeclass)
|
|
||||||
|
|
||||||
# Setup input for the create command
|
location = dbid_to_obj(location, _ObjectDB)
|
||||||
|
destination = dbid_to_obj(destination, _ObjectDB)
|
||||||
location = handle_dbref(location, _ObjectDB)
|
home = dbid_to_obj(home, _ObjectDB)
|
||||||
destination = handle_dbref(destination, _ObjectDB)
|
|
||||||
home = handle_dbref(home, _ObjectDB)
|
|
||||||
if not home:
|
if not home:
|
||||||
try:
|
try:
|
||||||
home = handle_dbref(settings.DEFAULT_HOME, _ObjectDB) if not nohome else None
|
home = dbid_to_obj(settings.DEFAULT_HOME, _ObjectDB) if not nohome else None
|
||||||
except _ObjectDB.DoesNotExist:
|
except _ObjectDB.DoesNotExist:
|
||||||
raise _ObjectDB.DoesNotExist("settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed." %
|
raise _ObjectDB.DoesNotExist("settings.DEFAULT_HOME (= '%s') does not exist, or the setting is malformed." %
|
||||||
settings.DEFAULT_HOME)
|
settings.DEFAULT_HOME)
|
||||||
|
|
||||||
# create new database object all in one go
|
# create new instance
|
||||||
new_db_object = _ObjectDB(db_key=key, db_location=location,
|
new_object = typeclass(db_key=key, db_location=location,
|
||||||
db_destination=destination, db_home=home,
|
db_destination=destination, db_home=home,
|
||||||
db_typeclass_path=typeclass)
|
db_typeclass_path=typeclass.path)
|
||||||
|
# store the call signature for the signal
|
||||||
if not key:
|
new_object._createdict = {"key":key, "location":location, "destination":destination,
|
||||||
# the object should always have a key, so if not set we give a default
|
"home":home, "typeclass":typeclass.path, "permissions":permissions,
|
||||||
new_db_object.key = "#%i" % new_db_object.dbid
|
"locks":locks, "aliases":aliases, "destination":destination,
|
||||||
|
"report_to":report_to, "nohome":nohome}
|
||||||
# this will either load the typeclass or the default one (will also save object)
|
# this will trigger the save signal which in turn calls the
|
||||||
new_object = new_db_object.typeclass
|
# at_instance_creation hook on the typeclass, where the _createdict can be
|
||||||
|
# used.
|
||||||
if not _GA(new_object, "is_typeclass")(typeclass, exact=True):
|
new_object.save()
|
||||||
# this will fail if we gave a typeclass as input and it still
|
|
||||||
# gave us a default
|
|
||||||
try:
|
|
||||||
SharedMemoryModel.delete(new_db_object)
|
|
||||||
except AssertionError:
|
|
||||||
# this happens if object was never created
|
|
||||||
pass
|
|
||||||
if report_to:
|
|
||||||
report_to = handle_dbref(report_to, _ObjectDB)
|
|
||||||
_GA(report_to, "msg")("Error creating %s (%s).\n%s" % (new_db_object.key, typeclass,
|
|
||||||
_GA(new_db_object, "typeclass_last_errmsg")))
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
raise Exception(_GA(new_db_object, "typeclass_last_errmsg"))
|
|
||||||
|
|
||||||
# from now on we can use the typeclass object
|
|
||||||
# as if it was the database object.
|
|
||||||
|
|
||||||
# call the hook methods. This is where all at_creation
|
|
||||||
# customization happens as the typeclass stores custom
|
|
||||||
# things on its database object.
|
|
||||||
|
|
||||||
# note - this may override input keys, locations etc!
|
|
||||||
new_object.basetype_setup() # setup the basics of Exits, Characters etc.
|
|
||||||
new_object.at_object_creation()
|
|
||||||
|
|
||||||
# we want the input to override that set in the hooks, so
|
|
||||||
# we re-apply those if needed
|
|
||||||
if new_object.key != key:
|
|
||||||
new_object.key = key
|
|
||||||
if new_object.location != location:
|
|
||||||
new_object.location = location
|
|
||||||
if new_object.home != home:
|
|
||||||
new_object.home = home
|
|
||||||
if new_object.destination != destination:
|
|
||||||
new_object.destination = destination
|
|
||||||
|
|
||||||
# custom-given perms/locks do overwrite hooks
|
|
||||||
if permissions:
|
|
||||||
new_object.permissions.add(permissions)
|
|
||||||
if locks:
|
|
||||||
new_object.locks.add(locks)
|
|
||||||
if aliases:
|
|
||||||
new_object.aliases.add(aliases)
|
|
||||||
|
|
||||||
# trigger relevant move_to hooks in order to display messages.
|
|
||||||
if location:
|
|
||||||
location.at_object_receive(new_object, None)
|
|
||||||
new_object.at_after_move(None)
|
|
||||||
|
|
||||||
# post-hook setup (mainly used by Exits)
|
|
||||||
new_object.basetype_posthook_setup()
|
|
||||||
|
|
||||||
return new_object
|
return new_object
|
||||||
|
|
||||||
#alias for create_object
|
#alias for create_object
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,11 @@ import imp
|
||||||
import types
|
import types
|
||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
import importlib
|
|
||||||
import textwrap
|
import textwrap
|
||||||
import datetime
|
import datetime
|
||||||
import random
|
import random
|
||||||
import traceback
|
import traceback
|
||||||
|
from importlib import import_module
|
||||||
from inspect import ismodule
|
from inspect import ismodule
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from twisted.internet import threads, defer, reactor
|
from twisted.internet import threads, defer, reactor
|
||||||
|
|
@ -347,16 +347,43 @@ def dbref(dbref, reqhash=True):
|
||||||
Output is the integer part.
|
Output is the integer part.
|
||||||
"""
|
"""
|
||||||
if reqhash:
|
if reqhash:
|
||||||
return (int(dbref.lstrip('#')) if (isinstance(dbref, basestring) and
|
num = (int(dbref.lstrip('#')) if (isinstance(dbref, basestring) and
|
||||||
dbref.startswith("#") and
|
dbref.startswith("#") and
|
||||||
dbref.lstrip('#').isdigit())
|
dbref.lstrip('#').isdigit())
|
||||||
else None)
|
else None)
|
||||||
|
return num if num > 0 else None
|
||||||
elif isinstance(dbref, basestring):
|
elif isinstance(dbref, basestring):
|
||||||
dbref = dbref.lstrip('#')
|
dbref = dbref.lstrip('#')
|
||||||
return int(dbref) if dbref.isdigit() else None
|
return int(dbref) if dbref.isdigit() and int(dbref) > 0 else None
|
||||||
return dbref if isinstance(dbref, int) else None
|
else:
|
||||||
|
return dbref if isinstance(dbref, int) else None
|
||||||
|
|
||||||
|
|
||||||
|
def dbid_to_obj(inp, objclass, raise_errors=True):
|
||||||
|
"""
|
||||||
|
Convert a #dbid to a valid object of objclass. objclass
|
||||||
|
should be a valid object class to filter against (objclass.filter ...)
|
||||||
|
If not raise_errors is set, this will swallow errors of non-existing
|
||||||
|
objects.
|
||||||
|
"""
|
||||||
|
dbid = dbref(inp)
|
||||||
|
if not dbid:
|
||||||
|
# we only convert #dbrefs
|
||||||
|
return inp
|
||||||
|
try:
|
||||||
|
if int(inp) < 0:
|
||||||
|
return None
|
||||||
|
except ValueError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# if we get to this point, inp is an integer dbref; get the matching object
|
||||||
|
try:
|
||||||
|
return objclass.objects.get(id=inp)
|
||||||
|
except Exception:
|
||||||
|
if raise_errors:
|
||||||
|
raise
|
||||||
|
return inp
|
||||||
|
|
||||||
def to_unicode(obj, encoding='utf-8', force_string=False):
|
def to_unicode(obj, encoding='utf-8', force_string=False):
|
||||||
"""
|
"""
|
||||||
This decodes a suitable object to the unicode format. Note that
|
This decodes a suitable object to the unicode format. Note that
|
||||||
|
|
@ -887,15 +914,48 @@ def fuzzy_import_from_module(path, variable, default=None, defaultpaths=None):
|
||||||
paths = [path] + make_iter(defaultpaths)
|
paths = [path] + make_iter(defaultpaths)
|
||||||
for modpath in paths:
|
for modpath in paths:
|
||||||
try:
|
try:
|
||||||
mod = importlib.import_module(path)
|
mod = import_module(path)
|
||||||
except ImportError, ex:
|
except ImportError, ex:
|
||||||
if not str(ex) == "No module named %s" % path:
|
if not str(ex).startswith ("No module named %s" % path):
|
||||||
# this means the module was found but it
|
# this means the module was found but it
|
||||||
# triggers an ImportError on import.
|
# triggers an ImportError on import.
|
||||||
raise ex
|
raise ex
|
||||||
return getattr(mod, variable, default)
|
return getattr(mod, variable, default)
|
||||||
return default
|
return default
|
||||||
|
|
||||||
|
def class_from_module(path, defaultpaths=None):
|
||||||
|
"""
|
||||||
|
Return a class from a module, given the module's path. This is
|
||||||
|
primarily used to convert db_typeclass_path:s to classes.
|
||||||
|
|
||||||
|
if a list of defaultpaths is given, try subsequent runs by
|
||||||
|
prepending those paths to the given path.
|
||||||
|
"""
|
||||||
|
cls = None
|
||||||
|
if defaultpaths:
|
||||||
|
paths = [path] + make_iter(defaultpaths) if defaultpaths else []
|
||||||
|
else:
|
||||||
|
paths = [path]
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
path, clsname = path.rsplit(".", 1)
|
||||||
|
try:
|
||||||
|
mod = import_module(path)
|
||||||
|
except ImportError, ex:
|
||||||
|
# normally this is due to a not-found property
|
||||||
|
if not str(ex).startswith ("No module named %s" % path):
|
||||||
|
raise ex
|
||||||
|
try:
|
||||||
|
cls = getattr(mod, clsname)
|
||||||
|
break
|
||||||
|
except AttributeError, ex:
|
||||||
|
if not str(ex).startswith("Object 'module' has no attribute '%s'" % clsname):
|
||||||
|
raise ex
|
||||||
|
if not cls:
|
||||||
|
raise ImportError("Could not load typeclass '%s'." % path)
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
def init_new_player(player):
|
def init_new_player(player):
|
||||||
"""
|
"""
|
||||||
Helper method to call all hooks, set flags etc on a newly created
|
Helper method to call all hooks, set flags etc on a newly created
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue