Added the capability of evennia commands to consist of more than one word. So the examples.red_button parent can now be pressed with the command 'push button' instead of 'pushbutton' as before. The variable COMMAND_MAXLEN in the config defines how many words your commands may maximum contain (don't set it higher than needed for efficiency reasons).

The main drawback of multi-word commands are that they can not have any switches; they are intended for in-game use (in states and on objects). Use normal one-word commands for administration and more complex commands with many options.
/Griatch
This commit is contained in:
Griatch 2009-10-18 19:29:18 +00:00
parent 0c29d359f6
commit a711e07b80
4 changed files with 115 additions and 53 deletions

View file

@ -104,6 +104,6 @@ def class_factory(source_obj):
"""
button = RedButton(source_obj)
# add the object-based commands to the button
button.command_table.add_command("pushbutton", cmd_push_button)
button.command_table.add_command("pullbutton", cmd_pull_button)
button.command_table.add_command("push button", cmd_push_button)
button.command_table.add_command("pull button", cmd_pull_button)
return button

View file

@ -3,8 +3,9 @@ This is the command processing module. It is instanced once in the main
server module and the handle() function is hit every time a player sends
something.
"""
import time
#import time
from traceback import format_exc
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
import defines_global
import cmdtable
@ -13,6 +14,8 @@ import logger
import comsys
import alias_mgr
COMMAND_MAXLEN = settings.COMMAND_MAXLEN
class UnknownCommand(Exception):
"""
Throw this when a user enters an an invalid command.
@ -47,6 +50,8 @@ class Command(object):
command_switches = []
# The un-parsed argument provided. IE: if input is "look dog", this is "dog".
command_argument = None
# list of tuples for possible multi-space commands and their arguments
command_alternatives = None
# A reference to the command function looked up in a command table.
command_function = None
# An optional dictionary that is passed through the command table as extra_vars.
@ -66,45 +71,63 @@ class Command(object):
Breaks the command up into the main command string, a list of switches,
and a string containing the argument provided with the command. More
specific processing is left up to the individual command functions.
"""
try:
"""
Break the command in half into command and argument. If the
command string can't be parsed, it has no argument and is
handled by the except ValueError block below.
"""
# Lop off the return at the end.
self.raw_input = self.raw_input.strip('\r')
# Break the command up into the root command and its arguments.
(self.command_string, self.command_argument) = self.raw_input.split(' ', 1)
# Yank off trailing and leading spaces.
self.command_argument = self.command_argument.strip()
self.command_string = self.command_string.strip()
"""
This is a really important behavior to note. If the user enters
anything other than a string with some character in it, the value
of the argument is None, not an empty string.
"""
if self.command_string == '':
self.command_string = None
if self.command_argument == '':
self.command_argument = None
if self.command_string == None:
"""
This prevents any bad stuff from happening as a result of
trying to further parse a None object.
"""
return
except ValueError:
"""
No arguments. IE: look, who.
"""
self.command_string = self.raw_input
# Parse command_string for switches, regardless of what happens.
self.parse_command_switches()
The command can come in two forms:
command/switches arg
command_with_spaces arg
The first form is the normal one, used for administration and other commands
that benefit from the use of switches and options. The drawback is that it
can only consist of one single word (no spaces).
The second form, which does not accept switches, allows for longer command
names (e.g. 'press button' instead of pressbutton) and is mainly useful for
object-based commands for roleplay, puzzles etc.
"""
if not self.raw_input:
return
# add a space after the raw input; this cause split() to always
# create a list with at least two entries.
raw = "%s " % self.raw_input
cmd_words = raw.split()
try:
if '/' in cmd_words[0]:
# if we have switches we directly go for the first command form.
command_string, command_argument = \
(inp.strip() for inp in raw.split(' ', 1))
if command_argument:
self.command_argument = command_argument
if command_string:
# we have a valid command, store and parse switches.
self.command_string = command_string
self.parse_command_switches()
else:
# no switches - we need to save a list of all possible command
# names up to the max-length allowed.
command_maxlen = min(COMMAND_MAXLEN, len(cmd_words))
command_alternatives = []
for spacecount in reversed(range(command_maxlen)):
# store all space-separated possible command names
# as tuples (commandname, args). They are stored with
# the longest possible name first.
try:
command_alternatives.append( (" ".join(cmd_words[:spacecount+1]),
" ".join(cmd_words[spacecount+1:])) )
except IndexError:
continue
if command_alternatives:
# store alternatives. Store the one-word command
# as the default command name.
one_word_command = command_alternatives.pop()
self.command_string = one_word_command[0]
self.command_argument = one_word_command[1]
self.command_alternatives = command_alternatives
except IndexError:
# this SHOULD only happen if raw_input is malformed
# (like containing only control characters).
pass
def __init__(self, source_object, raw_input, session=None):
"""
Instantiates the Command object and does some preliminary parsing.
@ -179,6 +202,16 @@ def match_alias(command):
command.command_string = alias_mgr.CMD_ALIAS_LIST.get(
command.command_string,
command.command_string)
# Run aliasing on alternative command names (for commands with
# spaces in them)
if command.command_alternatives:
command_alternatives = []
for command_alternative in command.command_alternatives:
command_alternatives.append( (alias_mgr.CMD_ALIAS_LIST.get(
command_alternative[0],
command_alternative[0]),
command_alternative[1]) )
command.command_alternatives = command_alternatives
def get_aliased_message():
"""
@ -201,7 +234,6 @@ def match_alias(command):
elif first_char == ':':
command.command_argument = get_aliased_message()
command.command_string = "pose"
# command.command_string = "emote"
# Pose without space alias.
elif first_char == ';':
command.command_argument = get_aliased_message()
@ -298,13 +330,35 @@ def command_table_lookup(command, command_table, eval_perms=True,
evaluates the permissions tuple.
The test flag only checks without manipulating the command
neighbor (object) If this is supplied, we are looking at a object table and
must check for locks.
must check for locks.
In the case of one-word commands with switches, this is a
quick look-up. For non-switch commands the command might
however consist of several words separated by spaces up to
a certain max number of words. We don't know beforehand if one
of these match an entry in this particular command table. We search
them in order longest to shortest before deferring to the normal,
one-word assumption.
"""
# Get the command's function reference (Or False)
cmdtuple = command_table.get_command_tuple(command.command_string)
cmdtuple = None
if command.command_alternatives:
# we have command alternatives (due to spaces in command definition)
for cmd_alternative in command.command_alternatives:
# the alternatives are ordered longest -> shortest.
cmdtuple = command_table.get_command_tuple(cmd_alternative[0])
if cmdtuple:
# we have a match, so this is the 'right' command to use
# with this particular command table.
command.command_string = cmd_alternative[0]
command.command_argument = cmd_alternative[1]
if not cmdtuple:
# None of the alternatives match, go with the default one-word name
cmdtuple = command_table.get_command_tuple(command.command_string)
if cmdtuple:
# Check if this is just a test.
# if we get here we have found a command match in the table
if test:
# Check if this is just a test.
return True
# Check locks
if neighbor and not neighbor.scriptlink.use_lock(command.source_object):

View file

@ -696,10 +696,10 @@ def cmd_setcmdalias(command):
@setcmdalias - define shortcuts for commands
Usage:
@setcmdalias[/switch] [command = ] alias
@setcmdalias[/switch] alias [= command]
Switches:
list - view all command aliases (default)
list - view all command aliases
add - add alias
del - remove and existing alias
@ -713,22 +713,26 @@ def cmd_setcmdalias(command):
args = command.command_argument
switches = command.command_switches
if not args or 'list' in switches:
if "list" in switches:
# show all aliases
string = "Command aliases defined:"
aliases = CommandAlias.objects.all()
if not aliases:
string = "No command aliases defined."
for alias in aliases:
string += "\n %s = %s" % (alias.equiv_command, alias.user_input)
string += "\n %s -> %s" % (alias.user_input, alias.equiv_command)
source_object.emit_to(string)
return
if not args:
source_object.emit_to("Usage: @setcmdalias[/list/add/del] alias [= command]")
return
equiv_command = ""
user_input = ""
# analyze args
if '=' in args:
equiv_command, user_input = [arg.strip() for arg in args.split("=",1)]
user_input, equiv_command = [arg.strip() for arg in args.split("=",1)]
else:
user_input = args.strip()

View file

@ -66,10 +66,14 @@ DATABASE_HOST = ''
# Empty string defaults to localhost. Not used with sqlite3.
DATABASE_PORT = ''
# How many words a single command name may have (e.g. 'push button' instead of 'pushbutton')
# (commands with switches always accept only one word in the name, e.g. @sethelp/add)
COMMAND_MAXLEN = 3
## Command aliases
# These are convenient aliases set up when the game is started
# for the very first time. You can add/delete aliases in-game using
# the @cmdalias command.
# the @setcmdalias command.
COMMAND_ALIASES = {"@desc":"@describe",
"@dest":"@destroy", "@nuke":"@destroy",
"@tel":"@teleport",
@ -78,7 +82,7 @@ COMMAND_ALIASES = {"@desc":"@describe",
"ex":"examine",
"sa":"say",
"emote":"pose",
"p":"page" }
"p":"page"}
## Permissions
## The variables in this section are used by each evennia subsystem to tell which permissions to define.