Added inlineCallback operator for cmdhandler, allowing the system to yield more often.

This commit is contained in:
Griatch 2012-02-20 21:40:28 +01:00
parent c4f2c493d9
commit 641d829154
3 changed files with 63 additions and 43 deletions

View file

@ -69,12 +69,15 @@ class CmdQuell(MuxCommand):
self.caller.player.user.is_superuser = False self.caller.player.user.is_superuser = False
self.caller.player.user.save() self.caller.player.user.save()
try: def callback(ret):
ret = self.caller.execute_cmd(cmd) self.caller.msg(ret)
except Exception, e: def errback(err):
self.caller.msg(str(e)) self.caller.msg(err)
# this returns a deferred, so we need to assign callbacks
self.caller.execute_cmd(cmd).addCallbacks(callback, errback)
self.caller.permissions = oldperm self.caller.permissions = oldperm
self.caller.player.user.is_superuser = old_superuser self.caller.player.user.is_superuser = old_superuser
self.caller.player.user.save() self.caller.player.user.save()
return ret return

View file

@ -37,6 +37,7 @@ command line. The process is as follows:
from copy import copy from copy import copy
from traceback import format_exc from traceback import format_exc
from twisted.internet.defer import inlineCallbacks, returnValue
from django.conf import settings from django.conf import settings
from src.comms.channelhandler import CHANNELHANDLER from src.comms.channelhandler import CHANNELHANDLER
from src.commands.cmdsethandler import import_cmdset from src.commands.cmdsethandler import import_cmdset
@ -73,6 +74,7 @@ class ExecSystemCommand(Exception):
self.syscmd = syscmd self.syscmd = syscmd
self.sysarg = sysarg self.sysarg = sysarg
@inlineCallbacks
def get_and_merge_cmdsets(caller): def get_and_merge_cmdsets(caller):
""" """
Gather all relevant cmdsets and merge them. Note Gather all relevant cmdsets and merge them. Note
@ -80,7 +82,7 @@ def get_and_merge_cmdsets(caller):
""" """
# The calling object's cmdset # The calling object's cmdset
try: try:
caller.at_cmdset_get() yield caller.at_cmdset_get()
except Exception: except Exception:
logger.log_trace() logger.log_trace()
try: try:
@ -91,7 +93,7 @@ def get_and_merge_cmdsets(caller):
# Create cmdset for all player's available channels # Create cmdset for all player's available channels
channel_cmdset = None channel_cmdset = None
if not caller_cmdset.no_channels: if not caller_cmdset.no_channels:
channel_cmdset = CHANNELHANDLER.get_cmdset(caller) channel_cmdset = yield CHANNELHANDLER.get_cmdset(caller)
# Gather cmdsets from location, objects in location or carried # Gather cmdsets from location, objects in location or carried
local_objects_cmdsets = [None] local_objects_cmdsets = [None]
@ -101,15 +103,15 @@ def get_and_merge_cmdsets(caller):
if location and not caller_cmdset.no_objs: if location and not caller_cmdset.no_objs:
# Gather all cmdsets stored on objects in the room and # Gather all cmdsets stored on objects in the room and
# also in the caller's inventory and the location itself # also in the caller's inventory and the location itself
local_objlist = location.contents_get(exclude=caller.dbobj) + caller.contents + [location] local_objlist = yield location.contents_get(exclude=caller.dbobj) + caller.contents + [location]
for obj in local_objlist: for obj in local_objlist:
try: try:
# call hook in case we need to do dynamic changing to cmdset # call hook in case we need to do dynamic changing to cmdset
obj.at_cmdset_get() yield obj.at_cmdset_get()
except Exception: except Exception:
logger.log_trace() logger.log_trace()
local_objects_cmdsets = [obj.cmdset.current for obj in local_objlist local_objects_cmdsets = yield [obj.cmdset.current for obj in local_objlist
if (obj.cmdset.current and obj.locks.check(caller, 'call', no_superuser_bypass=True))] if (obj.cmdset.current and obj.locks.check(caller, 'call', no_superuser_bypass=True))]
for cset in local_objects_cmdsets: for cset in local_objects_cmdsets:
#This is necessary for object sets, or we won't be able to separate #This is necessary for object sets, or we won't be able to separate
#the command sets from each other in a busy room. #the command sets from each other in a busy room.
@ -124,9 +126,9 @@ def get_and_merge_cmdsets(caller):
cmdsets = [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets cmdsets = [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets
# weed out all non-found sets # weed out all non-found sets
cmdsets = [cmdset for cmdset in cmdsets if cmdset] cmdsets = yield [cmdset for cmdset in cmdsets if cmdset]
# sort cmdsets after reverse priority (highest prio are merged in last) # sort cmdsets after reverse priority (highest prio are merged in last)
cmdsets = sorted(cmdsets, key=lambda x: x.priority) cmdsets = yield sorted(cmdsets, key=lambda x: x.priority)
if cmdsets: if cmdsets:
# Merge all command sets into one, beginning with the lowest-prio one # Merge all command sets into one, beginning with the lowest-prio one
@ -134,18 +136,19 @@ def get_and_merge_cmdsets(caller):
for merging_cmdset in cmdsets: for merging_cmdset in cmdsets:
#print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype, #print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype,
# cmdset.key, cmdset.priority, cmdset.mergetype) # cmdset.key, cmdset.priority, cmdset.mergetype)
cmdset = merging_cmdset + cmdset cmdset = yield merging_cmdset + cmdset
else: else:
cmdset = None cmdset = None
for cset in (cset for cset in local_objects_cmdsets if cset): for cset in (cset for cset in local_objects_cmdsets if cset):
cset.duplicates = cset.old_duplicates cset.duplicates = cset.old_duplicates
return cmdset returnValue(cmdset)
# Main command-handler function # Main command-handler function
@inlineCallbacks
def cmdhandler(caller, raw_string, testing=False): def cmdhandler(caller, raw_string, testing=False):
""" """
This is the main function to handle any string sent to the engine. This is the main function to handle any string sent to the engine.
@ -154,13 +157,13 @@ def cmdhandler(caller, raw_string, testing=False):
raw_string - the command string given on the command line raw_string - the command string given on the command line
testing - if we should actually execute the command or not. testing - if we should actually execute the command or not.
if True, the command instance will be returned instead. if True, the command instance will be returned instead.
Note that this function returns a deferred!
""" """
try: # catch bugs in cmdhandler itself try: # catch bugs in cmdhandler itself
try: # catch special-type commands try: # catch special-type commands
cmdset = get_and_merge_cmdsets(caller) cmdset = yield get_and_merge_cmdsets(caller)
# print cmdset
if not cmdset: if not cmdset:
# this is bad and shouldn't happen. # this is bad and shouldn't happen.
raise NoCmdSets raise NoCmdSets
@ -168,17 +171,17 @@ def cmdhandler(caller, raw_string, testing=False):
raw_string = raw_string.strip() raw_string = raw_string.strip()
if not raw_string: if not raw_string:
# Empty input. Test for system command instead. # Empty input. Test for system command instead.
syscmd = cmdset.get(CMD_NOINPUT) syscmd = yield cmdset.get(CMD_NOINPUT)
sysarg = "" sysarg = ""
raise ExecSystemCommand(syscmd, sysarg) raise ExecSystemCommand(syscmd, sysarg)
# Parse the input string and match to available cmdset. # Parse the input string and match to available cmdset.
# This also checks for permissions, so all commands in match # This also checks for permissions, so all commands in match
# are commands the caller is allowed to call. # are commands the caller is allowed to call.
matches = COMMAND_PARSER(raw_string, cmdset, caller) matches = yield COMMAND_PARSER(raw_string, cmdset, caller)
# Deal with matches # Deal with matches
if not matches: if not matches:
# No commands match our entered command # No commands match our entered command
syscmd = cmdset.get(CMD_NOMATCH) syscmd = yield cmdset.get(CMD_NOMATCH)
if syscmd: if syscmd:
sysarg = raw_string sysarg = raw_string
else: else:
@ -187,12 +190,12 @@ def cmdhandler(caller, raw_string, testing=False):
if len(matches) > 1: if len(matches) > 1:
# We have a multiple-match # We have a multiple-match
syscmd = cmdset.get(CMD_MULTIMATCH) syscmd = yield cmdset.get(CMD_MULTIMATCH)
sysarg = "There where multiple matches." sysarg = "There where multiple matches."
if syscmd: if syscmd:
syscmd.matches = matches syscmd.matches = matches
else: else:
sysarg = at_multimatch_cmd(caller, matches) sysarg = yield at_multimatch_cmd(caller, matches)
raise ExecSystemCommand(syscmd, sysarg) raise ExecSystemCommand(syscmd, sysarg)
# At this point, we have a unique command match. # At this point, we have a unique command match.
@ -203,7 +206,7 @@ def cmdhandler(caller, raw_string, testing=False):
if hasattr(cmd, 'is_channel') and cmd.is_channel: if hasattr(cmd, 'is_channel') and cmd.is_channel:
# even if a user-defined syscmd is not defined, the # even if a user-defined syscmd is not defined, the
# found cmd is already a system command in its own right. # found cmd is already a system command in its own right.
syscmd = cmdset.get(CMD_CHANNEL) syscmd = yield cmdset.get(CMD_CHANNEL)
if syscmd: if syscmd:
# replace system command with custom version # replace system command with custom version
cmd = syscmd cmd = syscmd
@ -221,32 +224,34 @@ def cmdhandler(caller, raw_string, testing=False):
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'): if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):
# cmd.obj are automatically made available. # cmd.obj are automatically made available.
# we make sure to validate its scripts. # we make sure to validate its scripts.
cmd.obj.scripts.validate() yield cmd.obj.scripts.validate()
if testing: if testing:
# only return the command instance # only return the command instance
return cmd returnValue(cmd)
# pre-command hook # pre-command hook
cmd.at_pre_cmd() yield cmd.at_pre_cmd()
# Parse and execute # Parse and execute
cmd.parse() yield cmd.parse()
# (return value is normally None) # (return value is normally None)
ret = cmd.func() ret = yield cmd.func()
# post-command hook # post-command hook
cmd.at_post_cmd() yield cmd.at_post_cmd()
if cmd.save_for_next: if cmd.save_for_next:
# store a reference to this command, possibly # store a reference to this command, possibly
# accessible by the next command. # accessible by the next command.
caller.ndb.last_cmd = copy(cmd) caller.ndb.last_cmd = yield copy(cmd)
else: else:
caller.ndb.last_cmd = None caller.ndb.last_cmd = None
# Done! By default, Evennia does not use this return at all # Done! By default, Evennia does not use this return at all
return ret def callback(r):
return r
returnValue(ret).addCallback(r)
except ExecSystemCommand, exc: except ExecSystemCommand, exc:
# Not a normal command: run a system command, if available, # Not a normal command: run a system command, if available,
@ -266,7 +271,7 @@ def cmdhandler(caller, raw_string, testing=False):
if testing: if testing:
# only return the command instance # only return the command instance
return syscmd returnValue(syscmd)
# parse and run the command # parse and run the command
syscmd.parse() syscmd.parse()

View file

@ -9,6 +9,7 @@ from src.objects.models import ObjectDB, ObjAttribute
from src.players.models import PlayerAttribute from src.players.models import PlayerAttribute
from src.utils import create, utils, debug from src.utils import create, utils, debug
from src.commands.default.muxcommand import MuxCommand from src.commands.default.muxcommand import MuxCommand
from src.commands.cmdhandler import get_and_merge_cmdsets
# used by @find # used by @find
CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS
@ -1506,7 +1507,7 @@ class CmdExamine(ObjManipCommand):
string += "\n %s = %s" % (attr, value) string += "\n %s = %s" % (attr, value)
return string return string
def format_output(self, obj, raw=False): def format_output(self, obj, avail_cmdset, raw=False):
""" """
Helper function that creates a nice report about an object. Helper function that creates a nice report about an object.
@ -1579,8 +1580,8 @@ class CmdExamine(ObjManipCommand):
string += headers["cmdset"] % cmdsetstr string += headers["cmdset"] % cmdsetstr
# list the actually available commands # list the actually available commands
from src.commands.cmdhandler import get_and_merge_cmdsets #from src.commands.cmdhandler import get_and_merge_cmdsets
avail_cmdset = get_and_merge_cmdsets(obj) #avail_cmdset = get_and_merge_cmdsets(obj)
avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")]) avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")])
cmdsetstr = utils.fill(", ".join(avail_cmdset), indent=2) cmdsetstr = utils.fill(", ".join(avail_cmdset), indent=2)
@ -1620,6 +1621,18 @@ class CmdExamine(ObjManipCommand):
if "raw" in self.switches: if "raw" in self.switches:
msgdata = {"raw":True} msgdata = {"raw":True}
def get_cmdset_callback(cmdset):
"""
We make use of the cmdhandeler.get_and_merge_cmdsets below. This
is an asynchronous function, returning a Twisted deferred.
So in order to properly use this we need use this callback;
it is called with the result of get_and_merge_cmdsets, whenever
that function finishes. Taking the resulting cmdset, we continue
to format and output the result.
"""
string = self.format_output(obj, cmdset, raw=msgdata)
caller.msg(string.strip(), data=msgdata)
if not self.args: if not self.args:
# If no arguments are provided, examine the invoker's location. # If no arguments are provided, examine the invoker's location.
obj = caller.location obj = caller.location
@ -1627,12 +1640,11 @@ class CmdExamine(ObjManipCommand):
#If we don't have special info access, just look at the object instead. #If we don't have special info access, just look at the object instead.
caller.execute_cmd('look %s' % obj.name) caller.execute_cmd('look %s' % obj.name)
return return
string = self.format_output(obj, raw=msgdata) # using callback for printing result whenever function returns.
caller.msg(string.strip(), data=msgdata) get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback)
return return
# we have given a specific target object # we have given a specific target object
string = ""
for objdef in self.lhs_objattr: for objdef in self.lhs_objattr:
obj_name = objdef['name'] obj_name = objdef['name']
@ -1653,10 +1665,10 @@ class CmdExamine(ObjManipCommand):
if obj_attrs: if obj_attrs:
for attrname in obj_attrs: for attrname in obj_attrs:
# we are only interested in specific attributes # we are only interested in specific attributes
string += self.format_attributes(obj, attrname, crop=False, raw=msgdata) caller.msg(self.format_attributes(obj, attrname, crop=False, raw=msgdata))
else: else:
string += self.format_output(obj, raw=msgdata) # using callback to print results whenever function returns.
caller.msg(string.strip(), data=msgdata) get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback)
class CmdFind(MuxCommand): class CmdFind(MuxCommand):