Merge conflicts against master, including cmdhandler support for direct cmdobject input together with prefix-ignore mechanism from devel.
This commit is contained in:
commit
a648433db8
69 changed files with 2617 additions and 1771 deletions
|
|
@ -40,16 +40,21 @@ class ObjectCreateForm(forms.ModelForm):
|
|||
fields = '__all__'
|
||||
db_key = forms.CharField(label="Name/Key",
|
||||
widget=forms.TextInput(attrs={'size': '78'}),
|
||||
help_text="Main identifier, like 'apple', 'strong guy', 'Elizabeth' etc. If creating a Character, check so the name is unique among characters!",)
|
||||
help_text="Main identifier, like 'apple', 'strong guy', 'Elizabeth' etc. "
|
||||
"If creating a Character, check so the name is unique among characters!",)
|
||||
db_typeclass_path = forms.CharField(label="Typeclass",
|
||||
initial=settings.BASE_OBJECT_TYPECLASS,
|
||||
widget=forms.TextInput(attrs={'size': '78'}),
|
||||
help_text="This defines what 'type' of entity this is. This variable holds a Python path to a module with a valid Evennia Typeclass. If you are creating a Character you should use the typeclass defined by settings.BASE_CHARACTER_TYPECLASS or one derived from that.")
|
||||
help_text="This defines what 'type' of entity this is. This variable holds a "
|
||||
"Python path to a module with a valid Evennia Typeclass. If you are "
|
||||
"creating a Character you should use the typeclass defined by "
|
||||
"settings.BASE_CHARACTER_TYPECLASS or one derived from that.")
|
||||
db_cmdset_storage = forms.CharField(label="CmdSet",
|
||||
initial="",
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={'size': '78'}),
|
||||
help_text="Most non-character objects don't need a cmdset and can leave this field blank.")
|
||||
help_text="Most non-character objects don't need a cmdset"
|
||||
" and can leave this field blank.")
|
||||
raw_id_fields = ('db_destination', 'db_location', 'db_home')
|
||||
|
||||
|
||||
|
|
@ -63,8 +68,10 @@ class ObjectEditForm(ObjectCreateForm):
|
|||
fields = '__all__'
|
||||
db_lock_storage = forms.CharField(label="Locks",
|
||||
required=False,
|
||||
widget=forms.Textarea(attrs={'cols':'100', 'rows':'2'}),
|
||||
help_text="In-game lock definition string. If not given, defaults will be used. This string should be on the form <i>type:lockfunction(args);type2:lockfunction2(args);...")
|
||||
widget=forms.Textarea(attrs={'cols': '100', 'rows': '2'}),
|
||||
help_text="In-game lock definition string. If not given, defaults will be used. "
|
||||
"This string should be on the form "
|
||||
"<i>type:lockfunction(args);type2:lockfunction2(args);...")
|
||||
|
||||
|
||||
class ObjectDBAdmin(admin.ModelAdmin):
|
||||
|
|
@ -90,15 +97,15 @@ class ObjectDBAdmin(admin.ModelAdmin):
|
|||
form = ObjectEditForm
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'fields': (('db_key','db_typeclass_path'), ('db_lock_storage', ),
|
||||
('db_location', 'db_home'), 'db_destination','db_cmdset_storage'
|
||||
'fields': (('db_key', 'db_typeclass_path'), ('db_lock_storage', ),
|
||||
('db_location', 'db_home'), 'db_destination', 'db_cmdset_storage'
|
||||
)}),
|
||||
)
|
||||
|
||||
add_form = ObjectCreateForm
|
||||
add_fieldsets = (
|
||||
(None, {
|
||||
'fields': (('db_key','db_typeclass_path'),
|
||||
'fields': (('db_key', 'db_typeclass_path'),
|
||||
('db_location', 'db_home'), 'db_destination', 'db_cmdset_storage'
|
||||
)}),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ _MULTIMATCH_REGEX = re.compile(settings.SEARCH_MULTIMATCH_REGEX, re.I + re.U)
|
|||
|
||||
# Try to use a custom way to parse id-tagged multimatches.
|
||||
|
||||
|
||||
class ObjectDBManager(TypedObjectManager):
|
||||
"""
|
||||
This ObjectManager implements methods for searching
|
||||
|
|
@ -79,11 +80,13 @@ class ObjectDBManager(TypedObjectManager):
|
|||
if dbref:
|
||||
return dbref
|
||||
# not a dbref. Search by name.
|
||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||
if obj]) or Q()
|
||||
if exact:
|
||||
return self.filter(cand_restriction & Q(db_player__username__iexact=ostring))
|
||||
else: # fuzzy matching
|
||||
ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)).values_list("db_key", flat=True)
|
||||
else: # fuzzy matching
|
||||
ply_cands = self.filter(cand_restriction & Q(playerdb__username__istartswith=ostring)
|
||||
).values_list("db_key", flat=True)
|
||||
if candidates:
|
||||
index_matches = string_partial_matching(ply_cands, ostring, ret_index=True)
|
||||
return [obj for ind, obj in enumerate(make_iter(candidates)) if ind in index_matches]
|
||||
|
|
@ -103,7 +106,8 @@ class ObjectDBManager(TypedObjectManager):
|
|||
Returns:
|
||||
matches (list): The matching objects.
|
||||
"""
|
||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||
if obj]) or Q()
|
||||
return self.filter(cand_restriction & Q(db_key__iexact=oname, db_typeclass_path__exact=otypeclass_path))
|
||||
|
||||
# attr/property related
|
||||
|
|
@ -121,7 +125,9 @@ class ObjectDBManager(TypedObjectManager):
|
|||
matches (list): All objects having the given attribute_name defined at all.
|
||||
|
||||
"""
|
||||
cand_restriction = candidates != None and Q(db_attributes__db_obj__pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||
cand_restriction = candidates is not None and Q(db_attributes__db_obj__pk__in=[_GA(obj, "id") for obj
|
||||
in make_iter(candidates)
|
||||
if obj]) or Q()
|
||||
return list(self.filter(cand_restriction & Q(db_attributes__db_key=attribute_name)))
|
||||
|
||||
@returns_typeclass_list
|
||||
|
|
@ -144,20 +150,23 @@ class ObjectDBManager(TypedObjectManager):
|
|||
cannot be indexed, searching by Attribute key is to be preferred whenever possible.
|
||||
|
||||
"""
|
||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||
if obj]) or Q()
|
||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||
|
||||
## This doesn't work if attribute_value is an object. Workaround below
|
||||
# This doesn't work if attribute_value is an object. Workaround below
|
||||
|
||||
if isinstance(attribute_value, (basestring, int, float, bool)):
|
||||
return self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name, db_attributes__db_value=attribute_value))
|
||||
return self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name,
|
||||
db_attributes__db_value=attribute_value))
|
||||
else:
|
||||
# We have to loop for safety since the referenced lookup gives deepcopy error if attribute value is an object.
|
||||
# We must loop for safety since the referenced lookup gives deepcopy error if attribute value is an object.
|
||||
global _ATTR
|
||||
if not _ATTR:
|
||||
from evennia.typeclasses.models import Attribute as _ATTR
|
||||
cands = list(self.filter(cand_restriction & type_restriction & Q(db_attributes__db_key=attribute_name)))
|
||||
results = [attr.objectdb_set.all() for attr in _ATTR.objects.filter(objectdb__in=cands, db_value=attribute_value)]
|
||||
results = [attr.objectdb_set.all() for attr in _ATTR.objects.filter(objectdb__in=cands,
|
||||
db_value=attribute_value)]
|
||||
return chain(*results)
|
||||
|
||||
@returns_typeclass_list
|
||||
|
|
@ -174,8 +183,9 @@ class ObjectDBManager(TypedObjectManager):
|
|||
|
||||
"""
|
||||
property_name = "db_%s" % property_name.lstrip('db_')
|
||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||
querykwargs = {property_name:None}
|
||||
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||
if obj]) or Q()
|
||||
querykwargs = {property_name: None}
|
||||
try:
|
||||
return list(self.filter(cand_restriction).exclude(Q(**querykwargs)))
|
||||
except exceptions.FieldError:
|
||||
|
|
@ -198,8 +208,9 @@ class ObjectDBManager(TypedObjectManager):
|
|||
if isinstance(property_name, basestring):
|
||||
if not property_name.startswith('db_'):
|
||||
property_name = "db_%s" % property_name
|
||||
querykwargs = {property_name:property_value}
|
||||
cand_restriction = candidates != None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates) if obj]) or Q()
|
||||
querykwargs = {property_name: property_value}
|
||||
cand_restriction = candidates is not None and Q(pk__in=[_GA(obj, "id") for obj in make_iter(candidates)
|
||||
if obj]) or Q()
|
||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||
try:
|
||||
return list(self.filter(cand_restriction & type_restriction & Q(**querykwargs)))
|
||||
|
|
@ -207,7 +218,8 @@ class ObjectDBManager(TypedObjectManager):
|
|||
return []
|
||||
except ValueError:
|
||||
from evennia.utils import logger
|
||||
logger.log_err("The property '%s' does not support search criteria of the type %s." % (property_name, type(property_value)))
|
||||
logger.log_err("The property '%s' does not support search criteria of the type %s." %
|
||||
(property_name, type(property_value)))
|
||||
return []
|
||||
|
||||
@returns_typeclass_list
|
||||
|
|
@ -228,7 +240,7 @@ class ObjectDBManager(TypedObjectManager):
|
|||
|
||||
@returns_typeclass_list
|
||||
def get_objs_with_key_or_alias(self, ostring, exact=True,
|
||||
candidates=None, typeclasses=None):
|
||||
candidates=None, typeclasses=None):
|
||||
"""
|
||||
Args:
|
||||
ostring (str): A search criterion.
|
||||
|
|
@ -253,7 +265,7 @@ class ObjectDBManager(TypedObjectManager):
|
|||
|
||||
# build query objects
|
||||
candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
|
||||
cand_restriction = candidates != None and Q(pk__in=candidates_id) or Q()
|
||||
cand_restriction = candidates is not None and Q(pk__in=candidates_id) or Q()
|
||||
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
|
||||
if exact:
|
||||
# exact match - do direct search
|
||||
|
|
@ -264,7 +276,8 @@ class ObjectDBManager(TypedObjectManager):
|
|||
search_candidates = self.filter(cand_restriction & type_restriction)
|
||||
else:
|
||||
# fuzzy without supplied candidates - we select our own candidates
|
||||
search_candidates = self.filter(type_restriction & (Q(db_key__istartswith=ostring) | Q(db_tags__db_key__istartswith=ostring))).distinct()
|
||||
search_candidates = self.filter(type_restriction & (Q(db_key__istartswith=ostring) |
|
||||
Q(db_tags__db_key__istartswith=ostring))).distinct()
|
||||
# fuzzy matching
|
||||
key_strings = search_candidates.values_list("db_key", flat=True).order_by("id")
|
||||
|
||||
|
|
@ -275,10 +288,10 @@ class ObjectDBManager(TypedObjectManager):
|
|||
else:
|
||||
# match by alias rather than by key
|
||||
search_candidates = search_candidates.filter(db_tags__db_tagtype__iexact="alias",
|
||||
db_tags__db_key__icontains=ostring)
|
||||
db_tags__db_key__icontains=ostring)
|
||||
alias_strings = []
|
||||
alias_candidates = []
|
||||
#TODO create the alias_strings and alias_candidates lists more effiently?
|
||||
# TODO create the alias_strings and alias_candidates lists more efficiently?
|
||||
for candidate in search_candidates:
|
||||
for alias in candidate.aliases.all():
|
||||
alias_strings.append(alias)
|
||||
|
|
@ -343,13 +356,16 @@ class ObjectDBManager(TypedObjectManager):
|
|||
"""
|
||||
if attribute_name:
|
||||
# attribute/property search (always exact).
|
||||
matches = self.get_objs_with_db_property_value(attribute_name, searchdata, candidates=candidates, typeclasses=typeclass)
|
||||
matches = self.get_objs_with_db_property_value(attribute_name, searchdata,
|
||||
candidates=candidates, typeclasses=typeclass)
|
||||
if matches:
|
||||
return matches
|
||||
return self.get_objs_with_attr_value(attribute_name, searchdata, candidates=candidates, typeclasses=typeclass)
|
||||
return self.get_objs_with_attr_value(attribute_name, searchdata,
|
||||
candidates=candidates, typeclasses=typeclass)
|
||||
else:
|
||||
# normal key/alias search
|
||||
return self.get_objs_with_key_or_alias(searchdata, exact=exact, candidates=candidates, typeclasses=typeclass)
|
||||
return self.get_objs_with_key_or_alias(searchdata, exact=exact,
|
||||
candidates=candidates, typeclasses=typeclass)
|
||||
|
||||
if not searchdata and searchdata != 0:
|
||||
return []
|
||||
|
|
@ -372,7 +388,7 @@ class ObjectDBManager(TypedObjectManager):
|
|||
candidates = [cand for cand in make_iter(candidates) if cand]
|
||||
if typeclass:
|
||||
candidates = [cand for cand in candidates
|
||||
if _GA(cand, "db_typeclass_path") in typeclass]
|
||||
if _GA(cand, "db_typeclass_path") in typeclass]
|
||||
|
||||
dbref = not attribute_name and exact and use_dbref and self.dbref(searchdata)
|
||||
if dbref:
|
||||
|
|
@ -418,7 +434,6 @@ class ObjectDBManager(TypedObjectManager):
|
|||
|
||||
#
|
||||
# ObjectManager Copy method
|
||||
#
|
||||
|
||||
def copy_object(self, original_object, new_key=None,
|
||||
new_location=None, new_home=None,
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ class ContentsHandler(object):
|
|||
Re-initialize the content cache
|
||||
|
||||
"""
|
||||
self._pkcache.update(dict((obj.pk, None) for obj in
|
||||
ObjectDB.objects.filter(db_location=self.obj) if obj.pk))
|
||||
self._pkcache.update(dict((obj.pk, None) for obj in ObjectDB.objects.filter(db_location=self.obj) if obj.pk))
|
||||
|
||||
def get(self, exclude=None):
|
||||
"""
|
||||
|
|
@ -79,7 +78,7 @@ class ContentsHandler(object):
|
|||
return [self._idcache[pk] for pk in pks]
|
||||
except KeyError:
|
||||
# this means an actual failure of caching. Return real database match.
|
||||
logger.log_err("contents cache failed for %s." % (self.obj.key))
|
||||
logger.log_err("contents cache failed for %s." % self.obj.key)
|
||||
return list(ObjectDB.objects.filter(db_location=self.obj))
|
||||
|
||||
def add(self, obj):
|
||||
|
|
@ -110,11 +109,12 @@ class ContentsHandler(object):
|
|||
self._pkcache = {}
|
||||
self.init()
|
||||
|
||||
#------------------------------------------------------------
|
||||
# -------------------------------------------------------------
|
||||
#
|
||||
# ObjectDB
|
||||
#
|
||||
#------------------------------------------------------------
|
||||
# -------------------------------------------------------------
|
||||
|
||||
|
||||
class ObjectDB(TypedObject):
|
||||
"""
|
||||
|
|
@ -173,17 +173,18 @@ class ObjectDB(TypedObject):
|
|||
help_text='a Player connected to this object, if any.')
|
||||
# the session id associated with this player, if any
|
||||
db_sessid = models.CommaSeparatedIntegerField(null=True, max_length=32, verbose_name="session id",
|
||||
help_text="csv list of session ids of connected Player, if any.")
|
||||
help_text="csv list of session ids of connected Player, if any.")
|
||||
# The location in the game world. Since this one is likely
|
||||
# to change often, we set this with the 'location' property
|
||||
# to transparently handle Typeclassing.
|
||||
db_location = models.ForeignKey('self', related_name="locations_set", db_index=True, on_delete=models.SET_NULL,
|
||||
blank=True, null=True, verbose_name='game location')
|
||||
blank=True, null=True, verbose_name='game location')
|
||||
# a safety location, this usually don't change much.
|
||||
db_home = models.ForeignKey('self', related_name="homes_set", on_delete=models.SET_NULL,
|
||||
blank=True, null=True, verbose_name='home location')
|
||||
blank=True, null=True, verbose_name='home location')
|
||||
# destination of this object - primarily used by exits.
|
||||
db_destination = models.ForeignKey('self', related_name="destinations_set", db_index=True, on_delete=models.SET_NULL,
|
||||
db_destination = models.ForeignKey('self', related_name="destinations_set",
|
||||
db_index=True, on_delete=models.SET_NULL,
|
||||
blank=True, null=True, verbose_name='destination',
|
||||
help_text='a destination, used only by exit objects.')
|
||||
# database storage of persistant cmdsets.
|
||||
|
|
@ -204,28 +205,28 @@ class ObjectDB(TypedObject):
|
|||
|
||||
# cmdset_storage property handling
|
||||
def __cmdset_storage_get(self):
|
||||
"getter"
|
||||
"""getter"""
|
||||
storage = self.db_cmdset_storage
|
||||
return [path.strip() for path in storage.split(',')] if storage else []
|
||||
|
||||
def __cmdset_storage_set(self, value):
|
||||
"setter"
|
||||
self.db_cmdset_storage = ",".join(str(val).strip() for val in make_iter(value))
|
||||
"""setter"""
|
||||
self.db_cmdset_storage = ",".join(str(val).strip() for val in make_iter(value))
|
||||
self.save(update_fields=["db_cmdset_storage"])
|
||||
|
||||
def __cmdset_storage_del(self):
|
||||
"deleter"
|
||||
"""deleter"""
|
||||
self.db_cmdset_storage = None
|
||||
self.save(update_fields=["db_cmdset_storage"])
|
||||
cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set, __cmdset_storage_del)
|
||||
|
||||
# location getsetter
|
||||
def __location_get(self):
|
||||
"Get location"
|
||||
"""Get location"""
|
||||
return self.db_location
|
||||
|
||||
def __location_set(self, location):
|
||||
"Set location, checking for loops and allowing dbref"
|
||||
"""Set location, checking for loops and allowing dbref"""
|
||||
if isinstance(location, (basestring, int)):
|
||||
# allow setting of #dbref
|
||||
dbid = dbref(location, reqhash=False)
|
||||
|
|
@ -237,9 +238,9 @@ class ObjectDB(TypedObject):
|
|||
pass
|
||||
try:
|
||||
def is_loc_loop(loc, depth=0):
|
||||
"Recursively traverse target location, trying to catch a loop."
|
||||
"""Recursively traverse target location, trying to catch a loop."""
|
||||
if depth > 10:
|
||||
return
|
||||
return None
|
||||
elif loc == self:
|
||||
raise RuntimeError
|
||||
elif loc is None:
|
||||
|
|
@ -248,7 +249,7 @@ class ObjectDB(TypedObject):
|
|||
try:
|
||||
is_loc_loop(location)
|
||||
except RuntimeWarning:
|
||||
# we caught a infitite location loop!
|
||||
# we caught an infinite location loop!
|
||||
# (location1 is in location2 which is in location1 ...)
|
||||
pass
|
||||
|
||||
|
|
@ -281,7 +282,7 @@ class ObjectDB(TypedObject):
|
|||
return
|
||||
|
||||
def __location_del(self):
|
||||
"Cleanly delete the location reference"
|
||||
"""Cleanly delete the location reference"""
|
||||
self.db_location = None
|
||||
self.save(update_fields=["db_location"])
|
||||
location = property(__location_get, __location_set, __location_del)
|
||||
|
|
@ -311,7 +312,6 @@ class ObjectDB(TypedObject):
|
|||
[o.contents_cache.init() for o in self.__dbclass__.get_all_cached_instances()]
|
||||
|
||||
class Meta(object):
|
||||
"Define Django meta options"
|
||||
"""Define Django meta options"""
|
||||
verbose_name = "Object"
|
||||
verbose_name_plural = "Objects"
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ from evennia.commands.cmdsethandler import CmdSetHandler
|
|||
from evennia.commands import cmdhandler
|
||||
from evennia.utils import logger
|
||||
from evennia.utils.utils import (variable_from_module, lazy_property,
|
||||
make_iter, to_unicode, calledby)
|
||||
make_iter, to_unicode, calledby, is_iter)
|
||||
|
||||
_MULTISESSION_MODE = settings.MULTISESSION_MODE
|
||||
|
||||
|
|
@ -34,6 +34,7 @@ _SESSID_MAX = 16 if _MULTISESSION_MODE in (1, 3) else 1
|
|||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
|
||||
class ObjectSessionHandler(object):
|
||||
"""
|
||||
Handles the get/setting of the sessid
|
||||
|
|
@ -133,7 +134,7 @@ class ObjectSessionHandler(object):
|
|||
Remove session from handler.
|
||||
|
||||
Args:
|
||||
sessid (Session or int): Session or session id to remove.
|
||||
session (Session or int): Session or session id to remove.
|
||||
|
||||
"""
|
||||
try:
|
||||
|
|
@ -144,7 +145,7 @@ class ObjectSessionHandler(object):
|
|||
sessid_cache = self._sessid_cache
|
||||
if sessid in sessid_cache:
|
||||
sessid_cache.remove(sessid)
|
||||
self.obj.db_sessid = ",".join(str(val) for val in sessid_cache)
|
||||
self.obj.db_sessid = ",".join(str(val) for val in sessid_cache)
|
||||
self.obj.save(update_fields=["db_sessid"])
|
||||
|
||||
def clear(self):
|
||||
|
|
@ -167,10 +168,9 @@ class ObjectSessionHandler(object):
|
|||
return len(self._sessid_cache)
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Base class to inherit from.
|
||||
#
|
||||
|
||||
|
||||
class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
||||
"""
|
||||
|
|
@ -221,7 +221,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
|
||||
"""
|
||||
return self.db_player and self.db_player.is_superuser \
|
||||
and not self.db_player.attributes.get("_quell")
|
||||
and not self.db_player.attributes.get("_quell")
|
||||
|
||||
def contents_get(self, exclude=None):
|
||||
"""
|
||||
|
|
@ -241,7 +241,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
|
||||
"""
|
||||
con = self.contents_cache.get(exclude=exclude)
|
||||
#print "contents_get:", self, con, id(self), calledby()
|
||||
# print "contents_get:", self, con, id(self), calledby() # DEBUG
|
||||
return con
|
||||
contents = property(contents_get)
|
||||
|
||||
|
|
@ -370,8 +370,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
# do nick-replacement on search
|
||||
searchdata = self.nicks.nickreplace(searchdata, categories=("object", "player"), include_player=True)
|
||||
|
||||
if(global_search or (is_string and searchdata.startswith("#") and
|
||||
len(searchdata) > 1 and searchdata[1:].isdigit())):
|
||||
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
|
||||
|
|
@ -403,8 +403,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
use_dbref=use_dbref)
|
||||
if quiet:
|
||||
return results
|
||||
return _AT_SEARCH_RESULT(results, self, query=searchdata,
|
||||
nofound_string=nofound_string, multimatch_string=multimatch_string)
|
||||
return _AT_SEARCH_RESULT(results, self, query=searchdata,
|
||||
nofound_string=nofound_string, multimatch_string=multimatch_string)
|
||||
|
||||
def search_player(self, searchdata, quiet=False):
|
||||
"""
|
||||
|
|
@ -474,11 +474,9 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
# 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)
|
||||
raw_string = self.nicks.nickreplace(raw_string, categories=("inputline", "channel"), include_player=True)
|
||||
return cmdhandler.cmdhandler(self, raw_string, callertype="object", session=session, **kwargs)
|
||||
|
||||
|
||||
def msg(self, text=None, from_obj=None, session=None, options=None, **kwargs):
|
||||
"""
|
||||
Emits something to a session attached to the object.
|
||||
|
|
@ -549,21 +547,28 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
for obj in contents:
|
||||
func(obj, **kwargs)
|
||||
|
||||
def msg_contents(self, message, exclude=None, from_obj=None, mapping=None, **kwargs):
|
||||
def msg_contents(self, text=None, exclude=None, from_obj=None, mapping=None, **kwargs):
|
||||
"""
|
||||
Emits a message to all objects inside this object.
|
||||
|
||||
Args:
|
||||
message (str): Message to send.
|
||||
text (str or tuple): Message to send. If a tuple, this should be
|
||||
on the valid OOB outmessage form `(message, {kwargs})`,
|
||||
where kwargs are optional data passed to the `text`
|
||||
outputfunc.
|
||||
exclude (list, optional): A list of objects not to send to.
|
||||
from_obj (Object, optional): An object designated as the
|
||||
"sender" of the message. See `DefaultObject.msg()` for
|
||||
more info.
|
||||
mapping (dict, optional): A mapping of formatting keys
|
||||
`{"key":<object>, "key2":<object2>,...}. The keys
|
||||
must match `{key}` markers in `message` and will be
|
||||
must match `{key}` markers in the `text` if this is a string or
|
||||
in the internal `message` if `text` is a tuple. These
|
||||
formatting statements will be
|
||||
replaced by the return of `<object>.get_display_name(looker)`
|
||||
for every looker that is messaged.
|
||||
for every looker in contents that receives the
|
||||
message. This allows for every object to potentially
|
||||
get its own customized string.
|
||||
Kwargs:
|
||||
Keyword arguments will be passed on to `obj.msg()` for all
|
||||
messaged objects.
|
||||
|
|
@ -577,14 +582,23 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
not have `get_display_name()`, its string value will be used.
|
||||
|
||||
Example:
|
||||
Say char is a Character object and npc is an NPC object:
|
||||
Say Char is a Character object and Npc is an NPC object:
|
||||
|
||||
action = 'kicks'
|
||||
char.location.msg_contents(
|
||||
"{attacker} {action} {defender}",
|
||||
mapping=dict(attacker=char, defender=npc, action=action),
|
||||
exclude=(char, npc))
|
||||
"{attacker} kicks {defender}",
|
||||
mapping=dict(attacker=char, defender=npc), exclude=(char, npc))
|
||||
|
||||
This will result in everyone in the room seeing 'Char kicks NPC'
|
||||
where everyone may potentially see different results for Char and Npc
|
||||
depending on the results of `char.get_display_name(looker)` and
|
||||
`npc.get_display_name(looker)` for each particular onlooker
|
||||
|
||||
"""
|
||||
# we also accept an outcommand on the form (message, {kwargs})
|
||||
is_outcmd = text and is_iter(text)
|
||||
inmessage = text[0] if is_outcmd else text
|
||||
outkwargs = text[1] if is_outcmd and len(text) > 1 else {}
|
||||
|
||||
contents = self.contents
|
||||
if exclude:
|
||||
exclude = make_iter(exclude)
|
||||
|
|
@ -592,12 +606,12 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
for obj in contents:
|
||||
if mapping:
|
||||
substitutions = {t: sub.get_display_name(obj)
|
||||
if hasattr(sub, 'get_display_name')
|
||||
else str(sub)
|
||||
for t, sub in mapping.items()}
|
||||
obj.msg(message.format(**substitutions), from_obj=from_obj, **kwargs)
|
||||
if hasattr(sub, 'get_display_name')
|
||||
else str(sub) for t, sub in mapping.items()}
|
||||
outmessage = inmessage.format(**substitutions)
|
||||
else:
|
||||
obj.msg(message, from_obj=from_obj, **kwargs)
|
||||
outmessage = inmessage
|
||||
obj.msg(text=(outmessage, outkwargs), from_obj=from_obj, **kwargs)
|
||||
|
||||
def move_to(self, destination, quiet=False,
|
||||
emit_to_obj=None, use_destination=True, to_none=False, move_hooks=True):
|
||||
|
|
@ -642,7 +656,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
|
||||
"""
|
||||
def logerr(string="", err=None):
|
||||
"Simple log helper method"
|
||||
"""Simple log helper method"""
|
||||
logger.log_trace()
|
||||
self.msg("%s%s" % (string, "" if err is None else " (%s)" % err))
|
||||
return
|
||||
|
|
@ -658,7 +672,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
self.location = None
|
||||
return True
|
||||
emit_to_obj.msg(_("The destination doesn't exist."))
|
||||
return
|
||||
return False
|
||||
if destination.destination and use_destination:
|
||||
# traverse exits
|
||||
destination = destination.destination
|
||||
|
|
@ -667,7 +681,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
if move_hooks:
|
||||
try:
|
||||
if not self.at_before_move(destination):
|
||||
return
|
||||
return False
|
||||
except Exception as err:
|
||||
logerr(errtxt % "at_before_move()", err)
|
||||
return False
|
||||
|
|
@ -684,7 +698,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
return False
|
||||
|
||||
if not quiet:
|
||||
#tell the old room we are leaving
|
||||
# tell the old room we are leaving
|
||||
try:
|
||||
self.announce_move_from(destination)
|
||||
except Exception as err:
|
||||
|
|
@ -704,7 +718,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
self.announce_move_to(source_location)
|
||||
except Exception as err:
|
||||
logerr(errtxt % "announce_move_to()", err)
|
||||
return False
|
||||
return False
|
||||
|
||||
if move_hooks:
|
||||
# Perform eventual extra commands on the receiving location
|
||||
|
|
@ -779,7 +793,6 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
obj.msg(_(string))
|
||||
obj.move_to(home)
|
||||
|
||||
|
||||
def copy(self, new_key=None):
|
||||
"""
|
||||
Makes an identical copy of this object, identical except for a
|
||||
|
|
@ -803,15 +816,15 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
"""
|
||||
key = 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()):
|
||||
for _ 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
|
||||
|
|
@ -862,7 +875,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
self.attributes.clear()
|
||||
self.nicks.clear()
|
||||
self.aliases.clear()
|
||||
self.location = None # this updates contents_cache for our location
|
||||
self.location = None # this updates contents_cache for our location
|
||||
|
||||
# Perform the deletion of the object
|
||||
super(DefaultObject, self).delete()
|
||||
|
|
@ -955,8 +968,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
|
||||
self.basetype_posthook_setup()
|
||||
|
||||
|
||||
## hooks called by the game engine
|
||||
# hooks called by the game engine #
|
||||
|
||||
def basetype_setup(self):
|
||||
"""
|
||||
|
|
@ -1032,8 +1044,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
have no cmdsets.
|
||||
|
||||
Kwargs:
|
||||
Usually not set but could be used e.g. to force rebuilding
|
||||
of a dynamically created cmdset or similar.
|
||||
caller (Session, Object or Player): The caller requesting
|
||||
this cmdset.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
|
@ -1118,9 +1130,8 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
|
||||
Args:
|
||||
result (bool): The outcome of the access call.
|
||||
accessing_obj (Object or Player): The entity trying to
|
||||
gain access. access_type (str): The type of access that
|
||||
was requested.
|
||||
accessing_obj (Object or Player): The entity trying to gain access.
|
||||
access_type (str): The type of access that was requested.
|
||||
|
||||
Kwargs:
|
||||
Not used by default, added for possible expandability in a
|
||||
|
|
@ -1147,10 +1158,10 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
before it is even started.
|
||||
|
||||
"""
|
||||
#return has_perm(self, destination, "can_move")
|
||||
# return has_perm(self, destination, "can_move")
|
||||
return True
|
||||
|
||||
def announce_move_from(self, destination):
|
||||
def announce_move_from(self, destination, msg=None, mapping=None):
|
||||
"""
|
||||
Called if the move is to be announced. This is
|
||||
called while we are still standing in the old
|
||||
|
|
@ -1158,25 +1169,56 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
|
||||
Args:
|
||||
destination (Object): The place we are going to.
|
||||
msg (str, optional): a replacement message.
|
||||
mapping (dict, optional): additional mapping objects.
|
||||
|
||||
You can override this method and call its parent with a
|
||||
message to simply change the default message. In the string,
|
||||
you can use the following as mappings (between braces):
|
||||
object: the object which is moving.
|
||||
exit: the exit from which the object is moving (if found).
|
||||
origin: the location of the object before the move.
|
||||
destination: the location of the object after moving.
|
||||
|
||||
"""
|
||||
if not self.location:
|
||||
return
|
||||
string = "%s is leaving %s, heading for %s."
|
||||
location = self.location
|
||||
for obj in self.location.contents:
|
||||
if obj != self:
|
||||
obj.msg(string % (self.get_display_name(obj),
|
||||
location.get_display_name(obj) if location else "nowhere",
|
||||
destination.get_display_name(obj)))
|
||||
if msg:
|
||||
string = msg
|
||||
else:
|
||||
string = "{object} is leaving {origin}, heading for {destination}."
|
||||
|
||||
def announce_move_to(self, source_location):
|
||||
location = self.location
|
||||
exits = [o for o in location.contents if o.location is location and o.destination is destination]
|
||||
if not mapping:
|
||||
mapping = {}
|
||||
|
||||
mapping.update({
|
||||
"object": self,
|
||||
"exit": exits[0] if exits else "somwhere",
|
||||
"origin": location or "nowhere",
|
||||
"destination": destination or "nowhere",
|
||||
})
|
||||
|
||||
location.msg_contents(string, exclude=(self, ), mapping=mapping)
|
||||
|
||||
def announce_move_to(self, source_location, msg=None, mapping=None):
|
||||
"""
|
||||
Called after the move if the move was not quiet. At this point
|
||||
we are standing in the new location.
|
||||
|
||||
Args:
|
||||
source_location (Object): The place we came from
|
||||
msg (str, optional): the replacement message if location.
|
||||
mapping (dict, optional): additional mapping objects.
|
||||
|
||||
You can override this method and call its parent with a
|
||||
message to simply change the default message. In the string,
|
||||
you can use the following as mappings (between braces):
|
||||
object: the object which is moving.
|
||||
exit: the exit from which the object is moving (if found).
|
||||
origin: the location of the object before the move.
|
||||
destination: the location of the object after moving.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -1187,13 +1229,31 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
self.location.msg(string)
|
||||
return
|
||||
|
||||
string = "%s arrives to %s%s."
|
||||
location = self.location
|
||||
for obj in self.location.contents:
|
||||
if obj != self:
|
||||
obj.msg(string % (self.get_display_name(obj),
|
||||
location.get_display_name(obj) if location else "nowhere",
|
||||
" from %s" % source_location.get_display_name(obj) if source_location else ""))
|
||||
if source_location:
|
||||
if msg:
|
||||
string = msg
|
||||
else:
|
||||
string = "{object} arrives to {destination} from {origin}."
|
||||
else:
|
||||
string = "{object} arrives to {destination}."
|
||||
|
||||
origin = source_location
|
||||
destination = self.location
|
||||
exits = []
|
||||
if origin:
|
||||
exits = [o for o in destination.contents if o.location is destination and o.destination is origin]
|
||||
|
||||
if not mapping:
|
||||
mapping = {}
|
||||
|
||||
mapping.update({
|
||||
"object": self,
|
||||
"exit": exits[0] if exits else "somewhere",
|
||||
"origin": origin or "nowhere",
|
||||
"destination": destination or "nowhere",
|
||||
})
|
||||
|
||||
destination.msg_contents(string, exclude=(self, ), mapping=mapping)
|
||||
|
||||
def at_after_move(self, source_location):
|
||||
"""
|
||||
|
|
@ -1288,7 +1348,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
check for this. .
|
||||
|
||||
Consider this a pre-processing method before msg is passed on
|
||||
to the user sesssion. If this method returns False, the msg
|
||||
to the user session. If this method returns False, the msg
|
||||
will not be passed on.
|
||||
|
||||
Args:
|
||||
|
|
@ -1341,7 +1401,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
return ""
|
||||
# get and identify all objects
|
||||
visible = (con for con in self.contents if con != looker and
|
||||
con.access(looker, "view"))
|
||||
con.access(looker, "view"))
|
||||
exits, users, things = [], [], []
|
||||
for con in visible:
|
||||
key = con.get_display_name(looker)
|
||||
|
|
@ -1416,6 +1476,22 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
"""
|
||||
pass
|
||||
|
||||
def at_give(self, giver, getter):
|
||||
"""
|
||||
Called by the default `give` command when this object has been
|
||||
given.
|
||||
|
||||
Args:
|
||||
giver (Object): The object giving this object.
|
||||
getter (Object): The object getting this object.
|
||||
|
||||
Notes:
|
||||
This hook cannot stop the give from happening. Use
|
||||
permissions for that.
|
||||
|
||||
"""
|
||||
pass
|
||||
|
||||
def at_drop(self, dropper):
|
||||
"""
|
||||
Called by the default `drop` command when this object has been
|
||||
|
|
@ -1425,7 +1501,7 @@ class DefaultObject(with_metaclass(TypeclassBase, ObjectDB)):
|
|||
dropper (Object): The object which just dropped this object.
|
||||
|
||||
Notes:
|
||||
This hook cannot stop the pickup from happening. Use
|
||||
This hook cannot stop the drop from happening. Use
|
||||
permissions from that.
|
||||
|
||||
"""
|
||||
|
|
@ -1473,7 +1549,7 @@ class DefaultCharacter(DefaultObject):
|
|||
"""
|
||||
super(DefaultCharacter, self).basetype_setup()
|
||||
self.locks.add(";".join(["get:false()", # noone can pick up the character
|
||||
"call:false()"])) # no commands can be called on character from outside
|
||||
"call:false()"])) # no commands can be called on character from outside
|
||||
# add the default cmdset
|
||||
self.cmdset.add_default(settings.CMDSET_CHARACTER, permanent=True)
|
||||
|
||||
|
|
@ -1565,7 +1641,7 @@ class DefaultCharacter(DefaultObject):
|
|||
|
||||
#
|
||||
# Base Room object
|
||||
#
|
||||
|
||||
|
||||
class DefaultRoom(DefaultObject):
|
||||
"""
|
||||
|
|
@ -1581,7 +1657,7 @@ class DefaultRoom(DefaultObject):
|
|||
|
||||
super(DefaultRoom, self).basetype_setup()
|
||||
self.locks.add(";".join(["get:false()",
|
||||
"puppet:false()"])) # would be weird to puppet a room ...
|
||||
"puppet:false()"])) # would be weird to puppet a room ...
|
||||
self.location = None
|
||||
|
||||
|
||||
|
|
@ -1631,7 +1707,7 @@ class ExitCommand(command.Command):
|
|||
|
||||
#
|
||||
# Base Exit object
|
||||
#
|
||||
|
||||
|
||||
class DefaultExit(DefaultObject):
|
||||
"""
|
||||
|
|
@ -1683,8 +1759,8 @@ class DefaultExit(DefaultObject):
|
|||
exit_cmdset.add(cmd)
|
||||
return exit_cmdset
|
||||
|
||||
|
||||
# Command hooks
|
||||
|
||||
def basetype_setup(self):
|
||||
"""
|
||||
Setup exit-security
|
||||
|
|
@ -1696,8 +1772,8 @@ class DefaultExit(DefaultObject):
|
|||
super(DefaultExit, self).basetype_setup()
|
||||
|
||||
# setting default locks (overload these in at_object_creation()
|
||||
self.locks.add(";".join(["puppet:false()", # would be weird to puppet an exit ...
|
||||
"traverse:all()", # who can pass through exit by default
|
||||
self.locks.add(";".join(["puppet:false()", # would be weird to puppet an exit ...
|
||||
"traverse:all()", # who can pass through exit by default
|
||||
"get:false()"])) # noone can pick up the exit
|
||||
|
||||
# an exit should have a destination (this is replaced at creation time)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue