Cleaning some unnecessary whitespace, overall cleanup of various source codes.

This commit is contained in:
Griatch 2012-03-30 23:47:22 +02:00
parent d4c97d7df8
commit c0322c9eae
27 changed files with 1342 additions and 1318 deletions

View file

@ -2,35 +2,35 @@
Command handler
This module contains the infrastructure for accepting commands on the
command line. The process is as follows:
command line. The process is as follows:
1) The calling object (caller) inputs a string and triggers the command parsing system.
2) The system checks the state of the caller - loggedin or not
3) If no command string was supplied, we search the merged cmdset for system command CMD_NOINPUT
3) If no command string was supplied, we search the merged cmdset for system command CMD_NOINPUT
and branches to execute that. --> Finished
4) Cmdsets are gathered from different sources (in order of dropping priority):
channels - all available channel names are auto-created into a cmdset, to allow
4) Cmdsets are gathered from different sources (in order of dropping priority):
channels - all available channel names are auto-created into a cmdset, to allow
for giving the channel name and have the following immediately
sent to the channel. The sending is performed by the CMD_CHANNEL
system command.
object cmdsets - all objects at caller's location are scanned for non-empty
cmdsets. This includes cmdsets on exits.
cmdsets. This includes cmdsets on exits.
caller - the caller is searched for its own currently active cmdset.
player - lastly the cmdsets defined on caller.player are added.
5) All the gathered cmdsets (if more than one) are merged into one using the cmdset priority rules.
player - lastly the cmdsets defined on caller.player are added.
5) All the gathered cmdsets (if more than one) are merged into one using the cmdset priority rules.
6) If merged cmdset is empty, raise NoCmdSet exception (this should not happen, at least the
player should have a default cmdset available at all times). --> Finished
7) The raw input string is parsed using the parser defined by settings.COMMAND_PARSER. It
player should have a default cmdset available at all times). --> Finished
7) The raw input string is parsed using the parser defined by settings.COMMAND_PARSER. It
uses the available commands from the merged cmdset to know which commands to look for and
returns one or many matches.
returns one or many matches.
8) If match list is empty, branch to system command CMD_NOMATCH --> Finished
9) If match list has more than one element, branch to system command CMD_MULTIMATCH --> Finished
10) A single match was found. If this is a channel-command (i.e. the command name is that of a channel),
branch to CMD_CHANNEL --> Finished
branch to CMD_CHANNEL --> Finished
11) At this point we have found a normal command. We assign useful variables to it that
will be available to the command coder at run-time.
12) We have a unique cmdobject, primed for use. Call all hooks:
at_pre_cmd(), cmdobj.parse(), cmdobj.func() and finally at_post_cmd().
will be available to the command coder at run-time.
12) We have a unique cmdobject, primed for use. Call all hooks:
at_pre_cmd(), cmdobj.parse(), cmdobj.func() and finally at_post_cmd().
"""
@ -41,19 +41,19 @@ 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
from src.utils import logger, utils
from src.utils import logger, utils
from src.commands.cmdparser import at_multimatch_cmd
#This switches the command parser to a user-defined one.
# You have to restart the server for this to take effect.
# You have to restart the server for this to take effect.
COMMAND_PARSER = utils.mod_import(*settings.COMMAND_PARSER.rsplit('.', 1))
# There are a few system-hardcoded command names. These
# allow for custom behaviour when the command handler hits
# special situations -- it then calls a normal Command
# that you can customize!
# that you can customize!
# Import these variables and use them rather than trying
# to remember the actual string constants.
# to remember the actual string constants.
CMD_NOINPUT = "__noinput_command"
CMD_NOMATCH = "__nomatch_command"
@ -61,12 +61,12 @@ CMD_MULTIMATCH = "__multimatch_command"
CMD_CHANNEL = "__send_to_channel_command"
# this is the name of the command the engine calls when the player
# connects. It is expected to show the login screen.
CMD_LOGINSTART = "__unloggedin_look_command"
CMD_LOGINSTART = "__unloggedin_look_command"
class NoCmdSets(Exception):
"No cmdsets found. Critical error."
pass
pass
class ExecSystemCommand(Exception):
"Run a system command"
def __init__(self, syscmd, sysarg):
@ -77,10 +77,10 @@ class ExecSystemCommand(Exception):
@inlineCallbacks
def get_and_merge_cmdsets(caller):
"""
Gather all relevant cmdsets and merge them. Note
Gather all relevant cmdsets and merge them. Note
that this is only relevant for logged-in callers.
"""
# The calling object's cmdset
# The calling object's cmdset
try:
yield caller.at_cmdset_get()
except Exception:
@ -89,17 +89,17 @@ def get_and_merge_cmdsets(caller):
caller_cmdset = caller.cmdset.current
except AttributeError:
caller_cmdset = None
# Create cmdset for all player's available channels
channel_cmdset = None
if not caller_cmdset.no_channels:
channel_cmdset = yield CHANNELHANDLER.get_cmdset(caller)
# Gather cmdsets from location, objects in location or carried
local_objects_cmdsets = [None]
# Gather cmdsets from location, objects in location or carried
local_objects_cmdsets = [None]
location = None
if hasattr(caller, "location"):
location = caller.location
location = caller.location
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
@ -113,19 +113,19 @@ def get_and_merge_cmdsets(caller):
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
#This is necessary for object sets, or we won't be able to separate
#the command sets from each other in a busy room.
cset.old_duplicates = cset.duplicates
cset.duplicates = True
# Player object's commandsets
# Player object's commandsets
try:
player_cmdset = caller.player.cmdset.current
except AttributeError:
player_cmdset = None
cmdsets = [caller_cmdset] + [player_cmdset] + [channel_cmdset] + local_objects_cmdsets
# weed out all non-found sets
# weed out all non-found sets
cmdsets = yield [cmdset for cmdset in cmdsets if cmdset]
# sort cmdsets after reverse priority (highest prio are merged in last)
cmdsets = yield sorted(cmdsets, key=lambda x: x.priority)
@ -134,9 +134,9 @@ def get_and_merge_cmdsets(caller):
# Merge all command sets into one, beginning with the lowest-prio one
cmdset = cmdsets.pop(0)
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 = yield merging_cmdset + cmdset
#print "<%s(%s,%s)> onto <%s(%s,%s)>" % (merging_cmdset.key, merging_cmdset.priority, merging_cmdset.mergetype,
# cmdset.key, cmdset.priority, cmdset.mergetype)
cmdset = yield merging_cmdset + cmdset
else:
cmdset = None
@ -146,26 +146,26 @@ def get_and_merge_cmdsets(caller):
returnValue(cmdset)
# Main command-handler function
# 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.
This is the main function to handle any string sent to the engine.
caller - calling object
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.
Note that this function returns a deferred!
"""
Note that this function returns a deferred!
"""
try: # catch bugs in cmdhandler itself
try: # catch special-type commands
cmdset = yield get_and_merge_cmdsets(caller)
if not cmdset:
# this is bad and shouldn't happen.
# this is bad and shouldn't happen.
raise NoCmdSets
raw_string = raw_string.strip()
@ -182,7 +182,7 @@ def cmdhandler(caller, raw_string, testing=False):
if not matches:
# No commands match our entered command
syscmd = yield cmdset.get(CMD_NOMATCH)
if syscmd:
if syscmd:
sysarg = raw_string
else:
sysarg = "Huh? (Type \"help\" for help)"
@ -197,43 +197,43 @@ def cmdhandler(caller, raw_string, testing=False):
else:
sysarg = yield at_multimatch_cmd(caller, matches)
raise ExecSystemCommand(syscmd, sysarg)
# At this point, we have a unique command match.
# At this point, we have a unique command match.
match = matches[0]
cmdname, args, cmd = match[0], match[1], match[2]
# Check if this is a Channel match.
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 = yield cmdset.get(CMD_CHANNEL)
# even if a user-defined syscmd is not defined, the
# found cmd is already a system command in its own right.
syscmd = yield cmdset.get(CMD_CHANNEL)
if syscmd:
# replace system command with custom version
cmd = syscmd
cmd = syscmd
sysarg = "%s:%s" % (cmdname, args)
raise ExecSystemCommand(cmd, sysarg)
# A normal command.
# Assign useful variables to the instance
cmd.caller = caller
cmd.caller = caller
cmd.cmdstring = cmdname
cmd.args = args
cmd.cmdset = cmdset
if hasattr(cmd, 'obj') and hasattr(cmd.obj, 'scripts'):
# cmd.obj are automatically made available.
# we make sure to validate its scripts.
# we make sure to validate its scripts.
yield cmd.obj.scripts.validate()
if testing:
# only return the command instance
returnValue(cmd)
# pre-command hook
yield cmd.at_pre_cmd()
# Parse and execute
# Parse and execute
yield cmd.parse()
# (return value is normally None)
ret = yield cmd.func()
@ -246,15 +246,15 @@ def cmdhandler(caller, raw_string, testing=False):
# accessible by the next command.
caller.ndb.last_cmd = yield copy(cmd)
else:
caller.ndb.last_cmd = None
caller.ndb.last_cmd = None
# Done! This returns a deferred. By default, Evennia does
# not use this at all.
returnValue(ret)
except ExecSystemCommand, exc:
except ExecSystemCommand, exc:
# Not a normal command: run a system command, if available,
# or fall back to a return string.
# or fall back to a return string.
syscmd = exc.syscmd
sysarg = exc.sysarg
if syscmd:
@ -265,9 +265,9 @@ def cmdhandler(caller, raw_string, testing=False):
if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'):
# cmd.obj is automatically made available.
# we make sure to validate its scripts.
# we make sure to validate its scripts.
yield syscmd.obj.scripts.validate()
if testing:
# only return the command instance
returnValue(syscmd)
@ -282,21 +282,21 @@ def cmdhandler(caller, raw_string, testing=False):
except NoCmdSets:
# Critical error.
string = "No command sets found! This is a sign of a critical bug.\n"
string += "The error was logged.\n"
string += "The error was logged.\n"
string += "If logging out/in doesn't solve the problem, try to "
string += "contact the server admin through some other means "
string += "for assistance."
caller.msg(string)
logger.log_errmsg("No cmdsets found: %s" % caller)
except Exception:
# We should not end up here. If we do, it's a programming bug.
string = "%s\nAbove traceback is from an untrapped error."
string = "%s\nAbove traceback is from an untrapped error."
string += " Please file a bug report."
logger.log_trace(string)
caller.msg(string % format_exc())
except Exception:
except Exception:
# This catches exceptions in cmdhandler exceptions themselves
string = "%s\nAbove traceback is from a Command handler bug."
string += " Please contact an admin and/or file a bug report."

View file

@ -9,9 +9,9 @@ from src.utils.logger import log_trace
def cmdparser(raw_string, cmdset, caller, match_index=None):
"""
This function is called by the cmdhandler once it has
This function is called by the cmdhandler once it has
gathered all valid cmdsets for the calling player. raw_string
is the unparsed text entered by the caller.
is the unparsed text entered by the caller.
The cmdparser understand the following command combinations (where
[] marks optional parts.
@ -19,20 +19,20 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
[cmdname[ cmdname2 cmdname3 ...] [the rest]
A command may consist of any number of space-separated words of any
length, and contain any character.
length, and contain any character.
The parser makes use of the cmdset to find command candidates. The
parser return a list of matches. Each match is a tuple with its
first three elements being the parsed cmdname (lower case),
the remaining arguments, and the matched cmdobject from the cmdset.
first three elements being the parsed cmdname (lower case),
the remaining arguments, and the matched cmdobject from the cmdset.
"""
def create_match(cmdname, string, cmdobj):
"""
Evaluates the quality of a match by counting how many chars of cmdname
Evaluates the quality of a match by counting how many chars of cmdname
matches string (counting from beginning of string). We also calculate
a ratio from 0-1 describing how much cmdname matches string.
We return a tuple (cmdname, count, ratio, args, cmdobj).
a ratio from 0-1 describing how much cmdname matches string.
We return a tuple (cmdname, count, ratio, args, cmdobj).
"""
cmdlen, strlen = len(cmdname), len(string)
@ -41,13 +41,13 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
return (cmdname, args, cmdobj, cmdlen, mratio)
if not raw_string:
return None
return None
matches = []
# match everything that begins with a matching cmdname.
l_raw_string = raw_string.lower()
for cmd in cmdset:
for cmd in cmdset:
try:
matches.extend([create_match(cmdname, raw_string, cmd)
for cmdname in [cmd.key] + cmd.aliases
@ -58,13 +58,13 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
log_trace()
if not matches:
# no matches found.
# no matches found.
if '-' in raw_string:
# This could be due to the user trying to identify the
# command with a #num-<command> style syntax.
mindex, new_raw_string = raw_string.split("-", 1)
if mindex.isdigit():
mindex = int(mindex) - 1
mindex = int(mindex) - 1
# feed result back to parser iteratively
return cmdparser(new_raw_string, cmdset, caller, match_index=mindex)
@ -85,22 +85,22 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
if len(matches) > 1:
# still multiple matches. Fall back to ratio-based quality.
matches = sorted(matches, key=lambda m: m[4])
# only pick the highest rated ratio match
# only pick the highest rated ratio match
quality = [mat[4] for mat in matches]
matches = matches[-quality.count(quality[-1]):]
if len(matches) > 1 and match_index != None and 0 <= match_index < len(matches):
# We couldn't separate match by quality, but we have an index argument to
# tell us which match to use.
matches = [matches[match_index]]
matches = [matches[match_index]]
# no matter what we have at this point, we have to return it.
return matches
return matches
#------------------------------------------------------------
# Search parsers and support methods
# Search parsers and support methods
#------------------------------------------------------------
#
# Default functions for formatting and processing searches.
@ -109,7 +109,7 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
# replace from the settings file by setting the variables
#
# SEARCH_AT_RESULTERROR_HANDLER
# SEARCH_MULTIMATCH_PARSER
# SEARCH_MULTIMATCH_PARSER
#
# The the replacing modules must have the same inputs and outputs as
# those in this module.
@ -118,57 +118,57 @@ def cmdparser(raw_string, cmdset, caller, match_index=None):
def at_search_result(msg_obj, ostring, results, global_search=False):
"""
Called by search methods after a result of any type has been found.
Takes a search result (a list) and
formats eventual errors.
msg_obj - object to receive feedback.
ostring - original search string
msg_obj - object to receive feedback.
ostring - original search string
results - list of found matches (0, 1 or more)
global_search - if this was a global_search or not
(if it is, there might be an idea of supplying
dbrefs instead of only numbers)
Multiple matches are returned to the searching object
as
as
1-object
2-object
3-object
2-object
3-object
etc
"""
string = ""
if not results:
# no results.
if not results:
# no results.
string = "Could not find '%s'." % ostring
results = None
results = None
elif len(results) > 1:
# we have more than one match. We will display a
# list of the form 1-objname, 2-objname etc.
# list of the form 1-objname, 2-objname etc.
# check if the msg_object may se dbrefs
show_dbref = global_search
string += "More than one match for '%s'" % ostring
string += " (please narrow target):"
string += " (please narrow target):"
for num, result in enumerate(results):
invtext = ""
invtext = ""
dbreftext = ""
if hasattr(result, "location") and result.location == msg_obj:
invtext = " (carried)"
invtext = " (carried)"
if show_dbref:
dbreftext = "(#%i)" % result.id
string += "\n %i-%s%s%s" % (num+1, result.name,
dbreftext, invtext)
results = None
dbreftext = "(#%i)" % result.id
string += "\n %i-%s%s%s" % (num+1, result.name,
dbreftext, invtext)
results = None
else:
# we have exactly one match.
results = results[0]
if string:
if string:
msg_obj.msg(string.strip())
return results
return results
def at_multimatch_input(ostring):
"""
@ -186,9 +186,9 @@ def at_multimatch_input(ostring):
the lowest number, rather than 0 as in Python).
This parser version will identify search strings on the following
forms
forms
2-object
2-object
This will be parsed to (2, "object") and, if applicable, will tell
the engine to pick the second from a list of same-named matches of
@ -197,7 +197,7 @@ def at_multimatch_input(ostring):
Ex for use in a game session:
> look
You see: ball, ball, ball and ball.
You see: ball, ball, ball and ball.
> get ball
There where multiple matches for ball:
1-ball
@ -205,7 +205,7 @@ def at_multimatch_input(ostring):
3-ball
4-ball
> get 3-ball
You get the ball.
You get the ball.
"""
@ -213,7 +213,7 @@ def at_multimatch_input(ostring):
return (None, ostring)
if not '-' in ostring:
return (None, ostring)
try:
try:
index = ostring.find('-')
number = int(ostring[:index])-1
return (number, ostring[index+1:])
@ -229,7 +229,7 @@ def at_multimatch_cmd(caller, matches):
Format multiple command matches to a useful error.
"""
string = "There where multiple matches:"
for num, match in enumerate(matches):
for num, match in enumerate(matches):
# each match is a tuple (candidate, cmd)
cmdname, arg, cmd, dum, dum = match
@ -237,7 +237,7 @@ def at_multimatch_cmd(caller, matches):
if is_channel:
is_channel = " (channel)"
else:
is_channel = ""
is_channel = ""
if cmd.is_exit and cmd.destination:
is_exit = " (exit to %s)" % cmd.destination
else:

View file

@ -10,14 +10,14 @@ on-the-fly CmdSet that is some combination of the
previous ones. Their function are borrowed to a large parts from mathematical
Set theory, it should not be much of a problem to understand.
See CmdHandler for practical examples on how to apply cmdsets
See CmdHandler for practical examples on how to apply cmdsets
together to create interesting in-game effects.
"""
import copy
from src.utils.utils import inherits_from, is_iter
from src.utils.utils import inherits_from, is_iter
RECURSIVE_PROTECTION = False
RECURSIVE_PROTECTION = False
class CmdSetMeta(type):
"""
@ -29,14 +29,14 @@ class CmdSetMeta(type):
Fixes some things in the cmdclass
"""
# by default we key the cmdset the same as the
# name of its class.
# name of its class.
if not hasattr(mcs, 'key') or not mcs.key:
mcs.key = mcs.__name__
mcs.path = "%s.%s" % (mcs.__module__, mcs.__name__)
if not type(mcs.key_mergetypes) == dict:
mcs.key_mergetypes = {}
super(CmdSetMeta, mcs).__init__(*args, **kwargs)
@ -46,12 +46,12 @@ def union(cmdset_a, cmdset_b, duplicates=False):
"C = A U B. CmdSet A is assumed to have higher priority"
cmdset_c = cmdset_a.copy_this()
# we make copies, not refs by use of [:]
cmdset_c.commands = cmdset_a.commands[:]
cmdset_c.commands = cmdset_a.commands[:]
if duplicates and cmdset_a.priority == cmdset_b.priority:
cmdset_c.commands.extend(cmdset_b.commands)
else:
cmdset_c.commands.extend([cmd for cmd in cmdset_b if not cmd in cmdset_a])
return cmdset_c
return cmdset_c
def intersect(cmdset_a, cmdset_b, duplicates=False):
"C = A (intersect) B. A is assumed higher priority"
@ -62,7 +62,7 @@ def intersect(cmdset_a, cmdset_b, duplicates=False):
cmdset_c.add(cmdset_b.get(cmd))
else:
cmdset_c.commands = [cmd for cmd in cmdset_a if cmd in cmdset_b]
return cmdset_c
return cmdset_c
def replace(cmdset_a, cmdset_b, cmdset_c):
"C = A + B where the result is A."
@ -80,24 +80,24 @@ def instantiate(cmd):
"""
checks so that object is an instantiated command
and not, say a cmdclass. If it is, instantiate it.
Other types, like strings, are passed through.
"""
Other types, like strings, are passed through.
"""
try:
return cmd()
except TypeError:
return cmd
return cmd
class CmdSet(object):
"""
This class describes a unique cmdset that understands priorities. CmdSets
can be merged and made to perform various set operations on each other.
CmdSets have priorities that affect which of their ingoing commands gets used.
In the examples, cmdset A always have higher priority than cmdset B.
key - the name of the cmdset. This can be used on its own for game operations
In the examples, cmdset A always have higher priority than cmdset B.
mergetype (partly from Set theory):
key - the name of the cmdset. This can be used on its own for game operations
mergetype (partly from Set theory):
Union - The two command sets are merged so that as many
commands as possible of each cmdset ends up in the
@ -125,13 +125,13 @@ class CmdSet(object):
excempt from all merge operations - they are
ALWAYS included across mergers and only affected
if same-named system commands replace them.
priority- All cmdsets are always merged in pairs of two so that
the higher set's mergetype is applied to the
lower-priority cmdset. Default commands have priority 0,
lower-priority cmdset. Default commands have priority 0,
high-priority ones like Exits and Channels have 10 and 9. Priorities
can be negative as well to give default commands preference.
can be negative as well to give default commands preference.
duplicates - determines what happens when two sets of equal
priority merge. Default has the first of them in the
merger (i.e. A above) automatically taking
@ -146,7 +146,7 @@ class CmdSet(object):
select which ball to kick ... Allowing duplicates
only makes sense for Union and Intersect, the setting
is ignored for the other mergetypes.
key_mergetype (dict) - allows the cmdset to define a unique
mergetype for particular cmdsets. Format is
{CmdSetkeystring:mergetype}. Priorities still apply.
@ -155,14 +155,14 @@ class CmdSet(object):
Myevilcmdset no matter what overall mergetype this set
has.
no_objs - don't include any commands from nearby objects
no_objs - don't include any commands from nearby objects
when searching for suitable commands
no_exits - ignore the names of exits when matching against
commands
no_channels - ignore the name of channels when matching against
commands (WARNING- this is dangerous since the
player can then not even ask staff for help if
something goes wrong)
something goes wrong)
"""
@ -176,14 +176,14 @@ class CmdSet(object):
no_exits = False
no_objs = False
no_channels = False
permanent = False
permanent = False
def __init__(self, cmdsetobj=None, key=None):
"""
"""
Creates a new CmdSet instance.
cmdsetobj - this is the database object to which this particular
instance of cmdset is related. It is often a player but may also be a
instance of cmdset is related. It is often a player but may also be a
regular object.
"""
if key:
@ -194,12 +194,12 @@ class CmdSet(object):
# initialize system
self.at_cmdset_creation()
def at_cmdset_creation(self):
def at_cmdset_creation(self):
"""
Hook method - this should be overloaded in the inheriting
class, and should take care of populating the cmdset
by use of self.add().
"""
"""
pass
def add(self, cmd):
@ -207,20 +207,20 @@ class CmdSet(object):
Add a command, a list of commands or a cmdset to this cmdset.
Note that if cmd already exists in set,
it will replace the old one (no priority checking etc
at this point; this is often used to overload
default commands).
it will replace the old one (no priority checking etc
at this point; this is often used to overload
default commands).
If cmd is another cmdset class or -instance, the commands
of that command set is added to this one, as if they were part
of the original cmdset definition. No merging or priority checks
are made, rather later added commands will simply replace
existing ones to make a unique set.
are made, rather later added commands will simply replace
existing ones to make a unique set.
"""
if inherits_from(cmd, "src.commands.cmdset.CmdSet"):
# cmd is a command set so merge all commands in that set
# to this one. We raise a visible error if we created
# to this one. We raise a visible error if we created
# an infinite loop (adding cmdset to itself somehow)
try:
cmd = instantiate(cmd)
@ -234,46 +234,46 @@ class CmdSet(object):
else:
cmds = [instantiate(cmd)]
for cmd in cmds:
# add all commands
# add all commands
if not hasattr(cmd, 'obj'):
cmd.obj = self.cmdsetobj
try:
ic = self.commands.index(cmd)
self.commands[ic] = cmd # replace
self.commands[ic] = cmd # replace
except ValueError:
self.commands.append(cmd)
# extra run to make sure to avoid doublets
# extra run to make sure to avoid doublets
self.commands = list(set(self.commands))
#print "In cmdset.add(cmd):", self.key, cmd
def remove(self, cmd):
"""
Remove a command instance from the cmdset.
cmd can be either a cmd instance or a key string.
cmd can be either a cmd instance or a key string.
"""
cmd = instantiate(cmd)
self.commands = [oldcmd for oldcmd in self.commands if oldcmd != cmd]
def get(self, cmd):
"""
Return the command in this cmdset that matches the
given command. cmd may be either a command instance or
a key string.
a key string.
"""
cmd = instantiate(cmd)
for thiscmd in self.commands:
if thiscmd == cmd:
return thiscmd
return thiscmd
def count(self):
"Return number of commands in set"
return len(self.commands)
def get_system_cmds(self):
"""
Return system commands in the cmdset, defined as
commands starting with double underscore __.
These are excempt from merge operations.
These are excempt from merge operations.
"""
return [cmd for cmd in self.commands if cmd.key.startswith('__')]
@ -293,11 +293,11 @@ class CmdSet(object):
cmdset.duplicates = self.duplicates
cmdset.key_mergetypes = self.key_mergetypes.copy() #copy.deepcopy(self.key_mergetypes)
return cmdset
def make_unique(self, caller):
"""
This is an unsafe command meant to clean out a cmdset of
doublet commands after it has been created. It is useful
doublet commands after it has been created. It is useful
for commands inheriting cmdsets from the cmdhandler where
obj-based cmdsets always are added double. Doublets will
be weeded out with preference to commands defined on caller,
@ -313,14 +313,14 @@ class CmdSet(object):
else:
unique[cmd.key] = cmd
self.commands = unique.values()
def __str__(self):
"""
Show all commands in cmdset when printing it.
Show all commands in cmdset when printing it.
"""
return ", ".join([str(cmd) for cmd in sorted(self.commands, key=lambda o:o.key)])
def __iter__(self):
"""
Allows for things like 'for cmd in cmdset':
@ -331,14 +331,14 @@ class CmdSet(object):
"""
Returns True if this cmdset contains the given command (as defined
by command name and aliases). This allows for things like 'if cmd in cmdset'
"""
"""
return any(cmd == othercmd for cmd in self.commands)
def __add__(self, cmdset_b):
"""
Merge this cmdset (A) with another cmdset (B) using the + operator,
C = A + B
C = A + B
Here, we (by convention) say that 'A is merged onto B to form
C'. The actual merge operation used in the 'addition' depends
@ -356,9 +356,9 @@ class CmdSet(object):
# preserve system __commands
sys_commands = self.get_system_cmds() + cmdset_b.get_system_cmds()
if self.priority >= cmdset_b.priority:
if self.priority >= cmdset_b.priority:
# A higher or equal priority than B
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
mergetype = self.key_mergetypes.get(cmdset_b.key, self.mergetype)
if mergetype == "Intersect":
cmdset_c = intersect(self, cmdset_b, cmdset_b.duplicates)
elif mergetype == "Replace":
@ -369,7 +369,7 @@ class CmdSet(object):
cmdset_c = union(self, cmdset_b, cmdset_b.duplicates)
else:
# B higher priority than A
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
mergetype = cmdset_b.key_mergetypes.get(self.key, cmdset_b.mergetype)
if mergetype == "Intersect":
cmdset_c = intersect(cmdset_b, self, self.duplicates)
elif mergetype == "Replace":
@ -382,9 +382,9 @@ class CmdSet(object):
# we store actual_mergetype since key_mergetypes
# might be different from the main mergetype.
# This is used for diagnosis.
cmdset_c.actual_mergetype = mergetype
cmdset_c.actual_mergetype = mergetype
# return the system commands to the cmdset
cmdset_c.add(sys_commands)
return cmdset_c
return cmdset_c

View file

@ -63,7 +63,7 @@ can then implement separate sets for different situations. For
example, you can have a 'On a boat' set, onto which you then tack on
the 'Fishing' set. Fishing from a boat? No problem!
"""
import traceback
import traceback
from src.utils import logger, utils
from src.commands.cmdset import CmdSet
from src.server.models import ServerConfig
@ -74,21 +74,21 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
"""
This helper function is used by the cmdsethandler to load a cmdset
instance from a python module, given a python_path. It's usually accessed
through the cmdsethandler's add() and add_default() methods.
python_path - This is the full path to the cmdset object.
cmdsetobj - the database object/typeclass on which this cmdset is to be assigned
(this can be also channels and exits, as well as players but there will
through the cmdsethandler's add() and add_default() methods.
python_path - This is the full path to the cmdset object.
cmdsetobj - the database object/typeclass on which this cmdset is to be assigned
(this can be also channels and exits, as well as players but there will
always be such an object)
emit_to_obj - if given, error is emitted to this object (in addition to logging)
no_logging - don't log/send error messages. This can be useful if import_cmdset is just
used to check if this is a valid python path or not.
used to check if this is a valid python path or not.
function returns None if an error was encountered or path not found.
"""
"""
try:
try:
try:
#print "importing %s: CACHED_CMDSETS=%s" % (python_path, CACHED_CMDSETS)
wanted_cache_key = python_path
wanted_cache_key = python_path
cmdsetclass = CACHED_CMDSETS.get(wanted_cache_key, None)
errstring = ""
if not cmdsetclass:
@ -96,11 +96,11 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
# Not in cache. Reload from disk.
modulepath, classname = python_path.rsplit('.', 1)
module = __import__(modulepath, fromlist=[True])
cmdsetclass = module.__dict__[classname]
CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
cmdsetclass = module.__dict__[classname]
CACHED_CMDSETS[wanted_cache_key] = cmdsetclass
#instantiate the cmdset (and catch its errors)
if callable(cmdsetclass):
cmdsetclass = cmdsetclass(cmdsetobj)
cmdsetclass = cmdsetclass(cmdsetobj)
return cmdsetclass
except ImportError:
@ -111,20 +111,20 @@ def import_cmdset(python_path, cmdsetobj, emit_to_obj=None, no_logging=False):
errstring = "Error in loading cmdset: No cmdset class '%s' in %s."
errstring = errstring % (classname, modulepath)
raise
except Exception:
except Exception:
errstring = "\n%s\nCompile/Run error when loading cmdset '%s'."
errstring = errstring % (traceback.format_exc(), python_path)
raise
except Exception:
except Exception:
if errstring and not no_logging:
print errstring
logger.log_trace()
print errstring
logger.log_trace()
if emit_to_obj and not ServerConfig.objects.conf("server_starting_mode"):
object.__getattribute__(emit_to_obj, "msg")(errstring)
object.__getattribute__(emit_to_obj, "msg")(errstring)
logger.log_errmsg("Error: %s" % errstring)
#cannot raise - it kills the server if no base cmdset exists!
# classes
# classes
class CmdSetHandler(object):
"""
@ -134,35 +134,35 @@ class CmdSetHandler(object):
This is the set the game engine will retrieve when determining which
commands are available to the object. The cmdset_stack holds a history of all CmdSets
to allow the handler to remove/add cmdsets at will. Doing so will re-calculate
the 'current' cmdset.
the 'current' cmdset.
"""
def __init__(self, obj):
"""
This method is called whenever an object is recreated.
This method is called whenever an object is recreated.
obj - this is a reference to the game object this handler
belongs to.
"""
self.obj = obj
self.obj = obj
# the id of the "merged" current cmdset for easy access.
# the id of the "merged" current cmdset for easy access.
self.key = None
# this holds the "merged" current command set
# this holds the "merged" current command set
self.current = None
# this holds a history of CommandSets
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
# this tracks which mergetypes are actually in play in the stack
self.mergetype_stack = ["Union"]
self.mergetype_stack = ["Union"]
# the subset of the cmdset_paths that are to be stored in the database
self.permanent_paths = [""]
#self.update(init_mode=True) is then called from the object __init__.
def __str__(self):
"Display current commands"
"Display current commands"
string = ""
mergelist = []
if len(self.cmdset_stack) > 1:
@ -176,18 +176,18 @@ class CmdSetHandler(object):
if cmdset.permanent:
permstring = "perm"
if mergetype != cmdset.mergetype:
mergetype = "%s^" % (mergetype)
mergetype = "%s^" % (mergetype)
string += "\n %i: <%s (%s, prio %i, %s)>: %s" % \
(snum, cmdset.key, mergetype,
cmdset.priority, permstring, cmdset)
cmdset.priority, permstring, cmdset)
mergelist.append(str(snum))
string += "\n"
# Display the currently active cmdset, limited by self.obj's permissions
mergetype = self.mergetype_stack[-1]
mergetype = self.mergetype_stack[-1]
if mergetype != self.current.mergetype:
merged_on = self.cmdset_stack[-2].key
mergetype = "custom %s on cmdset '%s'" % (mergetype, merged_on)
mergetype = "custom %s on cmdset '%s'" % (mergetype, merged_on)
if mergelist:
string += " <Merged %s (%s, prio %i)>: %s" % ("+".join(mergelist), mergetype, self.current.priority, self.current)
else:
@ -196,74 +196,74 @@ class CmdSetHandler(object):
permstring = "perm"
string += " <%s (%s, prio %i, %s)>: %s" % (self.current.key, mergetype, self.current.priority, permstring,
", ".join(cmd.key for cmd in sorted(self.current, key=lambda o:o.key)))
return string.strip()
return string.strip()
def update(self, init_mode=False):
def update(self, init_mode=False):
"""
Re-adds all sets in the handler to have an updated
current set.
init_mode is used right after this handler was
created; it imports all permanent cmdsets from db.
current set.
init_mode is used right after this handler was
created; it imports all permanent cmdsets from db.
"""
if init_mode:
# reimport all permanent cmdsets
storage = self.obj.cmdset_storage
#print "cmdset_storage:", self.obj.cmdset_storage
#print "cmdset_storage:", self.obj.cmdset_storage
if storage:
self.cmdset_stack = []
self.cmdset_stack = []
for pos, path in enumerate(storage):
if pos == 0 and not path:
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
elif path:
cmdset = self.import_cmdset(path)
cmdset = self.import_cmdset(path)
if cmdset:
cmdset.permanent = True
self.cmdset_stack.append(cmdset)
# merge the stack into a new merged cmdset
new_current = None
new_current = None
self.mergetype_stack = []
for cmdset in self.cmdset_stack:
for cmdset in self.cmdset_stack:
try:
# for cmdset's '+' operator, order matters.
new_current = cmdset + new_current
# for cmdset's '+' operator, order matters.
new_current = cmdset + new_current
except TypeError:
continue
self.mergetype_stack.append(new_current.actual_mergetype)
self.current = new_current
def import_cmdset(self, cmdset_path, emit_to_obj=None):
"""
Method wrapper for import_cmdset.
Method wrapper for import_cmdset.
load a cmdset from a module.
cmdset_path - the python path to an cmdset object.
cmdset_path - the python path to an cmdset object.
emit_to_obj - object to send error messages to
"""
if not emit_to_obj:
emit_to_obj = self.obj
return import_cmdset(cmdset_path, self.obj, emit_to_obj)
def add(self, cmdset, emit_to_obj=None, permanent=False):
"""
Add a cmdset to the handler, on top of the old ones.
Default is to not make this permanent, i.e. the set
will not survive a server reset.
Default is to not make this permanent, i.e. the set
will not survive a server reset.
cmdset - can be a cmdset object or the python path to
such an object.
emit_to_obj - an object to receive error messages.
emit_to_obj - an object to receive error messages.
permanent - this cmdset will remain across a server reboot
Note: An interesting feature of this method is if you were to
send it an *already instantiated cmdset* (i.e. not a class),
the current cmdsethandler's obj attribute will then *not* be
transferred over to this already instantiated set (this is
because it might be used elsewhere and can cause strange effects).
because it might be used elsewhere and can cause strange effects).
This means you could in principle have the handler
launch command sets tied to a *different* object than the
handler. Not sure when this would be useful, but it's a 'quirk'
that has to be documented.
that has to be documented.
"""
if callable(cmdset):
if not utils.inherits_from(cmdset, CmdSet):
@ -278,13 +278,13 @@ class CmdSetHandler(object):
cmdset.permanent = True
storage = self.obj.cmdset_storage
if not storage:
storage = ["", cmdset.path]
storage = ["", cmdset.path]
else:
storage.append(cmdset.path)
self.obj.cmdset_storage = storage
else:
cmdset.permanent = False
self.cmdset_stack.append(cmdset)
cmdset.permanent = False
self.cmdset_stack.append(cmdset)
self.update()
def add_default(self, cmdset, emit_to_obj=None, permanent=True):
@ -292,11 +292,11 @@ class CmdSetHandler(object):
Add a new default cmdset. If an old default existed,
it is replaced. If permanent is set, the set will survive a reboot.
cmdset - can be a cmdset object or the python path to
an instance of such an object.
emit_to_obj - an object to receive error messages.
an instance of such an object.
emit_to_obj - an object to receive error messages.
permanent - save cmdset across reboots
See also the notes for self.add(), which applies here too.
"""
"""
if callable(cmdset):
if not utils.inherits_from(cmdset, CmdSet):
raise Exception("Only CmdSets can be added to the cmdsethandler!")
@ -311,9 +311,9 @@ class CmdSetHandler(object):
else:
self.cmdset_stack = [cmdset]
self.mergetype_stack = [cmdset.mergetype]
if permanent:
cmdset.permanent = True
cmdset.permanent = True
storage = self.obj.cmdset_storage
if storage:
storage[0] = cmdset.path
@ -321,12 +321,12 @@ class CmdSetHandler(object):
storage = [cmdset.path]
self.obj.cmdset_storage = storage
else:
cmdset.permanent = False
self.update()
cmdset.permanent = False
self.update()
def delete(self, cmdset=None):
"""
Remove a cmdset from the handler.
Remove a cmdset from the handler.
cmdset can be supplied either as a cmdset-key,
an instance of the CmdSet or a python path
@ -338,17 +338,17 @@ class CmdSetHandler(object):
"""
if len(self.cmdset_stack) < 2:
# don't allow deleting default cmdsets here.
# don't allow deleting default cmdsets here.
return
if not cmdset:
# remove the last one in the stack
# remove the last one in the stack
cmdset = self.cmdset_stack.pop()
if cmdset.permanent:
storage = self.obj.cmdset_storage
storage.pop()
self.obj.cmdset_storage = storage
else:
else:
# try it as a callable
if callable(cmdset) and hasattr(cmdset, 'path'):
delcmdsets = [cset for cset in self.cmdset_stack[1:] if cset.path == cmdset.path]
@ -358,21 +358,21 @@ class CmdSetHandler(object):
storage = []
if any(cset.permanent for cset in delcmdsets):
# only hit database if there's need to
storage = self.obj.cmdset_storage
for cset in delcmdsets:
# only hit database if there's need to
storage = self.obj.cmdset_storage
for cset in delcmdsets:
if cset.permanent:
try:
storage.remove(cset.path)
storage.remove(cset.path)
except ValueError:
pass
for cset in delcmdsets:
# clean the in-memory stack
# clean the in-memory stack
try:
self.cmdset_stack.remove(cset)
except ValueError:
pass
# re-sync the cmdsethandler.
pass
# re-sync the cmdsethandler.
self.update()
def delete_default(self):
@ -385,9 +385,9 @@ class CmdSetHandler(object):
storage[0] = ""
else:
storage = [""]
self.cmdset_storage = storage
self.cmdset_storage = storage
self.cmdset_stack[0] = CmdSet(cmdsetobj=self.obj, key="Empty")
else:
else:
self.cmdset_stack = [CmdSet(cmdsetobj=self.obj, key="Empty")]
self.update()
@ -405,21 +405,21 @@ class CmdSetHandler(object):
self.cmdset_stack = [self.cmdset_stack[0]]
self.mergetype_stack = [self.cmdset_stack[0].mergetype]
storage = self.obj.cmdset_storage
if storage:
if storage:
storage = storage[0]
self.obj.cmdset_storage = storage
self.obj.cmdset_storage = storage
self.update()
def has_cmdset(self, cmdset_key, must_be_default=False):
"""
checks so the cmdsethandler contains a cmdset with the given key.
must_be_default - only match against the default cmdset.
must_be_default - only match against the default cmdset.
"""
if must_be_default:
return self.cmdset_stack and self.cmdset_stack[0].key == cmdset_key
else:
return any([cmdset.key == cmdset_key for cmdset in self.cmdset_stack])
def all(self):
"""
@ -429,16 +429,16 @@ class CmdSetHandler(object):
def reset(self):
"""
Force reload of all cmdsets in handler. This should be called
after CACHED_CMDSETS have been cleared (normally by @reload).
Force reload of all cmdsets in handler. This should be called
after CACHED_CMDSETS have been cleared (normally by @reload).
"""
new_cmdset_stack = []
new_mergetype_stack = []
new_mergetype_stack = []
for cmdset in self.cmdset_stack:
if cmdset.key == "Empty":
new_cmdset_stack.append(cmdset)
new_mergetype_stack.append("Union")
else:
else:
new_cmdset_stack.append(self.import_cmdset(cmdset.path))
new_mergetype_stack.append(cmdset.mergetype)
self.cmdset_stack = new_cmdset_stack

View file

@ -1,7 +1,7 @@
"""
The base Command class.
All commands in Evennia inherit from the 'Command' class in this module.
All commands in Evennia inherit from the 'Command' class in this module.
"""
@ -12,19 +12,19 @@ from src.utils.utils import is_iter
class CommandMeta(type):
"""
This metaclass makes some minor on-the-fly convenience fixes to the command
class in case the admin forgets to put things in lowercase etc.
"""
class in case the admin forgets to put things in lowercase etc.
"""
def __init__(mcs, *args, **kwargs):
"""
Simply make sure all data are stored as lowercase and
do checking on all properties that should be in list form.
Sets up locks to be more forgiving.
Sets up locks to be more forgiving.
"""
mcs.key = mcs.key.lower()
if mcs.aliases and not is_iter(mcs.aliases):
try:
mcs.aliases = mcs.aliases.split(',')
except Exception:
except Exception:
mcs.aliases = []
mcs.aliases = [str(alias).strip() for alias in mcs.aliases]
if not hasattr(mcs, "save_for_next"):
@ -61,19 +61,19 @@ class CommandMeta(type):
# define their own parser method to handle the input. The
# advantage of this is inheritage; commands that have similar
# structure can parse the input string the same way, minimizing
# parsing errors.
# parsing errors.
class Command(object):
"""
Base command
Usage:
command [args]
This is the base command class. Inherit from this
to create new commands.
The cmdhandler makes the following variables available to the
to create new commands.
The cmdhandler makes the following variables available to the
command methods (so you can always assume them to be there):
self.caller - the game object calling the command
self.cmdstring - the command name used to trigger this command (allows
@ -84,10 +84,20 @@ class Command(object):
seldomly, notably for help-type commands, to create dynamic
help entries and lists)
cmd.obj - the object on which this command is defined. If a default command,
this is usually the same as caller.
this is usually the same as caller.
(Note that this initial string is also used by the system to create the help
entry for the command, so it's a good idea to format it similar to this one)
The following class properties can/should be defined on your child class:
key - identifier for command (e.g. "look")
aliases - (optional) list of aliases (e.g. ["l", "loo"])
locks - lock string (default is "cmd:all()")
help_category - how to organize this help entry in help system (default is "General")
auto_help - defaults to True. Allows for turning off auto-help generation
arg_regex - (optional) raw string regex defining how the argument part of the command should look
in order to match for this command (e.g. must it be a space between cmdname and arg?)
(Note that if auto_help is on, this initial string is also used by the system
to create the help entry for the command, so it's a good idea to format it similar to this one)
"""
# Tie our metaclass, for some convenience cleanup
__metaclass__ = CommandMeta
@ -103,20 +113,20 @@ class Command(object):
# this normally does not need to be changed. It allows to turn off
# auto-help entry creation for individual commands.
auto_help = True
# There is also the property 'obj'. This gets set by the system
auto_help = True
# There is also the property 'obj'. This gets set by the system
# on the fly to tie this particular command to a certain in-game entity.
# self.obj should NOT be defined here since it will not be overwritten
# if it already exists.
# self.obj should NOT be defined here since it will not be overwritten
# if it already exists.
def __init__(self):
self.lockhandler = LockHandler(self)
def __str__(self):
"Print the command"
return self.key
def __eq__(self, cmd):
"""
Compare two command instances to each other by matching their
@ -132,7 +142,7 @@ class Command(object):
"""
This implements searches like 'if query in cmd'. It's a fuzzy matching
used by the help system, returning True if query can be found
as a substring of the commands key or its aliases.
as a substring of the commands key or its aliases.
input can be either a command object or a command name.
"""
@ -156,11 +166,11 @@ class Command(object):
This hook is called by the cmdhandler to determine if srcobj
is allowed to execute this command. It should return a boolean
value and is not normally something that need to be changed since
it's using the Evennia permission system directly.
it's using the Evennia permission system directly.
"""
return self.lockhandler.check(srcobj, access_type, default=default)
# Common Command hooks
# Common Command hooks
def at_pre_cmd(self):
"""
@ -170,7 +180,7 @@ class Command(object):
def at_post_cmd(self):
"""
This hook is called after the command has finished executing
This hook is called after the command has finished executing
(after self.func()).
"""
pass
@ -181,31 +191,31 @@ class Command(object):
want, this function is run. If many of your commands have
a similar syntax (for example 'cmd arg1 = arg2') you should simply
define this once and just let other commands of the same form
inherit from this. See the docstring of this module for
which object properties are available to use
inherit from this. See the docstring of this module for
which object properties are available to use
(notably self.args).
"""
"""
pass
def func(self):
"""
This is the actual executing part of the command.
This is the actual executing part of the command.
It is called directly after self.parse(). See the docstring
of this module for which object properties are available
(beyond those set in self.parse())
"""
(beyond those set in self.parse())
"""
# a simple test command to show the available properties
string = "-" * 50
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
string += "\n{w%s{n - Command variables from evennia:\n" % self.key
string += "-" * 50
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
string += "\nname of cmd (self.key): {w%s{n\n" % self.key
string += "cmd aliases (self.aliases): {w%s{n\n" % self.aliases
string += "cmd perms (self.permissions): {w%s{n\n" % self.permissions
string += "help category (self.help_category): {w%s{n\n" % self.help_category
string += "object calling (self.caller): {w%s{n\n" % self.caller
string += "object storing cmdset (self.obj): {w%s{n\n" % self.obj
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
string += "command string given (self.cmdstring): {w%s{n\n" % self.cmdstring
# show cmdset.key instead of cmdset to shorten output
string += utils.fill("current cmdset (self.cmdset): {w%s{n\n" % self.cmdset)
self.caller.msg(string)

View file

@ -1,13 +1,13 @@
#
# This is Evennia's default connection screen. It is imported
# and run from game/gamesrc/world/connection_screens.py.
# This is Evennia's default connection screen. It is imported
# and run from game/gamesrc/world/connection_screens.py.
#
from src.utils import utils
from src.utils import utils
DEFAULT_SCREEN = \
"""{b=============================================================={n
Welcome to {gEvennia{n, version %s!
Welcome to {gEvennia{n, version %s!
If you have an existing account, connect to it by typing:
{wconnect <email> <password>{n