diff --git a/evennia/commands/cmdhandler.py b/evennia/commands/cmdhandler.py index baa58f08cc..c88eed42a7 100644 --- a/evennia/commands/cmdhandler.py +++ b/evennia/commands/cmdhandler.py @@ -199,15 +199,13 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype): local_obj_cmdsets = [None] @inlineCallbacks - def _get_channel_cmdset(player):#, player_cmdset): + def _get_channel_cmdset(player_or_obj): """ Helper-method; Get channel-cmdsets """ # Create cmdset for all player's available channels try: - channel_cmdset = None - #if not player_cmdset.no_channels: #TODO - channel_cmdset = yield CHANNELHANDLER.get_cmdset(player) + channel_cmdset = yield CHANNELHANDLER.get_cmdset(player_or_obj) returnValue([channel_cmdset]) except Exception: _msg_err(caller, _ERROR_CMDSETS) @@ -261,7 +259,7 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype): def _get_cmdsets(obj): """ Helper method; Get cmdset while making sure to trigger all - hooks safely. + hooks safely. Returns the stack and the valid options. """ try: yield obj.at_cmdset_get() @@ -269,43 +267,71 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype): _msg_err(caller, _ERROR_CMDSETS) raise ErrorReported try: - #returnValue(obj.cmdset.current) - returnValue(obj.cmdset.cmdset_stack) + # we don't return the 'duplicates' option since this happens per-merge + returnValue((obj.cmdset.current, obj.cmdset.cmdset_stack)) except AttributeError: - returnValue(None) + returnValue(((None, None, None), [])) + local_obj_cmdsets = [] if callertype == "session": # we are calling the command from the session level report_to = session - cmdsets = yield _get_cmdsets(session) + current, cmdsets = yield _get_cmdsets(session) if player: # this automatically implies logged-in - player_cmdsets = yield _get_cmdsets(player) - channel_cmdset = yield _get_channel_cmdset(player) - cmdsets += player_cmdsets + channel_cmdset + pcurrent, player_cmdsets = yield _get_cmdsets(player) + cmdsets += player_cmdsets + current = current + pcurrent if obj: - obj_cmdset = yield _get_cmdsets(obj) - local_obj_cmdsets = yield _get_local_obj_cmdsets(obj) - cmdsets.extend(obj_cmdset + local_obj_cmdsets) + ocurrent, obj_cmdsets = yield _get_cmdsets(obj) + current = current + ocurrent + cmdsets += obj_cmdsets + if not current.no_objs: + local_obj_cmdsets = yield _get_local_obj_cmdsets(obj) + if current.no_exits: + # filter out all exits + local_obj_cmdsets = [cmdset for cmdset in local_obj_cmdsets if cmdset.key != "ExitCmdSet"] + cmdsets += local_obj_cmdsets + if not current.no_channels: + # also objs may have channels + cmdsets += yield _get_channel_cmdset(obj) + if not current.no_channels: + cmdsets += yield _get_channel_cmdset(player) + elif callertype == "player": # we are calling the command from the player level report_to = player - player_cmdset = yield _get_cmdsets(player) - channel_cmdset = yield _get_channel_cmdset(player) - cmdsets = player_cmdset + channel_cmdset + current, cmdsets = yield _get_cmdsets(player) if obj: - obj_cmdset = yield _get_cmdsets(obj) - local_obj_cmdsets = yield _get_local_obj_cmdsets(obj) - cmdsets.extend(obj_cmdset + local_obj_cmdsets) + ocurrent, obj_cmdsets = yield _get_cmdsets(obj) + current = current + ocurrent + cmdsets += obj_cmdsets + if not current.no_objs: + local_obj_cmdsets = yield _get_local_obj_cmdsets(obj) + if current.no_exits: + # filter out all exits + local_obj_cmdsets = [cmdset for cmdset in local_obj_cmdsets if cmdset.key != "ExitCmdSet"] + cmdsets += local_obj_cmdsets + if not current.no_channels: + # also objs may have channels + cmdsets += yield _get_channel_cmdset(obj) + if not current.no_channels: + cmdsets += yield _get_channel_cmdset(player) + elif callertype == "object": # we are calling the command from the object level report_to = obj - obj_cmdset = yield _get_cmdsets(obj) - local_obj_cmdsets = yield _get_local_obj_cmdsets(obj) - cmdsets = obj_cmdset + local_obj_cmdsets + current, cmdsets = yield _get_cmdsets(obj) + if not current.no_objs: + local_obj_cmdsets = yield _get_local_obj_cmdsets(obj) + if current.no_exits: + # filter out all exits + local_obj_cmdsets = [cmdset for cmdset in local_obj_cmdsets if cmdset.key != "ExitCmdSet"] + cmdsets += yield local_obj_cmdsets + if not current.no_channels: + # also objs may have channels + cmdsets += yield _get_channel_cmdset(obj) else: raise Exception("get_and_merge_cmdsets: callertype %s is not valid." % callertype) - #cmdsets = yield [caller_cmdset] + [player_cmdset] + - # [channel_cmdset] + local_obj_cmdsets # weed out all non-found sets cmdsets = yield [cmdset for cmdset in cmdsets @@ -354,6 +380,7 @@ def get_and_merge_cmdsets(caller, session, player, obj, callertype): raise except Exception: _msg_err(caller, _ERROR_CMDSETS) + raise raise ErrorReported # Main command-handler function @@ -544,12 +571,6 @@ def cmdhandler(called_by, raw_string, _testing=False, callertype="session", sess match = matches[0] cmdname, args, cmd = match[0], match[1], match[2] - # check if we allow this type of command - if cmdset.no_channels and hasattr(cmd, "is_channel") and cmd.is_channel: - matches = [] - if cmdset.no_exits and hasattr(cmd, "is_exit") and cmd.is_exit: - matches = [] - if not matches: # No commands match our entered command syscmd = yield cmdset.get(CMD_NOMATCH) diff --git a/evennia/commands/cmdset.py b/evennia/commands/cmdset.py index 04815c639e..996672213b 100644 --- a/evennia/commands/cmdset.py +++ b/evennia/commands/cmdset.py @@ -390,23 +390,12 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): else: # Union cmdset_c = self._union(cmdset_a, self) - if self.priority == cmdset_a.priority: - # same prio - pass through if changed - cmdset_c.no_channels = self.no_channels if cmdset_a.no_channels is None else cmdset_a.no_channels - cmdset_c.no_exits = self.no_exits if cmdset_a.no_exits is None else cmdset_a.no_exits - cmdset_c.no_objs = self.no_objs if cmdset_a.no_objs is None else cmdset_a.no_objs - cmdset_c.duplicates = self.duplicates if cmdset_a.duplicates is None else cmdset_a.duplicates - else: - # pass through the options of the higher prio set - cmdset_c.no_channels = cmdset_a.no_channels - cmdset_c.no_exits = cmdset_a.no_exits - cmdset_c.no_objs = cmdset_a.no_objs - cmdset_c.duplicates = cmdset_a.duplicates - - if cmdset_a.key.startswith("_"): - # don't rename new output if the merge set's name starts with _ - # these are usually things like exitcmdsets and channelcmdsets) - cmdset_c.key = self.key + # pass through options whenever they are set, unless the merging or higher-prio + # set changes the setting (i.e. has a non-None value). + cmdset_c.no_channels = self.no_channels if cmdset_a.no_channels is None else cmdset_a.no_channels + cmdset_c.no_exits = self.no_exits if cmdset_a.no_exits is None else cmdset_a.no_exits + cmdset_c.no_objs = self.no_objs if cmdset_a.no_objs is None else cmdset_a.no_objs + cmdset_c.duplicates = self.duplicates if cmdset_a.duplicates is None else cmdset_a.duplicates else: # B higher priority than A @@ -425,15 +414,12 @@ class CmdSet(with_metaclass(_CmdSetMeta, object)): else: # Union cmdset_c = self._union(self, cmdset_a) - cmdset_c.no_channels = self.no_channels - cmdset_c.no_exits = self.no_exits - cmdset_c.no_objs = self.no_objs - cmdset_c.duplicates = self.duplicates - - # update or pass-through - if self.key.startswith("_"): - # don't rename new output if the merge set's name starts with _ - cmdset_c.key = cmdset_a.self.key + # pass through options whenever they are set, unless the higher-prio + # set changes the setting (i.e. has a non-None value). + cmdset_c.no_channels = cmdset_a.no_channels if self.no_channels is None else self.no_channels + cmdset_c.no_exits = cmdset_a.no_exits if self.no_exits is None else self.no_exits + cmdset_c.no_objs = cmdset_a.no_objs if self.no_objs is None else self.no_objs + cmdset_c.duplicates = cmdset_a.duplicates if self.duplicates is None else self.duplicates # we store actual_mergetype since key_mergetypes # might be different from the main mergetype. diff --git a/evennia/commands/tests.py b/evennia/commands/tests.py index 2b9da9913a..c238b7e3dd 100644 --- a/evennia/commands/tests.py +++ b/evennia/commands/tests.py @@ -170,6 +170,8 @@ class TestCmdSetMergers(TestCase): def test_option_transfer(self): "Test transfer of cmdset options" a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d + # the options should pass through since none of the other cmdsets care + # to change the setting from None. a.no_exits = True a.no_objs = True a.no_channels = True @@ -207,15 +209,25 @@ class TestCmdSetMergers(TestCase): c.priority = 1 d.priority = 2 cmdset_f = d + c + b + a # reverse, A low prio - self.assertFalse(cmdset_f.no_exits) - self.assertFalse(cmdset_f.no_objs) - self.assertFalse(cmdset_f.no_channels) - self.assertFalse(cmdset_f.duplicates) + self.assertTrue(cmdset_f.no_exits) + self.assertTrue(cmdset_f.no_objs) + self.assertTrue(cmdset_f.no_channels) + self.assertTrue(cmdset_f.duplicates) self.assertEqual(len(cmdset_f.commands), 4) cmdset_f = a + b + c + d # forward, A low prio + self.assertTrue(cmdset_f.no_exits) + self.assertTrue(cmdset_f.no_objs) + self.assertTrue(cmdset_f.no_channels) + self.assertTrue(cmdset_f.duplicates) + self.assertEqual(len(cmdset_f.commands), 4) + c.no_exits = False + b.no_objs = False + d.duplicates = False + # higher-prio sets will change the option up the chain + cmdset_f = a + b + c + d # forward, A low prio self.assertFalse(cmdset_f.no_exits) self.assertFalse(cmdset_f.no_objs) - self.assertFalse(cmdset_f.no_channels) + self.assertTrue(cmdset_f.no_channels) self.assertFalse(cmdset_f.duplicates) self.assertEqual(len(cmdset_f.commands), 4) a.priority = 0 @@ -245,17 +257,28 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): obj.cmdset.add(cmdset) def test_from_session(self): - self.set_cmdsets(self.session, self.cmdset_a) + a = self.cmdset_a + a.no_channels = True + self.set_cmdsets(self.session, a) deferred = cmdhandler.get_and_merge_cmdsets(self.session, self.session, None, None, "session") - _callback = lambda cmdset: self.assertEqual(cmdset.key, "A") + def _callback(cmdset): + self.assertEqual(cmdset.key, "A") deferred.addCallback(_callback) return deferred def test_from_player(self): - self.set_cmdsets(self.player, self.cmdset_a) + from evennia.commands.default.cmdset_player import PlayerCmdSet + a = self.cmdset_a + a.no_channels = True + self.set_cmdsets(self.player, a) deferred = cmdhandler.get_and_merge_cmdsets(self.player, None, self.player, None, "player") # get_and_merge_cmdsets converts to lower-case internally. - _callback = lambda cmdset: self.assertEqual(sum(1 for cmd in cmdset.commands if cmd.key in ("a", "b", "c", "d")), 4) + def _callback(cmdset): + pcmdset = PlayerCmdSet() + pcmdset.at_cmdset_creation() + pcmds = [cmd.key for cmd in pcmdset.commands] + ["a", "b", "c", "d"] + self.assertTrue(all(cmd.key in pcmds for cmd in cmdset.commands)) + #_callback = lambda cmdset: self.assertEqual(sum(1 for cmd in cmdset.commands if cmd.key in ("a", "b", "c", "d")), 4) deferred.addCallback(_callback) return deferred @@ -269,12 +292,33 @@ class TestGetAndMergeCmdSets(TwistedTestCase, EvenniaTest): def test_multimerge(self): a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d - d.no_exits = True + a.no_exits = True + a.no_channels = True self.set_cmdsets(self.obj1, a, b, c, d) deferred = cmdhandler.get_and_merge_cmdsets(self.obj1, None, None, self.obj1, "object") def _callback(cmdset): - self.assertEqual(cmdset.key, "D") self.assertTrue(cmdset.no_exits) + self.assertTrue(cmdset.no_channels) + self.assertEqual(cmdset.key, "D") + deferred.addCallback(_callback) + return deferred + + def test_autocmdsets(self): + import evennia + from evennia.commands.default.cmdset_player import PlayerCmdSet + from evennia.comms.channelhandler import CHANNEL_HANDLER + testchannel = evennia.create_channel("testchannel", locks="listen:all();send:all()") + CHANNEL_HANDLER.add(testchannel) + self.assertTrue(testchannel.connect(self.player)) + a, b, c, d = self.cmdset_a, self.cmdset_b, self.cmdset_c, self.cmdset_d + self.set_cmdsets(self.player, a, b, c, d) + deferred = cmdhandler.get_and_merge_cmdsets(self.session, self.session, self.player, self.char1, "session") + def _callback(cmdset): + pcmdset = PlayerCmdSet() + pcmdset.at_cmdset_creation() + pcmds = [cmd.key for cmd in pcmdset.commands] + ["a", "b", "c", "d"] + ["out"] + self.assertTrue(all(cmd.key or hasattr(cmd, "is_channel") in pcmds for cmd in cmdset.commands)) + self.assertTrue(any(hasattr(cmd, "is_channel") for cmd in cmdset.commands)) deferred.addCallback(_callback) return deferred diff --git a/evennia/comms/channelhandler.py b/evennia/comms/channelhandler.py index f5f1d3536e..271d898600 100644 --- a/evennia/comms/channelhandler.py +++ b/evennia/comms/channelhandler.py @@ -162,7 +162,7 @@ class ChannelHandler(object): """ self.cached_channel_cmds = [] - def add_channel(self, channel): + def add(self, channel): """ Add an individual channel to the handler. This should be called whenever a new channel is created. @@ -197,6 +197,7 @@ class ChannelHandler(object): channeldesc=channel.attributes.get("desc", default="").strip()) self.cached_channel_cmds.append(cmd) self.cached_cmdsets = {} + add_channel = add # legacy alias def update(self): """ @@ -220,7 +221,7 @@ class ChannelHandler(object): Args: source_object (Object): An object subscribing to one or more channels. - +hannelhandler import CHANNEL_HANDLER Returns: cmdsets (list): The Channel-Cmdsets `source_object` has access to. @@ -231,7 +232,7 @@ class ChannelHandler(object): else: # create a new cmdset holding all channels chan_cmdset = cmdset.CmdSet() - chan_cmdset.key = '_channelset' + chan_cmdset.key = 'ChannelCmdSet' chan_cmdset.priority = 101 chan_cmdset.duplicates = True for cmd in [cmd for cmd in self.cached_channel_cmds diff --git a/evennia/objects/objects.py b/evennia/objects/objects.py index 1977140d39..13824e5a7a 100644 --- a/evennia/objects/objects.py +++ b/evennia/objects/objects.py @@ -1684,7 +1684,7 @@ class DefaultExit(DefaultObject): obj=exidbobj) # create a cmdset exit_cmdset = cmdset.CmdSet(None) - exit_cmdset.key = '_exitset' + exit_cmdset.key = 'ExitCmdSet' exit_cmdset.priority = self.priority exit_cmdset.duplicates = True # add command to cmdset