From 641d8291547c442a78ed2c34e6dc9b74b935ca73 Mon Sep 17 00:00:00 2001 From: Griatch Date: Mon, 20 Feb 2012 21:40:28 +0100 Subject: [PATCH] Added inlineCallback operator for cmdhandler, allowing the system to yield more often. --- contrib/misc_commands.py | 13 ++++--- src/commands/cmdhandler.py | 61 +++++++++++++++++--------------- src/commands/default/building.py | 32 +++++++++++------ 3 files changed, 63 insertions(+), 43 deletions(-) diff --git a/contrib/misc_commands.py b/contrib/misc_commands.py index 7874c58d49..8d3133cc00 100644 --- a/contrib/misc_commands.py +++ b/contrib/misc_commands.py @@ -69,12 +69,15 @@ class CmdQuell(MuxCommand): self.caller.player.user.is_superuser = False self.caller.player.user.save() - try: - ret = self.caller.execute_cmd(cmd) - except Exception, e: - self.caller.msg(str(e)) + def callback(ret): + self.caller.msg(ret) + def errback(err): + 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.player.user.is_superuser = old_superuser self.caller.player.user.save() - return ret + return diff --git a/src/commands/cmdhandler.py b/src/commands/cmdhandler.py index 990ec7b1b0..2bbac35218 100644 --- a/src/commands/cmdhandler.py +++ b/src/commands/cmdhandler.py @@ -37,6 +37,7 @@ command line. The process is as follows: from copy import copy from traceback import format_exc +from twisted.internet.defer import inlineCallbacks, returnValue from django.conf import settings from src.comms.channelhandler import CHANNELHANDLER from src.commands.cmdsethandler import import_cmdset @@ -73,6 +74,7 @@ class ExecSystemCommand(Exception): self.syscmd = syscmd self.sysarg = sysarg +@inlineCallbacks def get_and_merge_cmdsets(caller): """ Gather all relevant cmdsets and merge them. Note @@ -80,7 +82,7 @@ def get_and_merge_cmdsets(caller): """ # The calling object's cmdset try: - caller.at_cmdset_get() + yield caller.at_cmdset_get() except Exception: logger.log_trace() try: @@ -91,7 +93,7 @@ def get_and_merge_cmdsets(caller): # Create cmdset for all player's available channels channel_cmdset = None 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 local_objects_cmdsets = [None] @@ -101,15 +103,15 @@ def get_and_merge_cmdsets(caller): if location and not caller_cmdset.no_objs: # Gather all cmdsets stored on objects in the room and # 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: try: # call hook in case we need to do dynamic changing to cmdset - obj.at_cmdset_get() + yield obj.at_cmdset_get() except Exception: logger.log_trace() - local_objects_cmdsets = [obj.cmdset.current for obj in local_objlist - if (obj.cmdset.current and obj.locks.check(caller, 'call', no_superuser_bypass=True))] + 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))] for cset in local_objects_cmdsets: #This is necessary for object sets, or we won't be able to separate #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 # 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) - cmdsets = sorted(cmdsets, key=lambda x: x.priority) + cmdsets = yield sorted(cmdsets, key=lambda x: x.priority) if cmdsets: # 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: #print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype, # cmdset.key, cmdset.priority, cmdset.mergetype) - cmdset = merging_cmdset + cmdset + cmdset = yield merging_cmdset + cmdset else: cmdset = None for cset in (cset for cset in local_objects_cmdsets if cset): cset.duplicates = cset.old_duplicates - return cmdset + returnValue(cmdset) # Main command-handler function +@inlineCallbacks def cmdhandler(caller, raw_string, testing=False): """ 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 testing - if we should actually execute the command or not. if True, the command instance will be returned instead. + + Note that this function returns a deferred! """ try: # catch bugs in cmdhandler itself try: # catch special-type commands - cmdset = get_and_merge_cmdsets(caller) - - # print cmdset + cmdset = yield get_and_merge_cmdsets(caller) if not cmdset: # this is bad and shouldn't happen. raise NoCmdSets @@ -168,17 +171,17 @@ def cmdhandler(caller, raw_string, testing=False): raw_string = raw_string.strip() if not raw_string: # Empty input. Test for system command instead. - syscmd = cmdset.get(CMD_NOINPUT) + syscmd = yield cmdset.get(CMD_NOINPUT) sysarg = "" raise ExecSystemCommand(syscmd, sysarg) # Parse the input string and match to available cmdset. # This also checks for permissions, so all commands in match # 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 if not matches: # No commands match our entered command - syscmd = cmdset.get(CMD_NOMATCH) + syscmd = yield cmdset.get(CMD_NOMATCH) if syscmd: sysarg = raw_string else: @@ -187,12 +190,12 @@ def cmdhandler(caller, raw_string, testing=False): if len(matches) > 1: # We have a multiple-match - syscmd = cmdset.get(CMD_MULTIMATCH) + syscmd = yield cmdset.get(CMD_MULTIMATCH) sysarg = "There where multiple matches." if syscmd: syscmd.matches = matches else: - sysarg = at_multimatch_cmd(caller, matches) + sysarg = yield at_multimatch_cmd(caller, matches) raise ExecSystemCommand(syscmd, sysarg) # 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: # even if a user-defined syscmd is not defined, the # found cmd is already a system command in its own right. - syscmd = cmdset.get(CMD_CHANNEL) + syscmd = yield cmdset.get(CMD_CHANNEL) if syscmd: # replace system command with custom version cmd = syscmd @@ -221,32 +224,34 @@ def cmdhandler(caller, raw_string, testing=False): if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'): # cmd.obj are automatically made available. # we make sure to validate its scripts. - cmd.obj.scripts.validate() + yield cmd.obj.scripts.validate() if testing: # only return the command instance - return cmd + returnValue(cmd) # pre-command hook - cmd.at_pre_cmd() + yield cmd.at_pre_cmd() # Parse and execute - cmd.parse() + yield cmd.parse() # (return value is normally None) - ret = cmd.func() + ret = yield cmd.func() # post-command hook - cmd.at_post_cmd() + yield cmd.at_post_cmd() if cmd.save_for_next: # store a reference to this command, possibly # accessible by the next command. - caller.ndb.last_cmd = copy(cmd) + caller.ndb.last_cmd = yield copy(cmd) else: caller.ndb.last_cmd = None # 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: # Not a normal command: run a system command, if available, @@ -266,7 +271,7 @@ def cmdhandler(caller, raw_string, testing=False): if testing: # only return the command instance - return syscmd + returnValue(syscmd) # parse and run the command syscmd.parse() diff --git a/src/commands/default/building.py b/src/commands/default/building.py index 495e5c63e3..5c766cd494 100644 --- a/src/commands/default/building.py +++ b/src/commands/default/building.py @@ -9,6 +9,7 @@ from src.objects.models import ObjectDB, ObjAttribute from src.players.models import PlayerAttribute from src.utils import create, utils, debug from src.commands.default.muxcommand import MuxCommand +from src.commands.cmdhandler import get_and_merge_cmdsets # used by @find CHAR_TYPECLASS = settings.BASE_CHARACTER_TYPECLASS @@ -1506,7 +1507,7 @@ class CmdExamine(ObjManipCommand): string += "\n %s = %s" % (attr, value) 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. @@ -1579,8 +1580,8 @@ class CmdExamine(ObjManipCommand): string += headers["cmdset"] % cmdsetstr # list the actually available commands - from src.commands.cmdhandler import get_and_merge_cmdsets - avail_cmdset = get_and_merge_cmdsets(obj) + #from src.commands.cmdhandler import get_and_merge_cmdsets + #avail_cmdset = get_and_merge_cmdsets(obj) avail_cmdset = sorted([cmd.key for cmd in avail_cmdset if cmd.access(obj, "cmd")]) cmdsetstr = utils.fill(", ".join(avail_cmdset), indent=2) @@ -1619,6 +1620,18 @@ class CmdExamine(ObjManipCommand): msgdata = None if "raw" in self.switches: 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 no arguments are provided, examine the invoker's location. @@ -1627,12 +1640,11 @@ class CmdExamine(ObjManipCommand): #If we don't have special info access, just look at the object instead. caller.execute_cmd('look %s' % obj.name) return - string = self.format_output(obj, raw=msgdata) - caller.msg(string.strip(), data=msgdata) - return + # using callback for printing result whenever function returns. + get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback) + return # we have given a specific target object - string = "" for objdef in self.lhs_objattr: obj_name = objdef['name'] @@ -1653,10 +1665,10 @@ class CmdExamine(ObjManipCommand): if obj_attrs: for attrname in obj_attrs: # 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: - string += self.format_output(obj, raw=msgdata) - caller.msg(string.strip(), data=msgdata) + # using callback to print results whenever function returns. + get_and_merge_cmdsets(obj).addCallback(get_cmdset_callback) class CmdFind(MuxCommand):