mirror of
https://github.com/evennia/evennia.git
synced 2026-04-06 16:44:08 +02:00
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
|
|
@ -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