Merge conflicts against master, including cmdhandler support for direct cmdobject input together with prefix-ignore mechanism from devel.

This commit is contained in:
Griatch 2017-04-01 16:08:23 +02:00
commit a648433db8
69 changed files with 2617 additions and 1771 deletions

View file

@ -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)